maytemuma commited on
Commit
6bdaab6
·
verified ·
1 Parent(s): b267d32

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +226 -108
app.py CHANGED
@@ -5,7 +5,6 @@ import pandas as pd
5
  from smolagents import (
6
  CodeAgent,
7
  DuckDuckGoSearchTool,
8
- WebSearchTool,
9
  VisitWebpageTool,
10
  InferenceClientModel,
11
  tool,
@@ -23,7 +22,7 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
23
  def download_file_from_api(task_id: str) -> str:
24
  """Downloads a file associated with a GAIA task and returns its text content.
25
  Use this tool when a question mentions an attached file, spreadsheet, image,
26
- audio, or any document that you need to read.
27
 
28
  Args:
29
  task_id: The task_id string for the question that has an associated file.
@@ -36,25 +35,29 @@ def download_file_from_api(task_id: str) -> str:
36
  response.raise_for_status()
37
 
38
  content_type = response.headers.get("Content-Type", "")
 
39
 
40
- # If it's a text-based file, return the text directly
41
- if "text" in content_type or "json" in content_type or "csv" in content_type:
42
- return response.text[:10000] # Limit to avoid context overflow
43
-
44
- # For Excel files
45
- if "spreadsheet" in content_type or "excel" in content_type:
46
- import openpyxl
47
- import io
48
- wb = openpyxl.load_workbook(io.BytesIO(response.content))
49
- result = []
50
- for sheet_name in wb.sheetnames:
51
- ws = wb[sheet_name]
52
- result.append(f"--- Sheet: {sheet_name} ---")
53
- for row in ws.iter_rows(values_only=True):
54
- result.append("\t".join([str(c) if c is not None else "" for c in row]))
55
- return "\n".join(result)[:10000]
56
-
57
- # For PDF files
 
 
 
58
  if "pdf" in content_type:
59
  try:
60
  import PyPDF2
@@ -63,70 +66,204 @@ def download_file_from_api(task_id: str) -> str:
63
  text = ""
64
  for page in reader.pages:
65
  text += page.extract_text() or ""
66
- return text[:10000] if text else "PDF found but could not extract text."
67
- except ImportError:
68
- return "PDF file detected but PyPDF2 not installed."
69
 
70
- # For other binary files, save and report
71
- suffix = ""
72
  if "image" in content_type:
73
- suffix = ".png"
74
- elif "audio" in content_type:
75
- suffix = ".mp3"
76
-
77
- with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as f:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  f.write(response.content)
79
- return f"File downloaded to {f.name} (type: {content_type}). File size: {len(response.content)} bytes."
80
 
81
  except Exception as e:
82
  return f"Error downloading file for task {task_id}: {str(e)}"
83
 
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  @tool
86
  def read_local_file(file_path: str) -> str:
87
- """Reads the content of a local file and returns it as a string.
88
 
89
  Args:
90
  file_path: The path to the file to read.
91
  """
92
  try:
93
  with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
94
- return f.read()[:10000]
95
  except Exception as e:
96
  return f"Error reading file: {str(e)}"
97
 
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  # =============================================
100
  # AGENT CLASS
101
  # =============================================
102
 
103
  class BasicAgent:
104
- """An agent that uses smolagents CodeAgent with web search and file tools
105
- to answer GAIA benchmark questions. Uses Hugging Face Inference API (no GPU needed)."""
 
106
 
107
  def __init__(self):
108
  print("Initializing SmolAgent for GAIA benchmark...")
109
 
110
- # Use HF Inference API - completely free, no GPU needed
111
  model = InferenceClientModel(
112
  model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
113
  token=os.getenv("HF_TOKEN"),
114
  max_tokens=2096,
115
- temperature=0.1, # Low temperature for more precise answers
116
  )
117
 
118
- # System prompt tailored for GAIA exact-match scoring
119
  system_prompt = """You are a precise AI assistant solving GAIA benchmark questions.
120
 
121
- CRITICAL RULES:
122
- 1. Your final answer must be ONLY the answer itself - no explanations, no prefixes like "The answer is", no extra text.
123
- 2. If the answer is a number, give just the number (e.g., "42" not "The answer is 42").
124
- 3. If the answer is a name, give just the name (e.g., "Paris" not "The answer is Paris").
125
- 4. If the answer is a list, use comma-separated values as requested.
126
- 5. Be precise and factual. Use tools to verify information.
127
- 6. If a question mentions a file or attachment, use the download_file_from_api tool with the task_id.
128
- 7. Always search the web when you need current or specific factual information.
129
- 8. Think step by step, but your FINAL output must be ONLY the answer.
 
 
 
 
 
 
 
 
 
130
  """
131
 
132
  self.agent = CodeAgent(
@@ -135,26 +272,26 @@ CRITICAL RULES:
135
  DuckDuckGoSearchTool(),
136
  VisitWebpageTool(),
137
  download_file_from_api,
 
 
138
  read_local_file,
 
139
  ],
140
- max_steps=8,
141
  verbosity_level=1,
142
  additional_authorized_imports=[
143
  "json", "re", "math", "datetime", "collections",
144
- "csv", "io", "os", "tempfile",
 
145
  ],
146
  )
147
 
148
- # Override system prompt
149
  self.agent.system_prompt = system_prompt + "\n\n" + self.agent.system_prompt
150
-
151
  print("SmolAgent initialized successfully!")
152
 
153
  def __call__(self, question: str, task_id: str = None) -> str:
154
- print(f"Agent processing question: {question[:80]}...")
155
 
156
- # Build the prompt with task_id context if available
157
- prompt = question
158
  if task_id:
159
  prompt = f"""Answer this question. If you need to download an attached file, use download_file_from_api with task_id="{task_id}".
160
 
@@ -170,34 +307,42 @@ Remember: respond with ONLY the final answer, nothing else."""
170
 
171
  try:
172
  result = self.agent.run(prompt)
173
- # Clean up the answer
174
  answer = str(result).strip()
175
- # Remove common prefixes that models add
176
- for prefix in [
177
- "The answer is ", "The answer is: ", "Answer: ", "FINAL ANSWER: ",
178
- "Final answer: ", "The final answer is ", "The final answer is: ",
179
- ]:
 
 
 
 
180
  if answer.lower().startswith(prefix.lower()):
181
  answer = answer[len(prefix):].strip()
182
- # Remove trailing periods if the answer is not a sentence
 
 
 
 
 
 
 
183
  if answer.endswith(".") and len(answer.split()) <= 5:
184
  answer = answer[:-1].strip()
185
- print(f"Agent answer: {answer}")
 
186
  return answer
 
187
  except Exception as e:
188
  print(f"Agent error: {e}")
189
  return "Unable to determine the answer."
190
 
191
 
192
  # =============================================
193
- # SUBMISSION LOGIC
194
  # =============================================
195
 
196
  def run_and_submit_all(profile: gr.OAuthProfile | None):
197
- """
198
- Fetches all questions, runs the agent on them, submits all answers,
199
- and displays the results.
200
- """
201
  space_id = os.getenv("SPACE_ID")
202
 
203
  if profile:
@@ -211,7 +356,6 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
211
  questions_url = f"{api_url}/questions"
212
  submit_url = f"{api_url}/submit"
213
 
214
- # 1. Instantiate Agent
215
  try:
216
  agent = BasicAgent()
217
  except Exception as e:
@@ -221,24 +365,19 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
221
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
222
  print(agent_code)
223
 
224
- # 2. Fetch Questions
225
  print(f"Fetching questions from: {questions_url}")
226
  try:
227
  response = requests.get(questions_url, timeout=15)
228
  response.raise_for_status()
229
  questions_data = response.json()
230
  if not questions_data:
231
- print("Fetched questions list is empty.")
232
  return "Fetched questions list is empty or invalid format.", None
233
  print(f"Fetched {len(questions_data)} questions.")
234
  except requests.exceptions.RequestException as e:
235
- print(f"Error fetching questions: {e}")
236
  return f"Error fetching questions: {e}", None
237
  except Exception as e:
238
- print(f"An unexpected error occurred fetching questions: {e}")
239
  return f"An unexpected error occurred fetching questions: {e}", None
240
 
241
- # 3. Run Agent on each question
242
  results_log = []
243
  answers_payload = []
244
  print(f"Running agent on {len(questions_data)} questions...")
@@ -246,11 +385,11 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
246
  task_id = item.get("task_id")
247
  question_text = item.get("question")
248
  if not task_id or question_text is None:
249
- print(f"Skipping item with missing task_id or question: {item}")
250
  continue
251
- print(f"\n{'='*50}")
252
- print(f"Question {i+1}/{len(questions_data)} - Task: {task_id}")
253
- print(f"{'='*50}")
 
254
  try:
255
  submitted_answer = agent(question_text, task_id=task_id)
256
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
@@ -268,19 +407,13 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
268
  })
269
 
270
  if not answers_payload:
271
- print("Agent did not produce any answers to submit.")
272
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
273
 
274
- # 4. Prepare Submission
275
  submission_data = {
276
  "username": username.strip(),
277
  "agent_code": agent_code,
278
  "answers": answers_payload
279
  }
280
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
281
- print(status_update)
282
-
283
- # 5. Submit
284
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
285
  try:
286
  response = requests.post(submit_url, json=submission_data, timeout=120)
@@ -294,8 +427,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
294
  f"Message: {result_data.get('message', 'No message received.')}"
295
  )
296
  print("Submission successful.")
297
- results_df = pd.DataFrame(results_log)
298
- return final_status, results_df
299
  except requests.exceptions.HTTPError as e:
300
  error_detail = f"Server responded with status {e.response.status_code}."
301
  try:
@@ -303,35 +435,23 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
303
  error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
304
  except requests.exceptions.JSONDecodeError:
305
  error_detail += f" Response: {e.response.text[:500]}"
306
- status_message = f"Submission Failed: {error_detail}"
307
- print(status_message)
308
- results_df = pd.DataFrame(results_log)
309
- return status_message, results_df
310
  except requests.exceptions.Timeout:
311
- status_message = "Submission Failed: The request timed out."
312
- print(status_message)
313
- results_df = pd.DataFrame(results_log)
314
- return status_message, results_df
315
  except requests.exceptions.RequestException as e:
316
- status_message = f"Submission Failed: Network error - {e}"
317
- print(status_message)
318
- results_df = pd.DataFrame(results_log)
319
- return status_message, results_df
320
  except Exception as e:
321
- status_message = f"An unexpected error occurred during submission: {e}"
322
- print(status_message)
323
- results_df = pd.DataFrame(results_log)
324
- return status_message, results_df
325
 
326
 
327
- # --- Build Gradio Interface using Blocks ---
328
  with gr.Blocks() as demo:
329
- gr.Markdown("# 🤖 GAIA Agent - Final Assignment")
330
  gr.Markdown(
331
  """
332
  **Agent**: SmolAgent (CodeAgent) with Qwen2.5-Coder-32B via HF Inference API
333
 
334
- **Tools**: Web Search, Webpage Visitor, File Downloader
335
 
336
  **Instructions:**
337
  1. Log in to your Hugging Face account using the button below.
@@ -357,15 +477,13 @@ if __name__ == "__main__":
357
 
358
  if space_host_startup:
359
  print(f"✅ SPACE_HOST found: {space_host_startup}")
360
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
361
  else:
362
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
363
 
364
  if space_id_startup:
365
  print(f"✅ SPACE_ID found: {space_id_startup}")
366
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
367
  else:
368
- print("ℹ️ SPACE_ID environment variable not found (running locally?).")
369
 
370
  print("-"*60 + "\n")
371
  print("Launching Gradio Interface...")
 
5
  from smolagents import (
6
  CodeAgent,
7
  DuckDuckGoSearchTool,
 
8
  VisitWebpageTool,
9
  InferenceClientModel,
10
  tool,
 
22
  def download_file_from_api(task_id: str) -> str:
23
  """Downloads a file associated with a GAIA task and returns its text content.
24
  Use this tool when a question mentions an attached file, spreadsheet, image,
25
+ audio, document, or any file that you need to read or analyze.
26
 
27
  Args:
28
  task_id: The task_id string for the question that has an associated file.
 
35
  response.raise_for_status()
36
 
37
  content_type = response.headers.get("Content-Type", "")
38
+ print(f" [download_file] Content-Type: {content_type}, Size: {len(response.content)} bytes")
39
 
40
+ # --- TEXT-BASED FILES ---
41
+ if any(t in content_type for t in ["text", "json", "csv", "xml", "html"]):
42
+ return response.text[:15000]
43
+
44
+ # --- EXCEL FILES ---
45
+ if any(t in content_type for t in ["spreadsheet", "excel", "openxmlformats-officedocument"]):
46
+ try:
47
+ import openpyxl
48
+ import io
49
+ wb = openpyxl.load_workbook(io.BytesIO(response.content))
50
+ result = []
51
+ for sheet_name in wb.sheetnames:
52
+ ws = wb[sheet_name]
53
+ result.append(f"--- Sheet: {sheet_name} ---")
54
+ for row in ws.iter_rows(values_only=True):
55
+ result.append("\t".join([str(c) if c is not None else "" for c in row]))
56
+ return "\n".join(result)[:15000]
57
+ except Exception as e:
58
+ return f"Excel file detected but error reading it: {str(e)}"
59
+
60
+ # --- PDF FILES ---
61
  if "pdf" in content_type:
62
  try:
63
  import PyPDF2
 
66
  text = ""
67
  for page in reader.pages:
68
  text += page.extract_text() or ""
69
+ return text[:15000] if text.strip() else "PDF found but could not extract text (may be scanned/image-based)."
70
+ except Exception as e:
71
+ return f"PDF file detected but error reading: {str(e)}"
72
 
73
+ # --- IMAGE FILES ---
 
74
  if "image" in content_type:
75
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as f:
76
+ f.write(response.content)
77
+ img_path = f.name
78
+ return f"IMAGE_FILE_SAVED:{img_path}"
79
+
80
+ # --- AUDIO FILES ---
81
+ if any(t in content_type for t in ["audio", "mpeg", "wav", "mp3", "ogg"]):
82
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as f:
83
+ f.write(response.content)
84
+ audio_path = f.name
85
+ return f"AUDIO_FILE_SAVED:{audio_path}"
86
+
87
+ # --- PYTHON FILES ---
88
+ if "python" in content_type or "x-python" in content_type:
89
+ return response.text[:15000]
90
+
91
+ # --- WORD DOCUMENTS ---
92
+ if "wordprocessingml" in content_type or "msword" in content_type:
93
+ try:
94
+ import docx
95
+ import io
96
+ doc = docx.Document(io.BytesIO(response.content))
97
+ text = "\n".join([p.text for p in doc.paragraphs])
98
+ return text[:15000] if text.strip() else "Word doc found but no text extracted."
99
+ except Exception as e:
100
+ return f"Word document detected but error reading: {str(e)}"
101
+
102
+ # --- FALLBACK ---
103
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".bin") as f:
104
  f.write(response.content)
105
+ return f"File downloaded to {f.name} (type: {content_type}). Size: {len(response.content)} bytes. Could not auto-parse."
106
 
107
  except Exception as e:
108
  return f"Error downloading file for task {task_id}: {str(e)}"
109
 
110
 
111
+ @tool
112
+ def describe_image(image_path: str) -> str:
113
+ """Describes the content of an image file using an AI vision model.
114
+ Use this when you have an image file path (e.g. from IMAGE_FILE_SAVED)
115
+ and need to understand what the image shows, including any text in it.
116
+
117
+ Args:
118
+ image_path: The local file path to the image to describe.
119
+ """
120
+ try:
121
+ from huggingface_hub import InferenceClient
122
+
123
+ token = os.getenv("HF_TOKEN")
124
+ client = InferenceClient(token=token)
125
+
126
+ with open(image_path, "rb") as f:
127
+ image_bytes = f.read()
128
+
129
+ # Use BLIP2 for image captioning
130
+ result = client.image_to_text(
131
+ image=image_bytes,
132
+ model="Salesforce/blip2-opt-2.7b",
133
+ )
134
+
135
+ if isinstance(result, str):
136
+ description = result
137
+ elif hasattr(result, "generated_text"):
138
+ description = result.generated_text
139
+ else:
140
+ description = str(result)
141
+
142
+ return f"Image description: {description}"
143
+
144
+ except Exception as e:
145
+ return f"Could not describe image at {image_path}. Error: {str(e)}"
146
+
147
+
148
+ @tool
149
+ def transcribe_audio(audio_path: str) -> str:
150
+ """Transcribes an audio file to text using Whisper speech recognition.
151
+ Use this when you have an audio file path (e.g. from AUDIO_FILE_SAVED)
152
+ and need to know what is spoken in the recording.
153
+
154
+ Args:
155
+ audio_path: The local file path to the audio file to transcribe.
156
+ """
157
+ try:
158
+ from huggingface_hub import InferenceClient
159
+
160
+ token = os.getenv("HF_TOKEN")
161
+ client = InferenceClient(token=token)
162
+
163
+ with open(audio_path, "rb") as f:
164
+ audio_bytes = f.read()
165
+
166
+ result = client.automatic_speech_recognition(
167
+ audio=audio_bytes,
168
+ model="openai/whisper-large-v3-turbo",
169
+ )
170
+
171
+ if isinstance(result, str):
172
+ return f"Audio transcription: {result}"
173
+ elif hasattr(result, "text"):
174
+ return f"Audio transcription: {result.text}"
175
+ elif isinstance(result, dict):
176
+ return f"Audio transcription: {result.get('text', str(result))}"
177
+ else:
178
+ return f"Audio transcription: {str(result)}"
179
+
180
+ except Exception as e:
181
+ return f"Error transcribing audio at {audio_path}: {str(e)}"
182
+
183
+
184
  @tool
185
  def read_local_file(file_path: str) -> str:
186
+ """Reads the content of a local text file and returns it as a string.
187
 
188
  Args:
189
  file_path: The path to the file to read.
190
  """
191
  try:
192
  with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
193
+ return f.read()[:15000]
194
  except Exception as e:
195
  return f"Error reading file: {str(e)}"
196
 
197
 
198
+ @tool
199
+ def execute_python_file(file_path: str) -> str:
200
+ """Executes a Python script file and returns its stdout output.
201
+ Use this when you receive a .py file that needs to be run to get the answer.
202
+
203
+ Args:
204
+ file_path: The path to the Python file to execute.
205
+ """
206
+ import subprocess
207
+ try:
208
+ result = subprocess.run(
209
+ ["python3", file_path],
210
+ capture_output=True,
211
+ text=True,
212
+ timeout=30,
213
+ )
214
+ output = ""
215
+ if result.stdout:
216
+ output += result.stdout
217
+ if result.stderr:
218
+ output += f"\nSTDERR: {result.stderr}"
219
+ if result.returncode != 0:
220
+ output += f"\nReturn code: {result.returncode}"
221
+ return output.strip() if output.strip() else "Script executed but produced no output."
222
+ except subprocess.TimeoutExpired:
223
+ return "Script execution timed out after 30 seconds."
224
+ except Exception as e:
225
+ return f"Error executing Python file: {str(e)}"
226
+
227
+
228
  # =============================================
229
  # AGENT CLASS
230
  # =============================================
231
 
232
  class BasicAgent:
233
+ """An agent using smolagents CodeAgent with web search, file handling,
234
+ image description, and audio transcription tools.
235
+ Uses HF Inference API — no GPU needed."""
236
 
237
  def __init__(self):
238
  print("Initializing SmolAgent for GAIA benchmark...")
239
 
 
240
  model = InferenceClientModel(
241
  model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
242
  token=os.getenv("HF_TOKEN"),
243
  max_tokens=2096,
244
+ temperature=0.1,
245
  )
246
 
 
247
  system_prompt = """You are a precise AI assistant solving GAIA benchmark questions.
248
 
249
+ CRITICAL RULES FOR ANSWERING:
250
+ 1. Your final answer must be ONLY the answer itself no explanations, no "The answer is", no extra words.
251
+ 2. If the answer is a number, give just the number (e.g., "42").
252
+ 3. If the answer is a name, give just the name (e.g., "Paris").
253
+ 4. If asked for a comma-separated list, give just the list (e.g., "red, blue, green").
254
+ 5. Be precise and factual. Use tools to verify information when needed.
255
+
256
+ TOOL USAGE RULES:
257
+ 6. If a question mentions an attached file, image, audio, spreadsheet, or document, FIRST use download_file_from_api with the task_id.
258
+ 7. If download returns "IMAGE_FILE_SAVED:/some/path", then call describe_image("/some/path") to see what the image contains.
259
+ 8. If download returns "AUDIO_FILE_SAVED:/some/path", then call transcribe_audio("/some/path") to hear what is said.
260
+ 9. If the file is a Python script (.py), you can use read_local_file to view it or execute_python_file to run it.
261
+ 10. Use DuckDuckGoSearchTool when you need factual information from the internet.
262
+ 11. Use visit_webpage to read the full content of a specific URL.
263
+
264
+ REASONING:
265
+ 12. Think step by step but keep your FINAL output as ONLY the answer.
266
+ 13. Double-check your answer before giving it.
267
  """
268
 
269
  self.agent = CodeAgent(
 
272
  DuckDuckGoSearchTool(),
273
  VisitWebpageTool(),
274
  download_file_from_api,
275
+ describe_image,
276
+ transcribe_audio,
277
  read_local_file,
278
+ execute_python_file,
279
  ],
280
+ max_steps=10,
281
  verbosity_level=1,
282
  additional_authorized_imports=[
283
  "json", "re", "math", "datetime", "collections",
284
+ "csv", "io", "os", "tempfile", "subprocess",
285
+ "base64", "hashlib", "unicodedata", "string",
286
  ],
287
  )
288
 
 
289
  self.agent.system_prompt = system_prompt + "\n\n" + self.agent.system_prompt
 
290
  print("SmolAgent initialized successfully!")
291
 
292
  def __call__(self, question: str, task_id: str = None) -> str:
293
+ print(f"Agent processing: {question[:100]}...")
294
 
 
 
295
  if task_id:
296
  prompt = f"""Answer this question. If you need to download an attached file, use download_file_from_api with task_id="{task_id}".
297
 
 
307
 
308
  try:
309
  result = self.agent.run(prompt)
 
310
  answer = str(result).strip()
311
+
312
+ # Clean up common LLM prefixes
313
+ prefixes_to_remove = [
314
+ "The answer is ", "The answer is: ",
315
+ "Answer: ", "FINAL ANSWER: ",
316
+ "Final answer: ", "The final answer is ",
317
+ "The final answer is: ", "Result: ",
318
+ ]
319
+ for prefix in prefixes_to_remove:
320
  if answer.lower().startswith(prefix.lower()):
321
  answer = answer[len(prefix):].strip()
322
+
323
+ # Remove wrapping quotes
324
+ if len(answer) > 2 and \
325
+ ((answer.startswith('"') and answer.endswith('"')) or
326
+ (answer.startswith("'") and answer.endswith("'"))):
327
+ answer = answer[1:-1].strip()
328
+
329
+ # Remove trailing period for short answers
330
  if answer.endswith(".") and len(answer.split()) <= 5:
331
  answer = answer[:-1].strip()
332
+
333
+ print(f"Final answer: {answer}")
334
  return answer
335
+
336
  except Exception as e:
337
  print(f"Agent error: {e}")
338
  return "Unable to determine the answer."
339
 
340
 
341
  # =============================================
342
+ # SUBMISSION LOGIC
343
  # =============================================
344
 
345
  def run_and_submit_all(profile: gr.OAuthProfile | None):
 
 
 
 
346
  space_id = os.getenv("SPACE_ID")
347
 
348
  if profile:
 
356
  questions_url = f"{api_url}/questions"
357
  submit_url = f"{api_url}/submit"
358
 
 
359
  try:
360
  agent = BasicAgent()
361
  except Exception as e:
 
365
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
366
  print(agent_code)
367
 
 
368
  print(f"Fetching questions from: {questions_url}")
369
  try:
370
  response = requests.get(questions_url, timeout=15)
371
  response.raise_for_status()
372
  questions_data = response.json()
373
  if not questions_data:
 
374
  return "Fetched questions list is empty or invalid format.", None
375
  print(f"Fetched {len(questions_data)} questions.")
376
  except requests.exceptions.RequestException as e:
 
377
  return f"Error fetching questions: {e}", None
378
  except Exception as e:
 
379
  return f"An unexpected error occurred fetching questions: {e}", None
380
 
 
381
  results_log = []
382
  answers_payload = []
383
  print(f"Running agent on {len(questions_data)} questions...")
 
385
  task_id = item.get("task_id")
386
  question_text = item.get("question")
387
  if not task_id or question_text is None:
 
388
  continue
389
+ print(f"\n{'='*60}")
390
+ print(f" Question {i+1}/{len(questions_data)} Task: {task_id}")
391
+ print(f" Q: {question_text[:120]}...")
392
+ print(f"{'='*60}")
393
  try:
394
  submitted_answer = agent(question_text, task_id=task_id)
395
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
 
407
  })
408
 
409
  if not answers_payload:
 
410
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
411
 
 
412
  submission_data = {
413
  "username": username.strip(),
414
  "agent_code": agent_code,
415
  "answers": answers_payload
416
  }
 
 
 
 
417
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
418
  try:
419
  response = requests.post(submit_url, json=submission_data, timeout=120)
 
427
  f"Message: {result_data.get('message', 'No message received.')}"
428
  )
429
  print("Submission successful.")
430
+ return final_status, pd.DataFrame(results_log)
 
431
  except requests.exceptions.HTTPError as e:
432
  error_detail = f"Server responded with status {e.response.status_code}."
433
  try:
 
435
  error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
436
  except requests.exceptions.JSONDecodeError:
437
  error_detail += f" Response: {e.response.text[:500]}"
438
+ return f"Submission Failed: {error_detail}", pd.DataFrame(results_log)
 
 
 
439
  except requests.exceptions.Timeout:
440
+ return "Submission Failed: The request timed out.", pd.DataFrame(results_log)
 
 
 
441
  except requests.exceptions.RequestException as e:
442
+ return f"Submission Failed: Network error - {e}", pd.DataFrame(results_log)
 
 
 
443
  except Exception as e:
444
+ return f"An unexpected error occurred during submission: {e}", pd.DataFrame(results_log)
 
 
 
445
 
446
 
447
+ # --- Build Gradio Interface ---
448
  with gr.Blocks() as demo:
449
+ gr.Markdown("# 🤖 GAIA Agent Final Assignment")
450
  gr.Markdown(
451
  """
452
  **Agent**: SmolAgent (CodeAgent) with Qwen2.5-Coder-32B via HF Inference API
453
 
454
+ **Tools**: Web Search · Webpage Visitor · File Downloader · Image Describer · Audio Transcriber · Python Executor
455
 
456
  **Instructions:**
457
  1. Log in to your Hugging Face account using the button below.
 
477
 
478
  if space_host_startup:
479
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
480
  else:
481
+ print("ℹ️ SPACE_HOST not found (running locally?).")
482
 
483
  if space_id_startup:
484
  print(f"✅ SPACE_ID found: {space_id_startup}")
 
485
  else:
486
+ print("ℹ️ SPACE_ID not found (running locally?).")
487
 
488
  print("-"*60 + "\n")
489
  print("Launching Gradio Interface...")