mohdadrian commited on
Commit
fbdc7b4
·
verified ·
1 Parent(s): 3524511

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -50
app.py CHANGED
@@ -9,7 +9,7 @@ from duckduckgo_search import DDGS
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
- DELAY_BETWEEN_QUESTIONS = 8 # 8 seconds between questions to avoid rate limits
13
 
14
  # ============================================
15
  # GROQ CLIENT
@@ -28,12 +28,17 @@ def get_groq_client():
28
  def web_search(query: str) -> str:
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.get('title','')}: {r.get('body','')}" for r in results])
 
 
 
 
 
35
  except:
36
- return "Search failed"
37
 
38
 
39
  def get_task_file(task_id: str) -> dict:
@@ -51,26 +56,41 @@ def get_task_file(task_id: str) -> dict:
51
  if 'filename=' in disposition:
52
  filename = disposition.split('filename=')[-1].strip('"\'')
53
 
 
 
 
 
 
 
 
 
54
  # Text files
55
- if 'text' in content_type or filename.endswith(('.txt', '.py', '.md', '.csv', '.json')):
56
- return {"has_file": True, "content": response.text[:5000]}
 
57
 
58
  # Excel
59
  if 'excel' in content_type or 'spreadsheet' in content_type or filename.endswith(('.xlsx', '.xls')):
60
  try:
61
  from io import BytesIO
62
  df = pd.read_excel(BytesIO(response.content))
63
- return {"has_file": True, "content": df.to_string()}
64
- except:
65
- return {"has_file": True, "content": "[Excel file - parse failed]"}
 
 
 
66
 
67
- # Image
68
  if 'image' in content_type:
69
- return {"has_file": True, "content": "[IMAGE - cannot process]", "is_image": True}
 
 
70
 
71
- return {"has_file": True, "content": f"[Binary: {content_type}]"}
72
- except:
73
- return {"has_file": False}
 
74
 
75
 
76
  def reverse_string(text: str) -> str:
@@ -78,7 +98,26 @@ def reverse_string(text: str) -> str:
78
 
79
 
80
  def is_reversed(text: str) -> bool:
81
- return any(x in text.lower() for x in ['.rewsna', 'eht sa', 'tfel', 'drow eht'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
 
84
  # ============================================
@@ -87,61 +126,119 @@ def is_reversed(text: str) -> bool:
87
 
88
  class BasicAgent:
89
  def __init__(self):
90
- print("Initializing Groq agent...")
91
  self.client = get_groq_client()
92
  print("✅ Ready!")
93
 
94
- def ask(self, prompt: str) -> str:
95
- for attempt in range(3):
96
  try:
97
  response = self.client.chat.completions.create(
98
- model="llama-3.1-8b-instant", # Faster model with better rate limits
99
  messages=[{"role": "user", "content": prompt}],
100
  temperature=0,
101
- max_tokens=100,
102
  )
103
  return response.choices[0].message.content.strip()
104
  except Exception as e:
105
  if "rate" in str(e).lower() or "429" in str(e):
106
- wait = (attempt + 1) * 15
107
  print(f" ⏳ Rate limit, waiting {wait}s...")
108
  time.sleep(wait)
109
  else:
110
  return f"Error: {e}"
111
  return "unknown"
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  def __call__(self, question: str, task_id: str = None) -> str:
114
- context = ""
 
115
 
116
- # Handle reversed text
117
  if is_reversed(question):
118
  question = reverse_string(question)
 
119
 
120
- # Get file if exists
 
121
  if task_id:
122
- file = get_task_file(task_id)
123
- if file.get("has_file") and file.get("content"):
124
- context = f"FILE: {file['content']}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
- # Search if needed and no file
127
- if not context:
128
- keywords = ["who", "what", "when", "where", "how many", "album", "actor", "surname", "wikipedia"]
129
- if any(k in question.lower() for k in keywords):
130
- results = web_search(question[:80])
131
- if results != "Search failed":
132
- context = f"INFO: {results}\n\n"
 
 
 
 
 
 
133
 
134
- prompt = f"{context}Q: {question}\n\nAnswer with ONLY the final answer, nothing else:"
 
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  answer = self.ask(prompt)
137
-
138
- # Clean
139
- for p in ["Answer:", "The answer is:", "A:", "Final answer:"]:
140
- if answer.lower().startswith(p.lower()):
141
- answer = answer[len(p):].strip()
142
- answer = answer.strip('"\'.')
143
-
144
- return answer
145
 
146
 
147
  # ============================================
@@ -167,7 +264,8 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
167
 
168
  try:
169
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
170
- print(f"📋 {len(questions)} questions\n")
 
171
  except Exception as e:
172
  return f"❌ Fetch failed: {e}", None
173
 
@@ -182,16 +280,18 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
182
  print(f"[{i+1}/{len(questions)}] {question[:50]}...")
183
 
184
  answer = agent(question, task_id)
185
- print(f" → {answer[:40]}")
186
 
187
  answers.append({"task_id": task_id, "submitted_answer": answer})
188
  results.append({"#": i+1, "Q": question[:40]+"...", "A": answer[:50]})
189
 
 
190
  if i < len(questions) - 1:
 
191
  time.sleep(DELAY_BETWEEN_QUESTIONS)
192
 
193
  total = time.time() - start
194
- print(f"\n⏱️ {total:.0f}s")
195
 
196
  try:
197
  result = requests.post(
@@ -208,7 +308,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
208
  correct = result.get('correct_count', 0)
209
  total_q = result.get('total_attempted', 0)
210
 
211
- status = f"✅ Done in {total:.0f}s\n\n🎯 {score}% ({correct}/{total_q})\n\n"
212
  status += "🎉 PASSED!" if score >= 30 else f"Need {30-score}% more"
213
 
214
  return status, pd.DataFrame(results)
@@ -221,8 +321,12 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
221
  # ============================================
222
 
223
  with gr.Blocks() as demo:
224
- gr.Markdown("# 🎯 GAIA Agent")
225
- gr.Markdown("Groq + Llama 3.1 8B (fast, good rate limits)")
 
 
 
 
226
 
227
  gr.LoginButton()
228
  run_btn = gr.Button("🚀 Run", variant="primary", size="lg")
 
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
+ DELAY_BETWEEN_QUESTIONS = 15 # 15 seconds to avoid rate limits on 70B model
13
 
14
  # ============================================
15
  # GROQ CLIENT
 
28
  def web_search(query: str) -> str:
29
  try:
30
  with DDGS() as ddgs:
31
+ results = list(ddgs.text(query, max_results=5))
32
  if not results:
33
+ return ""
34
+ output = []
35
+ for r in results:
36
+ output.append(f"Title: {r.get('title','')}")
37
+ output.append(f"Snippet: {r.get('body','')}")
38
+ output.append("---")
39
+ return "\n".join(output)
40
  except:
41
+ return ""
42
 
43
 
44
  def get_task_file(task_id: str) -> dict:
 
56
  if 'filename=' in disposition:
57
  filename = disposition.split('filename=')[-1].strip('"\'')
58
 
59
+ result = {"has_file": True, "filename": filename, "type": content_type}
60
+
61
+ # Python files - return code
62
+ if filename.endswith('.py'):
63
+ result["content"] = response.text
64
+ result["is_code"] = True
65
+ return result
66
+
67
  # Text files
68
+ if 'text' in content_type or filename.endswith(('.txt', '.md', '.csv', '.json')):
69
+ result["content"] = response.text[:6000]
70
+ return result
71
 
72
  # Excel
73
  if 'excel' in content_type or 'spreadsheet' in content_type or filename.endswith(('.xlsx', '.xls')):
74
  try:
75
  from io import BytesIO
76
  df = pd.read_excel(BytesIO(response.content))
77
+ result["content"] = df.to_string()
78
+ result["is_excel"] = True
79
+ return result
80
+ except Exception as e:
81
+ result["content"] = f"[Excel parse error: {e}]"
82
+ return result
83
 
84
+ # Image - can't process
85
  if 'image' in content_type:
86
+ result["content"] = "[IMAGE FILE]"
87
+ result["is_image"] = True
88
+ return result
89
 
90
+ result["content"] = f"[File: {content_type}]"
91
+ return result
92
+ except Exception as e:
93
+ return {"has_file": False, "error": str(e)}
94
 
95
 
96
  def reverse_string(text: str) -> str:
 
98
 
99
 
100
  def is_reversed(text: str) -> bool:
101
+ indicators = ['.rewsna', 'eht sa', 'tfel', 'drow eht', 'etisoppo']
102
+ return any(x in text.lower() for x in indicators)
103
+
104
+
105
+ def execute_python(code: str) -> str:
106
+ """Safely execute Python code and return output"""
107
+ try:
108
+ import io
109
+ import sys
110
+ from contextlib import redirect_stdout
111
+
112
+ # Capture stdout
113
+ f = io.StringIO()
114
+ with redirect_stdout(f):
115
+ exec(code, {"__builtins__": __builtins__})
116
+
117
+ output = f.getvalue()
118
+ return output.strip() if output else "No output"
119
+ except Exception as e:
120
+ return f"Error: {e}"
121
 
122
 
123
  # ============================================
 
126
 
127
  class BasicAgent:
128
  def __init__(self):
129
+ print("Initializing Groq agent (70B model)...")
130
  self.client = get_groq_client()
131
  print("✅ Ready!")
132
 
133
+ def ask(self, prompt: str, max_retries: int = 3) -> str:
134
+ for attempt in range(max_retries):
135
  try:
136
  response = self.client.chat.completions.create(
137
+ model="llama-3.3-70b-versatile", # Smart model
138
  messages=[{"role": "user", "content": prompt}],
139
  temperature=0,
140
+ max_tokens=150,
141
  )
142
  return response.choices[0].message.content.strip()
143
  except Exception as e:
144
  if "rate" in str(e).lower() or "429" in str(e):
145
+ wait = (attempt + 1) * 20
146
  print(f" ⏳ Rate limit, waiting {wait}s...")
147
  time.sleep(wait)
148
  else:
149
  return f"Error: {e}"
150
  return "unknown"
151
 
152
+ def clean_answer(self, answer: str) -> str:
153
+ # Remove common prefixes
154
+ prefixes = [
155
+ "Answer:", "The answer is:", "The answer is", "A:",
156
+ "Final answer:", "Final answer", "Based on",
157
+ "I found that", "The result is", "**", "```"
158
+ ]
159
+ for p in prefixes:
160
+ if answer.lower().startswith(p.lower()):
161
+ answer = answer[len(p):].strip()
162
+
163
+ # Remove markdown and quotes
164
+ answer = answer.replace("**", "").replace("```", "").strip()
165
+ answer = answer.strip('"\'')
166
+
167
+ # If answer is too long or contains "I'm unable", return unknown
168
+ if "I'm unable" in answer or "I cannot" in answer or "I don't" in answer:
169
+ return "unknown"
170
+
171
+ # Remove trailing period for short answers
172
+ if answer.endswith('.') and len(answer.split()) <= 5:
173
+ answer = answer[:-1]
174
+
175
+ return answer.strip()
176
+
177
  def __call__(self, question: str, task_id: str = None) -> str:
178
+ original_question = question
179
+ context_parts = []
180
 
181
+ # 1. Handle reversed text
182
  if is_reversed(question):
183
  question = reverse_string(question)
184
+ context_parts.append(f"[Original was reversed. Decoded: {question}]")
185
 
186
+ # 2. Check for file
187
+ file_info = {"has_file": False}
188
  if task_id:
189
+ file_info = get_task_file(task_id)
190
+
191
+ if file_info.get("has_file"):
192
+ if file_info.get("is_code"):
193
+ # Execute Python code
194
+ code = file_info.get("content", "")
195
+ output = execute_python(code)
196
+ context_parts.append(f"Python code output: {output}")
197
+
198
+ elif file_info.get("is_excel"):
199
+ context_parts.append(f"Excel data:\n{file_info.get('content', '')[:3000]}")
200
+
201
+ elif file_info.get("is_image"):
202
+ context_parts.append("[This task has an image file which cannot be processed]")
203
+
204
+ else:
205
+ context_parts.append(f"File content:\n{file_info.get('content', '')[:3000]}")
206
 
207
+ # 3. Web search if needed (and no useful file)
208
+ if not file_info.get("has_file") or file_info.get("is_image"):
209
+ search_triggers = [
210
+ "who ", "what ", "when ", "where ", "how many", "how much",
211
+ "album", "actor", "movie", "wikipedia", "surname", "name",
212
+ "athlete", "pitcher", "yankee", "country", "competition",
213
+ "nominated", "published", "article", "mercedes", "sosa"
214
+ ]
215
+
216
+ if any(t in question.lower() for t in search_triggers):
217
+ search_results = web_search(question)
218
+ if search_results:
219
+ context_parts.append(f"Search results:\n{search_results[:2500]}")
220
 
221
+ # 4. Build prompt
222
+ context = "\n\n".join(context_parts) if context_parts else ""
223
 
224
+ prompt = f"""You must answer this question with ONLY the final answer.
225
+
226
+ RULES:
227
+ - Give ONLY the answer (a word, number, name, or short phrase)
228
+ - NO explanations, NO "I think", NO "Based on"
229
+ - If asked for a number, give just the number
230
+ - If asked for a name, give just the name
231
+ - If it's a list, give comma-separated items
232
+ - NEVER say "I'm unable to" or "I cannot" - give your best guess
233
+
234
+ {f"CONTEXT:{chr(10)}{context}" if context else ""}
235
+
236
+ QUESTION: {question}
237
+
238
+ YOUR ANSWER (just the answer):"""
239
+
240
  answer = self.ask(prompt)
241
+ return self.clean_answer(answer)
 
 
 
 
 
 
 
242
 
243
 
244
  # ============================================
 
264
 
265
  try:
266
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
267
+ print(f"📋 {len(questions)} questions")
268
+ print(f"⏱️ Expected time: ~{len(questions) * DELAY_BETWEEN_QUESTIONS // 60} minutes\n")
269
  except Exception as e:
270
  return f"❌ Fetch failed: {e}", None
271
 
 
280
  print(f"[{i+1}/{len(questions)}] {question[:50]}...")
281
 
282
  answer = agent(question, task_id)
283
+ print(f" → {answer[:50]}")
284
 
285
  answers.append({"task_id": task_id, "submitted_answer": answer})
286
  results.append({"#": i+1, "Q": question[:40]+"...", "A": answer[:50]})
287
 
288
+ # Delay to avoid rate limits
289
  if i < len(questions) - 1:
290
+ print(f" ⏳ Waiting {DELAY_BETWEEN_QUESTIONS}s...")
291
  time.sleep(DELAY_BETWEEN_QUESTIONS)
292
 
293
  total = time.time() - start
294
+ print(f"\n⏱️ Total: {total:.0f}s ({total/60:.1f} min)")
295
 
296
  try:
297
  result = requests.post(
 
308
  correct = result.get('correct_count', 0)
309
  total_q = result.get('total_attempted', 0)
310
 
311
+ status = f"✅ Done in {total/60:.1f} min\n\n🎯 {score}% ({correct}/{total_q})\n\n"
312
  status += "🎉 PASSED!" if score >= 30 else f"Need {30-score}% more"
313
 
314
  return status, pd.DataFrame(results)
 
321
  # ============================================
322
 
323
  with gr.Blocks() as demo:
324
+ gr.Markdown("# 🎯 GAIA Agent - Unit 4")
325
+ gr.Markdown("""
326
+ **Groq + Llama 3.3 70B** (smart model)
327
+
328
+ ⏱️ Takes ~5 minutes (15s delay between questions to avoid rate limits)
329
+ """)
330
 
331
  gr.LoginButton()
332
  run_btn = gr.Button("🚀 Run", variant="primary", size="lg")