File size: 7,114 Bytes
9b55593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"""
app.py
======
Gradio app for the Hugging Face Agents Course - Unit 4 (GAIA leaderboard).

This app exposes the agent (whichever framework solution is shipped in this
Space: smolagents / LangGraph / LlamaIndex) through a simple UI:

  * fetch the 20 evaluation questions from the scoring API,
  * run the agent on a single question, or on all questions,
  * locally self-score (optional, needs HF_TOKEN + GAIA dataset access),
  * submit the answers to the leaderboard.

Environment variables (set as Space secrets):
  LLM_API_KEY   required - key for an OpenAI-compatible endpoint
  LLM_BASE_URL  optional - default https://api.openai.com/v1
  LLM_MODEL     optional - default gpt-4o-mini
  HF_TOKEN      optional - GAIA-dataset file fallback + local self-scoring
  VISION_MODEL  optional - vision model for image questions
  WHISPER_MODEL optional - default 'small' (use 'base'/'tiny' on CPU)
"""

from __future__ import annotations

import os

import gradio as gr
import pandas as pd
import requests

import gaia_common as gc

# --- pick whichever framework solution is bundled in this Space -------------
SOLUTION_MODULES = ["solution_smolagents", "solution_langgraph", "solution_llamaindex"]
for _mod in SOLUTION_MODULES:
    try:
        solution = __import__(_mod)
        FRAMEWORK = _mod
        break
    except ImportError:
        continue
else:
    raise RuntimeError("No framework solution module found in this Space.")


def make_agent():
    if FRAMEWORK == "solution_smolagents":
        return solution.GAIAAgent()
    if FRAMEWORK == "solution_langgraph":
        return solution.GAIALangGraphAgent()
    return solution.GAIALlamaIndexAgent()


# --- core functions ---------------------------------------------------------

def build_question_options() -> list[str]:
    try:
        qs = gc.fetch_questions()
        if qs:
            return [f"{q['task_id']} :: {q['question'][:80]}" for q in qs]
    except Exception as e:
        print(f"Could not fetch questions at startup: {e}")
    return ["Questions unavailable at startup - reload after the API is reachable"]


def _run_single(task_id: str, question: str, agent) -> str:
    answer = agent(task_id, question)
    return gc.normalize_final_answer(answer)


def run_single(selection: str, profile: gr.OAuthProfile | None):
    if not profile:
        return "Please login with the Login button (required for submission).", None
    if not selection:
        return "Please choose a question from the dropdown.", None
    task_id = selection.split(" :: ")[0]
    questions = {q["task_id"]: q["question"] for q in gc.fetch_questions()}
    question = questions.get(task_id, "")
    try:
        agent = make_agent()
        answer = _run_single(task_id, question, agent)
        return f"QUESTION:\n{question}\n\nANSWER:\n{answer}", pd.DataFrame(
            [{"task_id": task_id, "submitted_answer": answer}]
        )
    except Exception as e:
        return f"Agent error: {e}", None


def run_all(submit: bool, profile: gr.OAuthProfile | None) -> tuple[str, pd.DataFrame]:
    if not profile:
        return "Please login with the Login button.", None
    if not os.environ.get("LLM_API_KEY") and not os.environ.get("HF_TOKEN"):
        return (
            "No LLM credentials found. Set the Space secrets LLM_API_KEY "
            "(OpenAI-compatible endpoint) to run the agent.",
            None,
        )

    agent_code = f"https://huggingface.co/spaces/{os.getenv('SPACE_ID', '?')}/tree/main"
    username = profile.username

    questions = gc.fetch_questions()
    rows = []
    answers_payload = []
    agent = make_agent()

    for item in questions:
        task_id = item["task_id"]
        question = item["question"]
        try:
            answer = agent(task_id, question)
        except Exception as e:
            answer = f"AGENT ERROR: {e}"
        rows.append({"Task ID": task_id, "Question": question, "Submitted Answer": answer})
        answers_payload.append({"task_id": task_id, "submitted_answer": answer})

    status = f"Agent finished {len(answers_payload)} questions.\n"

    if submit:
        try:
            res = gc.submit_answers(username, agent_code, answers_payload)
            status += (
                f"SUBMITTED for {res.get('username')} -> "
                f"score {res.get('score')}% ({res.get('correct_count')} correct)."
            )
        except Exception as e:
            status += f"Submission failed: {e}"
    else:
        local = gc.self_score(answers_payload)
        if local.get("total"):
            status += f"LOCAL self-score: {local['score']}% ({local['correct']}/{local['total']})."
        else:
            status += "Local self-scoring skipped (set HF_TOKEN + accept GAIA dataset gating)."

    return status, pd.DataFrame(rows)


def run_all_and_submit(profile: gr.OAuthProfile | None):
    return run_all(True, profile)


def run_all_selfscore(profile: gr.OAuthProfile | None):
    return run_all(False, profile)


# --- UI ---------------------------------------------------------------------

with gr.Blocks(title="GAIA Agent - Unit 4") as demo:
    gr.Markdown(
        f"# GAIA Level-1 Agent · framework: `{FRAMEWORK}`\n\n"
        "Course **Unit 4 hands-on** — answers 20 GAIA level-1 questions and submits "
        "them to the leaderboard. The agent uses the canonical GAIA system prompt "
        "and a toolbelt: web search, page fetch, file download (with GAIA-dataset "
        "fallback), whisper transcription, vision analysis and python execution."
    )

    with gr.Row():
        gr.LoginButton()
        questions_dd = gr.Dropdown(
            label="Pick a question",
            choices=build_question_options(),
            allow_custom_value=False,
        )
        run_single_btn = gr.Button("Run single question")

    single_out = gr.Textbox(label="Single-question result", lines=6, interactive=False)

    with gr.Row():
        run_all_btn = gr.Button("Run all 20 questions (self-score if possible)")
        submit_btn = gr.Button("Run all 20 questions AND submit to leaderboard")

    status_out = gr.Textbox(label="Status / submission result", lines=8, interactive=False)
    results_table = gr.DataFrame(label="Questions and agent answers", wrap=True)

    run_single_btn.click(run_single, inputs=[questions_dd], outputs=[single_out])
    run_all_btn.click(run_all_selfscore, inputs=[], outputs=[status_out, results_table])
    submit_btn.click(run_all_and_submit, inputs=[], outputs=[status_out, results_table])
    gr.Markdown(
        "### Instructions\n"
        "1. This Space runs a real agent — it calls an LLM API and may take several minutes "
        "for all 20 questions (audio transcription and YouTube downloads are the slowest).\n"
        "2. Set Space secrets: `LLM_API_KEY` (required), optionally `LLM_BASE_URL`, "
        "`LLM_MODEL`, `HF_TOKEN`, `VISION_MODEL`, `WHISPER_MODEL`.\n"
        "3. Log in, then click **Run all & submit**.\n\n"
        "> Keep this Space public so your code link on the leaderboard verifies your submission."
    )


if __name__ == "__main__":
    demo.launch()