Spaces:
Build error
Build error
Commit ·
b9744a5
1
Parent(s): e070fb4
Main commit
Browse files- .vscode/settings.json +8 -0
- app.py +79 -5
- requirements.txt +8 -0
- tools.py +0 -0
- tools/__pycache__/guestinforetriever.cpython-312.pyc +0 -0
- tools/__pycache__/search_tool.cpython-312.pyc +0 -0
- tools/__pycache__/weather_tool.cpython-312.pyc +0 -0
- tools/guestinforetriever.py +53 -0
- tools/search_tool.py +21 -0
- tools/weather_tool.py +41 -0
.vscode/settings.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cSpell.words": [
|
| 3 |
+
"guestinforetriever",
|
| 4 |
+
"huggingface",
|
| 5 |
+
"langchain",
|
| 6 |
+
"smolagents"
|
| 7 |
+
]
|
| 8 |
+
}
|
app.py
CHANGED
|
@@ -1,5 +1,40 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
from huggingface_hub import InferenceClient
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
def respond(
|
|
@@ -9,21 +44,60 @@ def respond(
|
|
| 9 |
max_tokens,
|
| 10 |
temperature,
|
| 11 |
top_p,
|
| 12 |
-
hf_token: gr.OAuthToken,
|
| 13 |
):
|
| 14 |
"""
|
| 15 |
For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
|
| 16 |
"""
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
messages.append({"role": "user", "content": message})
|
| 24 |
|
| 25 |
response = ""
|
| 26 |
-
|
| 27 |
for message in client.chat_completion(
|
| 28 |
messages,
|
| 29 |
max_tokens=max_tokens,
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from huggingface_hub import InferenceClient
|
| 3 |
+
import os
|
| 4 |
+
from typing import Optional
|
| 5 |
+
|
| 6 |
+
from datasets import load_dataset
|
| 7 |
+
from langchain.schema import Document
|
| 8 |
+
from tools.guestinforetriever import GuestInfoRetrieverTool
|
| 9 |
+
from tools.search_tool import SearchTool
|
| 10 |
+
from tools.weather_tool import WeatherTool
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
# Load dataset and initialize tools once at module import
|
| 14 |
+
try:
|
| 15 |
+
ds = load_dataset("agents-course/unit3-invitees")
|
| 16 |
+
docs = []
|
| 17 |
+
for split in ds.keys():
|
| 18 |
+
for item in ds[split]:
|
| 19 |
+
# attempt to use common text fields, fallback to stringified item
|
| 20 |
+
text = None
|
| 21 |
+
for key in ("text", "content", "body", "description", "name"):
|
| 22 |
+
if key in item and item[key]:
|
| 23 |
+
text = item[key]
|
| 24 |
+
break
|
| 25 |
+
if text is None:
|
| 26 |
+
text = str(item)
|
| 27 |
+
docs.append(Document(page_content=str(text), metadata={"source": f"{split}"}))
|
| 28 |
+
|
| 29 |
+
guest_tool = GuestInfoRetrieverTool(docs)
|
| 30 |
+
search_tool = SearchTool(docs)
|
| 31 |
+
except Exception:
|
| 32 |
+
# dataset load failed; provide empty fallback tools
|
| 33 |
+
docs = []
|
| 34 |
+
guest_tool = None
|
| 35 |
+
search_tool = None
|
| 36 |
+
|
| 37 |
+
weather_tool = WeatherTool()
|
| 38 |
|
| 39 |
|
| 40 |
def respond(
|
|
|
|
| 44 |
max_tokens,
|
| 45 |
temperature,
|
| 46 |
top_p,
|
| 47 |
+
hf_token: Optional[gr.OAuthToken],
|
| 48 |
):
|
| 49 |
"""
|
| 50 |
For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
|
| 51 |
"""
|
| 52 |
+
# simple command routing for tools
|
| 53 |
+
text = message.strip()
|
| 54 |
+
if text.lower().startswith("/guest "):
|
| 55 |
+
query = text[len("/guest "):].strip()
|
| 56 |
+
if guest_tool:
|
| 57 |
+
yield guest_tool.forward(query)
|
| 58 |
+
else:
|
| 59 |
+
yield "Guest retriever not available (dataset failed to load)."
|
| 60 |
+
return
|
| 61 |
|
| 62 |
+
if text.lower().startswith("/search "):
|
| 63 |
+
query = text[len("/search "):].strip()
|
| 64 |
+
if search_tool:
|
| 65 |
+
yield search_tool.forward(query)
|
| 66 |
+
else:
|
| 67 |
+
yield "Search tool not available (dataset failed to load)."
|
| 68 |
+
return
|
| 69 |
|
| 70 |
+
if text.lower().startswith("/weather "):
|
| 71 |
+
location = text[len("/weather "):].strip()
|
| 72 |
+
yield weather_tool.forward(location)
|
| 73 |
+
return
|
| 74 |
|
| 75 |
+
# Default: call the HF chat model
|
| 76 |
+
# Prefer the Gradio OAuth token, fall back to env var `HUGGINGFACEHUB_API_TOKEN`.
|
| 77 |
+
hf_token_value = None
|
| 78 |
+
if hf_token and getattr(hf_token, "token", None):
|
| 79 |
+
hf_token_value = hf_token.token
|
| 80 |
+
else:
|
| 81 |
+
hf_token_value = os.environ.get("HUGGINGFACEHUB_API_TOKEN")
|
| 82 |
+
|
| 83 |
+
if not hf_token_value:
|
| 84 |
+
yield (
|
| 85 |
+
"Missing Hugging Face API token. Please run `huggingface-cli login` or set the"
|
| 86 |
+
" environment variable `HUGGINGFACEHUB_API_TOKEN` with a valid token (starts with 'hf_')."
|
| 87 |
+
)
|
| 88 |
+
return
|
| 89 |
+
|
| 90 |
+
try:
|
| 91 |
+
client = InferenceClient(token=hf_token_value, model="openai/gpt-oss-20b")
|
| 92 |
+
except Exception as e:
|
| 93 |
+
yield f"Failed to initialize Hugging Face InferenceClient: {e}"
|
| 94 |
+
return
|
| 95 |
+
|
| 96 |
+
messages = [{"role": "system", "content": system_message}]
|
| 97 |
+
messages.extend(history)
|
| 98 |
messages.append({"role": "user", "content": message})
|
| 99 |
|
| 100 |
response = ""
|
|
|
|
| 101 |
for message in client.chat_completion(
|
| 102 |
messages,
|
| 103 |
max_tokens=max_tokens,
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=3.0
|
| 2 |
+
huggingface-hub>=0.14.0
|
| 3 |
+
datasets>=2.0.0
|
| 4 |
+
langchain>=0.0.200
|
| 5 |
+
langchain-community>=0.0.10
|
| 6 |
+
smolagents>=0.1.0
|
| 7 |
+
requests>=2.28.0
|
| 8 |
+
rank-bm25>=0.2.2
|
tools.py
DELETED
|
File without changes
|
tools/__pycache__/guestinforetriever.cpython-312.pyc
ADDED
|
Binary file (2.8 kB). View file
|
|
|
tools/__pycache__/search_tool.cpython-312.pyc
ADDED
|
Binary file (1.56 kB). View file
|
|
|
tools/__pycache__/weather_tool.cpython-312.pyc
ADDED
|
Binary file (2.53 kB). View file
|
|
|
tools/guestinforetriever.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from smolagents import Tool
|
| 2 |
+
from langchain_community.retrievers import BM25Retriever
|
| 3 |
+
from langchain.schema import Document
|
| 4 |
+
from datasets import load_dataset
|
| 5 |
+
from typing import List, Optional
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class GuestInfoRetrieverTool(Tool):
|
| 9 |
+
name = "guest_info_retriever"
|
| 10 |
+
description = "Retrieves detailed information about gala guests based on their name or relation."
|
| 11 |
+
inputs = {
|
| 12 |
+
"query": {
|
| 13 |
+
"type": "string",
|
| 14 |
+
"description": "The name or relation of the guest you want information about."
|
| 15 |
+
}
|
| 16 |
+
}
|
| 17 |
+
output_type = "string"
|
| 18 |
+
|
| 19 |
+
def __init__(self, docs: List[Document]):
|
| 20 |
+
# docs should be a list of `langchain.schema.Document`
|
| 21 |
+
self.is_initialized = True
|
| 22 |
+
self.retriever = BM25Retriever.from_documents(docs)
|
| 23 |
+
|
| 24 |
+
def forward(self, query: str) -> str:
|
| 25 |
+
results = self.retriever.get_relevant_documents(query)
|
| 26 |
+
if results:
|
| 27 |
+
return "\n\n".join([doc.page_content for doc in results[:3]])
|
| 28 |
+
else:
|
| 29 |
+
return "No matching guest information found."
|
| 30 |
+
|
| 31 |
+
@classmethod
|
| 32 |
+
def from_hf_dataset(cls, dataset_name: str, text_field: str = "text") -> "GuestInfoRetrieverTool":
|
| 33 |
+
"""Load a Hugging Face dataset and construct a BM25 retriever.
|
| 34 |
+
|
| 35 |
+
Args:
|
| 36 |
+
dataset_name: dataset identifier, e.g. 'agents-course/unit3-invitees'
|
| 37 |
+
text_field: field in the dataset entries containing the text to index.
|
| 38 |
+
"""
|
| 39 |
+
ds = load_dataset(dataset_name)
|
| 40 |
+
# assume first split and concatenate text fields
|
| 41 |
+
records = []
|
| 42 |
+
for split in ds.keys():
|
| 43 |
+
for item in ds[split]:
|
| 44 |
+
if text_field in item and item[text_field]:
|
| 45 |
+
records.append(Document(page_content=str(item[text_field]), metadata={"source": dataset_name}))
|
| 46 |
+
else:
|
| 47 |
+
# fallback: stringify whole item
|
| 48 |
+
records.append(Document(page_content=str(item), metadata={"source": dataset_name}))
|
| 49 |
+
|
| 50 |
+
return cls(records)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Note: factories instead of eager initialization — caller should instantiate with docs or use `from_hf_dataset`.
|
tools/search_tool.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from smolagents import Tool
|
| 2 |
+
from langchain_community.retrievers import BM25Retriever
|
| 3 |
+
from langchain.schema import Document
|
| 4 |
+
from typing import List
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class SearchTool(Tool):
|
| 8 |
+
name = "search"
|
| 9 |
+
description = "Performs a general search over provided documents. Use `/search <query>` to call."
|
| 10 |
+
inputs = {"query": {"type": "string", "description": "Search query"}}
|
| 11 |
+
output_type = "string"
|
| 12 |
+
|
| 13 |
+
def __init__(self, docs: List[Document]):
|
| 14 |
+
self.retriever = BM25Retriever.from_documents(docs)
|
| 15 |
+
|
| 16 |
+
def forward(self, query: str) -> str:
|
| 17 |
+
results = self.retriever.get_relevant_documents(query)
|
| 18 |
+
if not results:
|
| 19 |
+
return "No results found."
|
| 20 |
+
|
| 21 |
+
return "\n\n".join([f"- {r.page_content}" for r in results[:5]])
|
tools/weather_tool.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from smolagents import Tool
|
| 2 |
+
import requests
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class WeatherTool(Tool):
|
| 6 |
+
name = "weather"
|
| 7 |
+
description = "Returns current weather information for a given location. Use `/weather <location>` to call."
|
| 8 |
+
inputs = {"location": {"type": "string", "description": "City or location name"}}
|
| 9 |
+
output_type = "string"
|
| 10 |
+
|
| 11 |
+
def __init__(self, timeout: int = 5):
|
| 12 |
+
self.timeout = timeout
|
| 13 |
+
|
| 14 |
+
def forward(self, location: str) -> str:
|
| 15 |
+
# Use Open-Meteo geocoding + weather APIs (no API key required)
|
| 16 |
+
try:
|
| 17 |
+
geo = requests.get(
|
| 18 |
+
"https://geocoding-api.open-meteo.com/v1/search",
|
| 19 |
+
params={"name": location, "count": 1},
|
| 20 |
+
timeout=self.timeout,
|
| 21 |
+
).json()
|
| 22 |
+
|
| 23 |
+
if not geo.get("results"):
|
| 24 |
+
return f"Could not find location: {location}"
|
| 25 |
+
|
| 26 |
+
loc = geo["results"][0]
|
| 27 |
+
lat, lon = loc["latitude"], loc["longitude"]
|
| 28 |
+
|
| 29 |
+
weather = requests.get(
|
| 30 |
+
"https://api.open-meteo.com/v1/forecast",
|
| 31 |
+
params={"latitude": lat, "longitude": lon, "current_weather": True},
|
| 32 |
+
timeout=self.timeout,
|
| 33 |
+
).json()
|
| 34 |
+
|
| 35 |
+
cw = weather.get("current_weather")
|
| 36 |
+
if not cw:
|
| 37 |
+
return f"No weather data available for {location} (lat={lat}, lon={lon})"
|
| 38 |
+
|
| 39 |
+
return f"Weather for {loc.get('name', location)}, {loc.get('country','')}: {cw.get('temperature')}°C, wind {cw.get('windspeed')} km/h, conditions code {cw.get('weathercode')}"
|
| 40 |
+
except Exception as e:
|
| 41 |
+
return f"Weather lookup failed: {e}"
|