czrrr commited on
Commit
9535c3c
·
verified ·
1 Parent(s): 3fd5127

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -26
app.py CHANGED
@@ -14,7 +14,6 @@ from smolagents import (
14
  DuckDuckGoSearchTool,
15
  LiteLLMModel,
16
  Tool,
17
- VisitWebpageTool,
18
  WikipediaSearchTool,
19
  )
20
 
@@ -25,6 +24,7 @@ HTTP_TIMEOUT = 45
25
  MAX_EXTRACTED_CHARS = 35_000
26
  DEFAULT_HF_MODEL = "huggingface/openai/gpt-oss-120b"
27
  DEFAULT_GEMINI_MODEL = "gemini/gemini-3.5-flash"
 
28
 
29
 
30
  def clean_filename(value: str) -> str:
@@ -123,6 +123,31 @@ def download_gaia_attachment(task_id: str) -> tuple[bytes, str]:
123
  )
124
 
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  def extract_attachment_text(data: bytes, filename: str) -> str:
127
  """Extrai conteúdo legível dos formatos mais comuns do GAIA."""
128
  suffix = Path(filename).suffix.lower()
@@ -218,6 +243,74 @@ def extract_attachment_text(data: bytes, filename: str) -> str:
218
  return text
219
 
220
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  class InspectGaiaAttachmentTool(Tool):
222
  name = "inspect_gaia_attachment"
223
  description = (
@@ -426,12 +519,6 @@ class BasicAgent:
426
  gemini_api_key = os.getenv("GEMINI_API_KEY")
427
  configured_model = os.getenv("GAIA_MODEL_ID")
428
 
429
- if not gemini_api_key:
430
- raise RuntimeError(
431
- "O secret GEMINI_API_KEY é obrigatório porque todas as "
432
- "respostas passam pela revisão final do Gemini."
433
- )
434
-
435
  model_id = configured_model or DEFAULT_HF_MODEL
436
 
437
  if model_id.startswith("gemini/"):
@@ -466,12 +553,7 @@ class BasicAgent:
466
  "snippets. Use it to discover candidate sources. It does NOT open "
467
  "or read the full pages; call visit_webpage on a returned URL."
468
  )
469
- visit_page_tool = VisitWebpageTool(max_output_length=30_000)
470
- visit_page_tool.description = (
471
- "Opens one exact HTTP/HTTPS URL and returns the readable page "
472
- "content. Use it after web_search when facts depend on the page "
473
- "itself, a table, article text, or linked source."
474
- )
475
  wikipedia_tool = WikipediaSearchTool(
476
  user_agent="GAIA-Course-Agent/1.0 (educational project)",
477
  language="en",
@@ -490,14 +572,15 @@ class BasicAgent:
490
  InspectGaiaAttachmentTool(),
491
  YouTubeTranscriptTool(),
492
  AnalyzeGaiaImageTool(),
493
- ConsultGeminiTool(),
494
  ]
 
 
495
 
496
  self.agent = CodeAgent(
497
  tools=agent_tools,
498
  model=self.model,
499
  max_steps=10,
500
- planning_interval=4,
501
  additional_authorized_imports=[
502
  "collections",
503
  "datetime",
@@ -540,8 +623,18 @@ can be opened. Never invent a tool, use subprocess, or use shell commands.
540
  Do not repeat nearly identical searches; change the source or method.
541
 
542
  FINAL RESPONSE POLICY:
543
- Call final_answer with only the requested value. Never include reasoning,
544
- explanations, labels, Markdown, citations, or the words "FINAL ANSWER".
 
 
 
 
 
 
 
 
 
 
545
  - Quantity/count: return only the number, unless units or currency are requested.
546
  - Person: return only the requested name component.
547
  - City/country/code: return only that value.
@@ -560,11 +653,23 @@ explanations, labels, Markdown, citations, or the words "FINAL ANSWER".
560
  if not question:
561
  raise ValueError("Digite uma pergunta para testar o agente.")
562
 
563
- task_context = (
564
- f"GAIA task_id: {task_id}\n\nQuestion: {question}"
565
- if task_id
566
- else question
567
- )
 
 
 
 
 
 
 
 
 
 
 
 
568
  try:
569
  result = self.agent.run(task_context, reset=True)
570
  except Exception as exc:
@@ -653,6 +758,12 @@ explanations, labels, Markdown, citations, or the words "FINAL ANSWER".
653
  self, question: str, candidate: str, task_id: str | None = None
654
  ) -> str:
655
  """Revisa obrigatoriamente conteúdo e formato antes de salvar a resposta."""
 
 
 
 
 
 
656
  reviewer_model = os.getenv(
657
  "GAIA_GEMINI_REVIEW_MODEL", DEFAULT_GEMINI_MODEL
658
  )
@@ -748,10 +859,17 @@ Do not return JSON. Do not wrap the fields in Markdown or a code block.
748
  except Exception as exc:
749
  last_error = exc
750
 
751
- raise RuntimeError(
752
- "A revisão obrigatória do Gemini falhou; a resposta não foi salva. "
753
- f"Detalhe: {last_error}"
754
- )
 
 
 
 
 
 
 
755
 
756
 
757
  def empty_results() -> pd.DataFrame:
 
14
  DuckDuckGoSearchTool,
15
  LiteLLMModel,
16
  Tool,
 
17
  WikipediaSearchTool,
18
  )
19
 
 
24
  MAX_EXTRACTED_CHARS = 35_000
25
  DEFAULT_HF_MODEL = "huggingface/openai/gpt-oss-120b"
26
  DEFAULT_GEMINI_MODEL = "gemini/gemini-3.5-flash"
27
+ TASK_FILE_CACHE = {}
28
 
29
 
30
  def clean_filename(value: str) -> str:
 
123
  )
124
 
125
 
126
+ def get_task_file_name(task_id: str) -> str:
127
+ """Retorna o nome oficial do anexo ou uma string vazia."""
128
+ task_id = str(task_id or "").strip()
129
+ if not task_id:
130
+ return ""
131
+ if task_id in TASK_FILE_CACHE:
132
+ return TASK_FILE_CACHE[task_id]
133
+
134
+ try:
135
+ response = requests.get(
136
+ f"{DEFAULT_API_URL}/questions", timeout=HTTP_TIMEOUT
137
+ )
138
+ response.raise_for_status()
139
+ for item in response.json():
140
+ item_task_id = str(item.get("task_id", "")).strip()
141
+ TASK_FILE_CACHE[item_task_id] = str(
142
+ item.get("file_name") or ""
143
+ ).strip()
144
+ except Exception as exc:
145
+ print(f"Could not load task attachment metadata: {exc}")
146
+ return ""
147
+
148
+ return TASK_FILE_CACHE.get(task_id, "")
149
+
150
+
151
  def extract_attachment_text(data: bytes, filename: str) -> str:
152
  """Extrai conteúdo legível dos formatos mais comuns do GAIA."""
153
  suffix = Path(filename).suffix.lower()
 
243
  return text
244
 
245
 
246
+ class OpenWebPageTool(Tool):
247
+ name = "visit_webpage"
248
+ description = (
249
+ "Opens and reads one exact HTTP/HTTPS page. Use it after web_search to "
250
+ "verify article text, tables, archives, papers, and linked sources. "
251
+ "It automatically retries blocked HTML pages through a text mirror. "
252
+ "It is not a binary-file downloader."
253
+ )
254
+ inputs = {
255
+ "url": {
256
+ "type": "string",
257
+ "description": "The complete HTTP or HTTPS URL to open.",
258
+ }
259
+ }
260
+ output_type = "string"
261
+
262
+ def forward(self, url: str) -> str:
263
+ from markdownify import markdownify
264
+
265
+ url = str(url or "").strip()
266
+ if not re.match(r"^https?://", url, flags=re.I):
267
+ return "Invalid URL: visit_webpage requires a full HTTP/HTTPS URL."
268
+
269
+ headers = {
270
+ "User-Agent": (
271
+ "Mozilla/5.0 (compatible; GAIA-Course-Agent/1.0; "
272
+ "+https://huggingface.co/learn/agents-course)"
273
+ )
274
+ }
275
+ errors = []
276
+ targets = [url]
277
+ if "r.jina.ai/http" not in url:
278
+ without_scheme = re.sub(r"^https?://", "", url)
279
+ targets.append(f"https://r.jina.ai/http://{without_scheme}")
280
+
281
+ for target in targets:
282
+ try:
283
+ response = requests.get(
284
+ target,
285
+ headers=headers,
286
+ timeout=HTTP_TIMEOUT,
287
+ allow_redirects=True,
288
+ )
289
+ response.raise_for_status()
290
+ content_type = response.headers.get("content-type", "").lower()
291
+ if (
292
+ "text/" not in content_type
293
+ and "json" not in content_type
294
+ and "xml" not in content_type
295
+ ):
296
+ return (
297
+ f"The URL returned binary content ({content_type}). "
298
+ "Use inspect_gaia_attachment for official GAIA files."
299
+ )
300
+
301
+ text = response.text
302
+ if "html" in content_type:
303
+ text = markdownify(text)
304
+ text = text.strip()
305
+ if len(text) > 30_000:
306
+ text = text[:30_000] + "\n[page truncated]"
307
+ return text or "The page was retrieved but contained no text."
308
+ except Exception as exc:
309
+ errors.append(f"{target}: {exc}")
310
+
311
+ return "Error fetching the webpage: " + " | ".join(errors)
312
+
313
+
314
  class InspectGaiaAttachmentTool(Tool):
315
  name = "inspect_gaia_attachment"
316
  description = (
 
519
  gemini_api_key = os.getenv("GEMINI_API_KEY")
520
  configured_model = os.getenv("GAIA_MODEL_ID")
521
 
 
 
 
 
 
 
522
  model_id = configured_model or DEFAULT_HF_MODEL
523
 
524
  if model_id.startswith("gemini/"):
 
553
  "snippets. Use it to discover candidate sources. It does NOT open "
554
  "or read the full pages; call visit_webpage on a returned URL."
555
  )
556
+ visit_page_tool = OpenWebPageTool()
 
 
 
 
 
557
  wikipedia_tool = WikipediaSearchTool(
558
  user_agent="GAIA-Course-Agent/1.0 (educational project)",
559
  language="en",
 
572
  InspectGaiaAttachmentTool(),
573
  YouTubeTranscriptTool(),
574
  AnalyzeGaiaImageTool(),
 
575
  ]
576
+ if gemini_api_key:
577
+ agent_tools.append(ConsultGeminiTool())
578
 
579
  self.agent = CodeAgent(
580
  tools=agent_tools,
581
  model=self.model,
582
  max_steps=10,
583
+ planning_interval=None,
584
  additional_authorized_imports=[
585
  "collections",
586
  "datetime",
 
623
  Do not repeat nearly identical searches; change the source or method.
624
 
625
  FINAL RESPONSE POLICY:
626
+ Call final_answer with only the requested value. The final action must always
627
+ be valid executable code inside the required code tags, for example:
628
+ <code>
629
+ final_answer("Claus")
630
+ </code>
631
+ or:
632
+ <code>
633
+ final_answer(5)
634
+ </code>
635
+ Never write the answer as plain text outside a final_answer tool call. Never
636
+ include reasoning, explanations, labels, Markdown, citations, or the words
637
+ "FINAL ANSWER" inside the submitted value.
638
  - Quantity/count: return only the number, unless units or currency are requested.
639
  - Person: return only the requested name component.
640
  - City/country/code: return only that value.
 
653
  if not question:
654
  raise ValueError("Digite uma pergunta para testar o agente.")
655
 
656
+ if task_id:
657
+ attachment_name = get_task_file_name(task_id)
658
+ attachment_context = (
659
+ f"Official attachment: {attachment_name}. "
660
+ "Use the appropriate attachment tool."
661
+ if attachment_name
662
+ else (
663
+ "Official attachment: NONE. Do not call "
664
+ "inspect_gaia_attachment or analyze_gaia_image."
665
+ )
666
+ )
667
+ task_context = (
668
+ f"GAIA task_id: {task_id}\n"
669
+ f"{attachment_context}\n\nQuestion: {question}"
670
+ )
671
+ else:
672
+ task_context = question
673
  try:
674
  result = self.agent.run(task_context, reset=True)
675
  except Exception as exc:
 
758
  self, question: str, candidate: str, task_id: str | None = None
759
  ) -> str:
760
  """Revisa obrigatoriamente conteúdo e formato antes de salvar a resposta."""
761
+ if not self.gemini_api_key:
762
+ fallback = self.deterministic_answer_cleanup(candidate)
763
+ print("Gemini review status: SKIPPED — GEMINI_API_KEY is missing")
764
+ print(f"Primary answer preserved: {fallback}")
765
+ return fallback
766
+
767
  reviewer_model = os.getenv(
768
  "GAIA_GEMINI_REVIEW_MODEL", DEFAULT_GEMINI_MODEL
769
  )
 
859
  except Exception as exc:
860
  last_error = exc
861
 
862
+ fallback = self.deterministic_answer_cleanup(candidate)
863
+ if not fallback:
864
+ raise RuntimeError(
865
+ "A revisão do Gemini falhou e a resposta primária estava vazia. "
866
+ f"Detalhe: {last_error}"
867
+ )
868
+
869
+ print(f"Candidate answer: {fallback}")
870
+ print("Gemini review status: FAILED — primary answer preserved")
871
+ print(f"Gemini review error: {last_error}")
872
+ return fallback
873
 
874
 
875
  def empty_results() -> pd.DataFrame: