summaryrefslogtreecommitdiff
path: root/personal_agent/tools/weather.py
diff options
context:
space:
mode:
authorbatsumaru <>2026-09-15 15:13:34 +0900
committerbatsumaru <>2026-09-15 15:13:34 +0900
commit1a67c5edb6fa6165e99cc25ed380303ed817af09 (patch)
treed2eab77e7275a559bfd639fd80b15321b5e514f3 /personal_agent/tools/weather.py
parent5629cabbb38c7d91e66a419ba7cb3a93a9a988a1 (diff)
Cleaned up project tree
Diffstat (limited to 'personal_agent/tools/weather.py')
-rw-r--r--personal_agent/tools/weather.py122
1 files changed, 122 insertions, 0 deletions
diff --git a/personal_agent/tools/weather.py b/personal_agent/tools/weather.py
new file mode 100644
index 0000000..7bb54ec
--- /dev/null
+++ b/personal_agent/tools/weather.py
@@ -0,0 +1,122 @@
+from datetime import UTC, datetime, timedelta, timezone
+
+import requests
+
+from personal_agent.tools.base import Tool, ToolError, ToolResult
+
+
+class WeatherTool(Tool):
+ @property
+ def name(self) -> str:
+ return "weather"
+
+ @property
+ def description(self) -> str:
+ return "Get current weather conditions for a location"
+
+ @property
+ def schema(self) -> dict[str, object]:
+ return {
+ "type": "object",
+ "properties": {
+ "location": {
+ "type": "string",
+ "description": ("The city or location to get weather for."),
+ },
+ },
+ "required": ["location"],
+ }
+
+ def execute(self, arguments: dict[str, object]) -> ToolResult:
+ location = arguments.get("location")
+
+ if not isinstance(location, str):
+ raise ToolError("location must be a string")
+
+ geocoding_response = requests.get(
+ "https://geocoding-api.open-meteo.com/v1/search",
+ params={
+ "name": location,
+ "count": 1,
+ "language": "en",
+ "format": "json",
+ },
+ timeout=10,
+ )
+ geocoding_response.raise_for_status()
+
+ geocoding_data = geocoding_response.json()
+ results = geocoding_data.get("results")
+
+ if not results:
+ raise ToolError(f"location not found: {location}")
+
+ place = results[0]
+
+ latitude = place["latitude"]
+ longitude = place["longitude"]
+ timezone_name = place["timezone"]
+
+ forecast_response = requests.get(
+ "https://api.open-meteo.com/v1/forecast",
+ params={
+ "latitude": latitude,
+ "longitude": longitude,
+ "hourly": (
+ "temperature_2m,"
+ "precipitation_probability,"
+ "precipitation,"
+ "weather_code,"
+ "wind_speed_10m"
+ ),
+ "timezone": timezone_name,
+ "forecast_days": 2,
+ },
+ timeout=10,
+ )
+ forecast_response.raise_for_status()
+
+ forecast = forecast_response.json()
+ hourly = forecast["hourly"]
+
+ tomorrow = (
+ (datetime.now(UTC) + timedelta(days=1))
+ .astimezone(timezone(timedelta(seconds=forecast["utc_offset_seconds"])))
+ .date()
+ )
+
+ temperatures = []
+ precipitation_probabilities = []
+ precipitation = []
+ wind_speeds = []
+ weather_codes = []
+
+ for index, timestamp in enumerate(hourly["time"]):
+ forecast_date = datetime.fromisoformat(timestamp).date()
+
+ if forecast_date != tomorrow:
+ continue
+
+ temperatures.append(hourly["temperature_2m"][index])
+ precipitation_probabilities.append(
+ hourly["precipitation_probability"][index]
+ )
+ precipitation.append(hourly["precipitation"][index])
+ wind_speeds.append(hourly["wind_speed_10m"][index])
+ weather_codes.append(hourly["weather_code"][index])
+
+ if not temperatures:
+ raise ToolError("no forecast data available for tomorrow")
+ return ToolResult(
+ content=(
+ f"Location: {place['name']}, {place['country']}\n"
+ f"Forecast date: {tomorrow}\n"
+ f"Temperature: {min(temperatures):.1f}–"
+ f"{max(temperatures):.1f} °C\n"
+ f"Maximum precipitation probability: "
+ f"{max(precipitation_probabilities)}%\n"
+ f"Total precipitation: {sum(precipitation):.1f} mm\n"
+ f"Maximum wind speed: {max(wind_speeds):.1f} km/h\n"
+ f"Weather codes: {sorted(set(weather_codes))}"
+ )
+ )