Skip to content

simplify weather example #2129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 30 additions & 83 deletions examples/pydantic_ai_examples/weather_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@
from __future__ import annotations as _annotations

import asyncio
import os
import urllib.parse
from dataclasses import dataclass
from typing import Any

import logfire
from devtools import debug
from httpx import AsyncClient
from pydantic import BaseModel

from pydantic_ai import Agent, ModelRetry, RunContext
from pydantic_ai import Agent, RunContext

# 'if-token-present' means nothing will be sent (and the example will work) if you don't have logfire configured
logfire.configure(send_to_logfire='if-token-present')
Expand All @@ -31,51 +29,38 @@
@dataclass
class Deps:
client: AsyncClient
weather_api_key: str | None
geo_api_key: str | None


weather_agent = Agent(
'openai:gpt-4o',
'openai:gpt-4.1-mini',
# 'Be concise, reply with one sentence.' is enough for some models (like openai) to use
# the below tools appropriately, but others like anthropic and gemini require a bit more direction.
instructions=(
'Be concise, reply with one sentence.'
'Use the `get_lat_lng` tool to get the latitude and longitude of the locations, '
'then use the `get_weather` tool to get the weather.'
),
instructions='Be concise, reply with one sentence.',
deps_type=Deps,
retries=2,
)


class LatLng(BaseModel):
lat: float
lng: float


@weather_agent.tool
async def get_lat_lng(
ctx: RunContext[Deps], location_description: str
) -> dict[str, float]:
async def get_lat_lng(ctx: RunContext[Deps], location_description: str) -> LatLng:
"""Get the latitude and longitude of a location.

Args:
ctx: The context.
location_description: A description of a location.
"""
if ctx.deps.geo_api_key is None:
# if no API key is provided, return a dummy response (London)
return {'lat': 51.1, 'lng': -0.1}

params = {'access_token': ctx.deps.geo_api_key}
loc = urllib.parse.quote(location_description)
# NOTE: the response here will be random, and is not related to the location description.
r = await ctx.deps.client.get(
f'https://api.mapbox.com/geocoding/v5/mapbox.places/{loc}.json', params=params
'https://demo-endpoints.pydantic.workers.dev/latlng',
params={'location': location_description},
)
r.raise_for_status()
data = r.json()

if features := data['features']:
lat, lng = features[0]['center']
return {'lat': lat, 'lng': lng}
else:
raise ModelRetry('Could not find the location')
return LatLng.model_validate_json(r.content)


@weather_agent.tool
Expand All @@ -87,70 +72,32 @@ async def get_weather(ctx: RunContext[Deps], lat: float, lng: float) -> dict[str
lat: Latitude of the location.
lng: Longitude of the location.
"""
if ctx.deps.weather_api_key is None:
# if no API key is provided, return a dummy response
return {'temperature': '21 °C', 'description': 'Sunny'}

params = {
'apikey': ctx.deps.weather_api_key,
'location': f'{lat},{lng}',
'units': 'metric',
}
with logfire.span('calling weather API', params=params) as span:
r = await ctx.deps.client.get(
'https://api.tomorrow.io/v4/weather/realtime', params=params
)
r.raise_for_status()
data = r.json()
span.set_attribute('response', data)

values = data['data']['values']
# https://docs.tomorrow.io/reference/data-layers-weather-codes
code_lookup = {
1000: 'Clear, Sunny',
1100: 'Mostly Clear',
1101: 'Partly Cloudy',
1102: 'Mostly Cloudy',
1001: 'Cloudy',
2000: 'Fog',
2100: 'Light Fog',
4000: 'Drizzle',
4001: 'Rain',
4200: 'Light Rain',
4201: 'Heavy Rain',
5000: 'Snow',
5001: 'Flurries',
5100: 'Light Snow',
5101: 'Heavy Snow',
6000: 'Freezing Drizzle',
6001: 'Freezing Rain',
6200: 'Light Freezing Rain',
6201: 'Heavy Freezing Rain',
7000: 'Ice Pellets',
7101: 'Heavy Ice Pellets',
7102: 'Light Ice Pellets',
8000: 'Thunderstorm',
}
# NOTE: the responses here will be random, and are not related to the lat and lng.
temp_response, descr_response = await asyncio.gather(
ctx.deps.client.get(
'https://demo-endpoints.pydantic.workers.dev/number',
params={'min': 10, 'max': 30},
),
ctx.deps.client.get(
'https://demo-endpoints.pydantic.workers.dev/weather',
params={'lat': lat, 'lng': lng},
),
)
temp_response.raise_for_status()
descr_response.raise_for_status()
return {
'temperature': f'{values["temperatureApparent"]:0.0f}°C',
'description': code_lookup.get(values['weatherCode'], 'Unknown'),
'temperature': f'{temp_response.text} °C',
'description': descr_response.text,
}


async def main():
async with AsyncClient() as client:
logfire.instrument_httpx(client, capture_all=True)
# create a free API key at https://www.tomorrow.io/weather-api/
weather_api_key = os.getenv('WEATHER_API_KEY')
# create a free API key at https://www.mapbox.com/
geo_api_key = os.getenv('GEO_API_KEY')
deps = Deps(
client=client, weather_api_key=weather_api_key, geo_api_key=geo_api_key
)
deps = Deps(client=client)
result = await weather_agent.run(
'What is the weather like in London and in Wiltshire?', deps=deps
)
debug(result)
print('Response:', result.output)


Expand Down
6 changes: 1 addition & 5 deletions examples/pydantic_ai_examples/weather_agent_gradio.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations as _annotations

import json
import os

from httpx import AsyncClient

Expand All @@ -18,10 +17,7 @@
TOOL_TO_DISPLAY_NAME = {'get_lat_lng': 'Geocoding API', 'get_weather': 'Weather API'}

client = AsyncClient()
weather_api_key = os.getenv('WEATHER_API_KEY')
# create a free API key at https://geocode.maps.co/
geo_api_key = os.getenv('GEO_API_KEY')
deps = Deps(client=client, weather_api_key=weather_api_key, geo_api_key=geo_api_key)
deps = Deps(client=client)


async def stream_from_agent(prompt: str, chatbot: list[dict], past_messages: list):
Expand Down