|
| 1 | +"""Backend Tool Rendering feature.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +from datetime import datetime |
| 6 | +import json |
| 7 | +from textwrap import dedent |
| 8 | +from zoneinfo import ZoneInfo |
| 9 | + |
| 10 | +import httpx |
| 11 | +from llama_index.llms.openai import OpenAI |
| 12 | +from llama_index.protocols.ag_ui.router import get_ag_ui_workflow_router |
| 13 | + |
| 14 | + |
| 15 | +def get_weather_condition(code: int) -> str: |
| 16 | + """Map weather code to human-readable condition. |
| 17 | +
|
| 18 | + Args: |
| 19 | + code: WMO weather code. |
| 20 | +
|
| 21 | + Returns: |
| 22 | + Human-readable weather condition string. |
| 23 | + """ |
| 24 | + conditions = { |
| 25 | + 0: "Clear sky", |
| 26 | + 1: "Mainly clear", |
| 27 | + 2: "Partly cloudy", |
| 28 | + 3: "Overcast", |
| 29 | + 45: "Foggy", |
| 30 | + 48: "Depositing rime fog", |
| 31 | + 51: "Light drizzle", |
| 32 | + 53: "Moderate drizzle", |
| 33 | + 55: "Dense drizzle", |
| 34 | + 56: "Light freezing drizzle", |
| 35 | + 57: "Dense freezing drizzle", |
| 36 | + 61: "Slight rain", |
| 37 | + 63: "Moderate rain", |
| 38 | + 65: "Heavy rain", |
| 39 | + 66: "Light freezing rain", |
| 40 | + 67: "Heavy freezing rain", |
| 41 | + 71: "Slight snow fall", |
| 42 | + 73: "Moderate snow fall", |
| 43 | + 75: "Heavy snow fall", |
| 44 | + 77: "Snow grains", |
| 45 | + 80: "Slight rain showers", |
| 46 | + 81: "Moderate rain showers", |
| 47 | + 82: "Violent rain showers", |
| 48 | + 85: "Slight snow showers", |
| 49 | + 86: "Heavy snow showers", |
| 50 | + 95: "Thunderstorm", |
| 51 | + 96: "Thunderstorm with slight hail", |
| 52 | + 99: "Thunderstorm with heavy hail", |
| 53 | + } |
| 54 | + return conditions.get(code, "Unknown") |
| 55 | + |
| 56 | + |
| 57 | +async def get_weather(location: str) -> str: |
| 58 | + """Get current weather for a location. |
| 59 | +
|
| 60 | + Args: |
| 61 | + location: City name. |
| 62 | +
|
| 63 | + Returns: |
| 64 | + Dictionary with weather information including temperature, feels like, |
| 65 | + humidity, wind speed, wind gust, conditions, and location name. |
| 66 | + """ |
| 67 | + async with httpx.AsyncClient() as client: |
| 68 | + # Geocode the location |
| 69 | + geocoding_url = ( |
| 70 | + f"https://geocoding-api.open-meteo.com/v1/search?name={location}&count=1" |
| 71 | + ) |
| 72 | + geocoding_response = await client.get(geocoding_url) |
| 73 | + geocoding_data = geocoding_response.json() |
| 74 | + |
| 75 | + if not geocoding_data.get("results"): |
| 76 | + raise ValueError(f"Location '{location}' not found") |
| 77 | + |
| 78 | + result = geocoding_data["results"][0] |
| 79 | + latitude = result["latitude"] |
| 80 | + longitude = result["longitude"] |
| 81 | + name = result["name"] |
| 82 | + |
| 83 | + # Get weather data |
| 84 | + weather_url = ( |
| 85 | + f"https://api.open-meteo.com/v1/forecast?" |
| 86 | + f"latitude={latitude}&longitude={longitude}" |
| 87 | + f"¤t=temperature_2m,apparent_temperature,relative_humidity_2m," |
| 88 | + f"wind_speed_10m,wind_gusts_10m,weather_code" |
| 89 | + ) |
| 90 | + weather_response = await client.get(weather_url) |
| 91 | + weather_data = weather_response.json() |
| 92 | + |
| 93 | + current = weather_data["current"] |
| 94 | + |
| 95 | + return json.dumps({ |
| 96 | + "temperature": current["temperature_2m"], |
| 97 | + "feelsLike": current["apparent_temperature"], |
| 98 | + "humidity": current["relative_humidity_2m"], |
| 99 | + "windSpeed": current["wind_speed_10m"], |
| 100 | + "windGust": current["wind_gusts_10m"], |
| 101 | + "conditions": get_weather_condition(current["weather_code"]), |
| 102 | + "location": name, |
| 103 | + }) |
| 104 | + |
| 105 | + |
| 106 | +# Create the router with weather tools |
| 107 | +backend_tool_rendering_router = get_ag_ui_workflow_router( |
| 108 | + llm=OpenAI(model="gpt-4o-mini"), |
| 109 | + backend_tools=[get_weather], |
| 110 | + system_prompt=dedent( |
| 111 | + """ |
| 112 | + You are a helpful weather assistant that provides accurate weather information. |
| 113 | +
|
| 114 | + Your primary function is to help users get weather details for specific locations. When responding: |
| 115 | + - Always ask for a location if none is provided |
| 116 | + - If the location name isn’t in English, please translate it |
| 117 | + - If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York") |
| 118 | + - Include relevant details like humidity, wind conditions, and precipitation |
| 119 | + - Keep responses concise but informative |
| 120 | +
|
| 121 | + Use the get_weather tool to fetch current weather data. |
| 122 | + """ |
| 123 | + ), |
| 124 | +) |
0 commit comments