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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -141
app.py CHANGED
@@ -9,8 +9,7 @@ from duckduckgo_search import DDGS
9
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
- TIMEOUT_PER_QUESTION = 30
13
- DELAY_BETWEEN_QUESTIONS = 6 # Longer delay to avoid rate limits
14
 
15
  # ============================================
16
  # GROQ CLIENT
@@ -23,32 +22,27 @@ def get_groq_client():
23
  return Groq(api_key=api_key)
24
 
25
  # ============================================
26
- # TOOL FUNCTIONS
27
  # ============================================
28
 
29
- def web_search(query: str, num_results: int = 3) -> str:
30
- """Search the web"""
31
  try:
32
  with DDGS() as ddgs:
33
- results = list(ddgs.text(query, max_results=num_results))
34
  if not results:
35
- return "No results found"
36
- output = []
37
- for r in results:
38
- output.append(f"- {r.get('title', '')}: {r.get('body', '')}")
39
- return "\n".join(output)
40
- except Exception as e:
41
- return f"Search error: {e}"
42
 
43
 
44
  def get_task_file(task_id: str) -> dict:
45
- """Get GAIA task file"""
46
  try:
47
  url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
48
  response = requests.get(url, timeout=15)
49
 
50
  if response.status_code == 404:
51
- return {"has_file": False, "content": ""}
52
 
53
  content_type = response.headers.get('content-type', '').lower()
54
  disposition = response.headers.get('content-disposition', '')
@@ -57,138 +51,97 @@ def get_task_file(task_id: str) -> dict:
57
  if 'filename=' in disposition:
58
  filename = disposition.split('filename=')[-1].strip('"\'')
59
 
60
- result = {"has_file": True, "filename": filename, "type": content_type}
61
-
62
- # Text/code files
63
  if 'text' in content_type or filename.endswith(('.txt', '.py', '.md', '.csv', '.json')):
64
- result["content"] = response.text[:6000]
65
- return result
66
 
67
- # Excel files
68
- if 'spreadsheet' in content_type or 'excel' in content_type or filename.endswith(('.xlsx', '.xls')):
69
  try:
70
  from io import BytesIO
71
  df = pd.read_excel(BytesIO(response.content))
72
- result["content"] = f"Excel data:\n{df.to_string()}"
73
- return result
74
  except:
75
- result["content"] = "Excel file (cannot parse)"
76
- return result
77
 
78
- # Images - can't process
79
  if 'image' in content_type:
80
- result["content"] = "[IMAGE FILE - Cannot analyze]"
81
- result["is_image"] = True
82
- return result
83
-
84
- result["content"] = f"[Binary file: {content_type}]"
85
- return result
86
 
87
- except Exception as e:
88
- return {"has_file": False, "content": ""}
 
89
 
90
 
91
  def reverse_string(text: str) -> str:
92
  return text[::-1]
93
 
94
 
95
- def is_reversed_text(text: str) -> bool:
96
- indicators = ['.rewsna', 'eht sa', 'tfel', 'drow eht']
97
- return any(ind in text.lower() for ind in indicators)
98
 
99
 
100
  # ============================================
101
- # AGENT CLASS
102
  # ============================================
103
 
104
  class BasicAgent:
105
  def __init__(self):
106
  print("Initializing Groq agent...")
107
  self.client = get_groq_client()
108
- print("✅ Agent ready!")
109
 
110
- def ask_llm(self, prompt: str) -> str:
111
- """Ask Groq - using faster model with better rate limits"""
112
- max_retries = 2
113
-
114
- for attempt in range(max_retries):
115
  try:
116
- # Use mixtral - good balance of speed and quality
117
  response = self.client.chat.completions.create(
118
- model="mixtral-8x7b-32768", # Better rate limits than llama-70b
119
  messages=[{"role": "user", "content": prompt}],
120
  temperature=0,
121
- max_tokens=150,
122
- timeout=TIMEOUT_PER_QUESTION,
123
  )
124
  return response.choices[0].message.content.strip()
125
  except Exception as e:
126
  if "rate" in str(e).lower() or "429" in str(e):
127
- wait = (attempt + 1) * 10
128
- print(f" ⏳ Rate limited, waiting {wait}s...")
129
  time.sleep(wait)
130
  else:
131
  return f"Error: {e}"
132
-
133
  return "unknown"
134
 
135
- def clean_answer(self, answer: str) -> str:
136
- # Remove prefixes
137
- for prefix in ["Answer:", "The answer is:", "Final answer:", "A:", "The answer is", "**"]:
138
- if answer.lower().startswith(prefix.lower()):
139
- answer = answer[len(prefix):].strip()
 
 
 
 
 
 
 
140
 
141
- # Remove quotes and trailing punctuation
142
- answer = answer.strip('"\'')
143
- if answer.endswith('.') and len(answer.split()) <= 3:
144
- answer = answer[:-1]
 
 
 
145
 
146
- # Remove markdown
147
- answer = answer.replace("**", "").strip()
 
 
 
 
 
 
 
148
 
149
  return answer
150
-
151
- def __call__(self, question: str, task_id: str = None) -> str:
152
- try:
153
- context = ""
154
-
155
- # Check for reversed text
156
- if is_reversed_text(question):
157
- question = reverse_string(question)
158
- context += f"[Decoded reversed text]\n"
159
-
160
- # Check for file
161
- if task_id:
162
- file_info = get_task_file(task_id)
163
- if file_info.get("has_file") and file_info.get("content"):
164
- context += f"FILE:\n{file_info['content']}\n\n"
165
-
166
- # Web search for questions that need it
167
- needs_search = any(kw in question.lower() for kw in [
168
- "who ", "what ", "when ", "where ", "how many", "how much",
169
- "album", "actor", "movie", "wikipedia", "surname", "athlete",
170
- "pitcher", "country", "competition", "nominated"
171
- ])
172
-
173
- # Don't search if we have file content
174
- if context and "FILE:" in context:
175
- needs_search = False
176
-
177
- if needs_search:
178
- search_results = web_search(question[:100], 3)
179
- if "No results" not in search_results:
180
- context += f"SEARCH RESULTS:\n{search_results}\n\n"
181
-
182
- prompt = f"""{context}Question: {question}
183
-
184
- Give ONLY the final answer. No explanation. Be precise."""
185
-
186
- answer = self.ask_llm(prompt)
187
- return self.clean_answer(answer)
188
-
189
- except Exception as e:
190
- print(f" Error: {e}")
191
- return "unknown"
192
 
193
 
194
  # ============================================
@@ -202,71 +155,63 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
202
  username = profile.username
203
  space_id = os.getenv("SPACE_ID")
204
 
205
- print(f"\n{'='*50}")
206
- print(f"User: {username}")
207
-
208
  if not os.environ.get("GROQ_API_KEY"):
209
- return "❌ Add GROQ_API_KEY to Space secrets!", None
210
-
211
- print(" GROQ_API_KEY found")
212
- print(f"{'='*50}\n")
213
 
214
  try:
215
  agent = BasicAgent()
216
  except Exception as e:
217
- return f"❌ Agent init failed: {e}", None
218
 
219
  try:
220
  questions = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
221
  print(f"���� {len(questions)} questions\n")
222
  except Exception as e:
223
- return f"❌ Failed to fetch questions: {e}", None
224
 
225
  results = []
226
  answers = []
227
- start_time = time.time()
228
 
229
  for i, q in enumerate(questions):
230
  task_id = q.get("task_id")
231
  question = q.get("question", "")
232
 
233
- print(f"[{i+1}/{len(questions)}] {question[:60]}...")
234
 
235
- try:
236
- answer = agent(question, task_id)
237
- print(f" → {answer[:50]}")
238
- except Exception as e:
239
- answer = "unknown"
240
- print(f" ✗ {e}")
241
 
242
  answers.append({"task_id": task_id, "submitted_answer": answer})
243
- results.append({"#": i+1, "Question": question[:50]+"...", "Answer": answer[:60]})
244
 
245
- # Delay between questions
246
  if i < len(questions) - 1:
247
  time.sleep(DELAY_BETWEEN_QUESTIONS)
248
 
249
- total_time = time.time() - start_time
250
- print(f"\n⏱️ {total_time:.0f}s total")
251
 
252
- # Submit
253
  try:
254
- submission = {
255
- "username": username,
256
- "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
257
- "answers": answers
258
- }
259
- result = requests.post(f"{DEFAULT_API_URL}/submit", json=submission, timeout=60).json()
 
 
 
260
 
261
  score = result.get('score', 0)
262
  correct = result.get('correct_count', 0)
263
- total = result.get('total_attempted', 0)
264
 
265
- status = f"✅ Done in {total_time:.0f}s\n\n🎯 Score: {score}% ({correct}/{total})\n\n"
266
  status += "🎉 PASSED!" if score >= 30 else f"Need {30-score}% more"
267
 
268
  return status, pd.DataFrame(results)
269
-
270
  except Exception as e:
271
  return f"❌ Submit failed: {e}", pd.DataFrame(results)
272
 
@@ -276,17 +221,16 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
276
  # ============================================
277
 
278
  with gr.Blocks() as demo:
279
- gr.Markdown("# 🎯 GAIA Agent - Unit 4")
280
- gr.Markdown("**Groq + Mixtral 8x7B** (better rate limits)")
281
-
282
  gr.LoginButton()
283
  run_btn = gr.Button("🚀 Run", variant="primary", size="lg")
284
  status = gr.Textbox(label="Status", lines=5)
285
  table = gr.DataFrame(label="Results")
286
-
287
  run_btn.click(run_and_submit_all, outputs=[status, table])
288
 
289
  if __name__ == "__main__":
290
- print("🎯 GAIA Agent Starting...")
291
  print(f"GROQ_API_KEY: {'✅' if os.environ.get('GROQ_API_KEY') else '❌'}")
292
  demo.launch()
 
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
 
22
  return Groq(api_key=api_key)
23
 
24
  # ============================================
25
+ # TOOLS
26
  # ============================================
27
 
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:
 
40
  try:
41
  url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
42
  response = requests.get(url, timeout=15)
43
 
44
  if response.status_code == 404:
45
+ return {"has_file": False}
46
 
47
  content_type = response.headers.get('content-type', '').lower()
48
  disposition = response.headers.get('content-disposition', '')
 
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:
77
  return text[::-1]
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
  # ============================================
85
+ # AGENT
86
  # ============================================
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
  # ============================================
 
155
  username = profile.username
156
  space_id = os.getenv("SPACE_ID")
157
 
 
 
 
158
  if not os.environ.get("GROQ_API_KEY"):
159
+ return "❌ Add GROQ_API_KEY to secrets!", None
160
+
161
+ print(f"\n{'='*40}\nUser: {username}\n{'='*40}")
 
162
 
163
  try:
164
  agent = BasicAgent()
165
  except Exception as e:
166
+ return f"❌ Init failed: {e}", 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
 
174
  results = []
175
  answers = []
176
+ start = time.time()
177
 
178
  for i, q in enumerate(questions):
179
  task_id = q.get("task_id")
180
  question = q.get("question", "")
181
 
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(
198
+ f"{DEFAULT_API_URL}/submit",
199
+ json={
200
+ "username": username,
201
+ "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
202
+ "answers": answers
203
+ },
204
+ timeout=60
205
+ ).json()
206
 
207
  score = result.get('score', 0)
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)
 
215
  except Exception as e:
216
  return f"❌ Submit failed: {e}", pd.DataFrame(results)
217
 
 
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")
229
  status = gr.Textbox(label="Status", lines=5)
230
  table = gr.DataFrame(label="Results")
231
+
232
  run_btn.click(run_and_submit_all, outputs=[status, table])
233
 
234
  if __name__ == "__main__":
 
235
  print(f"GROQ_API_KEY: {'✅' if os.environ.get('GROQ_API_KEY') else '❌'}")
236
  demo.launch()