czrrr commited on
Commit
95a8cae
·
verified ·
1 Parent(s): 1837796

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +235 -149
app.py CHANGED
@@ -1,192 +1,278 @@
1
  import os
 
2
  import gradio as gr
3
- import requests
4
  import pandas as pd
 
5
  from smolagents import CodeAgent, DuckDuckGoSearchTool, LiteLLMModel
6
 
7
- # --- Constants ---
8
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
9
 
10
- # --- Agent Definition ---
11
  class BasicAgent:
12
  def __init__(self):
13
- print("Inicializando o Agente do GAIA...")
14
-
15
- # Define o modelo
16
- self.model = LiteLLMModel(model_id="huggingface/Qwen/Qwen2.5-Coder-32B-Instruct")
17
-
18
- # Define as ferramentas
19
- self.tools = [DuckDuckGoSearchTool()]
20
-
21
- # Prompt customizado para forçar o formato EXACT MATCH do GAIA
22
- custom_prompt = """
23
- You are an expert AI assistant solving tasks from the GAIA benchmark.
24
- Your final answer MUST be extremely concise and exact.
25
- Do NOT include any conversational text, explanations, or the words "FINAL ANSWER" in your final output.
26
- If the question asks for a comma-separated list, provide ONLY the list.
27
- If the question asks for a number, provide ONLY the number.
28
- """
29
-
30
- # Instancia o agente
31
  self.agent = CodeAgent(
32
- tools=self.tools,
33
  model=self.model,
34
  max_steps=6,
35
- description="Agent designed to solve GAIA benchmark questions with exact match answers."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  )
37
-
38
- # Injetando a instrução no sistema
39
- self.agent.system_prompt = custom_prompt + "\n" + self.agent.system_prompt
40
 
41
  def __call__(self, question: str) -> str:
42
- print(f"Agent received question (first 50 chars): {question[:50]}...")
43
- try:
44
- # O agente executa a pesquisa e raciocina
45
- resposta_final = self.agent.run(question)
46
-
47
- # Limpeza básica de segurança para evitar que a string "FINAL ANSWER" vaze
48
- resposta_limpa = str(resposta_final).replace("FINAL ANSWER", "").strip()
49
-
50
- print(f"Agent returning answer: {resposta_limpa}")
51
- return resposta_limpa
52
- except Exception as e:
53
- print(f"Erro durante o raciocínio do agente: {e}")
54
- return "ERROR"
55
-
56
- # --- Core Functions ---
 
 
 
 
 
57
 
58
  def run_agent_only(profile: gr.OAuthProfile | None):
59
- """
60
- Busca as perguntas, roda o agente para gerar as respostas e retorna
61
- os dados para visualização e o payload para o estado do Gradio.
62
- """
63
  if not profile:
64
- return "Please Login to Hugging Face first.", pd.DataFrame(), []
65
-
66
- api_url = DEFAULT_API_URL
67
- questions_url = f"{api_url}/questions"
68
 
69
  try:
70
  agent = BasicAgent()
71
- except Exception as e:
72
- return f"Error initializing agent: {e}", pd.DataFrame(), []
73
 
74
- print(f"Fetching questions from: {questions_url}")
75
  try:
76
- response = requests.get(questions_url, timeout=15)
77
  response.raise_for_status()
78
- questions_data = response.json()
79
- if not questions_data:
80
- return "Fetched questions list is empty.", pd.DataFrame(), []
81
- except Exception as e:
82
- return f"Error fetching questions: {e}", pd.DataFrame(), []
83
-
84
- results_log = []
85
- answers_payload = []
86
-
87
- print(f"Running agent on {len(questions_data)} questions...")
88
- for item in questions_data:
89
  task_id = item.get("task_id")
90
- question_text = item.get("question")
91
-
92
- if not task_id or question_text is None:
93
  continue
94
-
95
  try:
96
- submitted_answer = agent(question_text)
97
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
98
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
99
- except Exception as e:
100
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"ERROR: {e}"})
101
-
102
- if not answers_payload:
103
- return "Agent did not produce any answers.", pd.DataFrame(results_log), []
104
-
105
- status_update = f"Agent finished! Processed {len(answers_payload)} questions. Please review the table below before submitting."
106
- results_df = pd.DataFrame(results_log)
107
-
108
- # Retorna o status, a tabela visível e o payload invisível (para o gr.State)
109
- return status_update, results_df, answers_payload
110
-
111
-
112
- def submit_to_leaderboard(profile: gr.OAuthProfile | None, answers_payload: list):
113
- """
114
- Pega as respostas validadas no estado do Gradio e envia para a API.
115
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  if not profile:
117
- return "Please Login to Hugging Face first."
118
-
119
- if not answers_payload or len(answers_payload) == 0:
120
- return "No answers to submit. Please run the agent first."
121
-
122
- username = profile.username
123
- space_id = os.getenv("SPACE_ID", "local-environment")
124
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
125
- submit_url = f"{DEFAULT_API_URL}/submit"
126
-
127
- submission_data = {
128
- "username": username.strip(),
129
- "agent_code": agent_code,
130
- "answers": answers_payload
 
 
 
 
 
 
 
131
  }
132
-
133
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
134
  try:
135
- response = requests.post(submit_url, json=submission_data, timeout=60)
 
 
 
 
136
  response.raise_for_status()
137
- result_data = response.json()
138
-
139
- final_status = (
140
- f" Submission Successful!\n"
141
- f"User: {result_data.get('username')}\n"
142
- f"Overall Score: {result_data.get('score', 'N/A')}% "
143
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
144
- f"Message: {result_data.get('message', 'No message received.')}"
145
  )
146
- return final_status
147
- except requests.exceptions.RequestException as e:
148
- return f"❌ Submission Failed: {e}"
149
- except Exception as e:
150
- return f" An unexpected error occurred: {e}"
 
 
151
 
152
 
153
- # --- Build Gradio Interface ---
154
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
155
- gr.Markdown("# GAIA Agent Evaluation - Two-Step Runner")
156
  gr.Markdown(
157
- """
158
- **Instruções de Validação:**
159
- 1. Faça o Login no Hugging Face.
160
- 2. Clique em **'1. Run Agent & Preview'**. O agente vai processar as questões e a tabela será preenchida. (Isso pode demorar vários minutos).
161
- 3. Valide as respostas na tabela. Se o formato estiver correto (Exact Match), clique em **'2. Submit to Leaderboard'** para enviar sua pontuação oficial.
162
- """
163
  )
164
-
165
  gr.LoginButton()
166
 
167
- with gr.Row():
168
- btn_run = gr.Button("1. Run Agent & Preview Answers", variant="secondary")
169
- btn_submit = gr.Button("2. Submit to Leaderboard (Final)", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
- status_output = gr.Textbox(label="System Status", lines=3, interactive=False)
172
- results_table = gr.DataFrame(label="Agent Answers Preview", wrap=True)
173
-
174
- # Variável de estado invisível para armazenar o payload JSON entre os cliques dos botões
175
- stored_answers = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
- # Evento do Botão 1: Roda o agente, atualiza a interface e salva no estado
178
- btn_run.click(
179
- fn=run_agent_only,
180
- outputs=[status_output, results_table, stored_answers]
181
- )
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
- # Evento do Botão 2: Lê o estado e envia para a API
184
- btn_submit.click(
185
- fn=submit_to_leaderboard,
186
- inputs=[stored_answers],
187
- outputs=[status_output]
188
- )
189
 
190
  if __name__ == "__main__":
191
- print("\n" + "-"*30 + " App Starting " + "-"*30)
192
- demo.launch(debug=True, share=False)
 
1
  import os
2
+
3
  import gradio as gr
 
4
  import pandas as pd
5
+ import requests
6
  from smolagents import CodeAgent, DuckDuckGoSearchTool, LiteLLMModel
7
 
8
+
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
+ RESULT_COLUMNS = ["Task ID", "Question", "Submitted Answer"]
11
+
12
 
 
13
  class BasicAgent:
14
  def __init__(self):
15
+ print("Inicializando o agente GAIA...")
16
+
17
+ self.model = LiteLLMModel(
18
+ model_id="huggingface/Qwen/Qwen2.5-Coder-32B-Instruct"
19
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  self.agent = CodeAgent(
21
+ tools=[DuckDuckGoSearchTool()],
22
  model=self.model,
23
  max_steps=6,
24
+ description=(
25
+ "Agent designed to solve GAIA benchmark questions with "
26
+ "exact-match answers."
27
+ ),
28
+ )
29
+
30
+ exact_match_prompt = """
31
+ You are an expert AI assistant solving tasks from the GAIA benchmark.
32
+ Return only the requested final answer.
33
+ Do not add explanations, conversational text, markdown, or the words
34
+ "FINAL ANSWER".
35
+ If a comma-separated list is requested, return only that list.
36
+ If a number is requested, return only that number.
37
+ """
38
+ self.agent.system_prompt = (
39
+ exact_match_prompt.strip() + "\n\n" + self.agent.system_prompt
40
  )
 
 
 
41
 
42
  def __call__(self, question: str) -> str:
43
+ question = (question or "").strip()
44
+ if not question:
45
+ raise ValueError("Digite uma pergunta para testar o agente.")
46
+
47
+ result = self.agent.run(question)
48
+ return str(result).replace("FINAL ANSWER", "").strip()
49
+
50
+
51
+ def empty_results() -> pd.DataFrame:
52
+ return pd.DataFrame(columns=RESULT_COLUMNS)
53
+
54
+
55
+ def test_agent(question: str):
56
+ """Executa uma única pergunta sem buscar nem enviar a avaliação oficial."""
57
+ try:
58
+ answer = BasicAgent()(question)
59
+ return "Teste concluído. Nenhum resultado foi enviado.", answer
60
+ except Exception as exc:
61
+ return f"Erro no teste: {exc}", ""
62
+
63
 
64
  def run_agent_only(profile: gr.OAuthProfile | None):
65
+ """Busca as perguntas e gera uma tabela editável, sem enviar respostas."""
 
 
 
66
  if not profile:
67
+ return "Faça login no Hugging Face primeiro.", empty_results()
 
 
 
68
 
69
  try:
70
  agent = BasicAgent()
71
+ except Exception as exc:
72
+ return f"Erro ao inicializar o agente: {exc}", empty_results()
73
 
74
+ questions_url = f"{DEFAULT_API_URL}/questions"
75
  try:
76
+ response = requests.get(questions_url, timeout=30)
77
  response.raise_for_status()
78
+ questions = response.json()
79
+ if not questions:
80
+ return "A API retornou uma lista de perguntas vazia.", empty_results()
81
+ except Exception as exc:
82
+ return f"Erro ao buscar perguntas: {exc}", empty_results()
83
+
84
+ results = []
85
+ for item in questions:
 
 
 
86
  task_id = item.get("task_id")
87
+ question = item.get("question")
88
+ if not task_id or question is None:
 
89
  continue
90
+
91
  try:
92
+ answer = agent(question)
93
+ except Exception as exc:
94
+ answer = f"ERROR: {exc}"
95
+
96
+ results.append(
97
+ {
98
+ "Task ID": task_id,
99
+ "Question": question,
100
+ "Submitted Answer": answer,
101
+ }
102
+ )
103
+
104
+ if not results:
105
+ return "O agente não produziu respostas.", empty_results()
106
+
107
+ status = (
108
+ f"Execução concluída: {len(results)} respostas geradas. "
109
+ "Revise e, se necessário, edite a coluna 'Submitted Answer'. "
110
+ "Nada foi enviado ainda."
111
+ )
112
+ return status, pd.DataFrame(results, columns=RESULT_COLUMNS)
113
+
114
+
115
+ def normalize_results(results_table) -> list[dict]:
116
+ """Converte a tabela revisada no payload exigido pela API."""
117
+ if results_table is None:
118
+ return []
119
+
120
+ if isinstance(results_table, pd.DataFrame):
121
+ dataframe = results_table.copy()
122
+ else:
123
+ dataframe = pd.DataFrame(results_table, columns=RESULT_COLUMNS)
124
+
125
+ if dataframe.empty:
126
+ return []
127
+
128
+ missing = set(RESULT_COLUMNS) - set(dataframe.columns)
129
+ if missing:
130
+ raise ValueError(
131
+ "A tabela de revisão não contém as colunas esperadas: "
132
+ + ", ".join(sorted(missing))
133
+ )
134
+
135
+ answers = []
136
+ for _, row in dataframe.iterrows():
137
+ task_id = str(row["Task ID"]).strip()
138
+ answer = str(row["Submitted Answer"]).strip()
139
+ if not task_id or task_id.lower() == "nan":
140
+ continue
141
+ if not answer or answer.lower() == "nan":
142
+ raise ValueError(f"A tarefa {task_id} está sem resposta.")
143
+ if answer.startswith("ERROR:"):
144
+ raise ValueError(
145
+ f"A tarefa {task_id} ainda contém um erro. "
146
+ "Corrija a resposta antes de enviar."
147
+ )
148
+ answers.append({"task_id": task_id, "submitted_answer": answer})
149
+
150
+ return answers
151
+
152
+
153
+ def submit_to_leaderboard(
154
+ profile: gr.OAuthProfile | None, results_table
155
+ ):
156
+ """Envia exatamente os valores atualmente visíveis na tabela revisada."""
157
  if not profile:
158
+ return "Faça login no Hugging Face primeiro."
159
+
160
+ try:
161
+ answers = normalize_results(results_table)
162
+ except Exception as exc:
163
+ return f"Envio bloqueado: {exc}"
164
+
165
+ if not answers:
166
+ return "Não há respostas para enviar. Execute a avaliação primeiro."
167
+
168
+ space_id = os.getenv("SPACE_ID")
169
+ if not space_id:
170
+ return (
171
+ "Envio bloqueado: a variável SPACE_ID não foi encontrada. "
172
+ "Publique/execute este app em um Hugging Face Space."
173
+ )
174
+
175
+ submission = {
176
+ "username": profile.username.strip(),
177
+ "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
178
+ "answers": answers,
179
  }
180
+
 
181
  try:
182
+ response = requests.post(
183
+ f"{DEFAULT_API_URL}/submit",
184
+ json=submission,
185
+ timeout=90,
186
+ )
187
  response.raise_for_status()
188
+ result = response.json()
189
+ return (
190
+ "Envio realizado com sucesso!\n"
191
+ f"Usuário: {result.get('username')}\n"
192
+ f"Pontuação: {result.get('score', 'N/A')}% "
193
+ f"({result.get('correct_count', '?')}/"
194
+ f"{result.get('total_attempted', '?')} corretas)\n"
195
+ f"Mensagem: {result.get('message', 'Sem mensagem.')}"
196
  )
197
+ except requests.exceptions.RequestException as exc:
198
+ detail = ""
199
+ if exc.response is not None:
200
+ detail = f" Resposta da API: {exc.response.text[:500]}"
201
+ return f"Falha no envio: {exc}.{detail}"
202
+ except Exception as exc:
203
+ return f"Erro inesperado no envio: {exc}"
204
 
205
 
206
+ with gr.Blocks(theme=gr.themes.Soft(), title="GAIA Agent Evaluation") as demo:
207
+ gr.Markdown("# GAIA Agent Evaluation")
 
208
  gr.Markdown(
209
+ "Teste o agente isoladamente, gere as respostas oficiais para revisão "
210
+ "e então faça o envio final."
 
 
 
 
211
  )
 
212
  gr.LoginButton()
213
 
214
+ with gr.Tabs():
215
+ with gr.Tab("1. Testar agente"):
216
+ gr.Markdown(
217
+ "Use uma pergunta livre para verificar o modelo e a busca. "
218
+ "Este teste não acessa nem envia a avaliação oficial."
219
+ )
220
+ test_question = gr.Textbox(
221
+ label="Pergunta de teste",
222
+ lines=5,
223
+ placeholder="Digite uma pergunta para o agente...",
224
+ )
225
+ test_button = gr.Button("Executar teste", variant="secondary")
226
+ test_status = gr.Textbox(label="Status", interactive=False)
227
+ test_answer = gr.Textbox(
228
+ label="Resposta do agente", lines=5, interactive=False
229
+ )
230
+ test_button.click(
231
+ fn=test_agent,
232
+ inputs=[test_question],
233
+ outputs=[test_status, test_answer],
234
+ )
235
 
236
+ with gr.Tab("2. Executar e revisar"):
237
+ gr.Markdown(
238
+ "Gere as respostas das 20 questões. Você pode editar a coluna "
239
+ "'Submitted Answer' antes do envio."
240
+ )
241
+ run_button = gr.Button(
242
+ "Executar avaliação sem enviar", variant="secondary"
243
+ )
244
+ run_status = gr.Textbox(label="Status", lines=4, interactive=False)
245
+ results_table = gr.DataFrame(
246
+ headers=RESULT_COLUMNS,
247
+ datatype=["str", "str", "str"],
248
+ value=empty_results(),
249
+ label="Respostas para revisão",
250
+ wrap=True,
251
+ interactive=True,
252
+ )
253
+ run_button.click(
254
+ fn=run_agent_only,
255
+ outputs=[run_status, results_table],
256
+ )
257
 
258
+ with gr.Tab("3. Enviar resultado"):
259
+ gr.Markdown(
260
+ "O botão abaixo envia os valores atuais da tabela da aba "
261
+ "anterior. Confira todas as respostas antes de continuar."
262
+ )
263
+ submit_button = gr.Button(
264
+ "Enviar respostas revisadas ao leaderboard",
265
+ variant="primary",
266
+ )
267
+ submit_status = gr.Textbox(
268
+ label="Resultado do envio", lines=6, interactive=False
269
+ )
270
+ submit_button.click(
271
+ fn=submit_to_leaderboard,
272
+ inputs=[results_table],
273
+ outputs=[submit_status],
274
+ )
275
 
 
 
 
 
 
 
276
 
277
  if __name__ == "__main__":
278
+ demo.launch(debug=True, share=False)