rnrahate007 commited on
Commit
c7c3fca
Β·
verified Β·
1 Parent(s): a32cdf1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -178
app.py CHANGED
@@ -1,244 +1,281 @@
1
  import os
2
- import gradio as gr
3
  import requests
4
- import inspect
 
5
  import pandas as pd
 
 
6
 
7
- # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
11
- import os
12
- import google.generativeai as genai
13
 
14
- class BasicAgent:
15
- def __init__(self):
16
- print("Gemini Agent initialized")
 
 
 
 
 
17
 
18
- genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
 
 
19
 
20
- # βœ… latest + stable + cheap
21
- self.model = genai.GenerativeModel("gemini-2.5-flash")
 
 
 
 
 
22
 
23
- def __call__(self, question: str) -> str:
24
- try:
25
- base_prompt = f"""
26
- You are solving benchmark reasoning questions.
27
 
28
- RULES:
29
- - Think carefully step by step
30
- - Extract exact final answer
31
- - DO NOT explain
32
- - RETURN ONLY final answer
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- Question:
35
- {question}
 
 
 
 
 
 
 
36
  """
37
 
38
- # πŸ”₯ Step 1: First attempt
39
- response1 = self.model.generate_content(base_prompt)
40
- ans1 = response1.text.strip()
41
 
42
- # πŸ”₯ Step 2: Self-check (THIS IS THE MAGIC)
43
- verify_prompt = f"""
44
- Question: {question}
45
 
46
- Proposed Answer: {ans1}
 
 
 
47
 
48
- Is this answer correct?
49
- If wrong, give corrected FINAL answer only.
50
- If correct, repeat the answer.
51
 
52
- Return ONLY answer.
53
- """
54
 
55
- response2 = self.model.generate_content(verify_prompt)
56
- final = response2.text.strip()
 
 
57
 
58
- # πŸ”₯ Clean output (VERY IMPORTANT for exact match)
59
- final = final.replace("Final Answer:", "").strip()
60
- final = final.split("\n")[0].strip()
61
- final = final.split(".")[0].strip()
62
 
63
- return final[:100]
 
64
 
65
- except Exception as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  return "N/A"
67
 
68
-
 
 
 
 
69
 
70
- def run_and_submit_all( profile: gr.OAuthProfile | None):
71
- """
72
- Fetches all questions, runs the BasicAgent on them, submits all answers,
73
- and displays the results.
74
- """
75
- # --- Determine HF Space Runtime URL and Repo URL ---
76
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- if profile:
79
- username= f"{profile.username}"
80
- print(f"User logged in: {username}")
81
- else:
82
- print("User not logged in.")
83
- return "Please Login to Hugging Face with the button.", None
84
 
85
  api_url = DEFAULT_API_URL
86
  questions_url = f"{api_url}/questions"
87
- submit_url = f"{api_url}/submit"
88
 
89
- # 1. Instantiate Agent ( modify this part to create your agent)
90
  try:
91
  agent = BasicAgent()
92
  except Exception as e:
93
- print(f"Error instantiating agent: {e}")
94
- return f"Error initializing agent: {e}", None
95
- # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
96
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
97
- print(agent_code)
98
-
99
- # 2. Fetch Questions
100
- print(f"Fetching questions from: {questions_url}")
101
  try:
102
- response = requests.get(questions_url, timeout=15)
103
- response.raise_for_status()
104
- questions_data = response.json()
105
  if not questions_data:
106
- print("Fetched questions list is empty.")
107
- return "Fetched questions list is empty or invalid format.", None
108
  print(f"Fetched {len(questions_data)} questions.")
109
- except requests.exceptions.RequestException as e:
110
- print(f"Error fetching questions: {e}")
111
- return f"Error fetching questions: {e}", None
112
- except requests.exceptions.JSONDecodeError as e:
113
- print(f"Error decoding JSON response from questions endpoint: {e}")
114
- print(f"Response text: {response.text[:500]}")
115
- return f"Error decoding server response for questions: {e}", None
116
  except Exception as e:
117
- print(f"An unexpected error occurred fetching questions: {e}")
118
- return f"An unexpected error occurred fetching questions: {e}", None
 
 
 
119
 
120
- # 3. Run your Agent
121
- results_log = []
122
- answers_payload = []
123
- print(f"Running agent on {len(questions_data)} questions...")
124
  for item in questions_data:
125
- task_id = item.get("task_id")
126
  question_text = item.get("question")
127
  if not task_id or question_text is None:
128
- print(f"Skipping item with missing task_id or question: {item}")
129
  continue
 
 
130
  try:
131
- submitted_answer = agent(question_text)
132
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
133
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
134
  except Exception as e:
135
- print(f"Error running agent on task {task_id}: {e}")
136
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
137
-
138
- if not answers_payload:
139
- print("Agent did not produce any answers to submit.")
140
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
141
 
142
- # 4. Prepare Submission
143
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
144
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
145
- print(status_update)
 
 
 
146
 
147
- # 5. Submit
148
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
 
 
 
 
 
 
 
 
149
  try:
150
- response = requests.post(submit_url, json=submission_data, timeout=60)
151
- response.raise_for_status()
152
- result_data = response.json()
153
  final_status = (
154
- f"Submission Successful!\n"
155
- f"User: {result_data.get('username')}\n"
156
- f"Overall Score: {result_data.get('score', 'N/A')}% "
157
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
158
- f"Message: {result_data.get('message', 'No message received.')}"
159
  )
160
- print("Submission successful.")
161
- results_df = pd.DataFrame(results_log)
162
- return final_status, results_df
163
  except requests.exceptions.HTTPError as e:
164
- error_detail = f"Server responded with status {e.response.status_code}."
165
- try:
166
- error_json = e.response.json()
167
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
168
- except requests.exceptions.JSONDecodeError:
169
- error_detail += f" Response: {e.response.text[:500]}"
170
- status_message = f"Submission Failed: {error_detail}"
171
- print(status_message)
172
- results_df = pd.DataFrame(results_log)
173
- return status_message, results_df
174
- except requests.exceptions.Timeout:
175
- status_message = "Submission Failed: The request timed out."
176
- print(status_message)
177
- results_df = pd.DataFrame(results_log)
178
- return status_message, results_df
179
- except requests.exceptions.RequestException as e:
180
- status_message = f"Submission Failed: Network error - {e}"
181
- print(status_message)
182
- results_df = pd.DataFrame(results_log)
183
- return status_message, results_df
184
  except Exception as e:
185
- status_message = f"An unexpected error occurred during submission: {e}"
186
- print(status_message)
187
- results_df = pd.DataFrame(results_log)
188
- return status_message, results_df
189
 
 
 
190
 
191
- # --- Build Gradio Interface using Blocks ---
 
 
 
192
  with gr.Blocks() as demo:
193
- gr.Markdown("# Basic Agent Evaluation Runner")
194
  gr.Markdown(
195
  """
196
- **Instructions:**
197
-
198
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
199
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
200
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
201
 
202
- ---
203
- **Disclaimers:**
204
- Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
205
- This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
206
  """
207
  )
208
-
209
  gr.LoginButton()
 
 
 
210
 
211
- run_button = gr.Button("Run Evaluation & Submit All Answers")
212
-
213
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
214
- # Removed max_rows=10 from DataFrame constructor
215
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
216
 
217
- run_button.click(
218
- fn=run_and_submit_all,
219
- outputs=[status_output, results_table]
220
- )
221
 
222
  if __name__ == "__main__":
223
- print("\n" + "-"*30 + " App Starting " + "-"*30)
224
- # Check for SPACE_HOST and SPACE_ID at startup for information
225
- space_host_startup = os.getenv("SPACE_HOST")
226
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
227
-
228
- if space_host_startup:
229
- print(f"βœ… SPACE_HOST found: {space_host_startup}")
230
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
231
- else:
232
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
233
-
234
- if space_id_startup: # Print repo URLs if SPACE_ID is found
235
- print(f"βœ… SPACE_ID found: {space_id_startup}")
236
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
237
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
238
- else:
239
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
240
-
241
- print("-"*(60 + len(" App Starting ")) + "\n")
242
-
243
- print("Launching Gradio Interface for Basic Agent Evaluation...")
244
  demo.launch(debug=True, share=False)
 
1
  import os
2
+ import re
3
  import requests
4
+ import traceback
5
+ import gradio as gr
6
  import pandas as pd
7
+ import google.generativeai as genai
8
+ from google.generativeai.types import Tool, GoogleSearch
9
 
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
 
 
13
 
14
+ # ─────────────────────────────────────────────
15
+ # Helper: strip markdown / fences from output
16
+ # ─────────────────────────────────────────────
17
+ def clean_answer(text: str) -> str:
18
+ """
19
+ Normalise the model's raw output into a clean, exact-match-ready string.
20
+ """
21
+ text = text.strip()
22
 
23
+ # Remove code fences if the whole reply is wrapped in one
24
+ text = re.sub(r"^```[a-zA-Z]*\n?", "", text)
25
+ text = re.sub(r"```$", "", text)
26
 
27
+ # Remove common label prefixes the model likes to add
28
+ for prefix in [
29
+ "Final Answer:", "Answer:", "FINAL ANSWER:", "ANSWER:",
30
+ "The answer is:", "The final answer is:",
31
+ ]:
32
+ if text.lower().startswith(prefix.lower()):
33
+ text = text[len(prefix):].strip()
34
 
35
+ # Collapse internal newlines to spaces, then trim
36
+ text = " ".join(text.split())
 
 
37
 
38
+ # Do NOT split on "." β€” it breaks decimals, abbreviations, etc.
39
+ return text[:200]
40
+
41
+
42
+ # ─────────────────────────────────────────────
43
+ # Agent
44
+ # ─────────────────────────────────────────────
45
+ class BasicAgent:
46
+ """
47
+ A Gemini-powered agent that uses:
48
+ β€’ Google Search grounding β€” for real-time factual questions
49
+ β€’ Code execution β€” for maths / data problems
50
+ β€’ Self-verification pass β€” to catch obvious hallucinations
51
+
52
+ Falls back gracefully when tools aren't available.
53
+ """
54
+
55
+ SYSTEM_PROMPT = """You are an expert AI assistant solving GAIA benchmark questions.
56
 
57
+ RULES:
58
+ 1. Think step-by-step before answering.
59
+ 2. Use Google Search when you need current facts, dates, or specific data.
60
+ 3. Use code execution for any arithmetic, unit conversions, or data processing.
61
+ 4. Return ONLY the final answer β€” no explanation, no preamble, no punctuation tail.
62
+ 5. For numbers: no commas, correct decimal places (e.g. 42 not 42.0 if whole number).
63
+ 6. For lists: comma-separated values in alphabetical order unless asked otherwise.
64
+ 7. For yes/no questions: answer exactly "yes" or "no" (lowercase).
65
+ 8. Keep the answer as short as possible while being complete and correct.
66
  """
67
 
68
+ VERIFY_PROMPT = """Question: {question}
 
 
69
 
70
+ Proposed answer: {answer}
 
 
71
 
72
+ Review the answer carefully:
73
+ - Is it factually correct?
74
+ - Is it in the exact format requested?
75
+ - Is it as concise as possible?
76
 
77
+ If correct, repeat it unchanged.
78
+ If wrong or badly formatted, return ONLY the corrected answer.
 
79
 
80
+ Return ONLY the final answer β€” nothing else."""
 
81
 
82
+ def __init__(self):
83
+ api_key = os.getenv("GEMINI_API_KEY")
84
+ if not api_key:
85
+ raise EnvironmentError("GEMINI_API_KEY environment variable is not set.")
86
 
87
+ genai.configure(api_key=api_key)
 
 
 
88
 
89
+ # Tools available in gemini-2.5-flash
90
+ self._search_tool = Tool(google_search=GoogleSearch())
91
 
92
+ # Primary model β€” with search grounding + code execution
93
+ self.model = genai.GenerativeModel(
94
+ model_name="gemini-2.5-flash",
95
+ system_instruction=self.SYSTEM_PROMPT,
96
+ tools=[self._search_tool, "code_execution"],
97
+ )
98
+
99
+ # Verification model β€” plain text, no tools (avoids circular loops)
100
+ self.verify_model = genai.GenerativeModel(
101
+ model_name="gemini-2.5-flash",
102
+ system_instruction="You are a precise answer validator. Return ONLY the final answer.",
103
+ )
104
+
105
+ print("βœ… BasicAgent (Gemini 2.5 Flash + Search + Code) initialised.")
106
+
107
+ # ------------------------------------------------------------------
108
+ def __call__(self, question: str) -> str:
109
+ try:
110
+ return self._run(question)
111
+ except Exception as exc:
112
+ print(f"[AGENT ERROR] {exc}\n{traceback.format_exc()}")
113
  return "N/A"
114
 
115
+ # ------------------------------------------------------------------
116
+ def _run(self, question: str) -> str:
117
+ # ── Pass 1: full reasoning with tools ──────────────────────────
118
+ response1 = self.model.generate_content(question)
119
+ ans1 = self._extract_text(response1)
120
 
121
+ if not ans1 or ans1 == "N/A":
122
+ return "N/A"
123
+
124
+ ans1 = clean_answer(ans1)
125
+ print(f" [Pass 1] {ans1!r}")
126
+
127
+ # ── Pass 2: self-verification (plain model, no tools) ──────────
128
+ verify_prompt = self.VERIFY_PROMPT.format(question=question, answer=ans1)
129
+ response2 = self.verify_model.generate_content(verify_prompt)
130
+ ans2 = clean_answer(self._extract_text(response2))
131
+
132
+ print(f" [Pass 2] {ans2!r}")
133
+
134
+ return ans2 if ans2 else ans1
135
+
136
+ # ------------------------------------------------------------------
137
+ @staticmethod
138
+ def _extract_text(response) -> str:
139
+ """
140
+ Pull plain text out of a GenerateContentResponse regardless of
141
+ whether it contains tool-use / code-execution blocks.
142
+ """
143
+ try:
144
+ # Fast path β€” the .text property works when there's no ambiguity
145
+ return response.text.strip()
146
+ except Exception:
147
+ pass
148
+
149
+ # Slow path β€” iterate parts
150
+ texts = []
151
+ for candidate in response.candidates:
152
+ for part in candidate.content.parts:
153
+ if hasattr(part, "text") and part.text:
154
+ texts.append(part.text.strip())
155
+ return " ".join(texts).strip() if texts else "N/A"
156
+
157
+
158
+ # ─────────────────────────────────────────────
159
+ # Gradio runner
160
+ # ─────────────────────────────────────────────
161
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
162
+ space_id = os.getenv("SPACE_ID")
163
+
164
+ if not profile:
165
+ return "Please log in to Hugging Face first.", None
166
 
167
+ username = profile.username
168
+ print(f"Logged in as: {username}")
 
 
 
 
169
 
170
  api_url = DEFAULT_API_URL
171
  questions_url = f"{api_url}/questions"
172
+ submit_url = f"{api_url}/submit"
173
 
174
+ # ── Instantiate agent ───────────────────────────────────────────────
175
  try:
176
  agent = BasicAgent()
177
  except Exception as e:
178
+ return f"Error initialising agent: {e}", None
179
+
180
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "unknown"
181
+ print(f"Agent code URL: {agent_code}")
182
+
183
+ # ── Fetch questions ─────────────────────────────────────────────────
184
+ print(f"Fetching questions from {questions_url} …")
 
185
  try:
186
+ resp = requests.get(questions_url, timeout=15)
187
+ resp.raise_for_status()
188
+ questions_data = resp.json()
189
  if not questions_data:
190
+ return "Question list is empty.", None
 
191
  print(f"Fetched {len(questions_data)} questions.")
 
 
 
 
 
 
 
192
  except Exception as e:
193
+ return f"Error fetching questions: {e}", None
194
+
195
+ # ── Run agent ───────────────────────────────────────────────────────
196
+ results_log = []
197
+ answers_payload = []
198
 
 
 
 
 
199
  for item in questions_data:
200
+ task_id = item.get("task_id")
201
  question_text = item.get("question")
202
  if not task_id or question_text is None:
203
+ print(f"Skipping malformed item: {item}")
204
  continue
205
+
206
+ print(f"\n[{task_id}] Q: {question_text[:120]}")
207
  try:
208
+ answer = agent(question_text)
 
 
209
  except Exception as e:
210
+ answer = f"AGENT ERROR: {e}"
211
+ print(f" ERROR: {e}")
 
 
 
 
212
 
213
+ print(f" β†’ {answer!r}")
214
+ answers_payload.append({"task_id": task_id, "submitted_answer": answer})
215
+ results_log.append({
216
+ "Task ID": task_id,
217
+ "Question": question_text,
218
+ "Submitted Answer": answer,
219
+ })
220
 
221
+ if not answers_payload:
222
+ return "Agent produced no answers.", pd.DataFrame(results_log)
223
+
224
+ # ── Submit ──────────────────────────────────────────────────────────
225
+ submission_data = {
226
+ "username": username.strip(),
227
+ "agent_code": agent_code,
228
+ "answers": answers_payload,
229
+ }
230
+ print(f"\nSubmitting {len(answers_payload)} answers …")
231
  try:
232
+ resp = requests.post(submit_url, json=submission_data, timeout=120)
233
+ resp.raise_for_status()
234
+ result = resp.json()
235
  final_status = (
236
+ f"βœ… Submission successful!\n"
237
+ f"User: {result.get('username')}\n"
238
+ f"Score: {result.get('score', 'N/A')}% "
239
+ f"({result.get('correct_count', '?')}/{result.get('total_attempted', '?')} correct)\n"
240
+ f"Msg: {result.get('message', '')}"
241
  )
 
 
 
242
  except requests.exceptions.HTTPError as e:
243
+ detail = e.response.text[:500]
244
+ final_status = f"❌ Submission failed (HTTP {e.response.status_code}): {detail}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  except Exception as e:
246
+ final_status = f"❌ Submission error: {e}"
 
 
 
247
 
248
+ print(final_status)
249
+ return final_status, pd.DataFrame(results_log)
250
 
251
+
252
+ # ─────────────────────────────────────────────
253
+ # Gradio UI
254
+ # ─────────────────────────────────────────────
255
  with gr.Blocks() as demo:
256
+ gr.Markdown("# GAIA Agent Evaluation Runner")
257
  gr.Markdown(
258
  """
259
+ **How to use:**
260
+ 1. Clone this Space and set your `GEMINI_API_KEY` secret.
261
+ 2. Log in with the button below.
262
+ 3. Click **Run Evaluation** β€” the agent will answer all questions and submit automatically.
 
263
 
264
+ *Note: runs can take several minutes while the agent processes all questions.*
 
 
 
265
  """
266
  )
 
267
  gr.LoginButton()
268
+ run_btn = gr.Button("β–Ά Run Evaluation & Submit All Answers", variant="primary")
269
+ status_out = gr.Textbox(label="Status / Result", lines=6, interactive=False)
270
+ results_tbl = gr.DataFrame(label="Questions & Agent Answers", wrap=True)
271
 
272
+ run_btn.click(fn=run_and_submit_all, outputs=[status_out, results_tbl])
 
 
 
 
273
 
 
 
 
 
274
 
275
  if __name__ == "__main__":
276
+ print("\n" + "─" * 50)
277
+ for var in ("SPACE_HOST", "SPACE_ID", "GEMINI_API_KEY"):
278
+ val = os.getenv(var)
279
+ print(f"{'βœ…' if val else '⚠️ '} {var}: {'set' if val else 'NOT SET'}")
280
+ print("─" * 50 + "\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  demo.launch(debug=True, share=False)