khojoii commited on
Commit
bd2dbeb
·
1 Parent(s): 7b720a0

first commit

Browse files
Files changed (4) hide show
  1. agent.py +344 -0
  2. app.py +162 -0
  3. requirements.txt +10 -0
  4. test_local.py +91 -0
agent.py ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import base64
4
+ import tempfile
5
+ import requests
6
+
7
+ from smolagents import CodeAgent, LiteLLMModel, tool, DuckDuckGoSearchTool
8
+
9
+ API_URL = "https://agents-course-unit4-scoring.hf.space"
10
+
11
+ ANSWER_FORMATTING_RULES = """
12
+
13
+ ## CRITICAL ANSWER FORMATTING RULES (GAIA exact-match grading)
14
+
15
+ You MUST follow these rules when producing your final answer:
16
+
17
+ 1. Return ONLY the final answer value. No preamble, no explanation, no restated question.
18
+ Never write "The answer is..." or "I think..." — just the raw answer.
19
+ 2. Never include the literal string "FINAL ANSWER" anywhere in your output.
20
+ 3. Numbers: Use digits only. No commas, no currency symbols ($, €, £), no percent signs (%),
21
+ no unit abbreviations unless the question explicitly asks for them.
22
+ 4. Strings: Do NOT include articles (a, an, the) unless they are part of a proper noun.
23
+ Do not abbreviate unless the question explicitly asks for an abbreviation.
24
+ 5. Comma-separated lists: Apply the above rules to each element, join with ", ".
25
+ 6. Dates: Use the format explicitly requested. If none specified, use YYYY-MM-DD.
26
+ 7. For true/false questions, answer only "True" or "False" (no period).
27
+ """
28
+
29
+
30
+ def _clean_answer(raw: str) -> str:
31
+ """Post-process agent output to ensure a clean, gradable answer string."""
32
+ if raw is None:
33
+ return ""
34
+ if not isinstance(raw, str):
35
+ return str(raw)
36
+
37
+ answer = raw.strip()
38
+
39
+ for prefix in [
40
+ "FINAL ANSWER:", "FINAL ANSWER :", "Final Answer:", "Final Answer :",
41
+ "final answer:", "final answer :",
42
+ ]:
43
+ if answer.startswith(prefix):
44
+ answer = answer[len(prefix):].strip()
45
+ break
46
+
47
+ if len(answer) >= 2 and answer[0] == '"' and answer[-1] == '"':
48
+ answer = answer[1:-1].strip()
49
+ if len(answer) >= 2 and answer[0] == "'" and answer[-1] == "'":
50
+ answer = answer[1:-1].strip()
51
+
52
+ answer = re.sub(r"\s+", " ", answer).strip()
53
+
54
+ return answer
55
+
56
+
57
+ @tool
58
+ def download_task_file(task_id: str) -> str:
59
+ """Download the file attached to a GAIA task from the scoring API.
60
+
61
+ Args:
62
+ task_id: The task_id string from the questions endpoint.
63
+ """
64
+ url = f"{API_URL}/files/{task_id}"
65
+ try:
66
+ resp = requests.get(url, timeout=60)
67
+ resp.raise_for_status()
68
+ except Exception as e:
69
+ return f"Error downloading file for task {task_id}: {e}"
70
+
71
+ content_type = resp.headers.get("content-type", "")
72
+ cd = resp.headers.get("content-disposition", "")
73
+ if "filename=" in cd:
74
+ filename = cd.split("filename=")[-1].strip('" ')
75
+ else:
76
+ ext_map = {
77
+ "pdf": ".pdf", "csv": ".csv", "json": ".json",
78
+ "text": ".txt", "html": ".html", "xml": ".xml",
79
+ "xlsx": ".xlsx", "excel": ".xlsx",
80
+ "png": ".png", "jpeg": ".jpg", "jpg": ".jpg",
81
+ "gif": ".gif", "webp": ".webp",
82
+ "audio": ".mp3", "mpeg": ".mp3", "wav": ".wav",
83
+ "mp4": ".mp4", "ogg": ".ogg", "flac": ".flac",
84
+ }
85
+ ext = ".bin"
86
+ for key, val in ext_map.items():
87
+ if key in content_type:
88
+ ext = val
89
+ break
90
+ filename = f"{task_id}{ext}"
91
+
92
+ tmp_dir = tempfile.mkdtemp()
93
+ filepath = os.path.join(tmp_dir, filename)
94
+ with open(filepath, "wb") as f:
95
+ f.write(resp.content)
96
+
97
+ return (
98
+ f"File downloaded successfully.\n"
99
+ f"Path: {filepath}\n"
100
+ f"Filename: {filename}\n"
101
+ f"Size: {len(resp.content)} bytes\n"
102
+ f"Content-Type: {content_type}\n\n"
103
+ f"Now use the read_file tool on this path, or analyze_image / transcribe_audio if it is an image or audio file."
104
+ )
105
+
106
+
107
+ @tool
108
+ def visit_webpage(url: str) -> str:
109
+ """Visit a webpage and return its text content. Handles common sites like Wikipedia.
110
+
111
+ Args:
112
+ url: The full URL of the webpage to visit.
113
+ """
114
+ headers = {
115
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
116
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
117
+ "Accept-Language": "en-US,en;q=0.9",
118
+ }
119
+ try:
120
+ resp = requests.get(url, headers=headers, timeout=20)
121
+ resp.raise_for_status()
122
+ content_type = resp.headers.get("content-type", "")
123
+ if "text" not in content_type and "html" not in content_type:
124
+ return f"Response is not text (content-type: {content_type}). Cannot read as webpage."
125
+ text = resp.text
126
+ # Simple HTML tag stripping for readability
127
+ import re as _re
128
+ text = _re.sub(r"<script[^>]*>.*?</script>", "", text, flags=_re.DOTALL)
129
+ text = _re.sub(r"<style[^>]*>.*?</style>", "", text, flags=_re.DOTALL)
130
+ text = _re.sub(r"<[^>]+>", " ", text)
131
+ text = _re.sub(r"\s+", " ", text).strip()
132
+ if len(text) > 15000:
133
+ text = text[:15000] + "\n\n[Content truncated at 15000 characters]"
134
+ return text
135
+ except Exception as e:
136
+ return f"Error fetching the webpage: {e}"
137
+
138
+
139
+ @tool
140
+ def read_file(file_path: str) -> str:
141
+ """Read and parse a file from disk. Supports PDF, CSV, XLSX, JSON, plain text, and other text formats.
142
+
143
+ Args:
144
+ file_path: Absolute path to the file on disk.
145
+ """
146
+ if not os.path.exists(file_path):
147
+ return f"File not found: {file_path}"
148
+
149
+ ext = os.path.splitext(file_path)[1].lower()
150
+
151
+ try:
152
+ if ext == ".pdf":
153
+ from PyPDF2 import PdfReader
154
+ reader = PdfReader(file_path)
155
+ pages = []
156
+ for i, page in enumerate(reader.pages):
157
+ text = page.extract_text()
158
+ if text:
159
+ pages.append(f"--- Page {i + 1} ---\n{text}")
160
+ if pages:
161
+ return "\n\n".join(pages)
162
+ return (
163
+ "PDF text extraction returned empty. "
164
+ "This PDF may be image-based. Try converting it to images and using analyze_image."
165
+ )
166
+
167
+ if ext == ".csv":
168
+ import pandas as pd
169
+ df = pd.read_csv(file_path)
170
+ return df.to_string(index=False)
171
+
172
+ if ext in (".xlsx", ".xls"):
173
+ import pandas as pd
174
+ df = pd.read_excel(file_path)
175
+ return df.to_string(index=False)
176
+
177
+ if ext in (".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".tiff"):
178
+ return (
179
+ f"This is an image file at: {file_path}\n"
180
+ "Use the analyze_image tool to examine its contents."
181
+ )
182
+
183
+ if ext in (".mp3", ".wav", ".ogg", ".m4a", ".flac", ".mp4", ".aac"):
184
+ return (
185
+ f"This is an audio file at: {file_path}\n"
186
+ "Use the transcribe_audio tool to get its text content."
187
+ )
188
+
189
+ if ext in (".json",):
190
+ with open(file_path, "r", encoding="utf-8") as f:
191
+ return f.read()
192
+
193
+ if ext in (".zip",):
194
+ import zipfile
195
+ with zipfile.ZipFile(file_path, "r") as z:
196
+ names = z.namelist()
197
+ result = f"ZIP archive contains {len(names)} files:\n"
198
+ for name in names[:50]:
199
+ result += f" - {name}\n"
200
+ if len(names) > 50:
201
+ result += f" ... and {len(names) - 50} more.\n"
202
+ return result
203
+
204
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
205
+ return f.read()
206
+
207
+ except Exception as e:
208
+ return f"Error reading file {file_path}: {e}"
209
+
210
+
211
+ @tool
212
+ def analyze_image(file_path: str, question: str = "Describe this image in detail. Extract all text, numbers, labels, and data you can see.") -> str:
213
+ """Analyze an image using a vision-capable LLM. Use this for photos, charts, diagrams, screenshots, etc.
214
+
215
+ Args:
216
+ file_path: Absolute path to the image file.
217
+ question: What you want to know about the image.
218
+ """
219
+ if not os.path.exists(file_path):
220
+ return f"File not found: {file_path}"
221
+
222
+ try:
223
+ import litellm
224
+
225
+ with open(file_path, "rb") as f:
226
+ img_bytes = f.read()
227
+ img_b64 = base64.b64encode(img_bytes).decode()
228
+
229
+ ext = os.path.splitext(file_path)[1].lower()
230
+ mime_map = {
231
+ ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg",
232
+ ".gif": "image/gif", ".webp": "image/webp", ".bmp": "image/bmp",
233
+ ".tiff": "image/tiff",
234
+ }
235
+ mime = mime_map.get(ext, "image/png")
236
+
237
+ model_id = os.getenv("MODEL_ID", "gpt-4o")
238
+ api_key = (
239
+ os.getenv("OPENAI_API_KEY")
240
+ or os.getenv("ANTHROPIC_API_KEY")
241
+ or os.getenv("HF_TOKEN")
242
+ )
243
+
244
+ resp = litellm.completion(
245
+ model=model_id,
246
+ api_key=api_key,
247
+ messages=[{
248
+ "role": "user",
249
+ "content": [
250
+ {"type": "text", "text": question},
251
+ {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{img_b64}"}},
252
+ ],
253
+ }],
254
+ temperature=0,
255
+ )
256
+ return resp.choices[0].message.content
257
+ except Exception as e:
258
+ return f"Error analyzing image: {e}"
259
+
260
+
261
+ @tool
262
+ def transcribe_audio(file_path: str) -> str:
263
+ """Transcribe an audio file to text using OpenAI Whisper API.
264
+
265
+ Args:
266
+ file_path: Absolute path to the audio file.
267
+ """
268
+ if not os.path.exists(file_path):
269
+ return f"File not found: {file_path}"
270
+
271
+ try:
272
+ from openai import OpenAI
273
+
274
+ api_key = os.getenv("OPENAI_API_KEY") or os.getenv("HF_TOKEN")
275
+ client = OpenAI(api_key=api_key) if api_key else OpenAI()
276
+
277
+ with open(file_path, "rb") as f:
278
+ result = client.audio.transcriptions.create(
279
+ model="whisper-1",
280
+ file=f,
281
+ )
282
+ return result.text
283
+ except Exception as e:
284
+ return f"Error transcribing audio: {e}"
285
+
286
+
287
+ def build_question_prompt(item: dict) -> str:
288
+ """Build the full prompt to send to the agent, including file hints."""
289
+ question_text = item.get("question", "")
290
+ task_id = item.get("task_id", "")
291
+ file_name = item.get("file_name", "")
292
+
293
+ parts = [question_text]
294
+ parts.append(f"\n[TASK_ID: {task_id}]")
295
+
296
+ if file_name:
297
+ parts.append(
298
+ f"This task has an attached file: '{file_name}'. "
299
+ f"You MUST first call the download_task_file tool with task_id='{task_id}' "
300
+ f"to download it, then read or analyze the downloaded file before answering."
301
+ )
302
+
303
+ return "\n".join(parts)
304
+
305
+
306
+ def create_agent():
307
+ """Create and return a configured CodeAgent for GAIA benchmark questions."""
308
+ model_id = os.getenv("MODEL_ID", "gpt-4o")
309
+ api_key = (
310
+ os.getenv("OPENAI_API_KEY")
311
+ or os.getenv("ANTHROPIC_API_KEY")
312
+ or os.getenv("HF_TOKEN")
313
+ )
314
+
315
+ model = LiteLLMModel(
316
+ model_id=model_id,
317
+ api_key=api_key,
318
+ api_base=os.getenv("API_BASE"),
319
+ temperature=0,
320
+ )
321
+
322
+ tools = [
323
+ DuckDuckGoSearchTool(),
324
+ visit_webpage,
325
+ download_task_file,
326
+ read_file,
327
+ analyze_image,
328
+ transcribe_audio,
329
+ ]
330
+
331
+ agent = CodeAgent(
332
+ tools=tools,
333
+ model=model,
334
+ max_steps=12,
335
+ additional_authorized_imports=[
336
+ "math", "datetime", "json", "re", "os", "collections",
337
+ "itertools", "functools", "string", "random",
338
+ "pandas", "numpy", "requests",
339
+ ],
340
+ )
341
+
342
+ agent.prompt_templates["system_prompt"] += ANSWER_FORMATTING_RULES
343
+
344
+ return agent
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import pandas as pd
5
+
6
+ from agent import create_agent, _clean_answer, build_question_prompt
7
+
8
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
9
+
10
+
11
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
12
+ space_id = os.getenv("SPACE_ID")
13
+
14
+ if profile:
15
+ username = f"{profile.username}"
16
+ print(f"User logged in: {username}")
17
+ else:
18
+ print("User not logged in.")
19
+ return "Please Login to Hugging Face with the button.", None
20
+
21
+ api_url = DEFAULT_API_URL
22
+ questions_url = f"{api_url}/questions"
23
+ submit_url = f"{api_url}/submit"
24
+
25
+ try:
26
+ agent = create_agent()
27
+ except Exception as e:
28
+ print(f"Error instantiating agent: {e}")
29
+ return f"Error initializing agent: {e}", None
30
+
31
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
32
+ print(f"Agent code URL: {agent_code}")
33
+
34
+ print(f"Fetching questions from: {questions_url}")
35
+ try:
36
+ response = requests.get(questions_url, timeout=15)
37
+ response.raise_for_status()
38
+ questions_data = response.json()
39
+ if not questions_data:
40
+ print("Fetched questions list is empty.")
41
+ return "Fetched questions list is empty or invalid format.", None
42
+ print(f"Fetched {len(questions_data)} questions.")
43
+ except requests.exceptions.RequestException as e:
44
+ return f"Error fetching questions: {e}", None
45
+ except requests.exceptions.JSONDecodeError as e:
46
+ return f"Error decoding server response for questions: {e}", None
47
+ except Exception as e:
48
+ return f"An unexpected error occurred fetching questions: {e}", None
49
+
50
+ results_log = []
51
+ answers_payload = []
52
+ print(f"Running agent on {len(questions_data)} questions...")
53
+
54
+ for item in questions_data:
55
+ task_id = item.get("task_id")
56
+ question_text = item.get("question")
57
+ if not task_id or question_text is None:
58
+ print(f"Skipping item with missing task_id or question: {item}")
59
+ continue
60
+
61
+ prompt = build_question_prompt(item)
62
+
63
+ try:
64
+ raw_output = agent.run(prompt)
65
+ answer = _clean_answer(raw_output)
66
+ print(f"Task {task_id}: raw={raw_output!r} cleaned={answer!r}")
67
+ except Exception as e:
68
+ print(f"Error running agent on task {task_id}: {e}")
69
+ answer = f"AGENT ERROR: {e}"
70
+
71
+ answers_payload.append({"task_id": task_id, "submitted_answer": answer})
72
+ results_log.append({
73
+ "Task ID": task_id,
74
+ "Question": question_text,
75
+ "Submitted Answer": answer,
76
+ })
77
+
78
+ if not answers_payload:
79
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
80
+
81
+ submission_data = {
82
+ "username": username.strip(),
83
+ "agent_code": agent_code,
84
+ "answers": answers_payload,
85
+ }
86
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
87
+ print(status_update)
88
+
89
+ print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
90
+ try:
91
+ response = requests.post(submit_url, json=submission_data, timeout=60)
92
+ response.raise_for_status()
93
+ result_data = response.json()
94
+ final_status = (
95
+ f"Submission Successful!\n"
96
+ f"User: {result_data.get('username')}\n"
97
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
98
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
99
+ f"Message: {result_data.get('message', 'No message received.')}"
100
+ )
101
+ print("Submission successful.")
102
+ results_df = pd.DataFrame(results_log)
103
+ return final_status, results_df
104
+ except requests.exceptions.HTTPError as e:
105
+ error_detail = f"Server responded with status {e.response.status_code}."
106
+ try:
107
+ error_json = e.response.json()
108
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
109
+ except requests.exceptions.JSONDecodeError:
110
+ error_detail += f" Response: {e.response.text[:500]}"
111
+ return f"Submission Failed: {error_detail}", pd.DataFrame(results_log)
112
+ except requests.exceptions.Timeout:
113
+ return "Submission Failed: The request timed out.", pd.DataFrame(results_log)
114
+ except requests.exceptions.RequestException as e:
115
+ return f"Submission Failed: Network error - {e}", pd.DataFrame(results_log)
116
+ except Exception as e:
117
+ return f"An unexpected error occurred during submission: {e}", pd.DataFrame(results_log)
118
+
119
+
120
+ with gr.Blocks() as demo:
121
+ gr.Markdown("# GAIA Benchmark Agent Runner")
122
+ gr.Markdown(
123
+ """
124
+ **Instructions:**
125
+ 1. Log in to your Hugging Face account using the button below.
126
+ 2. Click **Run Evaluation & Submit All Answers** to run the agent on all questions and submit.
127
+ 3. Results and a per-question breakdown will appear below.
128
+
129
+ ---
130
+ **Note:** Running all questions takes several minutes. Each question triggers multi-step reasoning.
131
+ """
132
+ )
133
+
134
+ gr.LoginButton()
135
+ run_button = gr.Button("Run Evaluation & Submit All Answers")
136
+ status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
137
+ results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
138
+
139
+ run_button.click(
140
+ fn=run_and_submit_all,
141
+ outputs=[status_output, results_table],
142
+ )
143
+
144
+ if __name__ == "__main__":
145
+ print("\n" + "-" * 30 + " App Starting " + "-" * 30)
146
+ space_host = os.getenv("SPACE_HOST")
147
+ space_id = os.getenv("SPACE_ID")
148
+
149
+ if space_host:
150
+ print(f"SPACE_HOST: {space_host}")
151
+ print(f" Runtime URL: https://{space_host}.hf.space")
152
+ else:
153
+ print("SPACE_HOST not found (running locally?).")
154
+
155
+ if space_id:
156
+ print(f"SPACE_ID: {space_id}")
157
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id}/tree/main")
158
+ else:
159
+ print("SPACE_ID not found (running locally?).")
160
+
161
+ print("-" * 60 + "\n")
162
+ demo.launch(debug=True, share=False)
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ requests
3
+ smolagents[litellm]
4
+ duckduckgo-search
5
+ ddgs
6
+ PyPDF2
7
+ pandas
8
+ openpyxl
9
+ openai
10
+ Pillow
test_local.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Local test script for the GAIA benchmark agent.
3
+
4
+ Fetches questions from the scoring API and runs the agent on 2-3 questions
5
+ WITHOUT submitting answers. This lets you validate reasoning and answer
6
+ formatting before burning submission attempts.
7
+
8
+ Usage:
9
+ set MODEL_ID=gpt-4o
10
+ set OPENAI_API_KEY=sk-...
11
+ python test_local.py
12
+ """
13
+
14
+ import os
15
+ import sys
16
+ import requests
17
+
18
+ from agent import create_agent, _clean_answer, build_question_prompt
19
+
20
+
21
+ API_URL = "https://agents-course-unit4-scoring.hf.space"
22
+ NUM_QUESTIONS = 3
23
+
24
+
25
+ def main():
26
+ print("=" * 60)
27
+ print("GAIA Agent — Local Test")
28
+ print("=" * 60)
29
+
30
+ # Check that the agent can be created
31
+ print("\n[1] Creating agent...")
32
+ try:
33
+ agent = create_agent()
34
+ print(" Agent created successfully.")
35
+ except Exception as e:
36
+ print(f" Failed to create agent: {e}")
37
+ sys.exit(1)
38
+
39
+ # Fetch questions
40
+ print(f"\n[2] Fetching questions from {API_URL}/questions ...")
41
+ try:
42
+ resp = requests.get(f"{API_URL}/questions", timeout=15)
43
+ resp.raise_for_status()
44
+ questions = resp.json()
45
+ except Exception as e:
46
+ print(f" Failed to fetch questions: {e}")
47
+ sys.exit(1)
48
+
49
+ print(f" Got {len(questions)} total questions.")
50
+
51
+ # Prefer text-only questions first (no file attachment)
52
+ no_file = [q for q in questions if not q.get("file_name")]
53
+ with_file = [q for q in questions if q.get("file_name")]
54
+ selected = no_file[:NUM_QUESTIONS]
55
+ if len(selected) < NUM_QUESTIONS:
56
+ selected += with_file[: NUM_QUESTIONS - len(selected)]
57
+
58
+ print(f" Selected {len(selected)} questions to test.\n")
59
+
60
+ # Run agent on each
61
+ for i, item in enumerate(selected):
62
+ task_id = item.get("task_id", "???")
63
+ question = item.get("question", "")
64
+ file_name = item.get("file_name", "")
65
+ prompt = build_question_prompt(item)
66
+
67
+ print("=" * 60)
68
+ print(f"Question {i + 1}/{len(selected)}")
69
+ print(f" Task ID: {task_id}")
70
+ print(f" File: {file_name or '(none)'}")
71
+ print(f" Question: {question[:200]}{'...' if len(question) > 200 else ''}")
72
+ print("-" * 60)
73
+
74
+ try:
75
+ raw_output = agent.run(prompt)
76
+ except Exception as e:
77
+ raw_output = f"ERROR: {e}"
78
+
79
+ cleaned = _clean_answer(raw_output)
80
+
81
+ print(f" Raw output: {raw_output!r}")
82
+ print(f" Clean answer: {cleaned!r}")
83
+ print()
84
+
85
+ print("=" * 60)
86
+ print("Test complete.")
87
+ print("=" * 60)
88
+
89
+
90
+ if __name__ == "__main__":
91
+ main()