Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -23,7 +23,7 @@ RESULT_COLUMNS = ["Task ID", "Question", "Submitted Answer"]
|
|
| 23 |
HTTP_TIMEOUT = 45
|
| 24 |
MAX_EXTRACTED_CHARS = 35_000
|
| 25 |
DEFAULT_HF_MODEL = "huggingface/openai/gpt-oss-120b"
|
| 26 |
-
|
| 27 |
TASK_FILE_CACHE = {}
|
| 28 |
|
| 29 |
|
|
@@ -458,90 +458,27 @@ class AnalyzeGaiaImageTool(Tool):
|
|
| 458 |
return f"Could not analyze the GAIA image: {exc}"
|
| 459 |
|
| 460 |
|
| 461 |
-
class ConsultGeminiTool(Tool):
|
| 462 |
-
name = "consult_gemini"
|
| 463 |
-
description = (
|
| 464 |
-
"Asks Gemini for an independent second opinion on a difficult question "
|
| 465 |
-
"or on evidence already collected. Use it to verify reasoning, resolve "
|
| 466 |
-
"conflicting sources, or check the exact requested answer format. Do "
|
| 467 |
-
"not use it as a substitute for inspecting official attachments."
|
| 468 |
-
)
|
| 469 |
-
inputs = {
|
| 470 |
-
"request": {
|
| 471 |
-
"type": "string",
|
| 472 |
-
"description": (
|
| 473 |
-
"The complete question plus any relevant evidence and the "
|
| 474 |
-
"specific point Gemini should verify."
|
| 475 |
-
),
|
| 476 |
-
}
|
| 477 |
-
}
|
| 478 |
-
output_type = "string"
|
| 479 |
-
|
| 480 |
-
def forward(self, request: str) -> str:
|
| 481 |
-
api_key = os.getenv("GEMINI_API_KEY")
|
| 482 |
-
if not api_key:
|
| 483 |
-
return "Gemini consultation unavailable: GEMINI_API_KEY is missing."
|
| 484 |
-
|
| 485 |
-
model = os.getenv(
|
| 486 |
-
"GAIA_GEMINI_TOOL_MODEL", DEFAULT_GEMINI_MODEL
|
| 487 |
-
)
|
| 488 |
-
try:
|
| 489 |
-
response = completion(
|
| 490 |
-
model=model,
|
| 491 |
-
api_key=api_key,
|
| 492 |
-
messages=[
|
| 493 |
-
{
|
| 494 |
-
"role": "system",
|
| 495 |
-
"content": (
|
| 496 |
-
"You are a verification specialist assisting another "
|
| 497 |
-
"GAIA agent. Analyze the supplied question and evidence "
|
| 498 |
-
"critically. Identify uncertainty or contradictions, "
|
| 499 |
-
"then provide your recommended exact answer. Be concise "
|
| 500 |
-
"and never claim to have opened sources that were not "
|
| 501 |
-
"included in the request."
|
| 502 |
-
),
|
| 503 |
-
},
|
| 504 |
-
{"role": "user", "content": str(request)},
|
| 505 |
-
],
|
| 506 |
-
temperature=0,
|
| 507 |
-
max_tokens=900,
|
| 508 |
-
)
|
| 509 |
-
return str(response.choices[0].message.content).strip()
|
| 510 |
-
except Exception as exc:
|
| 511 |
-
return f"Gemini consultation failed: {exc}"
|
| 512 |
-
|
| 513 |
-
|
| 514 |
class BasicAgent:
|
| 515 |
def __init__(self):
|
| 516 |
print("Inicializando o agente GAIA...")
|
| 517 |
|
| 518 |
hf_token = os.getenv("HF_TOKEN")
|
| 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/"):
|
| 525 |
-
model_api_key = gemini_api_key
|
| 526 |
-
required_secret = "GEMINI_API_KEY"
|
| 527 |
-
else:
|
| 528 |
-
model_api_key = hf_token
|
| 529 |
-
required_secret = "HF_TOKEN"
|
| 530 |
-
|
| 531 |
-
if not model_api_key:
|
| 532 |
raise RuntimeError(
|
| 533 |
-
|
| 534 |
"Adicione a chave em Settings > Variables and secrets > Secrets."
|
| 535 |
)
|
| 536 |
|
| 537 |
self.model = LiteLLMModel(
|
| 538 |
model_id=model_id,
|
| 539 |
-
api_key=
|
| 540 |
temperature=0,
|
| 541 |
max_tokens=2_000,
|
| 542 |
)
|
| 543 |
self.hf_token = hf_token
|
| 544 |
-
self.gemini_api_key = gemini_api_key
|
| 545 |
self.model_id = model_id
|
| 546 |
print(f"Modelo principal selecionado: {model_id}")
|
| 547 |
|
|
@@ -573,8 +510,6 @@ class BasicAgent:
|
|
| 573 |
YouTubeTranscriptTool(),
|
| 574 |
AnalyzeGaiaImageTool(),
|
| 575 |
]
|
| 576 |
-
if gemini_api_key:
|
| 577 |
-
agent_tools.append(ConsultGeminiTool())
|
| 578 |
|
| 579 |
self.agent = CodeAgent(
|
| 580 |
tools=agent_tools,
|
|
@@ -613,9 +548,6 @@ TOOL ROUTING POLICY:
|
|
| 613 |
chess positions, or questions that visually depend on an attached image.
|
| 614 |
6. youtube_transcript retrieves spoken subtitles. Use it when asked what a
|
| 615 |
person said. It cannot answer purely visual video questions.
|
| 616 |
-
7. consult_gemini gives a second opinion. Use it after gathering evidence when
|
| 617 |
-
sources conflict or the candidate answer is uncertain. Include the complete
|
| 618 |
-
question, evidence, and candidate answer.
|
| 619 |
|
| 620 |
Research carefully, prefer primary or official sources, and cross-check
|
| 621 |
uncertain facts. A search snippet alone is insufficient when the source page
|
|
@@ -681,12 +613,12 @@ include reasoning, explanations, labels, Markdown, citations, or the words
|
|
| 681 |
):
|
| 682 |
raise RuntimeError(
|
| 683 |
f"Falha de autenticação no modelo {self.model_id}. "
|
| 684 |
-
"Verifique
|
| 685 |
-
"
|
| 686 |
) from exc
|
| 687 |
raise
|
| 688 |
candidate = self.format_exact_answer(question, str(result))
|
| 689 |
-
return self.
|
| 690 |
question=question,
|
| 691 |
candidate=candidate,
|
| 692 |
task_id=task_id,
|
|
@@ -754,18 +686,19 @@ include reasoning, explanations, labels, Markdown, citations, or the words
|
|
| 754 |
|
| 755 |
return self.deterministic_answer_cleanup(cleaned)
|
| 756 |
|
| 757 |
-
def
|
| 758 |
self, question: str, candidate: str, task_id: str | None = None
|
| 759 |
) -> str:
|
| 760 |
-
"""
|
| 761 |
-
|
|
|
|
| 762 |
fallback = self.deterministic_answer_cleanup(candidate)
|
| 763 |
-
print("
|
| 764 |
print(f"Primary answer preserved: {fallback}")
|
| 765 |
return fallback
|
| 766 |
|
| 767 |
reviewer_model = os.getenv(
|
| 768 |
-
"
|
| 769 |
)
|
| 770 |
review_prompt = f"""
|
| 771 |
You are the mandatory final reviewer for a GAIA exact-match answer.
|
|
@@ -811,7 +744,7 @@ Do not return JSON. Do not wrap the fields in Markdown or a code block.
|
|
| 811 |
)
|
| 812 |
response = completion(
|
| 813 |
model=reviewer_model,
|
| 814 |
-
api_key=
|
| 815 |
messages=[
|
| 816 |
{
|
| 817 |
"role": "user",
|
|
@@ -837,13 +770,13 @@ Do not return JSON. Do not wrap the fields in Markdown or a code block.
|
|
| 837 |
)
|
| 838 |
if not answer_match:
|
| 839 |
raise ValueError(
|
| 840 |
-
"
|
| 841 |
)
|
| 842 |
final_answer = self.deterministic_answer_cleanup(
|
| 843 |
answer_match.group(1)
|
| 844 |
)
|
| 845 |
if not final_answer:
|
| 846 |
-
raise ValueError("
|
| 847 |
|
| 848 |
changed = final_answer != candidate
|
| 849 |
note = (
|
|
@@ -852,9 +785,9 @@ Do not return JSON. Do not wrap the fields in Markdown or a code block.
|
|
| 852 |
else "Review completed without a note."
|
| 853 |
)
|
| 854 |
print(f"Candidate answer: {candidate}")
|
| 855 |
-
print(f"
|
| 856 |
-
print(f"
|
| 857 |
-
print(f"
|
| 858 |
return final_answer
|
| 859 |
except Exception as exc:
|
| 860 |
last_error = exc
|
|
@@ -862,13 +795,13 @@ Do not return JSON. Do not wrap the fields in Markdown or a code block.
|
|
| 862 |
fallback = self.deterministic_answer_cleanup(candidate)
|
| 863 |
if not fallback:
|
| 864 |
raise RuntimeError(
|
| 865 |
-
"A revisão do
|
| 866 |
f"Detalhe: {last_error}"
|
| 867 |
)
|
| 868 |
|
| 869 |
print(f"Candidate answer: {fallback}")
|
| 870 |
-
print("
|
| 871 |
-
print(f"
|
| 872 |
return fallback
|
| 873 |
|
| 874 |
|
|
|
|
| 23 |
HTTP_TIMEOUT = 45
|
| 24 |
MAX_EXTRACTED_CHARS = 35_000
|
| 25 |
DEFAULT_HF_MODEL = "huggingface/openai/gpt-oss-120b"
|
| 26 |
+
DEFAULT_GROQ_REVIEW_MODEL = "groq/qwen/qwen3.6-27b"
|
| 27 |
TASK_FILE_CACHE = {}
|
| 28 |
|
| 29 |
|
|
|
|
| 458 |
return f"Could not analyze the GAIA image: {exc}"
|
| 459 |
|
| 460 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
class BasicAgent:
|
| 462 |
def __init__(self):
|
| 463 |
print("Inicializando o agente GAIA...")
|
| 464 |
|
| 465 |
hf_token = os.getenv("HF_TOKEN")
|
|
|
|
| 466 |
configured_model = os.getenv("GAIA_MODEL_ID")
|
| 467 |
|
| 468 |
model_id = configured_model or DEFAULT_HF_MODEL
|
| 469 |
+
if not hf_token:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
raise RuntimeError(
|
| 471 |
+
"O secret HF_TOKEN não está configurado. "
|
| 472 |
"Adicione a chave em Settings > Variables and secrets > Secrets."
|
| 473 |
)
|
| 474 |
|
| 475 |
self.model = LiteLLMModel(
|
| 476 |
model_id=model_id,
|
| 477 |
+
api_key=hf_token,
|
| 478 |
temperature=0,
|
| 479 |
max_tokens=2_000,
|
| 480 |
)
|
| 481 |
self.hf_token = hf_token
|
|
|
|
| 482 |
self.model_id = model_id
|
| 483 |
print(f"Modelo principal selecionado: {model_id}")
|
| 484 |
|
|
|
|
| 510 |
YouTubeTranscriptTool(),
|
| 511 |
AnalyzeGaiaImageTool(),
|
| 512 |
]
|
|
|
|
|
|
|
| 513 |
|
| 514 |
self.agent = CodeAgent(
|
| 515 |
tools=agent_tools,
|
|
|
|
| 548 |
chess positions, or questions that visually depend on an attached image.
|
| 549 |
6. youtube_transcript retrieves spoken subtitles. Use it when asked what a
|
| 550 |
person said. It cannot answer purely visual video questions.
|
|
|
|
|
|
|
|
|
|
| 551 |
|
| 552 |
Research carefully, prefer primary or official sources, and cross-check
|
| 553 |
uncertain facts. A search snippet alone is insufficient when the source page
|
|
|
|
| 613 |
):
|
| 614 |
raise RuntimeError(
|
| 615 |
f"Falha de autenticação no modelo {self.model_id}. "
|
| 616 |
+
"Verifique se HF_TOKEN possui permissão para usar "
|
| 617 |
+
"Inference Providers."
|
| 618 |
) from exc
|
| 619 |
raise
|
| 620 |
candidate = self.format_exact_answer(question, str(result))
|
| 621 |
+
return self.review_answer_with_groq(
|
| 622 |
question=question,
|
| 623 |
candidate=candidate,
|
| 624 |
task_id=task_id,
|
|
|
|
| 686 |
|
| 687 |
return self.deterministic_answer_cleanup(cleaned)
|
| 688 |
|
| 689 |
+
def review_answer_with_groq(
|
| 690 |
self, question: str, candidate: str, task_id: str | None = None
|
| 691 |
) -> str:
|
| 692 |
+
"""Solicita uma segunda opinião gratuita no Groq, com fallback local."""
|
| 693 |
+
groq_api_key = os.getenv("GROQ_API_KEY")
|
| 694 |
+
if not groq_api_key:
|
| 695 |
fallback = self.deterministic_answer_cleanup(candidate)
|
| 696 |
+
print("Groq review status: SKIPPED — GROQ_API_KEY is missing")
|
| 697 |
print(f"Primary answer preserved: {fallback}")
|
| 698 |
return fallback
|
| 699 |
|
| 700 |
reviewer_model = os.getenv(
|
| 701 |
+
"GAIA_GROQ_REVIEW_MODEL", DEFAULT_GROQ_REVIEW_MODEL
|
| 702 |
)
|
| 703 |
review_prompt = f"""
|
| 704 |
You are the mandatory final reviewer for a GAIA exact-match answer.
|
|
|
|
| 744 |
)
|
| 745 |
response = completion(
|
| 746 |
model=reviewer_model,
|
| 747 |
+
api_key=groq_api_key,
|
| 748 |
messages=[
|
| 749 |
{
|
| 750 |
"role": "user",
|
|
|
|
| 770 |
)
|
| 771 |
if not answer_match:
|
| 772 |
raise ValueError(
|
| 773 |
+
"Groq did not return the <final_answer> field."
|
| 774 |
)
|
| 775 |
final_answer = self.deterministic_answer_cleanup(
|
| 776 |
answer_match.group(1)
|
| 777 |
)
|
| 778 |
if not final_answer:
|
| 779 |
+
raise ValueError("Groq returned an empty final_answer.")
|
| 780 |
|
| 781 |
changed = final_answer != candidate
|
| 782 |
note = (
|
|
|
|
| 785 |
else "Review completed without a note."
|
| 786 |
)
|
| 787 |
print(f"Candidate answer: {candidate}")
|
| 788 |
+
print(f"Groq reviewed answer: {final_answer}")
|
| 789 |
+
print(f"Groq changed answer: {changed}")
|
| 790 |
+
print(f"Groq review note: {note}")
|
| 791 |
return final_answer
|
| 792 |
except Exception as exc:
|
| 793 |
last_error = exc
|
|
|
|
| 795 |
fallback = self.deterministic_answer_cleanup(candidate)
|
| 796 |
if not fallback:
|
| 797 |
raise RuntimeError(
|
| 798 |
+
"A revisão do Groq falhou e a resposta primária estava vazia. "
|
| 799 |
f"Detalhe: {last_error}"
|
| 800 |
)
|
| 801 |
|
| 802 |
print(f"Candidate answer: {fallback}")
|
| 803 |
+
print("Groq review status: FAILED — primary answer preserved")
|
| 804 |
+
print(f"Groq review error: {last_error}")
|
| 805 |
return fallback
|
| 806 |
|
| 807 |
|