lmrkmrcs commited on
Commit
7c756ab
Β·
verified Β·
1 Parent(s): d793055

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -87
app.py CHANGED
@@ -8,10 +8,10 @@ from duckduckgo_search import DDGS
8
 
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
11
- TIMEOUT_PER_QUESTION = 30 # Max 30 seconds per question
12
 
13
  # ============================================
14
- # INITIALIZE GROQ CLIENT
15
  # ============================================
16
 
17
  def get_groq_client():
@@ -24,64 +24,146 @@ def get_groq_client():
24
  # TOOL FUNCTIONS
25
  # ============================================
26
 
27
- def web_search(query: str) -> str:
28
- """Search the web"""
29
  try:
30
  with DDGS() as ddgs:
31
- results = list(ddgs.text(query, max_results=3))
32
  if not results:
33
- return "No results"
34
- return "\n".join([f"- {r['title']}: {r['body']}" for r in results])
35
- except:
36
- return "Search failed"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
 
39
  def wikipedia_search(topic: str) -> str:
40
- """Get Wikipedia info"""
41
  try:
42
  url = "https://en.wikipedia.org/w/api.php"
 
43
  params = {"action": "query", "list": "search", "srsearch": topic, "format": "json", "srlimit": 1}
44
- data = requests.get(url, params=params, timeout=5).json()
45
 
46
  if not data.get("query", {}).get("search"):
47
- return "Not found"
48
 
49
  title = data["query"]["search"][0]["title"]
50
- params2 = {"action": "query", "titles": title, "prop": "extracts", "exintro": True, "explaintext": True, "format": "json"}
51
- pages = requests.get(url, params=params2, timeout=5).json().get("query", {}).get("pages", {})
 
 
 
 
 
 
 
 
 
52
 
53
  for page in pages.values():
54
- return page.get("extract", "")[:2000]
55
- return "No content"
56
- except:
57
- return "Wikipedia error"
 
58
 
59
 
60
- def get_task_file(task_id: str) -> str:
61
- """Get GAIA task file"""
62
  try:
63
  url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
64
- response = requests.get(url, timeout=10)
65
 
66
  if response.status_code == 404:
67
- return ""
68
 
69
  content_type = response.headers.get('content-type', '').lower()
 
 
 
 
 
 
70
 
71
- if 'text' in content_type or 'json' in content_type:
72
- return response.text[:4000]
73
 
74
- if 'excel' in content_type or 'spreadsheet' in content_type:
 
 
 
 
 
 
 
 
 
 
 
75
  try:
76
  from io import BytesIO
77
  df = pd.read_excel(BytesIO(response.content))
78
- return df.to_string()[:4000]
79
- except:
80
- return "Excel file"
 
 
 
 
 
 
 
 
 
81
 
82
- return f"File type: {content_type}"
83
- except:
84
- return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
 
87
  # ============================================
@@ -94,57 +176,121 @@ class BasicAgent:
94
  self.client = get_groq_client()
95
  print("βœ… Agent ready!")
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  def __call__(self, question: str, task_id: str = None) -> str:
98
  try:
99
- # Check for file first
100
- file_content = ""
 
 
 
 
 
 
 
 
101
  if task_id:
102
- file_content = get_task_file(task_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- # Check if we need to search
105
- needs_search = any(word in question.lower() for word in
106
- ["who is", "what is", "when did", "where is", "current", "latest", "recent"])
107
 
108
- search_results = ""
109
- if needs_search and not file_content:
110
- # Extract search query from question
111
- search_results = web_search(question[:100])
112
 
113
- # Build context
114
- context = ""
115
- if file_content:
116
- context += f"\n\nFile content:\n{file_content}"
117
- if search_results and search_results != "No results":
118
- context += f"\n\nSearch results:\n{search_results}"
 
 
 
 
 
119
 
120
- # Ask Groq
121
- prompt = f"""Answer this question with ONLY the final answer. No explanation.
122
- Be precise - just give the exact answer.
123
- {context}
124
 
125
- Question: {question}
 
 
 
 
 
 
126
 
127
- Answer:"""
128
 
129
- response = self.client.chat.completions.create(
130
- model="llama-3.3-70b-versatile",
131
- messages=[{"role": "user", "content": prompt}],
132
- temperature=0,
133
- max_tokens=200,
134
- timeout=TIMEOUT_PER_QUESTION,
135
- )
136
-
137
- answer = response.choices[0].message.content.strip()
138
-
139
- # Clean up common prefixes
140
- for prefix in ["Answer:", "The answer is:", "Final answer:", "A:"]:
141
- if answer.lower().startswith(prefix.lower()):
142
- answer = answer[len(prefix):].strip()
143
 
144
- # Remove quotes
145
- if (answer.startswith('"') and answer.endswith('"')) or \
146
- (answer.startswith("'") and answer.endswith("'")):
147
- answer = answer[1:-1]
148
 
149
  return answer
150
 
@@ -167,27 +313,23 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
167
  print(f"\n{'='*50}")
168
  print(f"User: {username}")
169
 
170
- # Check API key
171
  if not os.environ.get("GROQ_API_KEY"):
172
  return "❌ ERROR: Add GROQ_API_KEY to Space secrets!", None
173
 
174
  print("βœ… GROQ_API_KEY found")
175
  print(f"{'='*50}\n")
176
 
177
- # Init agent
178
  try:
179
  agent = BasicAgent()
180
  except Exception as e:
181
  return f"❌ Agent init failed: {e}", None
182
 
183
- # Get questions
184
  try:
185
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
186
  print(f"πŸ“‹ Got {len(questions)} questions\n")
187
  except Exception as e:
188
  return f"❌ Failed to fetch questions: {e}", None
189
 
190
- # Process each question
191
  results = []
192
  answers = []
193
 
@@ -204,7 +346,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
204
  try:
205
  answer = agent(question, task_id)
206
  q_time = time.time() - q_start
207
- print(f" βœ“ {answer[:50]}... ({q_time:.1f}s)")
208
  except Exception as e:
209
  answer = "unknown"
210
  print(f" βœ— Error: {e}")
@@ -212,14 +354,13 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
212
  answers.append({"task_id": task_id, "submitted_answer": answer})
213
  results.append({
214
  "#": i+1,
215
- "Question": question[:50]+"...",
216
- "Answer": answer[:60]
217
  })
218
 
219
  total_time = time.time() - start_time
220
  print(f"\n⏱️ Total time: {total_time:.1f}s ({total_time/60:.1f} min)")
221
 
222
- # Submit
223
  print(f"\nπŸ“€ Submitting {len(answers)} answers...")
224
 
225
  try:
@@ -257,16 +398,17 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
257
  # ============================================
258
 
259
  with gr.Blocks() as demo:
260
- gr.Markdown("# 🎯 GAIA Agent - Unit 4")
261
  gr.Markdown("""
262
  **Powered by Groq + Llama 3.3 70B**
263
 
264
- ⚑ Fast: ~5-10 minutes for all 20 questions
 
 
 
 
265
 
266
- **Setup:**
267
- 1. Add `GROQ_API_KEY` to Space secrets
268
- 2. Log in below
269
- 3. Click Run!
270
  """)
271
 
272
  gr.LoginButton()
@@ -278,12 +420,12 @@ with gr.Blocks() as demo:
278
 
279
  if __name__ == "__main__":
280
  print("="*50)
281
- print("🎯 GAIA Agent - Groq Edition")
282
  print("="*50)
283
 
284
  if os.environ.get("GROQ_API_KEY"):
285
  print("βœ… GROQ_API_KEY found")
286
  else:
287
- print("❌ GROQ_API_KEY missing - add to secrets!")
288
 
289
  demo.launch()
 
8
 
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
11
+ TIMEOUT_PER_QUESTION = 30
12
 
13
  # ============================================
14
+ # GROQ CLIENT
15
  # ============================================
16
 
17
  def get_groq_client():
 
24
  # TOOL FUNCTIONS
25
  # ============================================
26
 
27
+ def web_search(query: str, num_results: int = 5) -> str:
28
+ """Search the web with DuckDuckGo"""
29
  try:
30
  with DDGS() as ddgs:
31
+ results = list(ddgs.text(query, max_results=num_results))
32
  if not results:
33
+ return "No results found"
34
+ output = []
35
+ for r in results:
36
+ output.append(f"Title: {r.get('title', '')}")
37
+ output.append(f"Content: {r.get('body', '')}")
38
+ output.append(f"URL: {r.get('href', '')}")
39
+ output.append("---")
40
+ return "\n".join(output)
41
+ except Exception as e:
42
+ return f"Search error: {e}"
43
+
44
+
45
+ def visit_webpage(url: str) -> str:
46
+ """Get webpage content"""
47
+ try:
48
+ from bs4 import BeautifulSoup
49
+ headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
50
+ response = requests.get(url, headers=headers, timeout=10)
51
+ soup = BeautifulSoup(response.text, 'html.parser')
52
+ for tag in soup(['script', 'style', 'nav', 'footer', 'header']):
53
+ tag.decompose()
54
+ text = soup.get_text(separator=' ', strip=True)
55
+ return text[:6000]
56
+ except Exception as e:
57
+ return f"Error: {e}"
58
 
59
 
60
  def wikipedia_search(topic: str) -> str:
61
+ """Get Wikipedia content"""
62
  try:
63
  url = "https://en.wikipedia.org/w/api.php"
64
+ # Search
65
  params = {"action": "query", "list": "search", "srsearch": topic, "format": "json", "srlimit": 1}
66
+ data = requests.get(url, params=params, timeout=10).json()
67
 
68
  if not data.get("query", {}).get("search"):
69
+ return "No Wikipedia article found"
70
 
71
  title = data["query"]["search"][0]["title"]
72
+
73
+ # Get full content (not just intro)
74
+ params2 = {
75
+ "action": "query",
76
+ "titles": title,
77
+ "prop": "extracts",
78
+ "exintro": False, # Get full article
79
+ "explaintext": True,
80
+ "format": "json"
81
+ }
82
+ pages = requests.get(url, params=params2, timeout=10).json().get("query", {}).get("pages", {})
83
 
84
  for page in pages.values():
85
+ content = page.get("extract", "")
86
+ return f"Wikipedia - {title}:\n{content[:5000]}"
87
+ return "No content found"
88
+ except Exception as e:
89
+ return f"Wikipedia error: {e}"
90
 
91
 
92
+ def get_task_file(task_id: str) -> dict:
93
+ """Get GAIA task file - returns dict with content and type"""
94
  try:
95
  url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
96
+ response = requests.get(url, timeout=15)
97
 
98
  if response.status_code == 404:
99
+ return {"has_file": False, "content": ""}
100
 
101
  content_type = response.headers.get('content-type', '').lower()
102
+ disposition = response.headers.get('content-disposition', '')
103
+
104
+ # Get filename
105
+ filename = ""
106
+ if 'filename=' in disposition:
107
+ filename = disposition.split('filename=')[-1].strip('"\'')
108
 
109
+ result = {"has_file": True, "filename": filename, "type": content_type}
 
110
 
111
+ # Text files
112
+ if 'text' in content_type or filename.endswith(('.txt', '.py', '.md', '.csv')):
113
+ result["content"] = response.text[:8000]
114
+ return result
115
+
116
+ # JSON
117
+ if 'json' in content_type or filename.endswith('.json'):
118
+ result["content"] = response.text[:8000]
119
+ return result
120
+
121
+ # Excel files
122
+ if 'spreadsheet' in content_type or 'excel' in content_type or filename.endswith(('.xlsx', '.xls')):
123
  try:
124
  from io import BytesIO
125
  df = pd.read_excel(BytesIO(response.content))
126
+ result["content"] = f"Excel file with {len(df)} rows:\n{df.to_string()}"
127
+ result["dataframe"] = df
128
+ return result
129
+ except Exception as e:
130
+ result["content"] = f"Excel file (parse error: {e})"
131
+ return result
132
+
133
+ # Images
134
+ if 'image' in content_type or filename.endswith(('.png', '.jpg', '.jpeg', '.gif')):
135
+ result["content"] = "IMAGE FILE - Cannot process images"
136
+ result["is_image"] = True
137
+ return result
138
 
139
+ # Audio/Video
140
+ if 'audio' in content_type or 'video' in content_type:
141
+ result["content"] = "AUDIO/VIDEO FILE - Cannot process"
142
+ return result
143
+
144
+ # PDF
145
+ if 'pdf' in content_type or filename.endswith('.pdf'):
146
+ result["content"] = "PDF FILE - Cannot read directly"
147
+ return result
148
+
149
+ result["content"] = f"Binary file: {content_type}, {len(response.content)} bytes"
150
+ return result
151
+
152
+ except Exception as e:
153
+ return {"has_file": False, "content": f"Error: {e}"}
154
+
155
+
156
+ def reverse_string(text: str) -> str:
157
+ """Reverse a string"""
158
+ return text[::-1]
159
+
160
+
161
+ def is_reversed_text(text: str) -> bool:
162
+ """Check if text appears to be reversed"""
163
+ # Common reversed patterns
164
+ reversed_indicators = ['.rewsna', '.txet', 'eht si', 'tahw', 'erehw', 'nehw', 'ohw']
165
+ text_lower = text.lower()
166
+ return any(ind in text_lower for ind in reversed_indicators)
167
 
168
 
169
  # ============================================
 
176
  self.client = get_groq_client()
177
  print("βœ… Agent ready!")
178
 
179
+ def ask_llm(self, prompt: str, max_tokens: int = 300) -> str:
180
+ """Ask Groq LLM"""
181
+ try:
182
+ response = self.client.chat.completions.create(
183
+ model="llama-3.3-70b-versatile",
184
+ messages=[{"role": "user", "content": prompt}],
185
+ temperature=0,
186
+ max_tokens=max_tokens,
187
+ timeout=TIMEOUT_PER_QUESTION,
188
+ )
189
+ return response.choices[0].message.content.strip()
190
+ except Exception as e:
191
+ return f"LLM Error: {e}"
192
+
193
+ def clean_answer(self, answer: str) -> str:
194
+ """Clean up the answer"""
195
+ # Remove common prefixes
196
+ prefixes = [
197
+ "Answer:", "The answer is:", "Final answer:", "A:",
198
+ "The answer is", "Final answer", "Based on",
199
+ "According to", "The result is", "The result is:",
200
+ ]
201
+ for prefix in prefixes:
202
+ if answer.lower().startswith(prefix.lower()):
203
+ answer = answer[len(prefix):].strip()
204
+
205
+ # Remove quotes
206
+ if (answer.startswith('"') and answer.endswith('"')) or \
207
+ (answer.startswith("'") and answer.endswith("'")):
208
+ answer = answer[1:-1]
209
+
210
+ # Remove trailing periods for single-word answers
211
+ if answer.endswith('.') and ' ' not in answer:
212
+ answer = answer[:-1]
213
+
214
+ return answer.strip()
215
+
216
  def __call__(self, question: str, task_id: str = None) -> str:
217
  try:
218
+ context_parts = []
219
+
220
+ # 1. Check for reversed text in question
221
+ if is_reversed_text(question):
222
+ reversed_q = reverse_string(question)
223
+ context_parts.append(f"NOTE: The question appears to be reversed. Original: {reversed_q}")
224
+ question = reversed_q
225
+
226
+ # 2. Check for file
227
+ file_info = {"has_file": False}
228
  if task_id:
229
+ file_info = get_task_file(task_id)
230
+ if file_info.get("has_file"):
231
+ if file_info.get("is_image"):
232
+ context_parts.append("NOTE: This task has an IMAGE file which I cannot analyze.")
233
+ else:
234
+ context_parts.append(f"FILE CONTENT ({file_info.get('filename', 'file')}):\n{file_info.get('content', '')}")
235
+
236
+ # 3. Determine if web search needed
237
+ search_keywords = [
238
+ "who is", "who was", "who did", "who nominated",
239
+ "what is", "what was", "what are", "what did",
240
+ "when did", "when was",
241
+ "where is", "where was", "where were",
242
+ "how many", "how much",
243
+ "which", "name the", "find the",
244
+ "album", "movie", "actor", "actress", "singer", "artist",
245
+ "wikipedia", "article", "published",
246
+ "athlete", "player", "pitcher", "yankee",
247
+ "country", "city", "competition"
248
+ ]
249
 
250
+ needs_search = any(kw in question.lower() for kw in search_keywords)
 
 
251
 
252
+ # Don't search if we have good file content
253
+ if file_info.get("has_file") and not file_info.get("is_image"):
254
+ needs_search = False
 
255
 
256
+ # 4. Do web search if needed
257
+ if needs_search:
258
+ # Extract key search terms
259
+ search_query = question[:150]
260
+ # Remove common question words for better search
261
+ for word in ["what is the", "who is the", "who was the", "what are the", "how many"]:
262
+ search_query = search_query.lower().replace(word, "")
263
+
264
+ search_results = web_search(search_query.strip(), num_results=5)
265
+ if search_results and "No results" not in search_results:
266
+ context_parts.append(f"WEB SEARCH RESULTS:\n{search_results}")
267
 
268
+ # 5. Build final prompt
269
+ context = "\n\n".join(context_parts) if context_parts else ""
270
+
271
+ prompt = f"""You are answering a GAIA benchmark question. Give ONLY the final answer.
272
 
273
+ RULES:
274
+ - Be PRECISE and CONCISE
275
+ - Give ONLY the answer, no explanation
276
+ - If asked for a name, give just the name
277
+ - If asked for a number, give just the number
278
+ - If asked for a list, give comma-separated items
279
+ - Match the format requested in the question
280
 
281
+ {f"CONTEXT:{chr(10)}{context}" if context else ""}
282
 
283
+ QUESTION: {question}
284
+
285
+ ANSWER (just the answer, nothing else):"""
286
+
287
+ answer = self.ask_llm(prompt)
288
+ answer = self.clean_answer(answer)
 
 
 
 
 
 
 
 
289
 
290
+ # Special handling: if question asks for reversed text answer
291
+ if "reversed" in question.lower() and "spell" in question.lower():
292
+ # The answer might need to be reversed
293
+ pass # Keep as is, LLM should handle
294
 
295
  return answer
296
 
 
313
  print(f"\n{'='*50}")
314
  print(f"User: {username}")
315
 
 
316
  if not os.environ.get("GROQ_API_KEY"):
317
  return "❌ ERROR: Add GROQ_API_KEY to Space secrets!", None
318
 
319
  print("βœ… GROQ_API_KEY found")
320
  print(f"{'='*50}\n")
321
 
 
322
  try:
323
  agent = BasicAgent()
324
  except Exception as e:
325
  return f"❌ Agent init failed: {e}", None
326
 
 
327
  try:
328
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
329
  print(f"πŸ“‹ Got {len(questions)} questions\n")
330
  except Exception as e:
331
  return f"❌ Failed to fetch questions: {e}", None
332
 
 
333
  results = []
334
  answers = []
335
 
 
346
  try:
347
  answer = agent(question, task_id)
348
  q_time = time.time() - q_start
349
+ print(f" βœ“ {answer[:60]}... ({q_time:.1f}s)")
350
  except Exception as e:
351
  answer = "unknown"
352
  print(f" βœ— Error: {e}")
 
354
  answers.append({"task_id": task_id, "submitted_answer": answer})
355
  results.append({
356
  "#": i+1,
357
+ "Question": question[:60]+"...",
358
+ "Answer": answer[:80]
359
  })
360
 
361
  total_time = time.time() - start_time
362
  print(f"\n⏱️ Total time: {total_time:.1f}s ({total_time/60:.1f} min)")
363
 
 
364
  print(f"\nπŸ“€ Submitting {len(answers)} answers...")
365
 
366
  try:
 
398
  # ============================================
399
 
400
  with gr.Blocks() as demo:
401
+ gr.Markdown("# 🎯 GAIA Agent - Unit 4 (Improved)")
402
  gr.Markdown("""
403
  **Powered by Groq + Llama 3.3 70B**
404
 
405
+ **Improvements:**
406
+ - βœ… Better web search
407
+ - βœ… Reversed text detection
408
+ - βœ… Excel file reading
409
+ - βœ… Smarter answer formatting
410
 
411
+ **Setup:** Add `GROQ_API_KEY` to Space secrets
 
 
 
412
  """)
413
 
414
  gr.LoginButton()
 
420
 
421
  if __name__ == "__main__":
422
  print("="*50)
423
+ print("🎯 GAIA Agent - Improved Version")
424
  print("="*50)
425
 
426
  if os.environ.get("GROQ_API_KEY"):
427
  print("βœ… GROQ_API_KEY found")
428
  else:
429
+ print("❌ GROQ_API_KEY missing!")
430
 
431
  demo.launch()