Viske commited on
Commit
8d7de3f
·
verified ·
1 Parent(s): 46720b5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +235 -250
app.py CHANGED
@@ -4,27 +4,19 @@ import time
4
  import requests
5
  import gradio as gr
6
  import spaces
7
-
8
  from groq import Groq
9
 
10
  from prompt import SYSTEM_PROMPT
11
- from tools import (
12
- web_search,
13
- read_webpage,
14
- download_file,
15
- inspect_file,
16
- run_python,
17
- transcribe_audio,
18
- analyze_image,
19
- )
20
 
21
 
22
  SCORING_API = "https://agents-course-unit4-scoring.hf.space"
23
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
24
 
 
25
  TEXT_MODEL = os.getenv("GROQ_TEXT_MODEL", "openai/gpt-oss-120b")
26
 
27
- MAX_STEPS = 5
 
28
 
29
  client = Groq(api_key=GROQ_API_KEY)
30
 
@@ -39,7 +31,7 @@ def zerogpu_probe():
39
 
40
 
41
  # ---------------------------------------------------------
42
- # Real Groq function tools
43
  # ---------------------------------------------------------
44
 
45
  TOOLS = [
@@ -47,75 +39,112 @@ TOOLS = [
47
  "type": "function",
48
  "function": {
49
  "name": "web_search",
50
- "description": "Search the web for factual information. Use this when the answer requires external research.",
 
 
 
 
51
  "parameters": {
52
  "type": "object",
53
  "properties": {
54
  "query": {
55
  "type": "string",
56
- "description": "A concise web search query."
57
  }
58
  },
59
- "required": ["query"]
 
60
  }
61
  }
62
  },
 
63
  {
64
  "type": "function",
65
  "function": {
66
  "name": "read_webpage",
67
- "description": "Read and extract useful text from a webpage URL.",
 
 
 
 
 
 
68
  "parameters": {
69
  "type": "object",
70
  "properties": {
71
  "url": {
72
  "type": "string",
73
- "description": "The webpage URL to read."
 
 
 
 
 
 
 
 
74
  }
75
  },
76
- "required": ["url"]
 
77
  }
78
  }
79
  },
 
80
  {
81
  "type": "function",
82
  "function": {
83
  "name": "download_file",
84
- "description": "Download the attachment associated with a GAIA task. Must be used before inspecting, transcribing, or analyzing an attachment.",
 
 
 
85
  "parameters": {
86
  "type": "object",
87
  "properties": {
88
  "task_id": {
89
  "type": "string",
90
- "description": "The GAIA task ID."
91
  }
92
  },
93
- "required": ["task_id"]
 
94
  }
95
  }
96
  },
 
97
  {
98
  "type": "function",
99
  "function": {
100
  "name": "inspect_file",
101
- "description": "Inspect a downloaded attachment such as TXT, CSV, XLSX, PDF, PY, or other text/data files. The task_id is used to locate and download the attachment automatically.",
 
 
 
 
102
  "parameters": {
103
  "type": "object",
104
  "properties": {
105
  "task_id": {
106
  "type": "string",
107
- "description": "The GAIA task ID whose attachment should be inspected."
108
  }
109
  },
110
- "required": ["task_id"]
 
111
  }
112
  }
113
  },
 
114
  {
115
  "type": "function",
116
  "function": {
117
  "name": "run_python",
118
- "description": "Run Python code for calculations or analysis. Use this for numerical, tabular, or computational tasks.",
 
 
 
 
119
  "parameters": {
120
  "type": "object",
121
  "properties": {
@@ -124,45 +153,56 @@ TOOLS = [
124
  "description": "Python code to execute."
125
  }
126
  },
127
- "required": ["code"]
 
128
  }
129
  }
130
  },
 
131
  {
132
  "type": "function",
133
  "function": {
134
  "name": "transcribe_audio",
135
- "description": "Transcribe an audio attachment from a GAIA task. The task attachment is automatically downloaded first.",
 
 
 
136
  "parameters": {
137
  "type": "object",
138
  "properties": {
139
  "task_id": {
140
  "type": "string",
141
- "description": "The GAIA task ID."
142
  }
143
  },
144
- "required": ["task_id"]
 
145
  }
146
  }
147
  },
 
148
  {
149
  "type": "function",
150
  "function": {
151
  "name": "analyze_image",
152
- "description": "Analyze an image attachment from a GAIA task. The task attachment is automatically downloaded first.",
 
 
 
153
  "parameters": {
154
  "type": "object",
155
  "properties": {
156
  "task_id": {
157
  "type": "string",
158
- "description": "The GAIA task ID."
159
  },
160
  "question": {
161
  "type": "string",
162
- "description": "What should be determined from the image?"
163
  }
164
  },
165
- "required": ["task_id", "question"]
 
166
  }
167
  }
168
  }
@@ -170,86 +210,77 @@ TOOLS = [
170
 
171
 
172
  # ---------------------------------------------------------
173
- # Tool execution
174
  # ---------------------------------------------------------
175
 
176
- def execute_tool(name, args, task_id):
177
- try:
178
-
179
- if name == "web_search":
180
- return web_search(args["query"])
181
-
182
- if name == "read_webpage":
183
- return read_webpage(args["url"])
184
-
185
- if name == "download_file":
186
- path = download_file(task_id)
187
- return f"Attachment downloaded successfully.\nPath: {path}"
188
 
189
- if name == "inspect_file":
190
- path = download_file(task_id)
191
- return inspect_file(path)
 
 
 
 
 
192
 
193
- if name == "run_python":
194
- return run_python(args["code"])
 
 
 
 
195
 
196
- if name == "transcribe_audio":
197
- path = download_file(task_id)
198
- return transcribe_audio(path)
199
 
200
- if name == "analyze_image":
201
- path = download_file(task_id)
202
- return analyze_image(
203
- path,
204
- args.get("question", "")
205
- )
206
 
207
- return f"Unknown tool: {name}"
 
208
 
209
- except Exception as e:
210
- return f"Tool error: {type(e).__name__}: {str(e)}"
 
211
 
212
 
213
  # ---------------------------------------------------------
214
- # Groq call
215
  # ---------------------------------------------------------
216
 
217
- def call_groq(messages, use_tools=True):
 
 
218
 
219
- kwargs = {
220
- "model": TEXT_MODEL,
221
- "messages": messages,
222
- "temperature": 0.1,
223
- "max_completion_tokens": 1200,
224
- }
225
 
226
- if use_tools:
227
- kwargs["tools"] = TOOLS
228
- kwargs["tool_choice"] = "auto"
229
- else:
230
- # Important:
231
- # After receiving tool results, explicitly prevent another
232
- # tool call and force a final textual answer.
233
- kwargs["tools"] = TOOLS
234
- kwargs["tool_choice"] = "none"
235
 
236
- for attempt in range(3):
 
237
 
238
- try:
239
- response = client.chat.completions.create(**kwargs)
240
- return response.choices[0].message
241
 
242
- except Exception as e:
243
 
244
- print(
245
- f"Groq error (attempt {attempt + 1}/3): "
246
- f"{type(e).__name__}: {e}"
247
- )
248
 
249
- if attempt < 2:
250
- time.sleep(2 ** attempt)
 
 
 
 
 
 
251
 
252
- raise RuntimeError("Groq request failed after 3 attempts.")
253
 
254
 
255
  # ---------------------------------------------------------
@@ -258,7 +289,10 @@ def call_groq(messages, use_tools=True):
258
 
259
  def solve_question(task_id, question):
260
 
261
- print(f"\n Agent solving task {task_id}")
 
 
 
262
 
263
  messages = [
264
  {
@@ -270,128 +304,90 @@ def solve_question(task_id, question):
270
  "content": (
271
  f"GAIA TASK ID: {task_id}\n\n"
272
  f"QUESTION:\n{question}\n\n"
273
- "Solve this question. Use tools when necessary. "
274
- "Return only the exact final answer when finished."
275
  )
276
  }
277
  ]
278
 
279
  for step in range(1, MAX_STEPS + 1):
280
 
281
- print(f" Agent step {step}/{MAX_STEPS}")
 
 
 
 
 
282
 
283
- message = call_groq(messages, use_tools=True)
284
 
285
- # -------------------------------------------------
286
- # Model requested tools
287
- # -------------------------------------------------
 
288
 
289
- if message.tool_calls:
 
 
 
290
 
291
- print(
292
- " Tool calls:",
293
- [x.function.name for x in message.tool_calls]
294
- )
295
 
296
- # Add assistant message containing the tool calls
297
- messages.append(message)
298
 
299
- for tool_call in message.tool_calls:
300
 
301
- name = tool_call.function.name
 
 
 
302
 
303
- try:
304
- args = json.loads(tool_call.function.arguments)
305
- except Exception:
306
- args = {}
307
 
308
- print(f" Executing: {name}")
 
 
 
 
 
 
 
309
 
 
310
  result = execute_tool(
311
- name,
312
- args,
313
- task_id
314
  )
315
 
316
- # Keep context manageable.
317
- if result is None:
318
- result = ""
319
 
320
- result = str(result)
321
 
322
- if len(result) > 9000:
323
- result = result[:9000] + "\n[OUTPUT TRUNCATED]"
324
-
325
- print(
326
- f" Tool result: {len(result)} characters"
327
- )
328
-
329
- messages.append(
330
- {
331
- "role": "tool",
332
- "tool_call_id": tool_call.id,
333
- "name": name,
334
- "content": result
335
- }
336
  )
337
 
338
- # Continue agent loop.
339
- continue
340
-
341
- # -------------------------------------------------
342
- # No tool call = model has an answer
343
- # -------------------------------------------------
344
-
345
- content = message.content or ""
346
-
347
- print(" Model:", content[:1000])
348
-
349
- if content.strip():
350
- return clean_answer(content)
351
-
352
- return "ERROR: maximum reasoning steps reached."
353
-
354
-
355
- # ---------------------------------------------------------
356
- # Clean final answer
357
- # ---------------------------------------------------------
358
-
359
- def clean_answer(text):
360
-
361
- text = text.strip()
362
-
363
- # Sometimes the model still wraps the final answer in JSON.
364
- try:
365
- obj = json.loads(text)
366
-
367
- if isinstance(obj, dict):
368
-
369
- if "answer" in obj:
370
- return str(obj["answer"]).strip()
371
-
372
- if "final_answer" in obj:
373
- return str(obj["final_answer"]).strip()
374
-
375
- except Exception:
376
- pass
377
-
378
- # Remove accidental prefixes.
379
- prefixes = [
380
- "FINAL ANSWER:",
381
- "Final answer:",
382
- "final answer:",
383
- ]
384
-
385
- for prefix in prefixes:
386
 
387
- if text.startswith(prefix):
388
- text = text[len(prefix):].strip()
 
 
 
 
 
 
389
 
390
- return text
391
 
392
 
393
  # ---------------------------------------------------------
394
- # Get GAIA questions
395
  # ---------------------------------------------------------
396
 
397
  def get_questions():
@@ -405,27 +401,28 @@ def get_questions():
405
 
406
  data = response.json()
407
 
 
 
 
408
  if isinstance(data, dict):
409
  if "questions" in data:
410
  return data["questions"]
411
 
412
- return data
413
 
414
 
415
  # ---------------------------------------------------------
416
- # Submit answers
417
  # ---------------------------------------------------------
418
 
419
  def submit_answers(username, answers):
420
 
421
- agent_code = (
422
- f"https://huggingface.co/spaces/"
423
- f"{username}/Final_Assignment_Template/tree/main"
424
- )
425
-
426
  payload = {
427
  "username": username,
428
- "agent_code": agent_code,
 
 
 
429
  "answers": answers
430
  }
431
 
@@ -435,57 +432,48 @@ def submit_answers(username, answers):
435
  timeout=60
436
  )
437
 
438
- print("\nSUBMISSION RESPONSE:")
439
- print(response.text)
440
 
441
- return response.text
442
 
443
 
444
  # ---------------------------------------------------------
445
- # Main run
446
  # ---------------------------------------------------------
447
 
448
  def run_agent(username):
449
 
450
- print("\n" + "=" * 60)
451
- print("GAIA UNIT 4 AGENT")
452
- print("=" * 60)
453
 
454
- print(f"Username: {username}")
455
- print(f"Model: {TEXT_MODEL}")
 
 
 
 
456
 
457
  questions = get_questions()
458
 
459
- print(f"Questions: {len(questions)}")
460
- print("=" * 60)
461
 
462
  answers = []
463
 
464
- for index, item in enumerate(questions, 1):
465
 
466
- task_id = item.get("task_id")
467
  question = item.get("question")
468
 
469
- print("\n")
470
  print("=" * 60)
471
- print(f"QUESTION {index}/{len(questions)}")
472
  print(f"TASK ID: {task_id}")
473
  print("=" * 60)
474
 
475
- try:
476
-
477
- answer = solve_question(
478
- task_id,
479
- question
480
- )
481
-
482
- except Exception as e:
483
-
484
- answer = (
485
- f"ERROR: {type(e).__name__}: {str(e)}"
486
- )
487
-
488
- print("FINAL ANSWER:", answer)
489
 
490
  answers.append(
491
  {
@@ -494,35 +482,32 @@ def run_agent(username):
494
  }
495
  )
496
 
497
- # Small pause between questions.
498
  time.sleep(0.5)
499
 
500
- print("\n")
501
- print("=" * 60)
502
- print("SUBMITTING ALL ANSWERS")
503
- print("=" * 60)
504
 
505
  result = submit_answers(
506
- username,
507
- answers
508
  )
509
 
510
- return result
 
 
 
 
 
 
511
 
512
 
513
  # ---------------------------------------------------------
514
- # Gradio UI
515
  # ---------------------------------------------------------
516
 
517
- with gr.Blocks(title="GAIA Unit 4 Agent") as demo:
518
-
519
- gr.Markdown(
520
- """
521
- # GAIA Unit 4 Agent
522
 
523
- Enter your Hugging Face username and run the 20-question GAIA validation.
524
- """
525
- )
526
 
527
  username = gr.Textbox(
528
  label="Hugging Face Username",
@@ -530,13 +515,12 @@ Enter your Hugging Face username and run the 20-question GAIA validation.
530
  )
531
 
532
  run_button = gr.Button(
533
- "Run GAIA Agent",
534
- variant="primary"
535
  )
536
 
537
  output = gr.Textbox(
538
  label="Result",
539
- lines=10
540
  )
541
 
542
  run_button.click(
@@ -546,8 +530,9 @@ Enter your Hugging Face username and run the 20-question GAIA validation.
546
  )
547
 
548
 
549
- demo.launch(
550
- debug=True,
551
- share=False,
552
- ssr_mode=False
553
- )
 
 
4
  import requests
5
  import gradio as gr
6
  import spaces
 
7
  from groq import Groq
8
 
9
  from prompt import SYSTEM_PROMPT
10
+ from tools import execute_tool
 
 
 
 
 
 
 
 
11
 
12
 
13
  SCORING_API = "https://agents-course-unit4-scoring.hf.space"
 
14
 
15
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
16
  TEXT_MODEL = os.getenv("GROQ_TEXT_MODEL", "openai/gpt-oss-120b")
17
 
18
+ MAX_STEPS = 6
19
+ MAX_TOOL_RESULT = 6000
20
 
21
  client = Groq(api_key=GROQ_API_KEY)
22
 
 
31
 
32
 
33
  # ---------------------------------------------------------
34
+ # Native Groq tools
35
  # ---------------------------------------------------------
36
 
37
  TOOLS = [
 
39
  "type": "function",
40
  "function": {
41
  "name": "web_search",
42
+ "description": (
43
+ "Search the public web for factual information. "
44
+ "Use this first for questions requiring current, historical, "
45
+ "Wikipedia, YouTube, NASA, IOC, news, or other web research."
46
+ ),
47
  "parameters": {
48
  "type": "object",
49
  "properties": {
50
  "query": {
51
  "type": "string",
52
+ "description": "The exact web search query."
53
  }
54
  },
55
+ "required": ["query"],
56
+ "additionalProperties": False
57
  }
58
  }
59
  },
60
+
61
  {
62
  "type": "function",
63
  "function": {
64
  "name": "read_webpage",
65
+ "description": (
66
+ "Read a webpage returned by web_search. "
67
+ "Normally provide the full URL from a search result. "
68
+ "If you do not know the URL, you may provide cursor as the "
69
+ "zero-based search-result index and optionally pattern. "
70
+ "The application will resolve the URL from the most recent search."
71
+ ),
72
  "parameters": {
73
  "type": "object",
74
  "properties": {
75
  "url": {
76
  "type": "string",
77
+ "description": "Full webpage URL. Optional if cursor is provided."
78
+ },
79
+ "cursor": {
80
+ "type": "integer",
81
+ "description": "Zero-based index of the most recent web-search result."
82
+ },
83
+ "pattern": {
84
+ "type": "string",
85
+ "description": "Optional text/topic to focus on while reading."
86
  }
87
  },
88
+ "required": [],
89
+ "additionalProperties": False
90
  }
91
  }
92
  },
93
+
94
  {
95
  "type": "function",
96
  "function": {
97
  "name": "download_file",
98
+ "description": (
99
+ "Download the attachment associated with the current GAIA task. "
100
+ "Use this before inspecting an Excel, PDF, image, audio, or Python attachment."
101
+ ),
102
  "parameters": {
103
  "type": "object",
104
  "properties": {
105
  "task_id": {
106
  "type": "string",
107
+ "description": "The current GAIA task ID."
108
  }
109
  },
110
+ "required": ["task_id"],
111
+ "additionalProperties": False
112
  }
113
  }
114
  },
115
+
116
  {
117
  "type": "function",
118
  "function": {
119
  "name": "inspect_file",
120
+ "description": (
121
+ "Inspect the current GAIA attachment. "
122
+ "Useful for Excel, CSV, PDF, TXT, Python, JSON and other files. "
123
+ "The application automatically downloads the task attachment."
124
+ ),
125
  "parameters": {
126
  "type": "object",
127
  "properties": {
128
  "task_id": {
129
  "type": "string",
130
+ "description": "The current GAIA task ID."
131
  }
132
  },
133
+ "required": ["task_id"],
134
+ "additionalProperties": False
135
  }
136
  }
137
  },
138
+
139
  {
140
  "type": "function",
141
  "function": {
142
  "name": "run_python",
143
+ "description": (
144
+ "Run Python code for calculations, data processing, string operations, "
145
+ "statistics, or analyzing a downloaded attachment. "
146
+ "Do not use shell commands."
147
+ ),
148
  "parameters": {
149
  "type": "object",
150
  "properties": {
 
153
  "description": "Python code to execute."
154
  }
155
  },
156
+ "required": ["code"],
157
+ "additionalProperties": False
158
  }
159
  }
160
  },
161
+
162
  {
163
  "type": "function",
164
  "function": {
165
  "name": "transcribe_audio",
166
+ "description": (
167
+ "Transcribe the audio attachment for the current GAIA task. "
168
+ "Use when the question depends on spoken audio."
169
+ ),
170
  "parameters": {
171
  "type": "object",
172
  "properties": {
173
  "task_id": {
174
  "type": "string",
175
+ "description": "The current GAIA task ID."
176
  }
177
  },
178
+ "required": ["task_id"],
179
+ "additionalProperties": False
180
  }
181
  }
182
  },
183
+
184
  {
185
  "type": "function",
186
  "function": {
187
  "name": "analyze_image",
188
+ "description": (
189
+ "Analyze the image attachment for the current GAIA task. "
190
+ "Use this for visual questions, diagrams, screenshots, chess boards, etc."
191
+ ),
192
  "parameters": {
193
  "type": "object",
194
  "properties": {
195
  "task_id": {
196
  "type": "string",
197
+ "description": "The current GAIA task ID."
198
  },
199
  "question": {
200
  "type": "string",
201
+ "description": "What specifically must be determined from the image."
202
  }
203
  },
204
+ "required": ["task_id", "question"],
205
+ "additionalProperties": False
206
  }
207
  }
208
  }
 
210
 
211
 
212
  # ---------------------------------------------------------
213
+ # Groq call
214
  # ---------------------------------------------------------
215
 
216
+ def call_groq(messages, use_tools=True):
217
+ last_error = None
 
 
 
 
 
 
 
 
 
 
218
 
219
+ for attempt in range(3):
220
+ try:
221
+ kwargs = {
222
+ "model": TEXT_MODEL,
223
+ "messages": messages,
224
+ "temperature": 0,
225
+ "max_completion_tokens": 2500,
226
+ }
227
 
228
+ if use_tools:
229
+ kwargs["tools"] = TOOLS
230
+ kwargs["tool_choice"] = "auto"
231
+ else:
232
+ # No tools on final answer call.
233
+ kwargs["tool_choice"] = "none"
234
 
235
+ response = client.chat.completions.create(**kwargs)
236
+ return response.choices[0].message
 
237
 
238
+ except Exception as e:
239
+ last_error = e
240
+ print(f"Groq error (attempt {attempt + 1}/3): {type(e).__name__}: {e}")
 
 
 
241
 
242
+ if attempt < 2:
243
+ time.sleep(2 ** attempt)
244
 
245
+ raise RuntimeError(
246
+ f"Groq request failed after 3 attempts: {last_error}"
247
+ )
248
 
249
 
250
  # ---------------------------------------------------------
251
+ # Answer cleanup
252
  # ---------------------------------------------------------
253
 
254
+ def clean_answer(answer):
255
+ if answer is None:
256
+ return ""
257
 
258
+ answer = str(answer).strip()
 
 
 
 
 
259
 
260
+ if answer.startswith("```"):
261
+ lines = answer.splitlines()
 
 
 
 
 
 
 
262
 
263
+ if lines and lines[0].startswith("```"):
264
+ lines = lines[1:]
265
 
266
+ if lines and lines[-1].strip() == "```":
267
+ lines = lines[:-1]
 
268
 
269
+ answer = "\n".join(lines).strip()
270
 
271
+ if answer.upper().startswith("FINAL ANSWER:"):
272
+ answer = answer[len("FINAL ANSWER:"):].strip()
 
 
273
 
274
+ # Handle accidental JSON final response
275
+ try:
276
+ obj = json.loads(answer)
277
+
278
+ if isinstance(obj, dict) and "answer" in obj:
279
+ answer = str(obj["answer"]).strip()
280
+ except Exception:
281
+ pass
282
 
283
+ return answer
284
 
285
 
286
  # ---------------------------------------------------------
 
289
 
290
  def solve_question(task_id, question):
291
 
292
+ print()
293
+ print("=" * 60)
294
+ print(f"Agent solving task {task_id}")
295
+ print("=" * 60)
296
 
297
  messages = [
298
  {
 
304
  "content": (
305
  f"GAIA TASK ID: {task_id}\n\n"
306
  f"QUESTION:\n{question}\n\n"
307
+ "Solve this task and return only the exact answer when finished."
 
308
  )
309
  }
310
  ]
311
 
312
  for step in range(1, MAX_STEPS + 1):
313
 
314
+ print(f"Agent step {step}/{MAX_STEPS}")
315
+
316
+ try:
317
+ message = call_groq(messages, use_tools=True)
318
+ except Exception as e:
319
+ return f"ERROR: {e}"
320
 
321
+ tool_calls = message.tool_calls or []
322
 
323
+ if not tool_calls:
324
+ answer = clean_answer(message.content)
325
+ print(f"FINAL ANSWER: {answer}")
326
+ return answer
327
 
328
+ print(
329
+ "Tool calls:",
330
+ [call.function.name for call in tool_calls]
331
+ )
332
 
333
+ # Important: append the assistant tool-call message exactly as returned.
334
+ messages.append(message)
 
 
335
 
336
+ for tool_call in tool_calls:
 
337
 
338
+ name = tool_call.function.name
339
 
340
+ try:
341
+ args = json.loads(tool_call.function.arguments or "{}")
342
+ except Exception:
343
+ args = {}
344
 
345
+ print(f"Executing: {name}")
346
+ print(f"Arguments: {args}")
 
 
347
 
348
+ # Force current task ID into task-dependent tools.
349
+ if name in {
350
+ "download_file",
351
+ "inspect_file",
352
+ "transcribe_audio",
353
+ "analyze_image"
354
+ }:
355
+ args["task_id"] = task_id
356
 
357
+ try:
358
  result = execute_tool(
359
+ name=name,
360
+ args=args,
361
+ task_id=task_id
362
  )
363
 
364
+ except Exception as e:
365
+ result = f"Tool error: {type(e).__name__}: {e}"
 
366
 
367
+ result = str(result)
368
 
369
+ if len(result) > MAX_TOOL_RESULT:
370
+ result = (
371
+ result[:MAX_TOOL_RESULT]
372
+ + "\n[TOOL OUTPUT TRUNCATED]"
 
 
 
 
 
 
 
 
 
 
373
  )
374
 
375
+ print(f"Tool result: {len(result)} characters")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
 
377
+ messages.append(
378
+ {
379
+ "role": "tool",
380
+ "tool_call_id": tool_call.id,
381
+ "name": name,
382
+ "content": result
383
+ }
384
+ )
385
 
386
+ return "ERROR: Maximum reasoning steps reached."
387
 
388
 
389
  # ---------------------------------------------------------
390
+ # Fetch questions
391
  # ---------------------------------------------------------
392
 
393
  def get_questions():
 
401
 
402
  data = response.json()
403
 
404
+ if isinstance(data, list):
405
+ return data
406
+
407
  if isinstance(data, dict):
408
  if "questions" in data:
409
  return data["questions"]
410
 
411
+ raise RuntimeError("Unexpected /questions response format.")
412
 
413
 
414
  # ---------------------------------------------------------
415
+ # Submit
416
  # ---------------------------------------------------------
417
 
418
  def submit_answers(username, answers):
419
 
 
 
 
 
 
420
  payload = {
421
  "username": username,
422
+ "agent_code": (
423
+ "https://huggingface.co/spaces/"
424
+ f"{username}/Final_Assignment_Template/tree/main"
425
+ ),
426
  "answers": answers
427
  }
428
 
 
432
  timeout=60
433
  )
434
 
435
+ response.raise_for_status()
 
436
 
437
+ return response.json()
438
 
439
 
440
  # ---------------------------------------------------------
441
+ # Run all questions
442
  # ---------------------------------------------------------
443
 
444
  def run_agent(username):
445
 
446
+ if not GROQ_API_KEY:
447
+ return "ERROR: GROQ_API_KEY is not configured in Space Secrets."
 
448
 
449
+ username = username.strip()
450
+
451
+ if not username:
452
+ return "ERROR: Enter your Hugging Face username."
453
+
454
+ print("\nFetching GAIA questions...")
455
 
456
  questions = get_questions()
457
 
458
+ print(f"Found {len(questions)} questions.")
 
459
 
460
  answers = []
461
 
462
+ for i, item in enumerate(questions, start=1):
463
 
464
+ task_id = item.get("task_id") or item.get("id")
465
  question = item.get("question")
466
 
467
+ print()
468
  print("=" * 60)
469
+ print(f"QUESTION {i}/{len(questions)}")
470
  print(f"TASK ID: {task_id}")
471
  print("=" * 60)
472
 
473
+ answer = solve_question(
474
+ task_id=task_id,
475
+ question=question
476
+ )
 
 
 
 
 
 
 
 
 
 
477
 
478
  answers.append(
479
  {
 
482
  }
483
  )
484
 
485
+ # Small delay between questions.
486
  time.sleep(0.5)
487
 
488
+ print("\nSubmitting answers...")
 
 
 
489
 
490
  result = submit_answers(
491
+ username=username,
492
+ answers=answers
493
  )
494
 
495
+ print("\nSUBMISSION RESULT:")
496
+ print(result)
497
+
498
+ return json.dumps(
499
+ result,
500
+ indent=2
501
+ )
502
 
503
 
504
  # ---------------------------------------------------------
505
+ # Simple UI
506
  # ---------------------------------------------------------
507
 
508
+ with gr.Blocks(title="GAIA Agent") as demo:
 
 
 
 
509
 
510
+ gr.Markdown("# GAIA Unit 4 Agent")
 
 
511
 
512
  username = gr.Textbox(
513
  label="Hugging Face Username",
 
515
  )
516
 
517
  run_button = gr.Button(
518
+ "Run Agent"
 
519
  )
520
 
521
  output = gr.Textbox(
522
  label="Result",
523
+ lines=15
524
  )
525
 
526
  run_button.click(
 
530
  )
531
 
532
 
533
+ if __name__ == "__main__":
534
+ demo.launch(
535
+ debug=True,
536
+ share=False,
537
+ ssr_mode=False
538
+ )