Raj989898 commited on
Commit
4790a7a
·
verified ·
1 Parent(s): f0076fb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -80
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import gradio as gr
3
  import requests
4
  import pandas as pd
@@ -8,24 +9,63 @@ import sys
8
 
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  def download_task_file(task_id):
12
  url = f"{DEFAULT_API_URL}/files/{task_id}"
13
  try:
14
  resp = requests.get(url, timeout=30)
15
- if resp.status_code != 200:
 
 
16
  return None, None
17
  cd = resp.headers.get("content-disposition", "")
 
18
  fname = "task_file"
19
  if "filename=" in cd:
20
  fname = cd.split("filename=")[-1].strip().strip('"').strip("'")
21
- ext = os.path.splitext(fname)[-1] or ".bin"
22
- tmp = tempfile.NamedTemporaryFile(delete=False, suffix=ext)
 
 
 
 
 
 
 
23
  tmp.write(resp.content)
24
  tmp.close()
25
- print(f"Downloaded: {fname} ({len(resp.content)} bytes)")
26
  return tmp.name, fname
27
  except Exception as e:
28
- print(f"Download error: {e}")
29
  return None, None
30
 
31
  def read_file_contents(local_path, fname):
@@ -38,14 +78,15 @@ def read_file_contents(local_path, fname):
38
  df = pd.read_csv(local_path)
39
  return f"CSV shape={df.shape}\nColumns={list(df.columns)}\n\n{df.to_string()}"
40
  elif ext in (".py", ".txt", ".md", ".json"):
41
- with open(local_path) as f:
42
  return f.read()
43
  else:
44
  try:
45
- with open(local_path) as f:
46
- return f.read()
47
- except:
48
- return f"Binary: {fname}"
 
49
  except Exception as e:
50
  return f"Error: {e}"
51
 
@@ -54,8 +95,8 @@ def run_python_file(local_path):
54
  r = subprocess.run([sys.executable, local_path],
55
  capture_output=True, text=True, timeout=15)
56
  out = (r.stdout + r.stderr).strip()
57
- print(f"Python out: '{out[:200]}'")
58
- return out or "No output."
59
  except Exception as e:
60
  return f"Error: {e}"
61
 
@@ -67,20 +108,6 @@ def clean_answer(text):
67
  text = text[len(p):].strip()
68
  return text.split("\n")[0].strip().strip('"').strip("'").strip("*").strip()
69
 
70
- def call_groq(api_key, prompt, system="", max_tokens=128):
71
- url = "https://api.groq.com/openai/v1/chat/completions"
72
- headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
73
- msgs = []
74
- if system:
75
- msgs.append({"role": "system", "content": system})
76
- msgs.append({"role": "user", "content": prompt})
77
- body = {"model": "llama-3.3-70b-versatile", "messages": msgs,
78
- "temperature": 0.0, "max_tokens": max_tokens}
79
- resp = requests.post(url, headers=headers, json=body, timeout=60)
80
- if resp.status_code != 200:
81
- raise Exception(f"Groq {resp.status_code}: {resp.text[:200]}")
82
- return resp.json()["choices"][0]["message"]["content"].strip()
83
-
84
  def search_web(query, max_results=6):
85
  try:
86
  from duckduckgo_search import DDGS
@@ -99,7 +126,7 @@ def test_api():
99
  if not key:
100
  return "❌ GROQ_API_KEY not set!"
101
  try:
102
- ans = call_groq(key, "What is 2+2?", "Reply with only the number.")
103
  return f"✅ Groq working! Test: '{ans}'"
104
  except Exception as e:
105
  return f"❌ {e}"
@@ -112,31 +139,26 @@ class BasicAgent:
112
  def __init__(self):
113
  self.key = os.getenv("GROQ_API_KEY", "")
114
  if not self.key:
115
- raise RuntimeError("GROQ_API_KEY not set! Add in Space Settings → Secrets.")
116
  print(f"Agent ready. Key: {self.key[:8]}...")
117
 
118
  def ask(self, prompt, max_tokens=128):
119
- return clean_answer(call_groq(self.key, prompt, SYSTEM, max_tokens))
120
-
121
- def __call__(self, question: str) -> str:
122
- task_id = ""
123
- if question.startswith("[TASK_ID:"):
124
- end = question.index("]")
125
- task_id = question[9:end]
126
- question = question[end+1:].strip()
127
 
 
128
  print(f"\n{'='*50}\nTask: {task_id}\nQ: {question[:200]}")
129
 
130
  # Handle reversed text
131
  if "rewsna" in question or "dnatsrednu" in question:
132
  question = question[::-1]
133
- print(f"Reversed: {question}")
134
 
135
  file_ctx = ""
136
  is_py = False
137
 
138
  # Download file
139
  if task_id:
 
140
  lp, fn = download_task_file(task_id)
141
  if lp and fn:
142
  ext = os.path.splitext(fn)[-1].lower()
@@ -149,67 +171,69 @@ class BasicAgent:
149
  contents = read_file_contents(lp, fn)
150
  file_ctx = f"\n[File: {fn}]\n{contents[:6000]}\n"
151
  elif ext in (".png", ".jpg", ".jpeg"):
152
- file_ctx = f"\n[Image: {fn} attached — answer from your knowledge.]\n"
153
  else:
154
  contents = read_file_contents(lp, fn)
155
  file_ctx = f"\n[File: {fn}]\n{contents[:4000]}\n"
 
 
156
 
157
- # Web search (skip for python — output is the answer)
158
  search_ctx = ""
159
  if not is_py:
160
- r1 = search_web(question[:200])
161
- search_ctx = f"\n[Search results]\n{r1[:3500]}\n"
 
162
 
163
- # Per-question format instructions
164
- q_lower = question.lower()
165
  fmt = ""
166
- if "studio album" in q_lower:
167
- fmt = "\nIMPORTANT: Count ONLY solo studio albums (not live, compilation, or collaborative albums). Answer with a single integer."
168
- elif "first name" in q_lower:
169
- fmt = "\nIMPORTANT: Reply with the first name only."
170
- elif "surname" in q_lower or "last name" in q_lower:
171
- fmt = "\nIMPORTANT: Reply with the surname/last name only."
172
- elif "how many" in q_lower and ("at bat" in q_lower or "walk" in q_lower):
173
- fmt = "\nIMPORTANT: Reply with a single integer only."
174
- elif "country" in q_lower and "olympic" in q_lower:
175
- fmt = "\nIMPORTANT: Reply with the country name only. If tied, give alphabetically first country."
176
- elif "excel" in q_lower or ("total" in q_lower and "sale" in q_lower):
177
- fmt = "\nIMPORTANT: Plain number only. No $ sign, no commas (e.g. 12345.67)."
178
- elif "chess" in q_lower:
179
- fmt = "\nIMPORTANT: Reply with chess move in standard notation only (e.g. Qd8)."
180
- elif "pitcher" in q_lower and "number" in q_lower:
181
- fmt = "\nIMPORTANT: Reply with two last names comma-separated, in jersey number order (lower number first)."
182
- elif "wikipedia" in q_lower and "nominat" in q_lower:
183
- fmt = "\nIMPORTANT: Reply with the Wikipedia username only."
184
- elif ("grocery" in q_lower or "shopping" in q_lower) and "list" in q_lower:
185
- fmt = "\nIMPORTANT: Reply with comma-separated items in alphabetical order, all lowercase, using the scientific/botanical names the botany professor would use."
186
- elif "youtube" in q_lower or "video" in q_lower:
187
- fmt = "\nIMPORTANT: Reply with the exact short answer requested — a number, name, or brief quote."
188
- elif "grant" in q_lower or "award" in q_lower or "number" in q_lower:
189
- fmt = "\nIMPORTANT: Reply with the exact identifier/number only."
 
 
190
 
191
  prompt = (
192
  f"Question: {question}"
193
  f"{file_ctx}"
194
  f"{search_ctx}"
195
  f"{fmt}"
196
- "\n\nGive ONLY the final answer. Nothing else."
197
  )
198
 
199
  try:
200
  answer = self.ask(prompt, max_tokens=64)
201
- # If still too long, shorten
202
  if len(answer.split()) > 20:
203
- answer = clean_answer(call_groq(
204
  self.key,
205
- f"Extract only the shortest final answer (name/number/phrase) from:\n{answer}",
206
- "Reply with only the bare answer.",
207
- max_tokens=32
208
- ))
209
- print(f"Final: '{answer}'")
210
  return answer
211
  except Exception as e:
212
- print(f"Error: {e}")
213
  return ""
214
 
215
  def run_and_submit_all(profile: gr.OAuthProfile | None):
@@ -223,7 +247,6 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
223
  return f"❌ {e}", None
224
 
225
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
226
-
227
  try:
228
  resp = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
229
  resp.raise_for_status()
@@ -233,15 +256,15 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
233
  return f"Error: {e}", None
234
 
235
  results_log, answers_payload = [], []
236
-
237
  for i, item in enumerate(questions_data):
238
- task_id = item.get("task_id")
239
  question_text = item.get("question")
240
  if not task_id or question_text is None:
241
  continue
242
  print(f"\n[{i+1}/{len(questions_data)}]")
243
  try:
244
- ans = agent(f"[TASK_ID:{task_id}] {question_text}")
 
245
  except Exception as e:
246
  ans = ""
247
  answers_payload.append({"task_id": task_id, "submitted_answer": ans})
 
1
  import os
2
+ import time
3
  import gradio as gr
4
  import requests
5
  import pandas as pd
 
9
 
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
11
 
12
+ # Track API calls for rate limiting
13
+ _last_call_time = 0
14
+
15
+ def rate_limited_groq(api_key, prompt, system="", max_tokens=128):
16
+ """Call Groq with rate limiting — max 25 req/min to stay safe."""
17
+ global _last_call_time
18
+ # Ensure at least 2.5 seconds between calls (= 24/min, safely under 30 limit)
19
+ elapsed = time.time() - _last_call_time
20
+ if elapsed < 2.5:
21
+ time.sleep(2.5 - elapsed)
22
+ _last_call_time = time.time()
23
+
24
+ url = "https://api.groq.com/openai/v1/chat/completions"
25
+ headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
26
+ msgs = []
27
+ if system:
28
+ msgs.append({"role": "system", "content": system})
29
+ msgs.append({"role": "user", "content": prompt})
30
+ body = {"model": "llama-3.3-70b-versatile", "messages": msgs,
31
+ "temperature": 0.0, "max_tokens": max_tokens}
32
+ resp = requests.post(url, headers=headers, json=body, timeout=60)
33
+ if resp.status_code == 429:
34
+ print("Rate limited! Waiting 60s...")
35
+ time.sleep(60)
36
+ resp = requests.post(url, headers=headers, json=body, timeout=60)
37
+ if resp.status_code != 200:
38
+ raise Exception(f"Groq {resp.status_code}: {resp.text[:200]}")
39
+ return resp.json()["choices"][0]["message"]["content"].strip()
40
+
41
  def download_task_file(task_id):
42
  url = f"{DEFAULT_API_URL}/files/{task_id}"
43
  try:
44
  resp = requests.get(url, timeout=30)
45
+ print(f" File request: HTTP {resp.status_code}, size={len(resp.content)}, "
46
+ f"content-type={resp.headers.get('content-type','?')}")
47
+ if resp.status_code != 200 or len(resp.content) == 0:
48
  return None, None
49
  cd = resp.headers.get("content-disposition", "")
50
+ ct = resp.headers.get("content-type", "")
51
  fname = "task_file"
52
  if "filename=" in cd:
53
  fname = cd.split("filename=")[-1].strip().strip('"').strip("'")
54
+ ext = os.path.splitext(fname)[-1]
55
+ if not ext:
56
+ if "python" in ct: ext = ".py"
57
+ elif "excel" in ct or "spreadsheet" in ct: ext = ".xlsx"
58
+ elif "csv" in ct: ext = ".csv"
59
+ elif "image" in ct: ext = ".png"
60
+ else: ext = ".bin"
61
+ fname += ext
62
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=ext, prefix="gaia_")
63
  tmp.write(resp.content)
64
  tmp.close()
65
+ print(f" Saved: {fname} -> {tmp.name}")
66
  return tmp.name, fname
67
  except Exception as e:
68
+ print(f" Download error: {e}")
69
  return None, None
70
 
71
  def read_file_contents(local_path, fname):
 
78
  df = pd.read_csv(local_path)
79
  return f"CSV shape={df.shape}\nColumns={list(df.columns)}\n\n{df.to_string()}"
80
  elif ext in (".py", ".txt", ".md", ".json"):
81
+ with open(local_path, "r", errors="replace") as f:
82
  return f.read()
83
  else:
84
  try:
85
+ with open(local_path, "r", errors="replace") as f:
86
+ c = f.read()
87
+ if c.strip(): return c
88
+ except: pass
89
+ return f"Binary: {fname}"
90
  except Exception as e:
91
  return f"Error: {e}"
92
 
 
95
  r = subprocess.run([sys.executable, local_path],
96
  capture_output=True, text=True, timeout=15)
97
  out = (r.stdout + r.stderr).strip()
98
+ print(f" Python output: '{out[:200]}'")
99
+ return out if out else "No output."
100
  except Exception as e:
101
  return f"Error: {e}"
102
 
 
108
  text = text[len(p):].strip()
109
  return text.split("\n")[0].strip().strip('"').strip("'").strip("*").strip()
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  def search_web(query, max_results=6):
112
  try:
113
  from duckduckgo_search import DDGS
 
126
  if not key:
127
  return "❌ GROQ_API_KEY not set!"
128
  try:
129
+ ans = rate_limited_groq(key, "What is 2+2?", "Reply with only the number.")
130
  return f"✅ Groq working! Test: '{ans}'"
131
  except Exception as e:
132
  return f"❌ {e}"
 
139
  def __init__(self):
140
  self.key = os.getenv("GROQ_API_KEY", "")
141
  if not self.key:
142
+ raise RuntimeError("GROQ_API_KEY not set!")
143
  print(f"Agent ready. Key: {self.key[:8]}...")
144
 
145
  def ask(self, prompt, max_tokens=128):
146
+ return clean_answer(rate_limited_groq(self.key, prompt, SYSTEM, max_tokens))
 
 
 
 
 
 
 
147
 
148
+ def __call__(self, question: str, task_id: str = "") -> str:
149
  print(f"\n{'='*50}\nTask: {task_id}\nQ: {question[:200]}")
150
 
151
  # Handle reversed text
152
  if "rewsna" in question or "dnatsrednu" in question:
153
  question = question[::-1]
154
+ print(f" Reversed: {question}")
155
 
156
  file_ctx = ""
157
  is_py = False
158
 
159
  # Download file
160
  if task_id:
161
+ print(f" Attempting file download for task_id={task_id}")
162
  lp, fn = download_task_file(task_id)
163
  if lp and fn:
164
  ext = os.path.splitext(fn)[-1].lower()
 
171
  contents = read_file_contents(lp, fn)
172
  file_ctx = f"\n[File: {fn}]\n{contents[:6000]}\n"
173
  elif ext in (".png", ".jpg", ".jpeg"):
174
+ file_ctx = f"\n[Image: {fn} attached.]\n"
175
  else:
176
  contents = read_file_contents(lp, fn)
177
  file_ctx = f"\n[File: {fn}]\n{contents[:4000]}\n"
178
+ else:
179
+ print(f" No file found for this task.")
180
 
181
+ # Web search
182
  search_ctx = ""
183
  if not is_py:
184
+ results = search_web(question[:200])
185
+ if results and "error" not in results.lower():
186
+ search_ctx = f"\n[Search]\n{results[:3500]}\n"
187
 
188
+ # Format hints
189
+ q = question.lower()
190
  fmt = ""
191
+ if "studio album" in q:
192
+ fmt = "\nCount only SOLO studio albums (exclude collaborative albums). Single integer answer."
193
+ elif "first name" in q:
194
+ fmt = "\nFirst name only."
195
+ elif "surname" in q or "last name" in q:
196
+ fmt = "\nSurname only."
197
+ elif "at bat" in q or "at-bat" in q:
198
+ fmt = "\nSingle integer only."
199
+ elif "how many" in q:
200
+ fmt = "\nSingle integer only."
201
+ elif "ioc" in q or ("country" in q and "olympic" in q):
202
+ fmt = "\nIOC country code only (3 letters, e.g. USA, GBR). If tied, alphabetically first."
203
+ elif "excel" in q or ("sale" in q and "food" in q):
204
+ fmt = "\nUSD with two decimal places (e.g. 89.50). No $ sign."
205
+ elif "chess" in q:
206
+ fmt = "\nChess move in algebraic notation only."
207
+ elif "pitcher" in q and "number" in q:
208
+ fmt = "\nTwo last names, comma-separated, pitcher with lower jersey number first."
209
+ elif "wikipedia" in q and "nominat" in q:
210
+ fmt = "\nWikipedia username only."
211
+ elif "grocery" in q or ("shopping" in q and "list" in q):
212
+ fmt = "\nComma-separated list, alphabetical order."
213
+ elif "youtube" in q or "video" in q:
214
+ fmt = "\nExact short answer only quote, number, or name."
215
+ elif "grant" in q or "award number" in q:
216
+ fmt = "\nExact identifier only."
217
 
218
  prompt = (
219
  f"Question: {question}"
220
  f"{file_ctx}"
221
  f"{search_ctx}"
222
  f"{fmt}"
223
+ "\n\nGive ONLY the final answer."
224
  )
225
 
226
  try:
227
  answer = self.ask(prompt, max_tokens=64)
 
228
  if len(answer.split()) > 20:
229
+ answer = clean_answer(rate_limited_groq(
230
  self.key,
231
+ f"Extract only the shortest final answer from:\n{answer}",
232
+ "Reply with only the bare answer.", max_tokens=32))
233
+ print(f" Final: '{answer}'")
 
 
234
  return answer
235
  except Exception as e:
236
+ print(f" Error: {e}")
237
  return ""
238
 
239
  def run_and_submit_all(profile: gr.OAuthProfile | None):
 
247
  return f"❌ {e}", None
248
 
249
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
 
250
  try:
251
  resp = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15)
252
  resp.raise_for_status()
 
256
  return f"Error: {e}", None
257
 
258
  results_log, answers_payload = [], []
 
259
  for i, item in enumerate(questions_data):
260
+ task_id = item.get("task_id", "")
261
  question_text = item.get("question")
262
  if not task_id or question_text is None:
263
  continue
264
  print(f"\n[{i+1}/{len(questions_data)}]")
265
  try:
266
+ # Pass task_id directly — no injection needed
267
+ ans = agent(question_text, task_id=task_id)
268
  except Exception as e:
269
  ans = ""
270
  answers_payload.append({"task_id": task_id, "submitted_answer": ans})