Spaces:
Sleeping
Sleeping
Update tools to use @tool decorator format
Browse files- app.py +3 -8
- tools/__init__.py +4 -4
- tools/final_answer.py +8 -40
- tools/search.py +36 -55
- tools/weather.py +40 -143
app.py
CHANGED
|
@@ -1,14 +1,9 @@
|
|
| 1 |
from smolagents import CodeAgent, HfApiModel
|
| 2 |
import yaml
|
| 3 |
-
from tools import
|
| 4 |
from Gradio_UI import GradioUI
|
| 5 |
import os
|
| 6 |
|
| 7 |
-
# Initialize our tools
|
| 8 |
-
final_answer = FinalAnswerTool()
|
| 9 |
-
weather_tool = WeatherTool()
|
| 10 |
-
search_tool = DuckDuckGoSearchTool()
|
| 11 |
-
|
| 12 |
# Get the Hugging Face token from environment variable
|
| 13 |
hf_token = os.getenv('HUGGINGFACE_TOKEN')
|
| 14 |
if not hf_token:
|
|
@@ -30,8 +25,8 @@ agent = CodeAgent(
|
|
| 30 |
model=model,
|
| 31 |
tools=[
|
| 32 |
final_answer,
|
| 33 |
-
|
| 34 |
-
|
| 35 |
],
|
| 36 |
max_steps=6,
|
| 37 |
verbosity_level=1,
|
|
|
|
| 1 |
from smolagents import CodeAgent, HfApiModel
|
| 2 |
import yaml
|
| 3 |
+
from tools import final_answer, get_weather, duck_duck_go_search
|
| 4 |
from Gradio_UI import GradioUI
|
| 5 |
import os
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
# Get the Hugging Face token from environment variable
|
| 8 |
hf_token = os.getenv('HUGGINGFACE_TOKEN')
|
| 9 |
if not hf_token:
|
|
|
|
| 25 |
model=model,
|
| 26 |
tools=[
|
| 27 |
final_answer,
|
| 28 |
+
duck_duck_go_search,
|
| 29 |
+
get_weather
|
| 30 |
],
|
| 31 |
max_steps=6,
|
| 32 |
verbosity_level=1,
|
tools/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"""Tools for the agent."""
|
| 2 |
|
| 3 |
-
from .final_answer import
|
| 4 |
-
from .weather import
|
| 5 |
-
from .search import
|
| 6 |
|
| 7 |
-
__all__ = ["
|
|
|
|
| 1 |
"""Tools for the agent."""
|
| 2 |
|
| 3 |
+
from .final_answer import final_answer
|
| 4 |
+
from .weather import get_weather
|
| 5 |
+
from .search import duck_duck_go_search
|
| 6 |
|
| 7 |
+
__all__ = ["final_answer", "get_weather", "duck_duck_go_search"]
|
tools/final_answer.py
CHANGED
|
@@ -1,41 +1,9 @@
|
|
| 1 |
-
from smolagents.tools import
|
| 2 |
-
from typing import Any, Optional
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
"answer": {
|
| 12 |
-
"type": "string",
|
| 13 |
-
"description": "The final answer to provide to the user."
|
| 14 |
-
}
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
def __call__(self, answer: str) -> str:
|
| 18 |
-
"""
|
| 19 |
-
Provide a final answer to the user's query.
|
| 20 |
-
|
| 21 |
-
Args:
|
| 22 |
-
answer: The final answer to provide to the user.
|
| 23 |
-
|
| 24 |
-
Returns:
|
| 25 |
-
A confirmation message.
|
| 26 |
-
"""
|
| 27 |
-
return answer
|
| 28 |
-
|
| 29 |
-
forward = __call__
|
| 30 |
-
|
| 31 |
-
def __str__(self) -> str:
|
| 32 |
-
return "final_answer"
|
| 33 |
-
|
| 34 |
-
@property
|
| 35 |
-
def signature(self) -> str:
|
| 36 |
-
return "final_answer(answer: str) -> str"
|
| 37 |
-
|
| 38 |
-
def argument_descriptions(self) -> dict:
|
| 39 |
-
return {
|
| 40 |
-
"answer": "The final answer to provide to the user."
|
| 41 |
-
}
|
|
|
|
| 1 |
+
from smolagents.tools import tool
|
|
|
|
| 2 |
|
| 3 |
+
@tool
|
| 4 |
+
def final_answer(answer: str) -> str:
|
| 5 |
+
"""Use this tool to provide your final answer to the user's question.
|
| 6 |
+
Args:
|
| 7 |
+
answer: The final answer to provide to the user.
|
| 8 |
+
"""
|
| 9 |
+
return answer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tools/search.py
CHANGED
|
@@ -1,61 +1,42 @@
|
|
| 1 |
-
from smolagents.tools import
|
| 2 |
import requests
|
| 3 |
from typing import Any, List, Dict, Optional
|
| 4 |
import json
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
}
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
def __call__(self, query: str) -> str:
|
| 20 |
-
"""
|
| 21 |
-
Search the web for information using DuckDuckGo.
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
'
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
# Add the abstract if available
|
| 45 |
-
if data.get('Abstract'):
|
| 46 |
-
results.append(f"Abstract: {data['Abstract']}")
|
| 47 |
-
|
| 48 |
-
# Add related topics
|
| 49 |
-
if data.get('RelatedTopics'):
|
| 50 |
-
for i, topic in enumerate(data['RelatedTopics'][:5]): # Limit to 5 topics
|
| 51 |
-
if 'Text' in topic:
|
| 52 |
-
results.append(f"Result {i+1}: {topic['Text']}")
|
| 53 |
-
|
| 54 |
-
if not results:
|
| 55 |
-
return f"No results found for query: {query}"
|
| 56 |
-
|
| 57 |
-
return "\n\n".join(results)
|
| 58 |
-
except Exception as e:
|
| 59 |
-
return f"Error searching for {query}: {str(e)}"
|
| 60 |
-
|
| 61 |
-
forward = __call__
|
|
|
|
| 1 |
+
from smolagents.tools import tool
|
| 2 |
import requests
|
| 3 |
from typing import Any, List, Dict, Optional
|
| 4 |
import json
|
| 5 |
|
| 6 |
+
@tool
|
| 7 |
+
def duck_duck_go_search(query: str) -> str:
|
| 8 |
+
"""Search the web for information using DuckDuckGo.
|
| 9 |
+
Args:
|
| 10 |
+
query: A string representing the search query.
|
| 11 |
+
"""
|
| 12 |
+
try:
|
| 13 |
+
# Using the DuckDuckGo API
|
| 14 |
+
url = "https://api.duckduckgo.com/"
|
| 15 |
+
params = {
|
| 16 |
+
'q': query,
|
| 17 |
+
'format': 'json',
|
| 18 |
+
'no_html': 1,
|
| 19 |
+
'skip_disambig': 1
|
| 20 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
response = requests.get(url, params=params)
|
| 23 |
+
data = response.json()
|
| 24 |
+
|
| 25 |
+
results = []
|
| 26 |
+
|
| 27 |
+
# Add the abstract if available
|
| 28 |
+
if data.get('Abstract'):
|
| 29 |
+
results.append(f"Abstract: {data['Abstract']}")
|
| 30 |
+
|
| 31 |
+
# Add related topics
|
| 32 |
+
if data.get('RelatedTopics'):
|
| 33 |
+
for i, topic in enumerate(data['RelatedTopics'][:5]): # Limit to 5 topics
|
| 34 |
+
if 'Text' in topic:
|
| 35 |
+
results.append(f"Result {i+1}: {topic['Text']}")
|
| 36 |
+
|
| 37 |
+
if not results:
|
| 38 |
+
return f"No results found for query: {query}"
|
| 39 |
+
|
| 40 |
+
return "\n\n".join(results)
|
| 41 |
+
except Exception as e:
|
| 42 |
+
return f"Error searching for {query}: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tools/weather.py
CHANGED
|
@@ -1,151 +1,48 @@
|
|
| 1 |
-
from smolagents.tools import
|
| 2 |
import requests
|
| 3 |
from typing import Any, Optional
|
| 4 |
import os # Added for accessing environment variables
|
| 5 |
import re
|
| 6 |
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
"""
|
| 22 |
-
Get the current weather for a specified location.
|
| 23 |
-
|
| 24 |
-
Args:
|
| 25 |
-
location: A string representing a city (e.g., 'New York', 'Paris').
|
| 26 |
-
|
| 27 |
-
Returns:
|
| 28 |
-
A JSON blob with the current weather information in imperial units. The JSON blob should be formatted as a string. The response should be in the following format:
|
| 29 |
-
{
|
| 30 |
-
"coord": {
|
| 31 |
-
"lon": 7.367,
|
| 32 |
-
"lat": 45.133
|
| 33 |
-
},
|
| 34 |
-
"weather": [
|
| 35 |
-
{
|
| 36 |
-
"id": 501,
|
| 37 |
-
"main": "Rain",
|
| 38 |
-
"description": "moderate rain",
|
| 39 |
-
"icon": "10d"
|
| 40 |
-
}
|
| 41 |
-
],
|
| 42 |
-
"base": "stations",
|
| 43 |
-
"main": {
|
| 44 |
-
"temp": 284.2,
|
| 45 |
-
"feels_like": 282.93,
|
| 46 |
-
"temp_min": 283.06,
|
| 47 |
-
"temp_max": 286.82,
|
| 48 |
-
"pressure": 1021,
|
| 49 |
-
"humidity": 60,
|
| 50 |
-
"sea_level": 1021,
|
| 51 |
-
"grnd_level": 910
|
| 52 |
-
},
|
| 53 |
-
"visibility": 10000,
|
| 54 |
-
"wind": {
|
| 55 |
-
"speed": 4.09,
|
| 56 |
-
"deg": 121,
|
| 57 |
-
"gust": 3.47
|
| 58 |
-
},
|
| 59 |
-
"rain": {
|
| 60 |
-
"1h": 2.73
|
| 61 |
-
},
|
| 62 |
-
"clouds": {
|
| 63 |
-
"all": 83
|
| 64 |
-
},
|
| 65 |
-
"dt": 1726660758,
|
| 66 |
-
"sys": {
|
| 67 |
-
"type": 1,
|
| 68 |
-
"id": 6736,
|
| 69 |
-
"country": "IT",
|
| 70 |
-
"sunrise": 1726636384,
|
| 71 |
-
"sunset": 1726680975
|
| 72 |
-
},
|
| 73 |
-
"timezone": 7200,
|
| 74 |
-
"id": 3165523,
|
| 75 |
-
"name": "Province of Turin",
|
| 76 |
-
"cod": 200
|
| 77 |
-
}
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
main.pressure Atmospheric pressure on the sea level, hPa
|
| 90 |
-
main.humidity Humidity, %
|
| 91 |
-
main.temp_min Minimum temperature at the moment. This is minimal currently observed temperature (within large megalopolises and urban areas). Please find more info here. Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit
|
| 92 |
-
main.temp_max Maximum temperature at the moment. This is maximal currently observed temperature (within large megalopolises and urban areas). Please find more info here. Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit
|
| 93 |
-
main.sea_level Atmospheric pressure on the sea level, hPa
|
| 94 |
-
main.grnd_level Atmospheric pressure on the ground level, hPa
|
| 95 |
-
visibility Visibility, meter. The maximum value of the visibility is 10 km
|
| 96 |
-
wind.speed Wind speed. Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour
|
| 97 |
-
wind.deg Wind direction, degrees (meteorological)
|
| 98 |
-
wind.gust Wind gust. Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour
|
| 99 |
-
clouds.all Cloudiness, %
|
| 100 |
-
rain.1h(where available)Precipitation, mm/h. Please note that only mm/h as units of measurement are available for this parameter
|
| 101 |
-
snow.1h(where available) Precipitation, mm/h. Please note that only mm/h as units of measurement are available for this parameter
|
| 102 |
-
dt Time of data calculation, unix, UTC
|
| 103 |
-
sys.type Internal parameter
|
| 104 |
-
sys.id Internal parameter
|
| 105 |
-
sys.message Internal parameter
|
| 106 |
-
sys.country Country code (GB, JP etc.)
|
| 107 |
-
sys.sunrise Sunrise time, unix, UTC
|
| 108 |
-
sys.sunset Sunset time, unix, UTC
|
| 109 |
-
timezone Shift in seconds from UTC
|
| 110 |
-
id City ID. Please note that built-in geocoder functionality has been deprecated. Learn more here
|
| 111 |
-
name City name. Please note that built-in geocoder functionality has been deprecated. Learn more here
|
| 112 |
-
cod Internal parameter
|
| 113 |
-
"""
|
| 114 |
-
# Validate that location contains only allowed characters: letters, digits, spaces, and hyphens
|
| 115 |
-
if not re.fullmatch(r'[A-Za-z0-9\s-]+', location):
|
| 116 |
-
return "Error: Location contains invalid characters. Only letters, digits, spaces, and hyphens are allowed. I cannot disambiguate between cities with the same name."
|
| 117 |
-
try:
|
| 118 |
-
# Get the API key from environment variables
|
| 119 |
-
api_key = os.getenv('OPENWEATHERMAP_API_KEY')
|
| 120 |
-
if not api_key:
|
| 121 |
-
return "Error: OPENWEATHERMAP_API_KEY not set in environment variables."
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
if not geo_data:
|
| 128 |
-
return f"Error: Could not geocode location '{location}'."
|
| 129 |
-
lat = geo_data[0].get('lat')
|
| 130 |
-
lon = geo_data[0].get('lon')
|
| 131 |
-
if lat is None or lon is None:
|
| 132 |
-
return f"Error: Latitude or longitude not found for location '{location}'."
|
| 133 |
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
return f"The current weather in {location} is {temp}°C with {weather_desc}. Humidity: {humidity}%, Wind speed: {wind_speed} m/s."
|
| 146 |
-
else:
|
| 147 |
-
return f"Error fetching weather for {location}: {weather_data.get('message', 'Unknown error')}"
|
| 148 |
-
except Exception as e:
|
| 149 |
-
return f"Error fetching weather for {location}: {str(e)}"
|
| 150 |
-
|
| 151 |
-
forward = __call__
|
|
|
|
| 1 |
+
from smolagents.tools import tool
|
| 2 |
import requests
|
| 3 |
from typing import Any, Optional
|
| 4 |
import os # Added for accessing environment variables
|
| 5 |
import re
|
| 6 |
|
| 7 |
+
@tool
|
| 8 |
+
def get_weather(location: str) -> str:
|
| 9 |
+
"""Get the current weather for a specified location.
|
| 10 |
+
Args:
|
| 11 |
+
location: A string representing a city (e.g., 'New York', 'Paris').
|
| 12 |
+
"""
|
| 13 |
+
# Validate that location contains only allowed characters: letters, digits, spaces, and hyphens
|
| 14 |
+
if not re.fullmatch(r'[A-Za-z0-9\s-]+', location):
|
| 15 |
+
return "Error: Location contains invalid characters. Only letters, digits, spaces, and hyphens are allowed. I cannot disambiguate between cities with the same name."
|
| 16 |
+
try:
|
| 17 |
+
# Get the API key from environment variables
|
| 18 |
+
api_key = os.getenv('OPENWEATHERMAP_API_KEY')
|
| 19 |
+
if not api_key:
|
| 20 |
+
return "Error: OPENWEATHERMAP_API_KEY not set in environment variables."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
# First, geocode the location to get latitude and longitude
|
| 23 |
+
geo_url = f"http://api.openweathermap.org/geo/1.0/direct?q={location}&limit=1&appid={api_key}"
|
| 24 |
+
geo_response = requests.get(geo_url)
|
| 25 |
+
geo_data = geo_response.json()
|
| 26 |
+
if not geo_data:
|
| 27 |
+
return f"Error: Could not geocode location '{location}'."
|
| 28 |
+
lat = geo_data[0].get('lat')
|
| 29 |
+
lon = geo_data[0].get('lon')
|
| 30 |
+
if lat is None or lon is None:
|
| 31 |
+
return f"Error: Latitude or longitude not found for location '{location}'."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
# Call the OpenWeatherMap weather API 2.5 endpoint
|
| 34 |
+
weather_url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={api_key}&units=imperial"
|
| 35 |
+
weather_response = requests.get(weather_url)
|
| 36 |
+
weather_data = weather_response.json()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
+
if weather_response.status_code == 200:
|
| 39 |
+
current = weather_data.get('current', {})
|
| 40 |
+
temp = current.get('temp', 'N/A')
|
| 41 |
+
weather_desc = current.get('weather', [{}])[0].get('description', 'N/A')
|
| 42 |
+
humidity = current.get('humidity', 'N/A')
|
| 43 |
+
wind_speed = current.get('wind_speed', 'N/A')
|
| 44 |
+
return f"The current weather in {location} is {temp}°C with {weather_desc}. Humidity: {humidity}%, Wind speed: {wind_speed} m/s."
|
| 45 |
+
else:
|
| 46 |
+
return f"Error fetching weather for {location}: {weather_data.get('message', 'Unknown error')}"
|
| 47 |
+
except Exception as e:
|
| 48 |
+
return f"Error fetching weather for {location}: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|