from typing import Any, Optional from smolagents.tools import Tool import datetime import random import os import requests class GetWeatherForecastTool(Tool): name = "get_weather_forecast" description = "Gets the weather forecast for a travel destination." inputs = { 'destination': {'type': 'string', 'description': 'City or location name'}, 'days': {'type': 'integer', 'description': 'Number of days to forecast (default: 3)', 'nullable': True} } output_type = "string" def __init__(self, api_key=None): super().__init__() # You can set an API key for a real weather service like OpenWeatherMap self.api_key = api_key or os.environ.get("WEATHER_API_KEY") # Weather conditions for demo/fallback self.weather_conditions = [ "Sunny", "Partly Cloudy", "Cloudy", "Light Rain", "Heavy Rain", "Thunderstorms", "Windy", "Foggy", "Snow", "Clear" ] def forward(self, destination: str, days: int = 3) -> str: try: # Try to use a real weather API if the API key is available if self.api_key: try: url = f"http://api.openweathermap.org/data/2.5/forecast?q={destination}&appid={self.api_key}&units=metric" response = requests.get(url) data = response.json() if response.status_code != 200: # Fall back to demo method if API call fails return self._generate_demo_forecast(destination, days) # Process and format forecast data forecast_text = f"🌦️ Weather forecast for {destination}:\n\n" # Group forecasts by day forecasts_by_day = {} for item in data['list'][:days * 8]: # API returns data in 3-hour intervals date = item['dt_txt'].split(' ')[0] if date not in forecasts_by_day: forecasts_by_day[date] = [] forecasts_by_day[date].append(item) # Format each day's forecast for date, items in list(forecasts_by_day.items())[:days]: day_name = datetime.datetime.strptime(date, "%Y-%m-%d").strftime("%A") temps = [item['main']['temp'] for item in items] avg_temp = sum(temps) / len(temps) conditions = [item['weather'][0]['main'] for item in items] most_common = max(set(conditions), key=conditions.count) precipitation = any('Rain' in item['weather'][0]['main'] or 'Snow' in item['weather'][0]['main'] for item in items) precipitation_chance = "60%" if precipitation else "0%" forecast_text += f"• {day_name}, {date}: {most_common}, {min(temps):.1f}°C to {max(temps):.1f}°C" if precipitation: forecast_text += f", {precipitation_chance} chance of precipitation" forecast_text += "\n" # Add packing recommendations forecast_text += self._generate_packing_tips(forecasts_by_day) return forecast_text except Exception: # Fall back to demo method if any error occurs return self._generate_demo_forecast(destination, days) # If no API key is available, use the demo method return self._generate_demo_forecast(destination, days) except Exception as e: return f"Error retrieving weather data for {destination}: {str(e)}" def _generate_demo_forecast(self, destination: str, days: int) -> str: # Create a deterministic but seemingly random forecast based on destination name seed = sum(ord(c) for c in destination) random.seed(seed) # Generate forecast data forecast_text = f"🌦️ Weather forecast for {destination}:\n\n" today = datetime.datetime.now() temp_base = random.randint(10, 25) # Base temperature varies by destination for i in range(days): day = today + datetime.timedelta(days=i) day_name = day.strftime("%A") date = day.strftime("%b %d") # "Random" but deterministic weather for the demo condition = self.weather_conditions[random.randint(0, len(self.weather_conditions)-1)] temp_high = temp_base + random.randint(0, 10) # Celsius temp_low = temp_high - random.randint(5, 15) precipitation = random.randint(0, 100) if "Rain" in condition or "Snow" in condition or "Thunder" in condition else 0 forecast_text += f"• {day_name}, {date}: {condition}, {temp_low}°C to {temp_high}°C" if precipitation > 0: forecast_text += f", {precipitation}% chance of precipitation" forecast_text += "\n" # Add packing recommendations cold_days = sum(1 for i in range(days) if temp_base + 5 < 15) rainy_days = sum(1 for i in range(days) if "Rain" in self.weather_conditions[random.randint(0, len(self.weather_conditions)-1)]) hot_days = sum(1 for i in range(days) if temp_base + 5 > 25) forecast_text += "\n🧳 Packing tips: " if cold_days > days/2: forecast_text += "Bring warm layers and a jacket. " elif cold_days > 0: forecast_text += "Pack a light jacket for cooler periods. " if rainy_days > 0: forecast_text += "Don't forget an umbrella or rain gear. " if hot_days > days/2: forecast_text += "Bring sunscreen, sunglasses, and light clothing. " return forecast_text def _generate_packing_tips(self, forecasts_by_day): # Extract temperature ranges across all days all_temps = [] has_rain = False has_snow = False for day_items in forecasts_by_day.values(): for item in day_items: all_temps.append(item['main']['temp']) if 'Rain' in item['weather'][0]['main']: has_rain = True if 'Snow' in item['weather'][0]['main']: has_snow = True min_temp = min(all_temps) if all_temps else 0 max_temp = max(all_temps) if all_temps else 30 # Generate packing tips based on conditions tips = "\n🧳 Packing tips: " if min_temp < 5: tips += "Bring a heavy winter coat, gloves, and hat. " elif min_temp < 15: tips += "Pack a warm jacket and layers. " elif min_temp < 20: tips += "Bring a light jacket for evenings. " if max_temp > 25: tips += "Pack light, breathable clothing for warm days. " if has_rain: tips += "Don't forget an umbrella and waterproof footwear. " if has_snow: tips += "Bring waterproof boots and warm socks. " if max_temp > 22: tips += "Sunscreen and sunglasses are recommended. " return tips