diff options
| author | batsumaru <> | 2026-09-15 15:13:34 +0900 |
|---|---|---|
| committer | batsumaru <> | 2026-09-15 15:13:34 +0900 |
| commit | 1a67c5edb6fa6165e99cc25ed380303ed817af09 (patch) | |
| tree | d2eab77e7275a559bfd639fd80b15321b5e514f3 /personal_agent/tools/weather.py | |
| parent | 5629cabbb38c7d91e66a419ba7cb3a93a9a988a1 (diff) | |
Cleaned up project tree
Diffstat (limited to 'personal_agent/tools/weather.py')
| -rw-r--r-- | personal_agent/tools/weather.py | 122 |
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))}" + ) + ) |
