chughtaihamad commited on
Commit
b9744a5
·
1 Parent(s): e070fb4

Main commit

Browse files
.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
- client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
 
 
 
 
 
 
 
 
18
 
19
- messages = [{"role": "system", "content": system_message}]
 
 
 
 
 
 
20
 
21
- messages.extend(history)
 
 
 
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}"