mohdadrian commited on
Commit
87acbb5
Β·
verified Β·
1 Parent(s): 35bdbb2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -312
app.py CHANGED
@@ -1,328 +1,117 @@
1
  import os
2
- import re
3
  import time
4
  import requests
5
  import gradio as gr
6
  import pandas as pd
7
  from groq import Groq
8
- from duckduckgo_search import DDGS
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
- DELAY_BETWEEN_QUESTIONS = 15
13
 
14
  # ============================================
15
- # GROQ CLIENT
16
  # ============================================
17
 
18
- def get_groq_client():
19
- api_key = os.environ.get("GROQ_API_KEY")
20
- if not api_key:
21
- raise ValueError("GROQ_API_KEY not set!")
22
- return Groq(api_key=api_key)
23
-
24
- # ============================================
25
- # TOOLS
26
- # ============================================
27
-
28
- def web_search(query: str) -> str:
29
- """Search with DuckDuckGo"""
30
- try:
31
- with DDGS() as ddgs:
32
- results = list(ddgs.text(query, max_results=5))
33
- if not results:
34
- return ""
35
- output = []
36
- for r in results:
37
- output.append(f"- {r.get('title','')}: {r.get('body','')}")
38
- return "\n".join(output)
39
- except Exception as e:
40
- print(f" Search error: {e}")
41
- return ""
42
-
43
-
44
- def fetch_task_file(task_id: str) -> dict:
45
- """Fetch file from GAIA API"""
46
- if not task_id:
47
- return {"has_file": False}
48
 
49
- try:
50
- url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
51
- print(f" Fetching file: {url}")
52
-
53
- response = requests.get(url, timeout=30)
54
- print(f" Response status: {response.status_code}")
55
-
56
- if response.status_code == 404:
57
- print(f" No file for this task")
58
- return {"has_file": False}
59
-
60
- if response.status_code != 200:
61
- print(f" Error status: {response.status_code}")
62
- return {"has_file": False}
63
-
64
- content_type = response.headers.get('content-type', '').lower()
65
- disposition = response.headers.get('content-disposition', '')
66
-
67
- # Extract filename
68
- filename = "unknown"
69
- if 'filename=' in disposition:
70
- filename = disposition.split('filename=')[-1].strip('"\'').strip()
71
- elif 'filename*=' in disposition:
72
- filename = disposition.split('filename*=')[-1].strip('"\'').strip()
73
-
74
- print(f" File: {filename}, Type: {content_type}")
75
-
76
- result = {"has_file": True, "filename": filename, "content_type": content_type}
77
-
78
- # Python files
79
- if filename.endswith('.py') or 'python' in content_type:
80
- result["content"] = response.text
81
- result["file_type"] = "python"
82
- print(f" Python file, {len(response.text)} chars")
83
- return result
84
-
85
- # Text files
86
- if 'text' in content_type or filename.endswith(('.txt', '.md', '.csv', '.json')):
87
- result["content"] = response.text
88
- result["file_type"] = "text"
89
- return result
90
-
91
- # Excel files
92
- if 'excel' in content_type or 'spreadsheet' in content_type or filename.endswith(('.xlsx', '.xls')):
93
- try:
94
- from io import BytesIO
95
- df = pd.read_excel(BytesIO(response.content))
96
- result["content"] = df.to_csv(index=False)
97
- result["dataframe"] = df
98
- result["file_type"] = "excel"
99
- print(f" Excel file, {len(df)} rows")
100
- return result
101
- except Exception as e:
102
- print(f" Excel parse error: {e}")
103
- result["content"] = f"Excel file (error: {e})"
104
- result["file_type"] = "excel"
105
- return result
106
-
107
- # Images
108
- if 'image' in content_type or filename.endswith(('.png', '.jpg', '.jpeg', '.gif')):
109
- result["file_type"] = "image"
110
- result["content"] = "[IMAGE - cannot process]"
111
- return result
112
-
113
- # Audio/Video
114
- if 'audio' in content_type or 'video' in content_type:
115
- result["file_type"] = "media"
116
- result["content"] = "[MEDIA - cannot process]"
117
- return result
118
-
119
- # Try as text
120
  try:
121
- result["content"] = response.text[:8000]
122
- result["file_type"] = "text"
123
- return result
 
 
124
  except:
125
- result["content"] = "[Binary file]"
126
- result["file_type"] = "binary"
127
- return result
128
-
129
- except Exception as e:
130
- print(f" File fetch error: {e}")
131
- return {"has_file": False, "error": str(e)}
132
-
133
-
134
- def run_python_code(code: str) -> str:
135
- """Execute Python code and return output"""
136
- try:
137
- import io
138
- import sys
139
-
140
- old_stdout = sys.stdout
141
- old_stderr = sys.stderr
142
- sys.stdout = stdout_buffer = io.StringIO()
143
- sys.stderr = stderr_buffer = io.StringIO()
144
-
145
- try:
146
- exec(code, {"__builtins__": __builtins__})
147
- except Exception as e:
148
- sys.stdout = old_stdout
149
- sys.stderr = old_stderr
150
- return f"Execution error: {e}"
151
-
152
- sys.stdout = old_stdout
153
- sys.stderr = old_stderr
154
-
155
- output = stdout_buffer.getvalue()
156
- errors = stderr_buffer.getvalue()
157
-
158
- if output:
159
- return output.strip()
160
- if errors:
161
- return f"Stderr: {errors.strip()}"
162
- return "No output"
163
-
164
- except Exception as e:
165
- return f"Error: {e}"
166
-
167
-
168
- def reverse_text(text: str) -> str:
169
- return text[::-1]
170
-
171
-
172
- def is_reversed(text: str) -> bool:
173
- """Check if text is reversed English"""
174
- patterns = ['.rewsna', 'eht sa', 'tfel', 'drow eht', 'etisoppo', 'tahW', 'erehW']
175
- return any(p in text for p in patterns)
176
-
177
-
178
- # ============================================
179
- # AGENT
180
- # ============================================
181
-
182
- class BasicAgent:
183
- def __init__(self):
184
- print("Initializing agent...")
185
- self.client = get_groq_client()
186
- print("βœ… Agent ready!")
187
 
188
  def ask(self, prompt: str) -> str:
189
- """Ask LLM with retries"""
190
- for attempt in range(3):
191
- try:
192
- response = self.client.chat.completions.create(
193
- model="llama-3.3-70b-versatile",
194
- messages=[{"role": "user", "content": prompt}],
195
- temperature=0.1,
196
- max_tokens=200,
197
- )
198
- return response.choices[0].message.content.strip()
199
- except Exception as e:
200
- if "rate" in str(e).lower() or "429" in str(e):
201
- wait = (attempt + 1) * 20
202
- print(f" Rate limit, waiting {wait}s...")
203
- time.sleep(wait)
204
- else:
205
- print(f" LLM error: {e}")
 
 
 
 
206
  return ""
207
- return ""
208
-
209
- def clean(self, answer: str) -> str:
210
- """Clean answer for exact match"""
211
- if not answer:
212
  return ""
213
-
214
- # Remove prefixes
215
- for p in ["Answer:", "The answer is:", "The answer is", "A:", "Final answer:", "**"]:
216
- if answer.lower().startswith(p.lower()):
217
- answer = answer[len(p):].strip()
218
-
219
- # Clean formatting
220
- answer = answer.replace("**", "").replace("```", "").strip()
221
- answer = answer.strip('"\'')
222
-
223
- # Remove trailing punctuation for short answers
224
- if answer.endswith('.') and len(answer.split()) <= 5:
225
- answer = answer[:-1]
226
-
227
- return answer.strip()
228
 
229
  def __call__(self, question: str, task_id: str = None) -> str:
230
- context_parts = []
231
-
232
- # === Handle reversed text ===
233
- if is_reversed(question):
234
- question = reverse_text(question)
235
- print(f" [Decoded reversed: {question[:50]}...]")
236
-
237
- # === Fetch file ===
238
- file_info = fetch_task_file(task_id)
239
-
240
- if file_info.get("has_file"):
241
- ftype = file_info.get("file_type", "")
242
- content = file_info.get("content", "")
243
-
244
- if ftype == "python" and content:
245
- print(f" [Executing Python...]")
246
- output = run_python_code(content)
247
- print(f" [Output: {output[:100]}]")
248
- context_parts.append(f"Python code output:\n{output}")
249
-
250
- elif ftype == "excel":
251
- context_parts.append(f"Excel data:\n{content[:4000]}")
252
-
253
- elif ftype == "text":
254
- context_parts.append(f"File content:\n{content[:4000]}")
255
-
256
- elif ftype in ["image", "media"]:
257
- context_parts.append("[This task has an image/media file that cannot be processed]")
258
-
259
- # === Web search ===
260
- do_search = not file_info.get("has_file") or file_info.get("file_type") in ["image", "media"]
261
 
262
- if do_search:
263
- results = web_search(question[:100])
264
- if results:
265
- context_parts.append(f"Web search:\n{results[:2500]}")
266
- print(f" [Search done]")
267
 
268
- # === Ask LLM ===
269
- context = "\n\n".join(context_parts)
270
 
271
- prompt = f"""Answer this question with ONLY the answer. No explanation.
272
-
273
- Rules:
274
- - Give just the answer (number, name, or short phrase)
275
- - No "The answer is" prefix
276
- - Be precise - exact match grading
277
- - If unsure, give your best guess
278
-
279
- {f"Context:{chr(10)}{context}" if context else ""}
280
 
281
- Question: {question}
282
-
283
- Answer:"""
284
 
285
  answer = self.ask(prompt)
286
- answer = self.clean(answer)
287
 
288
- # Don't return empty
289
- if not answer or "unable" in answer.lower() or "cannot" in answer.lower():
290
- # Try simpler prompt
291
- simple = self.ask(f"Answer in 1-3 words: {question}")
292
- answer = self.clean(simple) or "unknown"
293
 
294
- return answer
295
-
 
 
 
 
 
 
296
 
297
- # ============================================
298
- # MAIN
299
- # ============================================
300
 
301
  def run_and_submit_all(profile: gr.OAuthProfile | None):
302
  if not profile:
303
- return "Please log in first.", None
304
-
305
  username = profile.username
306
  space_id = os.getenv("SPACE_ID")
307
 
308
  if not os.environ.get("GROQ_API_KEY"):
309
- return "❌ Add GROQ_API_KEY to secrets!", None
310
-
311
- print(f"\n{'='*50}")
312
- print(f"User: {username}")
313
- print(f"{'='*50}")
314
-
315
  try:
316
  agent = BasicAgent()
317
  except Exception as e:
318
- return f"❌ Init failed: {e}", None
319
-
320
  try:
321
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
322
  print(f"πŸ“‹ {len(questions)} questions\n")
323
  except Exception as e:
324
- return f"❌ Fetch failed: {e}", None
325
-
326
  results = []
327
  answers = []
328
  start = time.time()
@@ -331,60 +120,42 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
331
  task_id = q.get("task_id")
332
  question = q.get("question", "")
333
 
334
- print(f"\n[{i+1}/{len(questions)}] Task: {task_id}")
335
- print(f"Q: {question[:70]}...")
336
-
337
  answer = agent(question, task_id)
338
- print(f"A: {answer}")
339
 
340
  answers.append({"task_id": task_id, "submitted_answer": answer})
341
- results.append({"#": i+1, "Question": question[:50]+"...", "Answer": answer})
342
 
343
- if i < len(questions) - 1:
344
- time.sleep(DELAY_BETWEEN_QUESTIONS)
345
-
346
  total = time.time() - start
347
- print(f"\n⏱️ {total/60:.1f} min total")
348
-
349
  try:
350
  result = requests.post(
351
  f"{DEFAULT_API_URL}/submit",
352
- json={
353
- "username": username,
354
- "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
355
- "answers": answers
356
- },
357
  timeout=60
358
  ).json()
359
 
360
  score = result.get('score', 0)
361
  correct = result.get('correct_count', 0)
362
- total_q = result.get('total_attempted', 0)
363
 
364
- status = f"βœ… Done in {total/60:.1f} min\n\n"
365
- status += f"🎯 Score: {score}% ({correct}/{total_q})\n\n"
366
  status += "πŸŽ‰ PASSED!" if score >= 30 else f"Need {30-score}% more"
367
 
368
  return status, pd.DataFrame(results)
369
  except Exception as e:
370
- return f"❌ Submit failed: {e}", pd.DataFrame(results)
371
 
372
 
373
- # ============================================
374
- # UI
375
- # ============================================
376
-
377
  with gr.Blocks() as demo:
378
- gr.Markdown("# 🎯 GAIA Agent - Unit 4")
379
- gr.Markdown("**Llama 3.3 70B** via Groq | ~5 min runtime")
380
-
381
  gr.LoginButton()
382
- run_btn = gr.Button("πŸš€ Run", variant="primary", size="lg")
383
- status = gr.Textbox(label="Status", lines=5)
384
- table = gr.DataFrame(label="Results")
385
-
386
- run_btn.click(run_and_submit_all, outputs=[status, table])
387
 
388
  if __name__ == "__main__":
389
- print(f"GROQ_API_KEY: {'βœ…' if os.environ.get('GROQ_API_KEY') else '❌'}")
390
  demo.launch()
 
1
  import os
 
2
  import time
3
  import requests
4
  import gradio as gr
5
  import pandas as pd
6
  from groq import Groq
 
7
 
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
10
 
11
  # ============================================
12
+ # SIMPLE FAST AGENT
13
  # ============================================
14
 
15
+ class BasicAgent:
16
+ def __init__(self):
17
+ api_key = os.environ.get("GROQ_API_KEY")
18
+ if not api_key:
19
+ raise ValueError("GROQ_API_KEY not set!")
20
+ self.client = Groq(api_key=api_key)
21
+ print("βœ… Agent ready")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ def search(self, query: str) -> str:
24
+ """Quick web search"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  try:
26
+ from duckduckgo_search import DDGS
27
+ with DDGS() as ddgs:
28
+ results = list(ddgs.text(query, max_results=3))
29
+ if results:
30
+ return "\n".join([f"- {r.get('title','')}: {r.get('body','')}" for r in results])
31
  except:
32
+ pass
33
+ return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  def ask(self, prompt: str) -> str:
36
+ """Ask LLM - use 8B model for speed and rate limits"""
37
+ try:
38
+ response = self.client.chat.completions.create(
39
+ model="llama-3.1-8b-instant", # Fast, high rate limit
40
+ messages=[{"role": "user", "content": prompt}],
41
+ temperature=0,
42
+ max_tokens=50, # Short answers only
43
+ )
44
+ return response.choices[0].message.content.strip()
45
+ except Exception as e:
46
+ if "rate" in str(e).lower():
47
+ time.sleep(5)
48
+ try:
49
+ response = self.client.chat.completions.create(
50
+ model="llama-3.1-8b-instant",
51
+ messages=[{"role": "user", "content": prompt}],
52
+ temperature=0,
53
+ max_tokens=50,
54
+ )
55
+ return response.choices[0].message.content.strip()
56
+ except:
57
  return ""
 
 
 
 
 
58
  return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  def __call__(self, question: str, task_id: str = None) -> str:
61
+ # Handle reversed text
62
+ if '.rewsna' in question or 'eht sa' in question or 'tfel' in question:
63
+ question = question[::-1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ # Search for info
66
+ search_results = self.search(question[:80])
 
 
 
67
 
68
+ # Build prompt
69
+ context = f"Info: {search_results[:1000]}\n\n" if search_results else ""
70
 
71
+ prompt = f"""{context}Q: {question}
 
 
 
 
 
 
 
 
72
 
73
+ Give only the final answer in 1-5 words. No explanation."""
 
 
74
 
75
  answer = self.ask(prompt)
 
76
 
77
+ # Clean answer
78
+ for prefix in ["Answer:", "The answer is", "A:", "Final answer:"]:
79
+ if answer.lower().startswith(prefix.lower()):
80
+ answer = answer[len(prefix):].strip()
 
81
 
82
+ answer = answer.strip('."\'')
83
+
84
+ # Filter bad responses
85
+ if any(x in answer.lower() for x in ["i cannot", "i'm unable", "no code", "no image", "i don't"]):
86
+ answer = self.ask(f"Answer in 1-3 words only: {question}")
87
+ answer = answer.strip('."\'')
88
+
89
+ return answer if answer else "unknown"
90
 
 
 
 
91
 
92
  def run_and_submit_all(profile: gr.OAuthProfile | None):
93
  if not profile:
94
+ return "Please log in.", None
95
+
96
  username = profile.username
97
  space_id = os.getenv("SPACE_ID")
98
 
99
  if not os.environ.get("GROQ_API_KEY"):
100
+ return "❌ Add GROQ_API_KEY!", None
101
+
102
+ print(f"\n{'='*40}\nUser: {username}\n{'='*40}")
103
+
 
 
104
  try:
105
  agent = BasicAgent()
106
  except Exception as e:
107
+ return f"❌ {e}", None
108
+
109
  try:
110
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
111
  print(f"πŸ“‹ {len(questions)} questions\n")
112
  except Exception as e:
113
+ return f"❌ {e}", None
114
+
115
  results = []
116
  answers = []
117
  start = time.time()
 
120
  task_id = q.get("task_id")
121
  question = q.get("question", "")
122
 
123
+ print(f"[{i+1}] {question[:50]}...")
 
 
124
  answer = agent(question, task_id)
125
+ print(f" β†’ {answer}")
126
 
127
  answers.append({"task_id": task_id, "submitted_answer": answer})
128
+ results.append({"#": i+1, "Q": question[:40]+"...", "A": answer})
129
 
130
+ time.sleep(2) # Small delay
131
+
 
132
  total = time.time() - start
133
+ print(f"\n⏱️ {total:.0f}s")
134
+
135
  try:
136
  result = requests.post(
137
  f"{DEFAULT_API_URL}/submit",
138
+ json={"username": username, "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main", "answers": answers},
 
 
 
 
139
  timeout=60
140
  ).json()
141
 
142
  score = result.get('score', 0)
143
  correct = result.get('correct_count', 0)
 
144
 
145
+ status = f"βœ… Done in {total:.0f}s\n\n🎯 {score}% ({correct}/20)\n\n"
 
146
  status += "πŸŽ‰ PASSED!" if score >= 30 else f"Need {30-score}% more"
147
 
148
  return status, pd.DataFrame(results)
149
  except Exception as e:
150
+ return f"❌ {e}", pd.DataFrame(results)
151
 
152
 
 
 
 
 
153
  with gr.Blocks() as demo:
154
+ gr.Markdown("# 🎯 GAIA Agent")
155
+ gr.Markdown("Fast mode - ~2 min")
 
156
  gr.LoginButton()
157
+ gr.Button("πŸš€ Run", variant="primary").click(run_and_submit_all, outputs=[gr.Textbox(label="Status", lines=5), gr.DataFrame(label="Results")])
 
 
 
 
158
 
159
  if __name__ == "__main__":
160
+ print(f"GROQ: {'βœ…' if os.environ.get('GROQ_API_KEY') else '❌'}")
161
  demo.launch()