Hugo Bui commited on
Commit
4160511
·
unverified ·
1 Parent(s): a5f533a

add weather tool

Browse files
requirements.txt CHANGED
@@ -1 +1,5 @@
1
- huggingface_hub==0.25.2
 
 
 
 
 
1
+ huggingface_hub==0.25.2
2
+ requests>=2.25.0
3
+ smolagents>=0.1.0
4
+ python-dotenv>=0.19.0
5
+ markdownify>=0.11.0
tools/.env DELETED
@@ -1 +0,0 @@
1
- SERPAPI_API_KEY=5f1ba2f2179fafad997e2514ccaa634be88cc9d7c5d0382e9e5b528ba0e434c0
 
 
tools/__pycache__/weather_tool.cpython-310.pyc ADDED
Binary file (6.77 kB). View file
 
tools/__pycache__/weather_tool_v2.cpython-310.pyc ADDED
Binary file (10.2 kB). View file
 
tools/test_api_key.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script pour tester la validité de votre clé API OpenWeatherMap
4
+ """
5
+
6
+ import os
7
+ import requests
8
+ from dotenv import load_dotenv
9
+
10
+ def test_api_key():
11
+ # Charger les variables d'environnement
12
+ load_dotenv()
13
+
14
+ api_key = os.getenv('OPENWEATHER_API_KEY')
15
+
16
+ if not api_key:
17
+ print("❌ Aucune clé API trouvée dans le fichier .env")
18
+ print("Ajoutez OPENWEATHER_API_KEY=votre_cle dans votre fichier .env")
19
+ return False
20
+
21
+ print(f"🔑 Clé API trouvée: {api_key[:8]}...")
22
+
23
+ # Test simple avec l'API météo actuelle
24
+ test_url = "http://api.openweathermap.org/data/2.5/weather"
25
+ params = {
26
+ 'q': 'London',
27
+ 'appid': api_key,
28
+ 'units': 'metric'
29
+ }
30
+
31
+ try:
32
+ print("🌐 Test de la clé API...")
33
+ response = requests.get(test_url, params=params, timeout=10)
34
+
35
+ if response.status_code == 200:
36
+ data = response.json()
37
+ temp = data['main']['temp']
38
+ city = data['name']
39
+ print(f"✅ Clé API valide ! Température actuelle à {city}: {temp}°C")
40
+ return True
41
+ elif response.status_code == 401:
42
+ print("❌ Clé API invalide ou non activée")
43
+ print("Vérifiez votre clé API sur https://openweathermap.org/api")
44
+ print("Note: Les nouvelles clés peuvent prendre quelques heures à s'activer")
45
+ return False
46
+ elif response.status_code == 429:
47
+ print("⚠️ Limite de requêtes dépassée")
48
+ print("Attendez un moment avant de refaire un test")
49
+ return False
50
+ else:
51
+ print(f"❌ Erreur HTTP {response.status_code}: {response.text}")
52
+ return False
53
+
54
+ except requests.exceptions.RequestException as e:
55
+ print(f"❌ Erreur de connexion: {e}")
56
+ return False
57
+
58
+ if __name__ == "__main__":
59
+ print("=== Test de la clé API OpenWeatherMap ===\n")
60
+
61
+ success = test_api_key()
62
+
63
+ if not success:
64
+ print("\n📝 Instructions:")
65
+ print("1. Allez sur https://openweathermap.org/api")
66
+ print("2. Créez un compte gratuit")
67
+ print("3. Allez dans 'My API keys'")
68
+ print("4. Copiez votre clé API")
69
+ print("5. Ajoutez OPENWEATHER_API_KEY=votre_cle dans votre fichier .env")
70
+ print("6. Attendez quelques heures si la clé vient d'être créée")
71
+ else:
72
+ print("\n🎉 Votre clé API fonctionne parfaitement !")
73
+ print("Vous pouvez maintenant utiliser l'outil météo.")
tools/visit_webpage.py CHANGED
@@ -3,6 +3,7 @@ from smolagents.tools import Tool
3
  import requests
4
  import markdownify
5
  import smolagents
 
6
 
7
  class VisitWebpageTool(Tool):
8
  name = "visit_webpage"
 
3
  import requests
4
  import markdownify
5
  import smolagents
6
+ import re
7
 
8
  class VisitWebpageTool(Tool):
9
  name = "visit_webpage"
tools/weather_example.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Exemple d'utilisation de l'outil météo (API gratuite OpenWeatherMap)
4
+ """
5
+
6
+ from weather_tool import WeatherTool
7
+ from datetime import datetime, timedelta
8
+
9
+ def main():
10
+ # Initialiser l'outil météo (charge automatiquement la clé API depuis .env)
11
+ weather_tool = WeatherTool()
12
+
13
+ print("=== Exemples d'utilisation de l'outil météo ===\n")
14
+
15
+ # Exemple 1: Météo actuelle
16
+ print("1. Météo actuelle à Paris:")
17
+ result = weather_tool.forward("Paris")
18
+ print(result)
19
+ print("\n" + "="*50 + "\n")
20
+
21
+ # Exemple 2: Météo pour une date spécifique
22
+ print("2. Météo à Londres pour demain:")
23
+ tomorrow = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
24
+ result = weather_tool.forward("London", date=tomorrow)
25
+ print(result)
26
+ print("\n" + "="*50 + "\n")
27
+
28
+ # Exemple 3: Météo pour un pays
29
+ print("3. Météo à Tokyo dans 3 jours:")
30
+ future_date = (datetime.now() + timedelta(days=3)).strftime("%Y-%m-%d")
31
+ result = weather_tool.forward("Tokyo", date=future_date)
32
+ print(result)
33
+ print("\n" + "="*50 + "\n")
34
+
35
+ # Exemple 4: Météo avec une clé API spécifique
36
+ print("4. Météo à Berlin (avec clé API personnalisée):")
37
+ # result = weather_tool.forward("Berlin", api_key="votre_cle_api_ici")
38
+ result = weather_tool.forward("Berlin") # Utilise la clé du .env
39
+ print(result)
40
+
41
+ if __name__ == "__main__":
42
+ main()
tools/weather_tool.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional
2
+ from smolagents.tools import Tool
3
+ import requests
4
+ from datetime import datetime, timedelta
5
+ import json
6
+ import os
7
+ from dotenv import load_dotenv
8
+
9
+ class WeatherTool(Tool):
10
+ name = "weather_forecast"
11
+ description = "Obtient les prévisions météorologiques pour un pays/ville spécifique à une date donnée. Utilise l'API gratuite OpenWeatherMap 2.5."
12
+ inputs = {
13
+ 'location': {'type': 'string', 'description': 'Le nom de la ville ou du pays pour lequel obtenir la météo (ex: "Paris", "London", "Tokyo")'},
14
+ 'date': {'type': 'string', 'description': 'La date pour laquelle obtenir la météo au format YYYY-MM-DD (optionnel, par défaut aujourd\'hui)', 'nullable': True},
15
+ 'api_key': {'type': 'string', 'description': 'Clé API OpenWeatherMap (optionnel si définie dans les variables d\'environnement)', 'nullable': True}
16
+ }
17
+ output_type = "string"
18
+
19
+ def __init__(self, api_key: Optional[str] = None):
20
+ super().__init__()
21
+ # Charger les variables d'environnement depuis le fichier .env
22
+ load_dotenv()
23
+
24
+ # Utiliser la clé API fournie, sinon celle du .env
25
+ self.api_key = api_key or os.getenv('OPENWEATHER_API_KEY')
26
+ self.base_url = "http://api.openweathermap.org/data/2.5"
27
+
28
+ def forward(self, location: str, date: Optional[str] = None, api_key: Optional[str] = None) -> str:
29
+ try:
30
+ # Utiliser la clé API fournie ou celle par défaut
31
+ used_api_key = api_key or self.api_key
32
+
33
+ if not used_api_key:
34
+ return "Erreur: Clé API OpenWeatherMap requise. Ajoutez OPENWEATHER_API_KEY dans votre fichier .env ou obtenez une clé gratuite sur https://openweathermap.org/api"
35
+
36
+ # Parser la date si fournie
37
+ target_date = None
38
+ if date:
39
+ try:
40
+ target_date = datetime.strptime(date, "%Y-%m-%d")
41
+ except ValueError:
42
+ return f"Erreur: Format de date invalide. Utilisez YYYY-MM-DD (ex: 2024-01-15)"
43
+
44
+ # Obtenir les coordonnées de la localisation
45
+ geo_url = f"http://api.openweathermap.org/geo/1.0/direct"
46
+ geo_params = {
47
+ 'q': location,
48
+ 'limit': 1,
49
+ 'appid': used_api_key
50
+ }
51
+
52
+ geo_response = requests.get(geo_url, params=geo_params, timeout=10)
53
+ geo_response.raise_for_status()
54
+ geo_data = geo_response.json()
55
+
56
+ if not geo_data:
57
+ return f"Erreur: Localisation '{location}' non trouvée. Essayez avec le nom d'une ville ou d'un pays plus précis."
58
+
59
+ lat = geo_data[0]['lat']
60
+ lon = geo_data[0]['lon']
61
+ country = geo_data[0].get('country', '')
62
+ city_name = geo_data[0]['name']
63
+
64
+ # Utiliser l'API gratuite
65
+ return self._get_weather(lat, lon, city_name, country, target_date, used_api_key)
66
+
67
+ except requests.exceptions.Timeout:
68
+ return "Erreur: Délai d'attente dépassé. Veuillez réessayer."
69
+ except requests.exceptions.HTTPError as e:
70
+ if e.response.status_code == 401:
71
+ return "Erreur 401: Clé API invalide ou non activée. Vérifiez votre clé API OpenWeatherMap et assurez-vous qu'elle est activée (peut prendre quelques heures après création)."
72
+ elif e.response.status_code == 429:
73
+ return "Erreur 429: Limite de requêtes dépassée. Attendez avant de refaire une requête."
74
+ else:
75
+ return f"Erreur HTTP {e.response.status_code}: {str(e)}"
76
+ except requests.exceptions.RequestException as e:
77
+ return f"Erreur de requête: {str(e)}"
78
+ except Exception as e:
79
+ return f"Erreur inattendue: {str(e)}"
80
+
81
+ def _get_weather(self, lat: float, lon: float, city_name: str, country: str, target_date: Optional[datetime], api_key: str) -> str:
82
+ """Utilise l'API gratuite 2.5"""
83
+
84
+ if not target_date or target_date.date() == datetime.now().date():
85
+ # Météo actuelle
86
+ weather_url = f"{self.base_url}/weather"
87
+ params = {
88
+ 'lat': lat,
89
+ 'lon': lon,
90
+ 'appid': api_key,
91
+ 'units': 'metric',
92
+ 'lang': 'fr'
93
+ }
94
+
95
+ response = requests.get(weather_url, params=params, timeout=10)
96
+ response.raise_for_status()
97
+ data = response.json()
98
+
99
+ return self._format_current_weather(data, city_name, country)
100
+
101
+ elif target_date and target_date <= datetime.now() + timedelta(days=5):
102
+ # Prévisions sur 5 jours
103
+ forecast_url = f"{self.base_url}/forecast"
104
+ params = {
105
+ 'lat': lat,
106
+ 'lon': lon,
107
+ 'appid': api_key,
108
+ 'units': 'metric',
109
+ 'lang': 'fr'
110
+ }
111
+
112
+ response = requests.get(forecast_url, params=params, timeout=10)
113
+ response.raise_for_status()
114
+ data = response.json()
115
+
116
+ return self._format_forecast_weather(data, city_name, country, target_date)
117
+ else:
118
+ return "Erreur: Les prévisions ne sont disponibles que pour les 5 prochains jours maximum."
119
+
120
+ def _format_current_weather(self, data: dict, city_name: str, country: str) -> str:
121
+ """Formate les données météo actuelles"""
122
+ try:
123
+ weather = data['weather'][0]
124
+ main = data['main']
125
+ wind = data.get('wind', {})
126
+
127
+ result = f"🌤️ **Météo actuelle pour {city_name}, {country}**\n\n"
128
+ result += f"**Conditions:** {weather['description'].title()}\n"
129
+ result += f"**Température:** {main['temp']:.1f}°C (ressenti: {main['feels_like']:.1f}°C)\n"
130
+ result += f"**Humidité:** {main['humidity']}%\n"
131
+ result += f"**Pression:** {main['pressure']} hPa\n"
132
+
133
+ if 'speed' in wind:
134
+ result += f"**Vent:** {wind['speed']} m/s"
135
+ if 'deg' in wind:
136
+ result += f" ({self._wind_direction(wind['deg'])})"
137
+ result += "\n"
138
+
139
+ if 'visibility' in data:
140
+ result += f"**Visibilité:** {data['visibility']/1000:.1f} km\n"
141
+
142
+ return result
143
+
144
+ except KeyError as e:
145
+ return f"Erreur lors du formatage des données météo: {str(e)}"
146
+
147
+ def _format_forecast_weather(self, data: dict, city_name: str, country: str, target_date: datetime) -> str:
148
+ """Formate les prévisions météo pour une date spécifique"""
149
+ try:
150
+ target_date_str = target_date.strftime("%Y-%m-%d")
151
+
152
+ # Trouver les prévisions pour la date cible
153
+ forecasts_for_date = []
154
+ for forecast in data['list']:
155
+ forecast_date = datetime.fromtimestamp(forecast['dt']).strftime("%Y-%m-%d")
156
+ if forecast_date == target_date_str:
157
+ forecasts_for_date.append(forecast)
158
+
159
+ if not forecasts_for_date:
160
+ return f"Aucune prévision disponible pour le {target_date_str}"
161
+
162
+ result = f"🌤️ **Prévisions météo pour {city_name}, {country} - {target_date.strftime('%d/%m/%Y')}**\n\n"
163
+
164
+ for i, forecast in enumerate(forecasts_for_date):
165
+ time = datetime.fromtimestamp(forecast['dt']).strftime("%H:%M")
166
+ weather = forecast['weather'][0]
167
+ main = forecast['main']
168
+ wind = forecast.get('wind', {})
169
+
170
+ result += f"**{time}:**\n"
171
+ result += f" • Conditions: {weather['description'].title()}\n"
172
+ result += f" • Température: {main['temp']:.1f}°C (ressenti: {main['feels_like']:.1f}°C)\n"
173
+ result += f" • Humidité: {main['humidity']}%\n"
174
+
175
+ if 'speed' in wind:
176
+ result += f" • Vent: {wind['speed']} m/s"
177
+ if 'deg' in wind:
178
+ result += f" ({self._wind_direction(wind['deg'])})"
179
+ result += "\n"
180
+
181
+ if i < len(forecasts_for_date) - 1:
182
+ result += "\n"
183
+
184
+ return result
185
+
186
+ except KeyError as e:
187
+ return f"Erreur lors du formatage des prévisions: {str(e)}"
188
+
189
+
190
+
191
+ def _wind_direction(self, degrees: float) -> str:
192
+ """Convertit les degrés en direction du vent"""
193
+ directions = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
194
+ "S", "SSO", "SO", "OSO", "O", "ONO", "NO", "NNO"]
195
+ index = round(degrees / 22.5) % 16
196
+ return directions[index]