PLBot commited on
Commit
ba960dd
·
verified ·
1 Parent(s): e6b472b

Upload 4 files

Browse files
tools/currency_converter_tool.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional
2
+ from smolagents.tools import Tool
3
+ import os
4
+ import requests
5
+
6
+ class ConvertCurrencyTool(Tool):
7
+ name = "convert_currency"
8
+ description = "Converts an amount between currencies for travel budgeting."
9
+ inputs = {
10
+ 'amount': {'type': 'number', 'description': 'The amount to convert'},
11
+ 'from_currency': {'type': 'string', 'description': 'Source currency code (e.g., USD, EUR)'},
12
+ 'to_currency': {'type': 'string', 'description': 'Target currency code (e.g., JPY, GBP)'}
13
+ }
14
+ output_type = "string"
15
+
16
+ def __init__(self, api_key=None):
17
+ super().__init__()
18
+ # You can set an API key for a real currency API
19
+ self.api_key = api_key or os.environ.get("EXCHANGE_RATE_API_KEY")
20
+
21
+ # Common exchange rates (as of early 2025, for demo/fallback purposes)
22
+ self.exchange_rates = {
23
+ "USD": {"EUR": 0.92, "GBP": 0.79, "JPY": 149.50, "CAD": 1.35, "AUD": 1.52, "CNY": 7.20, "INR": 83.20, "MXN": 17.05},
24
+ "EUR": {"USD": 1.09, "GBP": 0.86, "JPY": 163.00, "CAD": 1.47, "AUD": 1.66, "CNY": 7.85, "INR": 90.70, "MXN": 18.60},
25
+ "GBP": {"USD": 1.27, "EUR": 1.16, "JPY": 189.30, "CAD": 1.71, "AUD": 1.92, "CNY": 9.10, "INR": 105.30, "MXN": 21.60},
26
+ "JPY": {"USD": 0.0067, "EUR": 0.0061, "GBP": 0.0053, "CAD": 0.0090, "AUD": 0.0102, "CNY": 0.0482, "INR": 0.5565, "MXN": 0.1141},
27
+ "CAD": {"USD": 0.74, "EUR": 0.68, "GBP": 0.58, "JPY": 110.70, "AUD": 1.13, "CNY": 5.33, "INR": 61.60, "MXN": 12.60},
28
+ "AUD": {"USD": 0.66, "EUR": 0.60, "GBP": 0.52, "JPY": 98.40, "CAD": 0.89, "CNY": 4.73, "INR": 54.70, "MXN": 11.20},
29
+ "CNY": {"USD": 0.14, "EUR": 0.13, "GBP": 0.11, "JPY": 20.80, "CAD": 0.19, "AUD": 0.21, "INR": 11.60, "MXN": 2.37},
30
+ "INR": {"USD": 0.012, "EUR": 0.011, "GBP": 0.0095, "JPY": 1.80, "CAD": 0.016, "AUD": 0.018, "CNY": 0.086, "MXN": 0.205},
31
+ "MXN": {"USD": 0.059, "EUR": 0.054, "GBP": 0.046, "JPY": 8.77, "CAD": 0.079, "AUD": 0.089, "CNY": 0.422, "INR": 4.88}
32
+ }
33
+
34
+ def forward(self, amount: float, from_currency: str, to_currency: str) -> str:
35
+ try:
36
+ # Normalize currency codes
37
+ from_currency = from_currency.upper().strip()
38
+ to_currency = to_currency.upper().strip()
39
+
40
+ # Try to use a real currency API if the API key is available
41
+ if self.api_key:
42
+ try:
43
+ url = f"https://v6.exchangerate-api.com/v6/{self.api_key}/pair/{from_currency}/{to_currency}/{amount}"
44
+ response = requests.get(url)
45
+ data = response.json()
46
+
47
+ if data.get('result') == 'success':
48
+ converted_amount = data.get('conversion_result')
49
+ rate = data.get('conversion_rate')
50
+
51
+ return f"💱 {amount:,.2f} {from_currency} = {converted_amount:,.2f} {to_currency}\n\nExchange rate: 1 {from_currency} = {rate} {to_currency}\n\n(Data from ExchangeRate-API)"
52
+ else:
53
+ # Fall back to stored rates if API call fails
54
+ return self._convert_with_stored_rates(amount, from_currency, to_currency)
55
+
56
+ except Exception:
57
+ # Fall back to stored rates if any error occurs
58
+ return self._convert_with_stored_rates(amount, from_currency, to_currency)
59
+
60
+ # If no API key is available, use the stored rates
61
+ return self._convert_with_stored_rates(amount, from_currency, to_currency)
62
+
63
+ except Exception as e:
64
+ return f"Error converting currency: {str(e)}"
65
+
66
+ def _convert_with_stored_rates(self, amount: float, from_currency: str, to_currency: str) -> str:
67
+ # Validate currencies
68
+ if from_currency not in self.exchange_rates:
69
+ return f"Sorry, I don't have exchange rate data for {from_currency}."
70
+
71
+ # If same currency, return original amount
72
+ if from_currency == to_currency:
73
+ return f"{amount} {from_currency} = {amount} {to_currency}"
74
+
75
+ # Direct conversion
76
+ if to_currency in self.exchange_rates[from_currency]:
77
+ rate = self.exchange_rates[from_currency][to_currency]
78
+ converted_amount = amount * rate
79
+ return f"💱 {amount:,.2f} {from_currency} = {converted_amount:,.2f} {to_currency}\n\nExchange rate: 1 {from_currency} = {rate} {to_currency}\n\n(Note: Rates are approximations for planning purposes only)"
80
+
81
+ # Try conversion through USD
82
+ if to_currency in self.exchange_rates and "USD" in self.exchange_rates[from_currency]:
83
+ usd_amount = amount * self.exchange_rates[from_currency]["USD"]
84
+ rate_to_target = self.exchange_rates["USD"].get(to_currency)
85
+
86
+ if rate_to_target:
87
+ converted_amount = usd_amount * rate_to_target
88
+ effective_rate = converted_amount / amount
89
+ return f"💱 {amount:,.2f} {from_currency} = {converted_amount:,.2f} {to_currency}\n\nExchange rate: 1 {from_currency} = {effective_rate:.4f} {to_currency}\n\n(Note: Rates are approximations for planning purposes only)"
90
+
91
+ return f"Sorry, I don't have exchange rate data from {from_currency} to {to_currency}."
tools/local_time_tool.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional
2
+ from smolagents.tools import Tool
3
+ import datetime
4
+ import pytz
5
+
6
+ class GetLocalTimeTool(Tool):
7
+ name = "get_local_time"
8
+ description = "Gets the current local time at a travel destination."
9
+ inputs = {'destination': {'type': 'string', 'description': 'A city or location name (e.g., "Paris", "Tokyo", "New York").'}}
10
+ output_type = "string"
11
+
12
+ def __init__(self):
13
+ super().__init__()
14
+ try:
15
+ import pytz
16
+ except ImportError as e:
17
+ raise ImportError(
18
+ "You must install package `pytz` to run this tool: for instance run `pip install pytz`."
19
+ ) from e
20
+
21
+ # Map of common tourist destinations to their timezones
22
+ self.destination_timezones = {
23
+ "london": "Europe/London",
24
+ "paris": "Europe/Paris",
25
+ "rome": "Europe/Rome",
26
+ "madrid": "Europe/Madrid",
27
+ "berlin": "Europe/Berlin",
28
+ "amsterdam": "Europe/Amsterdam",
29
+ "athens": "Europe/Athens",
30
+ "istanbul": "Europe/Istanbul",
31
+ "dubai": "Asia/Dubai",
32
+ "new delhi": "Asia/Kolkata",
33
+ "mumbai": "Asia/Kolkata",
34
+ "bangkok": "Asia/Bangkok",
35
+ "singapore": "Asia/Singapore",
36
+ "tokyo": "Asia/Tokyo",
37
+ "seoul": "Asia/Seoul",
38
+ "beijing": "Asia/Shanghai",
39
+ "shanghai": "Asia/Shanghai",
40
+ "hong kong": "Asia/Hong_Kong",
41
+ "sydney": "Australia/Sydney",
42
+ "melbourne": "Australia/Melbourne",
43
+ "auckland": "Pacific/Auckland",
44
+ "fiji": "Pacific/Fiji",
45
+ "honolulu": "Pacific/Honolulu",
46
+ "anchorage": "America/Anchorage",
47
+ "los angeles": "America/Los_Angeles",
48
+ "san francisco": "America/Los_Angeles",
49
+ "las vegas": "America/Los_Angeles",
50
+ "denver": "America/Denver",
51
+ "chicago": "America/Chicago",
52
+ "houston": "America/Chicago",
53
+ "new york": "America/New_York",
54
+ "miami": "America/New_York",
55
+ "toronto": "America/Toronto",
56
+ "mexico city": "America/Mexico_City",
57
+ "rio de janeiro": "America/Sao_Paulo",
58
+ "sao paulo": "America/Sao_Paulo",
59
+ "buenos aires": "America/Argentina/Buenos_Aires",
60
+ "cairo": "Africa/Cairo",
61
+ "cape town": "Africa/Johannesburg",
62
+ "johannesburg": "Africa/Johannesburg",
63
+ "nairobi": "Africa/Nairobi"
64
+ }
65
+
66
+ def forward(self, destination: str) -> str:
67
+ try:
68
+ # Normalize the destination name
69
+ normalized_dest = destination.lower().strip()
70
+
71
+ # Find the closest matching timezone
72
+ timezone = None
73
+ for city, tz in self.destination_timezones.items():
74
+ if city in normalized_dest or normalized_dest in city:
75
+ timezone = tz
76
+ break
77
+
78
+ if not timezone:
79
+ # If we don't have a direct match, try to find it through pytz
80
+ try:
81
+ # Try web search for timezone if available
82
+ import importlib
83
+ if importlib.util.find_spec("duckduckgo_search"):
84
+ from duckduckgo_search import DDGS
85
+ ddgs = DDGS()
86
+ results = ddgs.text(f"{destination} timezone")
87
+ if results:
88
+ # Simple heuristic to extract timezone from search results
89
+ for result in results:
90
+ body = result.get('body', '').lower()
91
+ if 'utc' in body or 'gmt' in body:
92
+ timezone_pos = body.find('utc') if 'utc' in body else body.find('gmt')
93
+ timezone_info = body[timezone_pos:timezone_pos+8]
94
+ return f"Based on web search, the timezone in {destination} appears to be around {timezone_info.upper()}. Current time information is not available."
95
+ except:
96
+ pass
97
+
98
+ return f"I don't have timezone information for {destination}. Please try a major city nearby."
99
+
100
+ # Get current time in that timezone
101
+ tz = pytz.timezone(timezone)
102
+ local_time = datetime.datetime.now(tz)
103
+
104
+ # Format the result
105
+ formatted_time = local_time.strftime("%I:%M %p on %A, %B %d, %Y")
106
+ time_diff = local_time.utcoffset().total_seconds() / 3600
107
+ sign = "+" if time_diff >= 0 else ""
108
+
109
+ return f"The current local time in {destination} is {formatted_time} (UTC{sign}{int(time_diff)})"
110
+
111
+ except Exception as e:
112
+ return f"Error getting local time for {destination}: {str(e)}"
tools/weather_forecast_tool.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional
2
+ from smolagents.tools import Tool
3
+ import datetime
4
+ import random
5
+ import os
6
+ import requests
7
+
8
+ class GetWeatherForecastTool(Tool):
9
+ name = "get_weather_forecast"
10
+ description = "Gets the weather forecast for a travel destination."
11
+ inputs = {
12
+ 'destination': {'type': 'string', 'description': 'City or location name'},
13
+ 'days': {'type': 'integer', 'description': 'Number of days to forecast (default: 3)'}
14
+ }
15
+ output_type = "string"
16
+
17
+ def __init__(self, api_key=None):
18
+ super().__init__()
19
+ # You can set an API key for a real weather service like OpenWeatherMap
20
+ self.api_key = api_key or os.environ.get("WEATHER_API_KEY")
21
+
22
+ # Weather conditions for demo/fallback
23
+ self.weather_conditions = [
24
+ "Sunny", "Partly Cloudy", "Cloudy", "Light Rain",
25
+ "Heavy Rain", "Thunderstorms", "Windy", "Foggy", "Snow", "Clear"
26
+ ]
27
+
28
+ def forward(self, destination: str, days: int = 3) -> str:
29
+ try:
30
+ # Try to use a real weather API if the API key is available
31
+ if self.api_key:
32
+ try:
33
+ url = f"http://api.openweathermap.org/data/2.5/forecast?q={destination}&appid={self.api_key}&units=metric"
34
+ response = requests.get(url)
35
+ data = response.json()
36
+
37
+ if response.status_code != 200:
38
+ # Fall back to demo method if API call fails
39
+ return self._generate_demo_forecast(destination, days)
40
+
41
+ # Process and format forecast data
42
+ forecast_text = f"🌦️ Weather forecast for {destination}:\n\n"
43
+
44
+ # Group forecasts by day
45
+ forecasts_by_day = {}
46
+ for item in data['list'][:days * 8]: # API returns data in 3-hour intervals
47
+ date = item['dt_txt'].split(' ')[0]
48
+ if date not in forecasts_by_day:
49
+ forecasts_by_day[date] = []
50
+ forecasts_by_day[date].append(item)
51
+
52
+ # Format each day's forecast
53
+ for date, items in list(forecasts_by_day.items())[:days]:
54
+ day_name = datetime.datetime.strptime(date, "%Y-%m-%d").strftime("%A")
55
+ temps = [item['main']['temp'] for item in items]
56
+ avg_temp = sum(temps) / len(temps)
57
+ conditions = [item['weather'][0]['main'] for item in items]
58
+ most_common = max(set(conditions), key=conditions.count)
59
+
60
+ precipitation = any('Rain' in item['weather'][0]['main'] or 'Snow' in item['weather'][0]['main'] for item in items)
61
+ precipitation_chance = "60%" if precipitation else "0%"
62
+
63
+ forecast_text += f"• {day_name}, {date}: {most_common}, {min(temps):.1f}°C to {max(temps):.1f}°C"
64
+ if precipitation:
65
+ forecast_text += f", {precipitation_chance} chance of precipitation"
66
+ forecast_text += "\n"
67
+
68
+ # Add packing recommendations
69
+ forecast_text += self._generate_packing_tips(forecasts_by_day)
70
+
71
+ return forecast_text
72
+
73
+ except Exception:
74
+ # Fall back to demo method if any error occurs
75
+ return self._generate_demo_forecast(destination, days)
76
+
77
+ # If no API key is available, use the demo method
78
+ return self._generate_demo_forecast(destination, days)
79
+
80
+ except Exception as e:
81
+ return f"Error retrieving weather data for {destination}: {str(e)}"
82
+
83
+ def _generate_demo_forecast(self, destination: str, days: int) -> str:
84
+ # Create a deterministic but seemingly random forecast based on destination name
85
+ seed = sum(ord(c) for c in destination)
86
+ random.seed(seed)
87
+
88
+ # Generate forecast data
89
+ forecast_text = f"🌦️ Weather forecast for {destination}:\n\n"
90
+
91
+ today = datetime.datetime.now()
92
+ temp_base = random.randint(10, 25) # Base temperature varies by destination
93
+
94
+ for i in range(days):
95
+ day = today + datetime.timedelta(days=i)
96
+ day_name = day.strftime("%A")
97
+ date = day.strftime("%b %d")
98
+
99
+ # "Random" but deterministic weather for the demo
100
+ condition = self.weather_conditions[random.randint(0, len(self.weather_conditions)-1)]
101
+ temp_high = temp_base + random.randint(0, 10) # Celsius
102
+ temp_low = temp_high - random.randint(5, 15)
103
+ precipitation = random.randint(0, 100) if "Rain" in condition or "Snow" in condition or "Thunder" in condition else 0
104
+
105
+ forecast_text += f"• {day_name}, {date}: {condition}, {temp_low}°C to {temp_high}°C"
106
+ if precipitation > 0:
107
+ forecast_text += f", {precipitation}% chance of precipitation"
108
+ forecast_text += "\n"
109
+
110
+ # Add packing recommendations
111
+ cold_days = sum(1 for i in range(days) if temp_base + 5 < 15)
112
+ rainy_days = sum(1 for i in range(days) if "Rain" in self.weather_conditions[random.randint(0, len(self.weather_conditions)-1)])
113
+ hot_days = sum(1 for i in range(days) if temp_base + 5 > 25)
114
+
115
+ forecast_text += "\n🧳 Packing tips: "
116
+ if cold_days > days/2:
117
+ forecast_text += "Bring warm layers and a jacket. "
118
+ elif cold_days > 0:
119
+ forecast_text += "Pack a light jacket for cooler periods. "
120
+
121
+ if rainy_days > 0:
122
+ forecast_text += "Don't forget an umbrella or rain gear. "
123
+
124
+ if hot_days > days/2:
125
+ forecast_text += "Bring sunscreen, sunglasses, and light clothing. "
126
+
127
+ return forecast_text
128
+
129
+ def _generate_packing_tips(self, forecasts_by_day):
130
+ # Extract temperature ranges across all days
131
+ all_temps = []
132
+ has_rain = False
133
+ has_snow = False
134
+
135
+ for day_items in forecasts_by_day.values():
136
+ for item in day_items:
137
+ all_temps.append(item['main']['temp'])
138
+ if 'Rain' in item['weather'][0]['main']:
139
+ has_rain = True
140
+ if 'Snow' in item['weather'][0]['main']:
141
+ has_snow = True
142
+
143
+ min_temp = min(all_temps) if all_temps else 0
144
+ max_temp = max(all_temps) if all_temps else 30
145
+
146
+ # Generate packing tips based on conditions
147
+ tips = "\n🧳 Packing tips: "
148
+
149
+ if min_temp < 5:
150
+ tips += "Bring a heavy winter coat, gloves, and hat. "
151
+ elif min_temp < 15:
152
+ tips += "Pack a warm jacket and layers. "
153
+ elif min_temp < 20:
154
+ tips += "Bring a light jacket for evenings. "
155
+
156
+ if max_temp > 25:
157
+ tips += "Pack light, breathable clothing for warm days. "
158
+
159
+ if has_rain:
160
+ tips += "Don't forget an umbrella and waterproof footwear. "
161
+
162
+ if has_snow:
163
+ tips += "Bring waterproof boots and warm socks. "
164
+
165
+ if max_temp > 22:
166
+ tips += "Sunscreen and sunglasses are recommended. "
167
+
168
+ return tips