Viske commited on
Commit
3e372ac
·
verified ·
1 Parent(s): c6c032e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +367 -162
app.py CHANGED
@@ -1,11 +1,13 @@
1
  import os
2
  import json
3
  import re
 
 
4
  import gradio as gr
5
  import requests
6
  import spaces
7
 
8
- from huggingface_hub import InferenceClient
9
 
10
  from prompt import SYSTEM_PROMPT
11
  from tools import execute_tool, download_file
@@ -17,29 +19,84 @@ from tools import execute_tool, download_file
17
 
18
  SCORING_API = "https://agents-course-unit4-scoring.hf.space"
19
 
20
- MODEL_ID = "Qwen/Qwen2.5-72B-Instruct"
 
 
 
 
 
21
 
22
- HF_TOKEN = os.getenv("HF_TOKEN")
23
 
24
- client = InferenceClient(
25
- model=MODEL_ID,
26
- token=HF_TOKEN
27
  )
28
 
29
 
30
  # ============================================================
31
- # ZERO GPU STARTUP PROBE
32
  # ============================================================
33
 
34
- # ZeroGPU requires at least one @spaces.GPU function to exist
35
- # when the Space starts.
 
36
  #
37
- # We DO NOT call this function.
38
- # Therefore it consumes no GPU quota.
39
 
40
  @spaces.GPU(duration=1)
41
  def zerogpu_probe():
42
- return "ok"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
 
45
  # ============================================================
@@ -47,68 +104,93 @@ def zerogpu_probe():
47
  # ============================================================
48
 
49
  def clean_answer(answer):
 
50
  if answer is None:
51
  return ""
52
 
53
  answer = str(answer).strip()
54
 
55
- answer = answer.replace("```", "")
56
- answer = answer.replace("FINAL ANSWER:", "")
57
- answer = answer.replace("Final Answer:", "")
58
- answer = answer.replace("THE ANSWER IS:", "")
59
- answer = answer.replace("The answer is:", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- answer = answer.strip()
62
 
63
- return answer
 
 
 
 
 
 
64
 
65
 
66
  # ============================================================
67
- # JSON EXTRACTION
68
  # ============================================================
69
 
70
- def extract_json(text):
71
 
72
- text = text.strip()
73
 
74
- # Direct JSON
75
- try:
76
- return json.loads(text)
77
- except:
78
- pass
79
 
80
- # Find JSON object inside model response
81
- match = re.search(r"\{.*\}", text, re.DOTALL)
82
-
83
- if match:
84
  try:
85
- return json.loads(match.group(0))
86
- except:
87
- pass
88
 
89
- return None
 
 
 
 
 
90
 
 
91
 
92
- # ============================================================
93
- # MODEL CALL
94
- # ============================================================
95
 
96
- def ask_model(messages):
97
 
98
- response = client.chat_completion(
99
- messages=messages,
100
- max_tokens=1200,
101
- temperature=0.1
102
- )
103
 
104
- return response.choices[0].message.content
 
 
 
 
 
 
 
 
105
 
106
 
107
  # ============================================================
108
- # SOLVE ONE GAIA QUESTION
109
  # ============================================================
110
 
111
- def solve_question(task_id, question):
 
 
 
112
 
113
  messages = [
114
  {
@@ -118,29 +200,50 @@ def solve_question(task_id, question):
118
  {
119
  "role": "user",
120
  "content": (
121
- f"GAIA TASK ID: {task_id}\n\n"
122
  f"QUESTION:\n{question}\n\n"
123
- "Solve this task completely. "
124
- "Use tools whenever necessary."
 
125
  )
126
  }
127
  ]
128
 
129
  current_file = None
130
 
131
- for step in range(12):
 
 
 
 
 
132
 
133
  try:
134
- response = ask_model(messages)
 
 
 
135
 
136
  except Exception as e:
137
- return f"ERROR: model request failed: {e}"
138
 
139
- parsed = extract_json(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  if parsed is None:
142
 
143
- # Give model another chance to follow JSON format
144
  messages.append(
145
  {
146
  "role": "assistant",
@@ -152,10 +255,11 @@ def solve_question(task_id, question):
152
  {
153
  "role": "user",
154
  "content": (
155
- "Your previous response was not valid JSON. "
156
- "Respond ONLY with either:\n"
157
- '{"action":"tool","tool":"TOOL_NAME","input":"..."}\n'
158
- "or:\n"
 
159
  '{"action":"final","answer":"..."}'
160
  )
161
  }
@@ -163,60 +267,59 @@ def solve_question(task_id, question):
163
 
164
  continue
165
 
166
- action = parsed.get("action")
 
 
167
 
168
  # ----------------------------------------------------
169
- # FINAL ANSWER
170
  # ----------------------------------------------------
171
 
172
  if action == "final":
173
 
174
- answer = parsed.get("answer", "")
 
 
 
175
 
176
- return clean_answer(answer)
 
 
177
 
178
  # ----------------------------------------------------
179
- # TOOL CALL
180
  # ----------------------------------------------------
181
 
182
  if action == "tool":
183
 
184
- tool_name = parsed.get("tool")
185
- tool_input = parsed.get("input", "")
 
186
 
187
- if not tool_name:
188
- messages.append(
189
- {
190
- "role": "user",
191
- "content": "You must provide a tool name."
192
- }
193
- )
194
- continue
195
 
196
- try:
 
 
197
 
198
- # ------------------------------------------------
199
- # Download file
200
- # ------------------------------------------------
201
 
 
202
  if tool_name == "download_file":
203
 
204
- file_path = download_file(
205
- task_id,
206
- tool_input
207
  )
208
 
209
- current_file = file_path
210
-
211
  result = (
212
- f"File downloaded successfully.\n"
213
- f"FILE_PATH: {file_path}"
214
  )
215
 
216
- # ------------------------------------------------
217
- # File tools automatically use current file
218
- # ------------------------------------------------
219
-
220
  elif tool_name in [
221
  "inspect_file",
222
  "transcribe_audio"
@@ -225,8 +328,8 @@ def solve_question(task_id, question):
225
  if not current_file:
226
 
227
  result = (
228
- "No file has been downloaded yet. "
229
- "Use download_file first."
230
  )
231
 
232
  else:
@@ -241,8 +344,8 @@ def solve_question(task_id, question):
241
  if not current_file:
242
 
243
  result = (
244
- "No image has been downloaded yet. "
245
- "Use download_file first."
246
  )
247
 
248
  else:
@@ -264,25 +367,39 @@ def solve_question(task_id, question):
264
 
265
  except Exception as e:
266
 
267
- result = f"TOOL ERROR: {e}"
 
 
268
 
269
- # Give tool result back to model
 
270
  messages.append(
271
  {
272
  "role": "assistant",
273
- "content": json.dumps(parsed)
 
 
274
  }
275
  )
276
 
 
 
 
 
 
 
 
 
 
 
277
  messages.append(
278
  {
279
  "role": "user",
280
  "content": (
281
- f"TOOL RESULT ({tool_name}):\n"
282
- f"{result}\n\n"
283
- "Continue solving the original question. "
284
- "Use another tool if necessary, otherwise return "
285
- "the final answer as JSON."
286
  )
287
  }
288
  )
@@ -297,18 +414,21 @@ def solve_question(task_id, question):
297
  {
298
  "role": "user",
299
  "content": (
300
- "Invalid action. Use exactly one of:\n"
301
  '{"action":"tool","tool":"TOOL_NAME","input":"..."}\n'
 
302
  '{"action":"final","answer":"..."}'
303
  )
304
  }
305
  )
306
 
307
- return "ERROR: agent exceeded maximum reasoning steps."
 
 
308
 
309
 
310
  # ============================================================
311
- # GET QUESTIONS
312
  # ============================================================
313
 
314
  def get_questions():
@@ -334,17 +454,22 @@ def get_questions():
334
 
335
 
336
  # ============================================================
337
- # SUBMIT ANSWERS
338
  # ============================================================
339
 
340
- def submit_answers(username, answers):
 
 
 
341
 
342
- space_id = os.getenv("SPACE_ID")
 
 
343
 
344
  if not space_id:
345
 
346
  raise RuntimeError(
347
- "SPACE_ID environment variable is missing."
348
  )
349
 
350
  agent_code = (
@@ -358,6 +483,14 @@ def submit_answers(username, answers):
358
  "answers": answers
359
  }
360
 
 
 
 
 
 
 
 
 
361
  response = requests.post(
362
  f"{SCORING_API}/submit",
363
  json=payload,
@@ -370,49 +503,93 @@ def submit_answers(username, answers):
370
 
371
 
372
  # ============================================================
373
- # MAIN EVALUATION
374
  # ============================================================
375
 
376
  def run_agent(username):
377
 
378
- if not username or not username.strip():
379
 
380
  return (
381
- "ERROR: Please enter your Hugging Face username.",
 
 
 
 
 
 
 
382
  []
383
  )
384
 
385
  username = username.strip()
386
 
387
- print("\n========================================")
 
388
  print("GAIA UNIT 4 AGENT")
389
- print("========================================")
 
 
 
 
390
 
391
- print(f"Username: {username}")
 
 
392
 
393
- questions = get_questions()
 
 
394
 
395
- print(f"Questions received: {len(questions)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
396
 
397
  answers_payload = []
 
398
  display_rows = []
399
 
400
- for index, question_data in enumerate(
 
 
 
 
401
  questions,
402
  start=1
403
  ):
404
 
405
- task_id = question_data.get("task_id")
 
 
406
 
407
- question = question_data.get(
408
  "question",
409
- question_data.get("prompt", "")
 
 
 
410
  )
411
 
412
- print("\n----------------------------------------")
413
- print(f"Question {index}/{len(questions)}")
414
- print(f"Task ID: {task_id}")
415
- print("----------------------------------------")
 
 
 
 
 
416
 
417
  try:
418
 
@@ -423,11 +600,17 @@ def run_agent(username):
423
 
424
  except Exception as e:
425
 
426
- answer = f"ERROR: {e}"
 
 
427
 
428
- answer = clean_answer(answer)
 
 
429
 
430
- print(f"ANSWER: {answer}")
 
 
431
 
432
  answers_payload.append(
433
  {
@@ -444,9 +627,17 @@ def run_agent(username):
444
  ]
445
  )
446
 
447
- print("\n========================================")
448
- print("SUBMITTING")
449
- print("========================================")
 
 
 
 
 
 
 
 
450
 
451
  try:
452
 
@@ -455,42 +646,56 @@ def run_agent(username):
455
  answers_payload
456
  )
457
 
458
- print("Submission result:")
459
- print(result)
460
 
461
- score = result.get(
462
- "score",
463
- result.get("total_score", "N/A")
464
  )
465
 
466
- correct = result.get(
467
- "correct_count",
468
- result.get("correct", "N/A")
469
- )
470
 
471
- total = result.get(
472
- "total_attempted",
473
- len(answers_payload)
 
 
 
 
 
 
474
  )
 
475
 
476
- status = (
477
- f"SUBMITTED\n"
478
- f"Score: {score}\n"
479
- f"Correct: {correct}/{total}"
 
480
  )
 
481
 
482
- return status, display_rows
 
 
 
483
 
484
- except Exception as e:
 
 
 
 
485
 
486
- return (
487
- f"SUBMISSION ERROR: {e}",
488
- display_rows
489
- )
490
 
491
 
492
  # ============================================================
493
- # SIMPLE UI
494
  # ============================================================
495
 
496
  with gr.Blocks(
@@ -499,22 +704,21 @@ with gr.Blocks(
499
 
500
  gr.Markdown(
501
  """
502
- # GAIA Unit 4 Agent
503
 
504
- Enter your Hugging Face username and run the agent.
505
 
506
- The agent will solve the 20 validation questions and submit
507
- the answers automatically.
508
- """
509
  )
510
 
511
  username = gr.Textbox(
512
  label="Hugging Face Username",
513
- placeholder="Your HF username"
514
  )
515
 
516
  run_button = gr.Button(
517
- "Run Agent & Submit",
518
  variant="primary"
519
  )
520
 
@@ -547,8 +751,9 @@ with gr.Blocks(
547
  )
548
 
549
 
550
- # IMPORTANT:
551
- # Disable Gradio SSR for better Spaces compatibility.
 
552
 
553
  demo.launch(
554
  debug=True,
 
1
  import os
2
  import json
3
  import re
4
+ import time
5
+
6
  import gradio as gr
7
  import requests
8
  import spaces
9
 
10
+ from groq import Groq
11
 
12
  from prompt import SYSTEM_PROMPT
13
  from tools import execute_tool, download_file
 
19
 
20
  SCORING_API = "https://agents-course-unit4-scoring.hf.space"
21
 
22
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
23
+
24
+ TEXT_MODEL = os.getenv(
25
+ "GROQ_TEXT_MODEL",
26
+ "openai/gpt-oss-120b"
27
+ )
28
 
29
+ MAX_STEPS = 5
30
 
31
+ client = Groq(
32
+ api_key=GROQ_API_KEY
 
33
  )
34
 
35
 
36
  # ============================================================
37
+ # ZERO GPU COMPATIBILITY
38
  # ============================================================
39
 
40
+ # The Space is running on ZeroGPU.
41
+ # Our actual agent does NOT need the GPU because Groq
42
+ # performs the model inference remotely.
43
  #
44
+ # This function exists so the Space is recognized as
45
+ # ZeroGPU-compatible.
46
 
47
  @spaces.GPU(duration=1)
48
  def zerogpu_probe():
49
+ return "ZeroGPU available"
50
+
51
+
52
+ # ============================================================
53
+ # JSON PARSING
54
+ # ============================================================
55
+
56
+ def extract_json(text):
57
+
58
+ if not text:
59
+ return None
60
+
61
+ text = text.strip()
62
+
63
+ try:
64
+ return json.loads(text)
65
+ except Exception:
66
+ pass
67
+
68
+ # Remove markdown code fences
69
+ cleaned = re.sub(
70
+ r"```(?:json)?",
71
+ "",
72
+ text,
73
+ flags=re.IGNORECASE
74
+ )
75
+
76
+ cleaned = cleaned.replace(
77
+ "```",
78
+ ""
79
+ ).strip()
80
+
81
+ try:
82
+ return json.loads(cleaned)
83
+ except Exception:
84
+ pass
85
+
86
+ # Find first JSON object
87
+ start = cleaned.find("{")
88
+ end = cleaned.rfind("}")
89
+
90
+ if start != -1 and end != -1 and end > start:
91
+
92
+ candidate = cleaned[start:end + 1]
93
+
94
+ try:
95
+ return json.loads(candidate)
96
+ except Exception:
97
+ pass
98
+
99
+ return None
100
 
101
 
102
  # ============================================================
 
104
  # ============================================================
105
 
106
  def clean_answer(answer):
107
+
108
  if answer is None:
109
  return ""
110
 
111
  answer = str(answer).strip()
112
 
113
+ answer = re.sub(
114
+ r"^```(?:text|json)?\s*",
115
+ "",
116
+ answer,
117
+ flags=re.IGNORECASE
118
+ )
119
+
120
+ answer = re.sub(
121
+ r"\s*```$",
122
+ "",
123
+ answer
124
+ )
125
+
126
+ prefixes = [
127
+ "FINAL ANSWER:",
128
+ "Final Answer:",
129
+ "THE ANSWER IS:",
130
+ "The answer is:",
131
+ "ANSWER:"
132
+ ]
133
 
134
+ for prefix in prefixes:
135
 
136
+ if answer.startswith(prefix):
137
+
138
+ answer = answer[
139
+ len(prefix):
140
+ ].strip()
141
+
142
+ return answer.strip()
143
 
144
 
145
  # ============================================================
146
+ # GROQ CALL
147
  # ============================================================
148
 
149
+ def call_groq(messages):
150
 
151
+ last_error = None
152
 
153
+ for attempt in range(3):
 
 
 
 
154
 
 
 
 
 
155
  try:
 
 
 
156
 
157
+ response = client.chat.completions.create(
158
+ model=TEXT_MODEL,
159
+ messages=messages,
160
+ temperature=0.1,
161
+ max_tokens=1200
162
+ )
163
 
164
+ return response.choices[0].message.content
165
 
166
+ except Exception as e:
 
 
167
 
168
+ last_error = e
169
 
170
+ print(
171
+ f"Groq error "
172
+ f"(attempt {attempt + 1}/3): {e}"
173
+ )
 
174
 
175
+ # Backoff for rate limits
176
+ if attempt < 2:
177
+ time.sleep(
178
+ 2 ** attempt
179
+ )
180
+
181
+ raise RuntimeError(
182
+ f"Groq request failed: {last_error}"
183
+ )
184
 
185
 
186
  # ============================================================
187
+ # SOLVE ONE QUESTION
188
  # ============================================================
189
 
190
+ def solve_question(
191
+ task_id,
192
+ question
193
+ ):
194
 
195
  messages = [
196
  {
 
200
  {
201
  "role": "user",
202
  "content": (
203
+ f"GAIA TASK ID:\n{task_id}\n\n"
204
  f"QUESTION:\n{question}\n\n"
205
+ "Solve this question. "
206
+ "Use tools only when necessary. "
207
+ "Return the exact requested answer."
208
  )
209
  }
210
  ]
211
 
212
  current_file = None
213
 
214
+ for step in range(MAX_STEPS):
215
+
216
+ print(
217
+ f" Agent step "
218
+ f"{step + 1}/{MAX_STEPS}"
219
+ )
220
 
221
  try:
222
+
223
+ response = call_groq(
224
+ messages
225
+ )
226
 
227
  except Exception as e:
 
228
 
229
+ return (
230
+ f"ERROR: model request failed: {e}"
231
+ )
232
+
233
+ print(
234
+ f" Model: {response[:500]}"
235
+ )
236
+
237
+ parsed = extract_json(
238
+ response
239
+ )
240
+
241
+ # ----------------------------------------------------
242
+ # Model failed JSON formatting
243
+ # ----------------------------------------------------
244
 
245
  if parsed is None:
246
 
 
247
  messages.append(
248
  {
249
  "role": "assistant",
 
255
  {
256
  "role": "user",
257
  "content": (
258
+ "Invalid format. "
259
+ "Respond ONLY with valid JSON.\n\n"
260
+ "For a tool:\n"
261
+ '{"action":"tool","tool":"TOOL_NAME","input":"..."}\n\n'
262
+ "For the final answer:\n"
263
  '{"action":"final","answer":"..."}'
264
  )
265
  }
 
267
 
268
  continue
269
 
270
+ action = parsed.get(
271
+ "action"
272
+ )
273
 
274
  # ----------------------------------------------------
275
+ # FINAL
276
  # ----------------------------------------------------
277
 
278
  if action == "final":
279
 
280
+ answer = parsed.get(
281
+ "answer",
282
+ ""
283
+ )
284
 
285
+ return clean_answer(
286
+ answer
287
+ )
288
 
289
  # ----------------------------------------------------
290
+ # TOOL
291
  # ----------------------------------------------------
292
 
293
  if action == "tool":
294
 
295
+ tool_name = parsed.get(
296
+ "tool"
297
+ )
298
 
299
+ tool_input = parsed.get(
300
+ "input",
301
+ ""
302
+ )
 
 
 
 
303
 
304
+ print(
305
+ f" Tool: {tool_name}"
306
+ )
307
 
308
+ try:
 
 
309
 
310
+ # Downloading a GAIA attachment
311
  if tool_name == "download_file":
312
 
313
+ current_file = download_file(
314
+ task_id
 
315
  )
316
 
 
 
317
  result = (
318
+ "File downloaded successfully.\n"
319
+ f"FILE_PATH: {current_file}"
320
  )
321
 
322
+ # Tools that require current file
 
 
 
323
  elif tool_name in [
324
  "inspect_file",
325
  "transcribe_audio"
 
328
  if not current_file:
329
 
330
  result = (
331
+ "No attachment has been downloaded. "
332
+ "Call download_file first."
333
  )
334
 
335
  else:
 
344
  if not current_file:
345
 
346
  result = (
347
+ "No image has been downloaded. "
348
+ "Call download_file first."
349
  )
350
 
351
  else:
 
367
 
368
  except Exception as e:
369
 
370
+ result = (
371
+ f"TOOL ERROR: {e}"
372
+ )
373
 
374
+ # Add the tool call and result
375
+ # to the conversation.
376
  messages.append(
377
  {
378
  "role": "assistant",
379
+ "content": json.dumps(
380
+ parsed
381
+ )
382
  }
383
  )
384
 
385
+ # Keep tool output reasonably short.
386
+ result = str(result)
387
+
388
+ if len(result) > 18000:
389
+
390
+ result = (
391
+ result[:18000]
392
+ + "\n[TOOL OUTPUT TRUNCATED]"
393
+ )
394
+
395
  messages.append(
396
  {
397
  "role": "user",
398
  "content": (
399
+ f"TOOL RESULT:\n{result}\n\n"
400
+ "Continue solving the original "
401
+ "question. If you now know the "
402
+ "answer, return the final JSON."
 
403
  )
404
  }
405
  )
 
414
  {
415
  "role": "user",
416
  "content": (
417
+ "Invalid action. Use exactly:\n"
418
  '{"action":"tool","tool":"TOOL_NAME","input":"..."}\n'
419
+ "or\n"
420
  '{"action":"final","answer":"..."}'
421
  )
422
  }
423
  )
424
 
425
+ return (
426
+ "ERROR: maximum reasoning steps reached."
427
+ )
428
 
429
 
430
  # ============================================================
431
+ # GET GAIA QUESTIONS
432
  # ============================================================
433
 
434
  def get_questions():
 
454
 
455
 
456
  # ============================================================
457
+ # SUBMIT
458
  # ============================================================
459
 
460
+ def submit_answers(
461
+ username,
462
+ answers
463
+ ):
464
 
465
+ space_id = os.getenv(
466
+ "SPACE_ID"
467
+ )
468
 
469
  if not space_id:
470
 
471
  raise RuntimeError(
472
+ "SPACE_ID is missing."
473
  )
474
 
475
  agent_code = (
 
483
  "answers": answers
484
  }
485
 
486
+ print(
487
+ "\nAgent code:"
488
+ )
489
+
490
+ print(
491
+ agent_code
492
+ )
493
+
494
  response = requests.post(
495
  f"{SCORING_API}/submit",
496
  json=payload,
 
503
 
504
 
505
  # ============================================================
506
+ # RUN ALL 20
507
  # ============================================================
508
 
509
  def run_agent(username):
510
 
511
+ if not GROQ_API_KEY:
512
 
513
  return (
514
+ "ERROR: GROQ_API_KEY is not configured.",
515
+ []
516
+ )
517
+
518
+ if not username:
519
+
520
+ return (
521
+ "ERROR: Enter your Hugging Face username.",
522
  []
523
  )
524
 
525
  username = username.strip()
526
 
527
+ print("\n")
528
+ print("=" * 60)
529
  print("GAIA UNIT 4 AGENT")
530
+ print("=" * 60)
531
+
532
+ print(
533
+ f"Username: {username}"
534
+ )
535
 
536
+ print(
537
+ f"Model: {TEXT_MODEL}"
538
+ )
539
 
540
+ # --------------------------------------------------------
541
+ # Get questions
542
+ # --------------------------------------------------------
543
 
544
+ try:
545
+
546
+ questions = get_questions()
547
+
548
+ except Exception as e:
549
+
550
+ return (
551
+ f"ERROR getting questions: {e}",
552
+ []
553
+ )
554
+
555
+ print(
556
+ f"Questions: {len(questions)}"
557
+ )
558
 
559
  answers_payload = []
560
+
561
  display_rows = []
562
 
563
+ # --------------------------------------------------------
564
+ # Sequential processing
565
+ # --------------------------------------------------------
566
+
567
+ for index, item in enumerate(
568
  questions,
569
  start=1
570
  ):
571
 
572
+ task_id = item.get(
573
+ "task_id"
574
+ )
575
 
576
+ question = item.get(
577
  "question",
578
+ item.get(
579
+ "prompt",
580
+ ""
581
+ )
582
  )
583
 
584
+ print("\n")
585
+ print("=" * 60)
586
+ print(
587
+ f"QUESTION {index}/{len(questions)}"
588
+ )
589
+ print(
590
+ f"TASK ID: {task_id}"
591
+ )
592
+ print("=" * 60)
593
 
594
  try:
595
 
 
600
 
601
  except Exception as e:
602
 
603
+ answer = (
604
+ f"ERROR: {e}"
605
+ )
606
 
607
+ answer = clean_answer(
608
+ answer
609
+ )
610
 
611
+ print(
612
+ f"FINAL ANSWER: {answer}"
613
+ )
614
 
615
  answers_payload.append(
616
  {
 
627
  ]
628
  )
629
 
630
+ # Small pause between questions.
631
+ time.sleep(0.5)
632
+
633
+ # --------------------------------------------------------
634
+ # Submit once
635
+ # --------------------------------------------------------
636
+
637
+ print("\n")
638
+ print("=" * 60)
639
+ print("SUBMITTING ALL ANSWERS")
640
+ print("=" * 60)
641
 
642
  try:
643
 
 
646
  answers_payload
647
  )
648
 
649
+ except Exception as e:
 
650
 
651
+ return (
652
+ f"SUBMISSION ERROR: {e}",
653
+ display_rows
654
  )
655
 
656
+ print(
657
+ "SUBMISSION RESULT:"
658
+ )
 
659
 
660
+ print(
661
+ result
662
+ )
663
+
664
+ score = result.get(
665
+ "score",
666
+ result.get(
667
+ "total_score",
668
+ "N/A"
669
  )
670
+ )
671
 
672
+ correct = result.get(
673
+ "correct_count",
674
+ result.get(
675
+ "correct",
676
+ "N/A"
677
  )
678
+ )
679
 
680
+ total = result.get(
681
+ "total_attempted",
682
+ len(answers_payload)
683
+ )
684
 
685
+ status = (
686
+ "SUBMITTED\n"
687
+ f"Score: {score}\n"
688
+ f"Correct: {correct}/{total}"
689
+ )
690
 
691
+ return (
692
+ status,
693
+ display_rows
694
+ )
695
 
696
 
697
  # ============================================================
698
+ # GRADIO UI
699
  # ============================================================
700
 
701
  with gr.Blocks(
 
704
 
705
  gr.Markdown(
706
  """
707
+ # GAIA Unit 4 Agent
708
 
709
+ Groq-powered autonomous agent for the GAIA Unit 4 final assignment.
710
 
711
+ Enter your Hugging Face username and run the evaluation.
712
+ """
 
713
  )
714
 
715
  username = gr.Textbox(
716
  label="Hugging Face Username",
717
+ placeholder="e.g. Viske"
718
  )
719
 
720
  run_button = gr.Button(
721
+ "Run 20 Questions & Submit",
722
  variant="primary"
723
  )
724
 
 
751
  )
752
 
753
 
754
+ # ============================================================
755
+ # LAUNCH
756
+ # ============================================================
757
 
758
  demo.launch(
759
  debug=True,