rnrahate007 commited on
Commit
7e85098
·
verified ·
1 Parent(s): 3bbd916

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -341
app.py CHANGED
@@ -1,357 +1,113 @@
1
  import os
2
  import re
3
- import base64
4
- import traceback
5
- import requests
6
- import gradio as gr
7
- import pandas as pd
8
- from google import genai
9
- from google.genai import types
10
-
11
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
-
13
- # ── Helpers ───────────────────────────────────────────────────────────
14
-
15
- def clean_answer(text: str) -> str:
16
- if not text:
17
- return "N/A"
18
- text = text.strip()
19
- text = re.sub(r"^```[a-zA-Z]*\s*", "", text)
20
- text = re.sub(r"\s*```$", "", text)
21
- for prefix in [
22
- "final answer:", "answer:", "the answer is:",
23
- "the final answer is:", "result:",
24
- ]:
25
- if text.lower().startswith(prefix):
26
- text = text[len(prefix):].strip()
27
- break
28
- return " ".join(text.split())[:300]
29
-
30
-
31
- def extract_text(response) -> str:
32
- """
33
- Collect ALL text and code-execution output parts, return the last one.
34
- Handles: plain text, google_search grounding, code_execution output.
35
- """
36
- parts_text = []
37
- try:
38
- for candidate in response.candidates:
39
- for part in candidate.content.parts:
40
- # Plain text part
41
- if hasattr(part, "text") and part.text and part.text.strip():
42
- parts_text.append(part.text.strip())
43
- # Code execution result
44
- if hasattr(part, "code_execution_result") and part.code_execution_result:
45
- out = getattr(part.code_execution_result, "output", "")
46
- if out and str(out).strip():
47
- parts_text.append(str(out).strip())
48
- except Exception as e:
49
- print(f" [extract_text error] {e}")
50
- if parts_text:
51
- return parts_text[-1]
52
- try:
53
- t = response.text
54
- return t.strip() if t else ""
55
- except Exception:
56
- return ""
57
-
58
-
59
- def fetch_file(task_id: str) -> tuple[bytes | None, str]:
60
- """
61
- Download the file attached to a GAIA task.
62
- Returns (file_bytes, filename) or (None, "").
63
- """
64
- url = f"{DEFAULT_API_URL}/files/{task_id}"
65
- try:
66
- r = requests.get(url, timeout=20)
67
- if r.status_code == 200:
68
- # Try to get filename from Content-Disposition header
69
- cd = r.headers.get("Content-Disposition", "")
70
- fname = ""
71
- if "filename=" in cd:
72
- fname = cd.split("filename=")[-1].strip().strip('"')
73
- if not fname:
74
- fname = f"attachment_{task_id}"
75
- print(f" [file] Downloaded {len(r.content)} bytes ({fname})")
76
- return r.content, fname
77
- except Exception as e:
78
- print(f" [file] Download failed: {e}")
79
- return None, ""
80
-
81
-
82
- def build_contents(question: str, file_bytes: bytes | None, fname: str) -> list:
83
- """
84
- Build the `contents` list for generate_content(), optionally including a file.
85
- Supports: images, PDFs, plain-text, CSV, audio.
86
- """
87
- if file_bytes is None:
88
- return [question]
89
-
90
- ext = fname.rsplit(".", 1)[-1].lower() if "." in fname else ""
91
-
92
- # --- Image ---
93
- if ext in ("png", "jpg", "jpeg", "gif", "webp", "bmp"):
94
- mime = {"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg",
95
- "gif": "image/gif", "webp": "image/webp", "bmp": "image/bmp"}.get(ext, "image/png")
96
- return [
97
- types.Part.from_bytes(data=file_bytes, mime_type=mime),
98
- question,
99
- ]
100
-
101
- # --- PDF ---
102
- if ext == "pdf":
103
- return [
104
- types.Part.from_bytes(data=file_bytes, mime_type="application/pdf"),
105
- question,
106
- ]
107
-
108
- # --- Audio ---
109
- if ext in ("mp3", "wav", "ogg", "flac", "m4a"):
110
- mime = {"mp3": "audio/mpeg", "wav": "audio/wav", "ogg": "audio/ogg",
111
- "flac": "audio/flac", "m4a": "audio/mp4"}.get(ext, "audio/mpeg")
112
- return [
113
- types.Part.from_bytes(data=file_bytes, mime_type=mime),
114
- question,
115
- ]
116
-
117
- # --- Plain text / CSV / code — embed as text context ---
118
- try:
119
- text_content = file_bytes.decode("utf-8", errors="replace")
120
- augmented = (
121
- f"The following file ({fname}) is attached:\n\n"
122
- f"```\n{text_content[:8000]}\n```\n\n"
123
- f"{question}"
124
- )
125
- return [augmented]
126
- except Exception:
127
- pass
128
-
129
- # Last resort: base64-encode unknown binary
130
- b64 = base64.b64encode(file_bytes).decode()
131
- return [f"[File {fname} base64]: {b64[:500]}…\n\n{question}"]
132
-
133
-
134
- # ── Agent ─────────────────────────────────────────────────────────────
135
 
136
  class BasicAgent:
137
 
138
- SYSTEM = """You are an expert assistant solving GAIA benchmark questions.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- RULES:
141
- - Use Google Search for any factual, date, count, or real-world question.
142
- - If a file is attached, read it carefully before answering.
143
- - Think step-by-step internally, but output ONLY the final answer.
144
 
145
- STRICT OUTPUT FORMAT:
146
- - Return ONLY the final answer. No explanation, no preamble, no trailing punctuation.
147
- - Numbers: digits only, no commas (1234567 not 1,234,567). Drop trailing .0 from whole numbers.
148
- - Lists: comma-separated, alphabetical order unless stated otherwise.
149
- - Yes/no: exactly "yes" or "no" (lowercase).
150
- - Names: full name unless the question asks for first or last name only.
151
- - Dates: match the format implied by the question."""
152
 
153
  def __init__(self):
154
  api_key = os.getenv("GEMINI_API_KEY")
155
  if not api_key:
156
- raise EnvironmentError("GEMINI_API_KEY not set.")
157
- self.client = genai.Client(api_key=api_key)
158
- self.model_id = "gemini-2.0-flash"
159
-
160
- self.search_config = types.GenerateContentConfig(
161
- system_instruction=self.SYSTEM,
162
- tools=[types.Tool(google_search=types.GoogleSearch())],
163
- temperature=0,
164
- )
165
- self.code_config = types.GenerateContentConfig(
166
- system_instruction=self.SYSTEM,
167
- tools=[types.Tool(code_execution=types.ToolCodeExecution())],
168
- temperature=0,
169
- )
170
- self.plain_config = types.GenerateContentConfig(
171
- system_instruction=self.SYSTEM,
172
- temperature=0,
173
- )
174
- print(f"✅ BasicAgent ready ({self.model_id})")
175
 
176
- def __call__(self, question: str, task_id: str = "") -> str:
177
- try:
178
- return self._run(question, task_id)
179
- except Exception as exc:
180
- print(f" [FATAL] {exc}\n{traceback.format_exc()}")
 
 
 
181
  return "N/A"
182
 
183
- def _run(self, question: str, task_id: str) -> str:
184
- # Try to download attached file
185
- file_bytes, fname = (None, "")
186
- if task_id:
187
- file_bytes, fname = fetch_file(task_id)
188
-
189
- contents = build_contents(question, file_bytes, fname)
190
- q_lower = question.lower()
191
-
192
- # Choose config
193
- has_file = file_bytes is not None
194
- needs_code = any(kw in q_lower for kw in [
195
- "calculate", "compute", "sum of", "average", "percentage",
196
- "multiply", "divide", "convert", "how many",
197
- ])
198
-
199
- if has_file:
200
- # Files: use plain config (search can't see the file)
201
- config = self.plain_config
202
- elif needs_code:
203
- config = self.code_config
204
- else:
205
- config = self.search_config
206
-
207
- resp = self.client.models.generate_content(
208
- model=self.model_id, contents=contents, config=config,
209
- )
210
- raw = extract_text(resp)
211
- ans = clean_answer(raw)
212
- print(f" [raw] {raw[:150]!r}")
213
- print(f" [ans] {ans!r}")
214
-
215
- # Fallback: plain call if empty
216
- if not ans or ans == "N/A":
217
- resp2 = self.client.models.generate_content(
218
- model=self.model_id, contents=contents, config=self.plain_config,
219
- )
220
- ans = clean_answer(extract_text(resp2))
221
- print(f" [fallback] {ans!r}")
222
-
223
- return ans or "N/A"
224
-
225
-
226
- # ── Shared run logic ──────────────────────────────────────────────────
227
-
228
- def _fetch_questions() -> list | str:
229
- try:
230
- r = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
231
- r.raise_for_status()
232
- data = r.json()
233
- print(f"Fetched {len(data)} questions.")
234
- return data
235
- except Exception as e:
236
- return f"Error fetching questions: {e}"
237
-
238
-
239
- def _run_agent_on_questions(agent, questions_data: list) -> tuple[list, list]:
240
- results_log, answers_payload = [], []
241
- for item in questions_data:
242
- task_id = item.get("task_id", "")
243
- q_text = item.get("question", "")
244
- if not task_id or q_text is None:
245
- continue
246
- print(f"\n── {task_id} ──")
247
- print(f"Q: {q_text[:150]}")
248
  try:
249
- answer = agent(q_text, task_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  except Exception as e:
251
- answer = "N/A"
252
- print(f" ERROR: {e}")
253
- print(f"→ {answer!r}")
254
- answers_payload.append({"task_id": task_id, "submitted_answer": answer})
255
- results_log.append({
256
- "Task ID": task_id,
257
- "Question": q_text[:120],
258
- "Submitted Answer": answer,
259
- "Has File": "yes" if item.get("file_name") else "no",
260
- })
261
- return results_log, answers_payload
262
-
263
-
264
- # ── Dry Run (no submit) ───────────────────────────────────────────────
265
-
266
- def dry_run(profile: gr.OAuthProfile | None):
267
- if not profile:
268
- return "Please log in first.", None
269
- try:
270
- agent = BasicAgent()
271
- except Exception as e:
272
- return f"Agent init error: {e}", None
273
-
274
- data = _fetch_questions()
275
- if isinstance(data, str):
276
- return data, None
277
-
278
- sample = data[:3] # first 3 questions only
279
- logs, _ = _run_agent_on_questions(agent, sample)
280
- return f"Dry run complete — showing {len(logs)} answers (not submitted).", pd.DataFrame(logs)
281
-
282
-
283
- # ── Full Run + Submit ─────────────────────────────────────────────────
284
-
285
- def run_and_submit_all(profile: gr.OAuthProfile | None):
286
- if not profile:
287
- return "Please log in to Hugging Face first.", None
288
-
289
- username = profile.username
290
- space_id = os.getenv("SPACE_ID", "")
291
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
292
-
293
- try:
294
- agent = BasicAgent()
295
- except Exception as e:
296
- return f"Error initialising agent: {e}", None
297
-
298
- data = _fetch_questions()
299
- if isinstance(data, str):
300
- return data, None
301
-
302
- results_log, answers_payload = _run_agent_on_questions(agent, data)
303
-
304
- if not answers_payload:
305
- return "No answers produced.", pd.DataFrame(results_log)
306
-
307
- try:
308
- r = requests.post(
309
- f"{DEFAULT_API_URL}/submit",
310
- json={"username": username, "agent_code": agent_code, "answers": answers_payload},
311
- timeout=120,
312
- )
313
- r.raise_for_status()
314
- res = r.json()
315
- status = (
316
- f"✅ Submission successful!\n"
317
- f"User: {res.get('username')}\n"
318
- f"Score: {res.get('score', 'N/A')}% "
319
- f"({res.get('correct_count', '?')}/{res.get('total_attempted', '?')} correct)\n"
320
- f"Msg: {res.get('message', '')}"
321
- )
322
- except requests.exceptions.HTTPError as e:
323
- status = f"❌ HTTP {e.response.status_code}: {e.response.text[:300]}"
324
- except Exception as e:
325
- status = f"❌ Error: {e}"
326
-
327
- print(status)
328
- return status, pd.DataFrame(results_log)
329
-
330
-
331
- # ── UI ────────────────────────────────────────────────────────────────
332
-
333
- with gr.Blocks() as demo:
334
- gr.Markdown("# GAIA Agent — Gemini 2.0 Flash + Search + File Support")
335
- gr.Markdown(
336
- "**Setup:** Add `GEMINI_API_KEY` to Space secrets, then log in.\n\n"
337
- "- **Dry Run** — answers the first 3 questions and shows results *without* submitting.\n"
338
- "- **Full Run** — answers all questions and submits to the leaderboard."
339
- )
340
- gr.LoginButton()
341
-
342
- with gr.Row():
343
- dry_btn = gr.Button("🔍 Dry Run (first 3, no submit)", variant="secondary")
344
- full_btn = gr.Button("▶ Full Run & Submit All", variant="primary")
345
-
346
- status = gr.Textbox(label="Status", lines=6, interactive=False)
347
- table = gr.DataFrame(label="Answers", wrap=True)
348
-
349
- dry_btn.click( fn=dry_run, outputs=[status, table])
350
- full_btn.click(fn=run_and_submit_all, outputs=[status, table])
351
-
352
-
353
- if __name__ == "__main__":
354
- print("\n── Env check ──")
355
- for v in ("SPACE_HOST", "SPACE_ID", "GEMINI_API_KEY"):
356
- print(f" {v}: {'SET' if os.getenv(v) else 'MISSING'}")
357
- demo.launch(debug=True, share=False)
 
1
  import os
2
  import re
3
+ import google.generativeai as genai
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  class BasicAgent:
6
 
7
+ def __init__(self):
8
+ api_key = os.getenv("GEMINI_API_KEY")
9
+ if not api_key:
10
+ raise ValueError("Missing GEMINI_API_KEY")
11
+
12
+ genai.configure(api_key=api_key)
13
+
14
+ # ✅ stable model
15
+ self.model = genai.GenerativeModel("gemini-1.5-flash")
16
+
17
+ print("✅ Gemini Agent Ready")
18
+
19
+ def clean(self, text: str) -> str:
20
+ if not text:
21
+ return "N/A"
22
+
23
+ text = text.strip()
24
+ text = text.replace("Final Answer:", "").strip()
25
+ text = re.sub(r"\n.*", "", text) # first line only
26
+ text = text.strip()
27
 
28
+ return text[:100]
 
 
 
29
 
30
+ import os
31
+ import re
32
+ import google.generativeai as genai
33
+
34
+ class BasicAgent:
 
 
35
 
36
  def __init__(self):
37
  api_key = os.getenv("GEMINI_API_KEY")
38
  if not api_key:
39
+ raise ValueError("Missing GEMINI_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
+ genai.configure(api_key=api_key)
42
+
43
+ self.model = genai.GenerativeModel("gemini-1.5-flash")
44
+
45
+ print(" PASS Agent Ready")
46
+
47
+ def clean(self, text: str) -> str:
48
+ if not text:
49
  return "N/A"
50
 
51
+ text = text.strip()
52
+
53
+ # remove junk
54
+ text = text.replace("Final Answer:", "")
55
+ text = text.replace("Answer:", "")
56
+ text = text.strip()
57
+
58
+ # keep only first line
59
+ text = text.split("\n")[0]
60
+
61
+ # remove sentences
62
+ text = text.split(".")[0]
63
+
64
+ return text.strip()[:100]
65
+
66
+ def __call__(self, question: str, task_id: str = "") -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  try:
68
+ # 🔥 STEP 1 — Solve
69
+ prompt1 = f"""
70
+ Solve the question.
71
+
72
+ IMPORTANT:
73
+ - Think step by step internally
74
+ - Return ONLY final answer
75
+ - No explanation
76
+
77
+ Question:
78
+ {question}
79
+ """
80
+ r1 = self.model.generate_content(prompt1)
81
+ ans1 = r1.text if hasattr(r1, "text") else ""
82
+
83
+ ans1 = self.clean(ans1)
84
+
85
+ # 🔥 STEP 2 — VERIFY (this is the magic)
86
+ prompt2 = f"""
87
+ Question: {question}
88
+
89
+ Proposed Answer: {ans1}
90
+
91
+ Check if this is correct.
92
+ If wrong, fix it.
93
+
94
+ Return ONLY final answer.
95
+ No explanation.
96
+ """
97
+ r2 = self.model.generate_content(prompt2)
98
+ final = r2.text if hasattr(r2, "text") else ""
99
+
100
+ final = self.clean(final)
101
+
102
+ # 🔥 fallback if broken
103
+ if not final or final == "N/A":
104
+ final = ans1
105
+
106
+ print("Q:", question[:80])
107
+ print("A:", final)
108
+
109
+ return final
110
+
111
  except Exception as e:
112
+ print("ERROR:", e)
113
+ return "N/A"