lmrkmrcs commited on
Commit
bc41e13
·
verified ·
1 Parent(s): 2593e67

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -174
app.py CHANGED
@@ -3,7 +3,7 @@ import re
3
  import requests
4
  import gradio as gr
5
  import pandas as pd
6
- from smolagents import ToolCallingAgent, tool, LiteLLMModel
7
 
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
@@ -13,50 +13,21 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
13
  # ============================================
14
 
15
  @tool
16
- def web_search(query: str) -> str:
17
- """
18
- Searches the web and returns results.
19
-
20
- Args:
21
- query: What to search for
22
-
23
- Returns:
24
- Search results
25
- """
26
- try:
27
- from duckduckgo_search import DDGS
28
- with DDGS() as ddgs:
29
- results = list(ddgs.text(query, max_results=3))
30
- if not results:
31
- return "No results found."
32
- output = []
33
- for r in results:
34
- output.append(f"- {r.get('title', '')}: {r.get('body', '')}")
35
- return "\n".join(output)
36
- except Exception as e:
37
- return f"Search error: {e}"
38
-
39
-
40
- @tool
41
- def visit_webpage(url: str) -> str:
42
  """
43
- Gets text content from a webpage.
44
 
45
  Args:
46
- url: The webpage URL
47
 
48
  Returns:
49
- Page text content
50
  """
 
51
  try:
52
- from bs4 import BeautifulSoup
53
- headers = {"User-Agent": "Mozilla/5.0"}
54
- response = requests.get(url, headers=headers, timeout=10)
55
- soup = BeautifulSoup(response.text, 'html.parser')
56
- for tag in soup(['script', 'style', 'nav', 'footer']):
57
- tag.decompose()
58
- text = soup.get_text(separator=' ', strip=True)
59
- return text[:5000] if len(text) > 5000 else text
60
  except Exception as e:
61
  return f"Error: {e}"
62
 
@@ -64,67 +35,29 @@ def visit_webpage(url: str) -> str:
64
  @tool
65
  def wikipedia_search(topic: str) -> str:
66
  """
67
- Searches Wikipedia for a topic.
68
 
69
  Args:
70
- topic: What to look up
71
 
72
  Returns:
73
- Wikipedia summary
74
  """
75
  try:
76
  url = "https://en.wikipedia.org/w/api.php"
77
- params = {
78
- "action": "query",
79
- "list": "search",
80
- "srsearch": topic,
81
- "format": "json",
82
- "srlimit": 1
83
- }
84
- response = requests.get(url, params=params, timeout=10)
85
- data = response.json()
86
 
87
  if not data.get("query", {}).get("search"):
88
  return "No Wikipedia article found."
89
 
90
  title = data["query"]["search"][0]["title"]
91
-
92
- params2 = {
93
- "action": "query",
94
- "titles": title,
95
- "prop": "extracts",
96
- "exintro": True,
97
- "explaintext": True,
98
- "format": "json"
99
- }
100
- response = requests.get(url, params=params2, timeout=10)
101
- pages = response.json().get("query", {}).get("pages", {})
102
 
103
  for page in pages.values():
104
- extract = page.get("extract", "")
105
- return f"{title}: {extract[:3000]}"
106
- return "Could not get content."
107
- except Exception as e:
108
- return f"Error: {e}"
109
-
110
-
111
- @tool
112
- def calculator(expression: str) -> str:
113
- """
114
- Calculates a math expression.
115
-
116
- Args:
117
- expression: Math like "2+2" or "sqrt(16)"
118
-
119
- Returns:
120
- The result
121
- """
122
- import math
123
- try:
124
- safe = {"sqrt": math.sqrt, "pow": pow, "abs": abs, "round": round,
125
- "sin": math.sin, "cos": math.cos, "pi": math.pi, "e": math.e,
126
- "log": math.log, "floor": math.floor, "ceil": math.ceil}
127
- return str(eval(expression, {"__builtins__": {}}, safe))
128
  except Exception as e:
129
  return f"Error: {e}"
130
 
@@ -132,13 +65,13 @@ def calculator(expression: str) -> str:
132
  @tool
133
  def get_task_file(task_id: str) -> str:
134
  """
135
- Gets the file attached to a GAIA task.
136
 
137
  Args:
138
  task_id: The task ID
139
 
140
  Returns:
141
- File content or description
142
  """
143
  try:
144
  url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
@@ -148,37 +81,27 @@ def get_task_file(task_id: str) -> str:
148
  return "No file for this task."
149
 
150
  content_type = response.headers.get('content-type', '').lower()
151
- disp = response.headers.get('content-disposition', '')
152
 
153
- filename = "file"
154
- if 'filename=' in disp:
155
- filename = disp.split('filename=')[-1].strip('"\'')
156
 
157
- # Text files
158
- if 'text' in content_type or filename.endswith(('.txt', '.csv', '.json', '.py', '.md')):
159
- content = response.text
160
- return f"File '{filename}':\n{content[:6000]}"
161
-
162
- # Excel
163
- if filename.endswith(('.xlsx', '.xls')):
164
  try:
165
  from io import BytesIO
166
  df = pd.read_excel(BytesIO(response.content))
167
- return f"Excel '{filename}':\n{df.to_string()}"
168
  except:
169
- return f"Excel file: {filename} (could not parse)"
170
-
171
- # Other
172
- return f"File: {filename} ({content_type}, {len(response.content)} bytes)"
173
 
 
174
  except Exception as e:
175
  return f"Error: {e}"
176
 
177
 
178
  @tool
179
- def reverse_string(text: str) -> str:
180
  """
181
- Reverses a string.
182
 
183
  Args:
184
  text: Text to reverse
@@ -190,62 +113,62 @@ def reverse_string(text: str) -> str:
190
 
191
 
192
  # ============================================
193
- # AGENT
194
  # ============================================
195
 
196
  class BasicAgent:
197
  def __init__(self):
198
- print("Initializing agent with Groq...")
199
-
200
- api_key = os.environ.get("GROQ_API_KEY")
201
- if not api_key:
202
- raise ValueError("GROQ_API_KEY not found in environment!")
203
 
204
- self.model = LiteLLMModel(
205
- model_id="groq/llama-3.3-70b-versatile",
206
- api_key=api_key,
 
 
207
  )
208
 
209
- self.agent = ToolCallingAgent(
210
  model=self.model,
211
  tools=[
212
- web_search,
213
- visit_webpage,
214
  wikipedia_search,
215
  calculator,
216
  get_task_file,
217
- reverse_string,
218
  ],
219
- max_steps=8,
220
- verbosity_level=1,
221
  )
222
  print("Agent ready!")
223
 
224
  def __call__(self, question: str, task_id: str = None) -> str:
 
 
225
  try:
226
- prompt = f"""Answer this question. Use tools if needed. Give ONLY the final answer, nothing else.
227
-
228
- If there's a file mentioned, use get_task_file("{task_id}") first.
229
 
230
  Question: {question}"""
231
 
232
  result = self.agent.run(prompt)
233
  answer = str(result).strip()
234
 
235
- # Clean prefixes
236
- for prefix in ["Answer:", "Final answer:", "The answer is:", "FINAL ANSWER:"]:
237
  if answer.lower().startswith(prefix.lower()):
238
  answer = answer[len(prefix):].strip()
239
 
240
- # Remove quotes
241
  if answer.startswith('"') and answer.endswith('"'):
242
  answer = answer[1:-1]
243
 
 
244
  return answer
245
 
246
  except Exception as e:
247
- print(f"Error: {e}")
248
- return "Unable to determine answer"
249
 
250
 
251
  # ============================================
@@ -253,33 +176,30 @@ Question: {question}"""
253
  # ============================================
254
 
255
  def run_and_submit_all(profile: gr.OAuthProfile | None):
256
- space_id = os.getenv("SPACE_ID")
257
-
258
  if not profile:
259
  return "Please log in first.", None
260
 
261
  username = profile.username
 
 
 
262
  print(f"User: {username}")
263
-
264
- # Check API key
265
- if not os.environ.get("GROQ_API_KEY"):
266
- return "ERROR: GROQ_API_KEY not set in Space secrets!", None
267
 
268
  # Init agent
269
  try:
270
  agent = BasicAgent()
271
  except Exception as e:
272
- return f"Agent init failed: {e}", None
273
 
274
  # Get questions
275
  try:
276
- response = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
277
- questions = response.json()
278
- print(f"Got {len(questions)} questions")
279
  except Exception as e:
280
- return f"Failed to get questions: {e}", None
281
 
282
- # Process questions
283
  results = []
284
  answers = []
285
 
@@ -287,21 +207,16 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
287
  task_id = q.get("task_id")
288
  question = q.get("question", "")
289
 
290
- print(f"\n[{i+1}/{len(questions)}] {question[:80]}...")
291
 
292
  try:
293
  answer = agent(question, task_id)
294
- print(f" → {answer[:80]}")
295
  except Exception as e:
296
- answer = f"Error: {e}"
297
- print(f" → ERROR: {e}")
298
 
299
  answers.append({"task_id": task_id, "submitted_answer": answer})
300
- results.append({
301
- "Q#": i+1,
302
- "Question": question[:60] + "...",
303
- "Answer": answer[:100]
304
- })
305
 
306
  # Submit
307
  print(f"\nSubmitting {len(answers)} answers...")
@@ -312,19 +227,15 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
312
  "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
313
  "answers": answers
314
  }
315
- response = requests.post(f"{DEFAULT_API_URL}/submit", json=submission, timeout=60)
316
- result = response.json()
317
 
318
  score = result.get('score', 0)
319
  correct = result.get('correct_count', 0)
320
  total = result.get('total_attempted', 0)
321
 
322
- status = f"""Submitted!
323
-
324
- Score: {score}% ({correct}/{total} correct)
325
-
326
- {"🎉 PASSED! You got 30%+" if score >= 30 else f"Need {30-score}% more to pass"}
327
- """
328
  return status, pd.DataFrame(results)
329
 
330
  except Exception as e:
@@ -337,29 +248,16 @@ Score: {score}% ({correct}/{total} correct)
337
 
338
  with gr.Blocks() as demo:
339
  gr.Markdown("# 🎯 GAIA Agent - Unit 4")
340
- gr.Markdown("""
341
- **Powered by Groq + Llama 3.3 70B**
342
-
343
- 1. Add `GROQ_API_KEY` to Space secrets
344
- 2. Log in below
345
- 3. Click Run
346
- """)
347
 
348
  gr.LoginButton()
349
  run_btn = gr.Button("🚀 Run Evaluation", variant="primary")
350
- status = gr.Textbox(label="Status", lines=6)
351
  table = gr.DataFrame(label="Results")
352
 
353
  run_btn.click(run_and_submit_all, outputs=[status, table])
354
 
355
  if __name__ == "__main__":
356
- print("=" * 50)
357
- print("GAIA Agent Starting")
358
- print("=" * 50)
359
-
360
- if os.environ.get("GROQ_API_KEY"):
361
- print("✅ GROQ_API_KEY found")
362
- else:
363
- print("❌ GROQ_API_KEY missing!")
364
-
365
  demo.launch()
 
3
  import requests
4
  import gradio as gr
5
  import pandas as pd
6
+ from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, tool, VisitWebpageTool
7
 
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
13
  # ============================================
14
 
15
  @tool
16
+ def calculator(expression: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  """
18
+ Calculates a math expression.
19
 
20
  Args:
21
+ expression: Math expression like "2+2" or "10*5"
22
 
23
  Returns:
24
+ Result as string
25
  """
26
+ import math
27
  try:
28
+ safe = {"sqrt": math.sqrt, "pow": pow, "abs": abs, "round": round,
29
+ "sin": math.sin, "cos": math.cos, "pi": math.pi, "log": math.log}
30
+ return str(eval(expression, {"__builtins__": {}}, safe))
 
 
 
 
 
31
  except Exception as e:
32
  return f"Error: {e}"
33
 
 
35
  @tool
36
  def wikipedia_search(topic: str) -> str:
37
  """
38
+ Gets Wikipedia summary for a topic.
39
 
40
  Args:
41
+ topic: What to search
42
 
43
  Returns:
44
+ Wikipedia extract
45
  """
46
  try:
47
  url = "https://en.wikipedia.org/w/api.php"
48
+ params = {"action": "query", "list": "search", "srsearch": topic, "format": "json", "srlimit": 1}
49
+ data = requests.get(url, params=params, timeout=10).json()
 
 
 
 
 
 
 
50
 
51
  if not data.get("query", {}).get("search"):
52
  return "No Wikipedia article found."
53
 
54
  title = data["query"]["search"][0]["title"]
55
+ params2 = {"action": "query", "titles": title, "prop": "extracts", "exintro": True, "explaintext": True, "format": "json"}
56
+ pages = requests.get(url, params=params2, timeout=10).json().get("query", {}).get("pages", {})
 
 
 
 
 
 
 
 
 
57
 
58
  for page in pages.values():
59
+ return page.get("extract", "No content")[:3000]
60
+ return "No content"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  except Exception as e:
62
  return f"Error: {e}"
63
 
 
65
  @tool
66
  def get_task_file(task_id: str) -> str:
67
  """
68
+ Gets file content for a GAIA task.
69
 
70
  Args:
71
  task_id: The task ID
72
 
73
  Returns:
74
+ File content
75
  """
76
  try:
77
  url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
 
81
  return "No file for this task."
82
 
83
  content_type = response.headers.get('content-type', '').lower()
 
84
 
85
+ if 'text' in content_type or 'json' in content_type:
86
+ return response.text[:5000]
 
87
 
88
+ if 'spreadsheet' in content_type or 'excel' in content_type:
 
 
 
 
 
 
89
  try:
90
  from io import BytesIO
91
  df = pd.read_excel(BytesIO(response.content))
92
+ return df.to_string()
93
  except:
94
+ return "Excel file (cannot read)"
 
 
 
95
 
96
+ return f"Binary file: {content_type}"
97
  except Exception as e:
98
  return f"Error: {e}"
99
 
100
 
101
  @tool
102
+ def reverse_text(text: str) -> str:
103
  """
104
+ Reverses text.
105
 
106
  Args:
107
  text: Text to reverse
 
113
 
114
 
115
  # ============================================
116
+ # AGENT CLASS
117
  # ============================================
118
 
119
  class BasicAgent:
120
  def __init__(self):
121
+ print("Initializing HuggingFace agent...")
 
 
 
 
122
 
123
+ # Use Qwen 32B - good balance of speed and quality
124
+ self.model = InferenceClientModel(
125
+ model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
126
+ token=os.environ.get("HF_TOKEN"),
127
+ timeout=60,
128
  )
129
 
130
+ self.agent = CodeAgent(
131
  model=self.model,
132
  tools=[
133
+ DuckDuckGoSearchTool(),
134
+ VisitWebpageTool(),
135
  wikipedia_search,
136
  calculator,
137
  get_task_file,
138
+ reverse_text,
139
  ],
140
+ max_steps=5, # Keep it short to avoid timeouts
141
+ verbosity_level=2,
142
  )
143
  print("Agent ready!")
144
 
145
  def __call__(self, question: str, task_id: str = None) -> str:
146
+ print(f" Processing: {question[:60]}...")
147
+
148
  try:
149
+ prompt = f"""Answer this question concisely. Use tools if needed.
150
+ If a file is mentioned, use get_task_file("{task_id}").
151
+ Give ONLY the final answer.
152
 
153
  Question: {question}"""
154
 
155
  result = self.agent.run(prompt)
156
  answer = str(result).strip()
157
 
158
+ # Clean up
159
+ for prefix in ["Answer:", "Final answer:", "The answer is"]:
160
  if answer.lower().startswith(prefix.lower()):
161
  answer = answer[len(prefix):].strip()
162
 
 
163
  if answer.startswith('"') and answer.endswith('"'):
164
  answer = answer[1:-1]
165
 
166
+ print(f" Answer: {answer[:60]}")
167
  return answer
168
 
169
  except Exception as e:
170
+ print(f" Error: {e}")
171
+ return "Unable to answer"
172
 
173
 
174
  # ============================================
 
176
  # ============================================
177
 
178
  def run_and_submit_all(profile: gr.OAuthProfile | None):
 
 
179
  if not profile:
180
  return "Please log in first.", None
181
 
182
  username = profile.username
183
+ space_id = os.getenv("SPACE_ID")
184
+
185
+ print(f"\n{'='*50}")
186
  print(f"User: {username}")
187
+ print(f"{'='*50}")
 
 
 
188
 
189
  # Init agent
190
  try:
191
  agent = BasicAgent()
192
  except Exception as e:
193
+ return f"Agent failed to init: {e}", None
194
 
195
  # Get questions
196
  try:
197
+ questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
198
+ print(f"Fetched {len(questions)} questions\n")
 
199
  except Exception as e:
200
+ return f"Failed to fetch questions: {e}", None
201
 
202
+ # Process each question
203
  results = []
204
  answers = []
205
 
 
207
  task_id = q.get("task_id")
208
  question = q.get("question", "")
209
 
210
+ print(f"[{i+1}/{len(questions)}] {task_id}")
211
 
212
  try:
213
  answer = agent(question, task_id)
 
214
  except Exception as e:
215
+ answer = "Error"
216
+ print(f" Failed: {e}")
217
 
218
  answers.append({"task_id": task_id, "submitted_answer": answer})
219
+ results.append({"#": i+1, "Question": question[:50]+"...", "Answer": answer[:80]})
 
 
 
 
220
 
221
  # Submit
222
  print(f"\nSubmitting {len(answers)} answers...")
 
227
  "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
228
  "answers": answers
229
  }
230
+ result = requests.post(f"{DEFAULT_API_URL}/submit", json=submission, timeout=60).json()
 
231
 
232
  score = result.get('score', 0)
233
  correct = result.get('correct_count', 0)
234
  total = result.get('total_attempted', 0)
235
 
236
+ status = f"✅ Done!\n\nScore: {score}% ({correct}/{total})\n\n"
237
+ status += "🎉 PASSED!" if score >= 30 else f"Need {30-score}% more to pass"
238
+
 
 
 
239
  return status, pd.DataFrame(results)
240
 
241
  except Exception as e:
 
248
 
249
  with gr.Blocks() as demo:
250
  gr.Markdown("# 🎯 GAIA Agent - Unit 4")
251
+ gr.Markdown("Using **HuggingFace** + **Qwen 32B**")
 
 
 
 
 
 
252
 
253
  gr.LoginButton()
254
  run_btn = gr.Button("🚀 Run Evaluation", variant="primary")
255
+ status = gr.Textbox(label="Status", lines=5)
256
  table = gr.DataFrame(label="Results")
257
 
258
  run_btn.click(run_and_submit_all, outputs=[status, table])
259
 
260
  if __name__ == "__main__":
261
+ print("Starting GAIA Agent (HuggingFace)...")
262
+ print(f"HF_TOKEN: {'✅ Found' if os.environ.get('HF_TOKEN') else '❌ Missing'}")
 
 
 
 
 
 
 
263
  demo.launch()