summaryrefslogtreecommitdiff
path: root/personal_agent/tools/weather.py
blob: 7bb54ecd03d5cd999400d2a76fc8dafb324a15c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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))}"
            )
        )