rnrahate007 commited on
Commit
81462b4
·
verified ·
1 Parent(s): adc5248

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -107
app.py CHANGED
@@ -1,81 +1,55 @@
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
 
@@ -84,27 +58,25 @@ Return ONLY the final answer — nothing else."""
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)
@@ -112,52 +84,44 @@ Return ONLY the final answer — nothing else."""
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
 
@@ -167,20 +131,16 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
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)
@@ -192,36 +152,28 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
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,
@@ -240,8 +192,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
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
 
@@ -249,23 +200,18 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
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
 
 
1
  import os
2
  import re
 
3
  import traceback
4
+ import requests
5
  import gradio as gr
6
  import pandas as pd
7
+ from google import genai
8
+ from google.genai import types
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
13
 
 
 
 
14
  def clean_answer(text: str) -> str:
 
 
 
15
  text = text.strip()
 
 
16
  text = re.sub(r"^```[a-zA-Z]*\n?", "", text)
17
  text = re.sub(r"```$", "", text)
 
 
18
  for prefix in [
19
  "Final Answer:", "Answer:", "FINAL ANSWER:", "ANSWER:",
20
  "The answer is:", "The final answer is:",
21
  ]:
22
  if text.lower().startswith(prefix.lower()):
23
  text = text[len(prefix):].strip()
 
 
24
  text = " ".join(text.split())
 
 
25
  return text[:200]
26
 
27
 
 
 
 
28
  class BasicAgent:
29
+ SYSTEM = """You are an expert AI assistant solving GAIA benchmark questions.
 
 
 
 
 
 
 
 
 
30
 
31
  RULES:
32
  1. Think step-by-step before answering.
33
+ 2. Use Google Search for any factual, date, or real-world questions.
34
+ 3. Use code execution for arithmetic, unit conversions, or data processing.
35
+ 4. Return ONLY the final answer — no explanation, no preamble.
36
+ 5. Numbers: no commas, correct decimals (42 not 42.0 if whole).
37
+ 6. Lists: comma-separated, alphabetical unless told otherwise.
38
+ 7. Yes/no questions: answer exactly "yes" or "no" (lowercase).
39
+ 8. Be as concise as possible while being complete and correct.
40
  """
41
 
42
+ VERIFY = """Question: {question}
43
 
44
  Proposed answer: {answer}
45
 
46
+ Review:
47
  - Is it factually correct?
48
+ - Is it in the exact requested format?
49
  - Is it as concise as possible?
50
 
51
  If correct, repeat it unchanged.
52
+ If wrong or mis-formatted, return ONLY the corrected answer.
53
 
54
  Return ONLY the final answer — nothing else."""
55
 
 
58
  if not api_key:
59
  raise EnvironmentError("GEMINI_API_KEY environment variable is not set.")
60
 
61
+ self.client = genai.Client(api_key=api_key)
62
+ self.model_id = "gemini-2.5-flash-preview-04-17"
63
 
64
+ self.tools = [
65
+ types.Tool(google_search=types.GoogleSearch()),
66
+ types.Tool(code_execution=types.ToolCodeExecution()),
67
+ ]
68
 
69
+ self.main_config = types.GenerateContentConfig(
70
+ system_instruction=self.SYSTEM,
71
+ tools=self.tools,
 
 
72
  )
73
 
74
+ self.verify_config = types.GenerateContentConfig(
 
 
75
  system_instruction="You are a precise answer validator. Return ONLY the final answer.",
76
  )
77
 
78
+ print("✅ BasicAgent (gemini-2.5-flash + Search + Code) initialised.")
79
 
 
80
  def __call__(self, question: str) -> str:
81
  try:
82
  return self._run(question)
 
84
  print(f"[AGENT ERROR] {exc}\n{traceback.format_exc()}")
85
  return "N/A"
86
 
 
87
  def _run(self, question: str) -> str:
88
+ resp1 = self.client.models.generate_content(
89
+ model=self.model_id,
90
+ contents=question,
91
+ config=self.main_config,
92
+ )
93
+ ans1 = clean_answer(self._extract_text(resp1))
94
+ print(f" [Pass 1] {ans1!r}")
95
 
96
  if not ans1 or ans1 == "N/A":
97
  return "N/A"
98
 
99
+ verify_prompt = self.VERIFY.format(question=question, answer=ans1)
100
+ resp2 = self.client.models.generate_content(
101
+ model=self.model_id,
102
+ contents=verify_prompt,
103
+ config=self.verify_config,
104
+ )
105
+ ans2 = clean_answer(self._extract_text(resp2))
 
106
  print(f" [Pass 2] {ans2!r}")
107
 
108
  return ans2 if ans2 else ans1
109
 
 
110
  @staticmethod
111
  def _extract_text(response) -> str:
 
 
 
 
112
  try:
113
+ if response.text:
114
+ return response.text.strip()
115
  except Exception:
116
  pass
 
 
117
  texts = []
118
  for candidate in response.candidates:
119
  for part in candidate.content.parts:
120
  if hasattr(part, "text") and part.text:
121
  texts.append(part.text.strip())
122
+ return " ".join(texts).strip() or "N/A"
123
 
124
 
 
 
 
125
  def run_and_submit_all(profile: gr.OAuthProfile | None):
126
  space_id = os.getenv("SPACE_ID")
127
 
 
131
  username = profile.username
132
  print(f"Logged in as: {username}")
133
 
134
+ questions_url = f"{DEFAULT_API_URL}/questions"
135
+ submit_url = f"{DEFAULT_API_URL}/submit"
 
136
 
 
137
  try:
138
  agent = BasicAgent()
139
  except Exception as e:
140
  return f"Error initialising agent: {e}", None
141
 
142
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main" if space_id else "unknown"
 
143
 
 
144
  print(f"Fetching questions from {questions_url} …")
145
  try:
146
  resp = requests.get(questions_url, timeout=15)
 
152
  except Exception as e:
153
  return f"Error fetching questions: {e}", None
154
 
155
+ results_log = []
156
+ answers_payload = []
 
157
 
158
  for item in questions_data:
159
  task_id = item.get("task_id")
160
  question_text = item.get("question")
161
  if not task_id or question_text is None:
 
162
  continue
163
 
164
+ print(f"\n[{task_id}] {question_text[:120]}")
165
  try:
166
  answer = agent(question_text)
167
  except Exception as e:
168
  answer = f"AGENT ERROR: {e}"
 
169
 
170
  print(f" → {answer!r}")
171
  answers_payload.append({"task_id": task_id, "submitted_answer": answer})
172
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": answer})
 
 
 
 
173
 
174
  if not answers_payload:
175
  return "Agent produced no answers.", pd.DataFrame(results_log)
176
 
 
177
  submission_data = {
178
  "username": username.strip(),
179
  "agent_code": agent_code,
 
192
  f"Msg: {result.get('message', '')}"
193
  )
194
  except requests.exceptions.HTTPError as e:
195
+ final_status = f"❌ HTTP {e.response.status_code}: {e.response.text[:500]}"
 
196
  except Exception as e:
197
  final_status = f"❌ Submission error: {e}"
198
 
 
200
  return final_status, pd.DataFrame(results_log)
201
 
202
 
 
 
 
203
  with gr.Blocks() as demo:
204
  gr.Markdown("# GAIA Agent Evaluation Runner")
205
  gr.Markdown(
206
  """
207
  **How to use:**
208
+ 1. Set `GEMINI_API_KEY` in your Space secrets.
209
  2. Log in with the button below.
210
+ 3. Click **Run Evaluation** — the agent answers all questions and submits automatically.
 
 
211
  """
212
  )
213
  gr.LoginButton()
214
+ run_btn = gr.Button("▶ Run Evaluation & Submit All Answers", variant="primary")
215
  status_out = gr.Textbox(label="Status / Result", lines=6, interactive=False)
216
  results_tbl = gr.DataFrame(label="Questions & Agent Answers", wrap=True)
217