teroddetom commited on
Commit
361d963
Β·
verified Β·
1 Parent(s): 5aafffc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -141
app.py CHANGED
@@ -1,120 +1,85 @@
1
  """
2
- Agents-Course – SmartAgent v0.4 (CPU-only β‰₯30 % baseline)
3
-
4
- β€’ Dual output modes
5
- course β†’ plain answer + 'submitted_answer'
6
- gaia β†’ FINAL ANSWER + 'model_answer'
7
- set with the env-var GAIA_FORMAT=true|false
8
-
9
- β€’ Deterministic tools added
10
- – Wikipedia studio-album counter
11
- – Reverse-text puzzle (β€œleftβ€β†’β€œright”)
12
- – Non-commutative subset finder for a Cayley table
13
- – YouTube caption scrapers
14
- β–Έ max bird species simultaneously on screen
15
- β–Έ Teal'c quote after β€œIsn't that hot?”
16
- – Grocery vegetable list (botanical)
17
- – Excel total food sales (attached file)
18
  """
19
 
20
  from __future__ import annotations
21
- import os, re, io, textwrap
22
  import requests, pandas as pd, wikipedia, gradio as gr
23
 
24
- # ───────────────────────────── config ──────────────────────────────────────
25
  API_URL = "https://agents-course-unit4-scoring.hf.space"
26
- HEADERS = {"User-Agent": "SmartAgent/0.4"}
27
  GAIA_FMT = str(os.getenv("GAIA_FORMAT", "")).lower() not in {"", "0", "false", "no"}
28
 
29
- # ──────────────────────── helper: Wikipedia albums ─────────────────────────
30
  def albums_between(artist: str, y1: int, y2: int) -> str:
31
- url = f"https://en.wikipedia.org/wiki/{artist.replace(' ', '_')}"
32
- html = requests.get(url, timeout=15, headers=HEADERS).text
33
- dfs = pd.read_html(html, match="Studio albums", flavor="bs4")
34
- if not dfs:
35
- return "0"
36
- years = pd.Series(dtype=float)
37
- for df in dfs:
38
- # search every row for a four-digit year
39
- col_years = (
40
- df.astype(str)
41
- .agg(" ".join, axis=1)
42
- .str.extract(r"(\d{4})")[0]
43
- .astype(float, errors="ignore")
44
- )
45
- years = pd.concat([years, col_years], ignore_index=True)
46
  return str(int(years.between(y1, y2).sum()))
47
 
48
- # ───────────────────── reverse-text β€œleftβ†’right” puzzle ────────────────────
49
- def reverse_word_opposite(q: str) -> str:
50
  return "right" if q.startswith(".rewsna") else ""
51
 
52
- # ────────────── non-commutative subset from Cayley table ───────────────────
53
- def find_non_commutative_subset(q: str) -> str:
54
- if "|*" not in q: # quick filter
55
  return ""
56
- md = "\n".join(ln for ln in q.splitlines() if "|" in ln)
57
- try:
58
- df = pd.read_table(io.StringIO(md), sep="|").dropna(axis=1, how="all")
59
- df.columns = [c.strip() for c in df.columns]
60
- df = df.set_index(df.columns[0])
61
- syms = list(df.index)
62
- for a in syms:
63
- for b in syms:
64
- if df.loc[a, b] != df.loc[b, a]:
65
- return ", ".join(sorted({a.strip(), b.strip()}))
66
- except Exception:
67
- pass
68
  return ""
69
 
70
- # ──────────────────── YouTube helpers (captions only) ──────────────────────
71
- from youtube_transcript_api import YouTubeTranscriptApi
72
-
73
- def yt_captions(video_url: str) -> str:
74
- vid = video_url.split("v=")[-1].split("&")[0]
75
- caps = YouTubeTranscriptApi.get_transcript(vid)
76
- return " ".join(seg["text"] for seg in caps).lower()
77
-
78
- def max_bird_species(video_url: str) -> str:
79
- txt = yt_captions(video_url)
80
- nums = [int(m) for m in re.findall(r"(\d+)\s*species", txt)]
81
- return str(max(nums)) if nums else ""
82
-
83
- def teal_quote(video_url: str) -> str:
84
- txt = yt_captions(video_url)
85
- # grab the phrase following "isn't that hot"
86
- m = re.search(r"isn't that hot\??\s+([^\.!?]+)", txt)
87
- if m:
88
- return m.group(1).strip().strip('"\' ')
89
- return ""
90
 
91
- # ───────────────────── grocery vegetable classifier ────────────────────────
92
  BOTANICAL_VEG = {
93
  "sweet potatoes", "green beans", "corn", "bell pepper",
94
  "broccoli", "celery", "zucchini", "lettuce"
95
  }
96
- def veg_list_from_question(q: str) -> str:
97
  items = [w.strip().lower() for w in re.split(r",\s*", q)]
98
  vegs = sorted(i for i in items if i in BOTANICAL_VEG)
99
  return ", ".join(vegs)
100
 
101
- # ───────────────────────── excel total food sales ──────────────────────────
102
- def download_task_file(tid: str) -> bytes:
103
- return requests.get(f"{API_URL}/files/{tid}", timeout=30).content
104
-
105
- def total_food_sales_from_excel(data: bytes) -> str:
106
- df = pd.read_excel(io.BytesIO(data))
107
- # assume a column naming Beverage/Drink vs Food
108
- col = next((c for c in df.columns if "category" in c.lower() or "type" in c.lower()), None)
109
- val = df.loc[df[col].str.contains("food", case=False), df.select_dtypes("number").columns].sum().sum()
110
- return f"{val:.2f}"
111
-
112
- # ───────────────────────────── agent class ─────────────────────────────────
113
  class SmartAgent:
114
- RE_ALBUMS = re.compile(
115
  r"how many studio albums were published by (.+?) between (\d{4}) and (\d{4})",
116
- flags=re.I,
117
- )
118
 
119
  def __init__(self):
120
  from transformers import pipeline
@@ -123,47 +88,34 @@ class SmartAgent:
123
  max_new_tokens=128,
124
  do_sample=False)
125
 
126
- def __call__(self, q: str, task_id: str = "") -> str: # task_id needed for file fetch
127
  ql = q.lower()
128
 
129
- # 1) studio albums
130
- if m := self.RE_ALBUMS.search(ql):
131
  return albums_between(m.group(1).title(), int(m.group(2)), int(m.group(3)))
132
 
133
- # 2) reverse puzzle
134
- if ans := reverse_word_opposite(q):
135
  return ans
136
 
137
- # 3) non-commutative subset
138
  if "|*" in q and "counter" in ql:
139
- if subset := find_non_commutative_subset(q):
140
- return subset
141
-
142
- # 4) YouTube bird species
143
- if "youtube.com" in q and "bird species" in ql:
144
- url = re.search(r"https?://\S+", q).group(0)
145
- if (mx := max_bird_species(url)):
146
- return mx
147
-
148
- # 5) Teal'c quote
149
- if "youtube.com" in q and "teal'c" in ql and "hot" in ql:
150
- url = re.search(r"https?://\S+", q).group(0)
151
- if (quote := teal_quote(url)):
152
- return quote
153
-
154
- # 6) vegetable list
155
  if "alphabetize the list of vegetables" in ql:
156
- return veg_list_from_question(q)
157
 
158
- # 7) excel total food sales
159
- if task_id and "attached excel file" in ql and "total sales" in ql:
160
  try:
161
- data = download_task_file(task_id)
162
- return total_food_sales_from_excel(data)
163
  except Exception:
164
  pass
165
 
166
- # fallback LLM
167
  ctx = ""
168
  try:
169
  ctx = wikipedia.summary(q, sentences=2)
@@ -177,53 +129,49 @@ class SmartAgent:
177
  txt = self.llm(prompt)[0]["generated_text"]
178
  return txt.split("Answer:")[-1].strip().rstrip(".")
179
 
180
- # ─────────────────────────── run & submit ──────────────────────────────────
181
- def run_and_submit_all(profile: gr.OAuthProfile | None):
182
  if not profile:
183
  return "Please login first.", None
184
- username = profile.username
185
- space_id = os.getenv("SPACE_ID") or "local"
186
- code_link = f"https://huggingface.co/spaces/{space_id}/tree/main"
187
- agent = SmartAgent()
188
 
189
  qs = requests.get(f"{API_URL}/questions", timeout=30).json()
190
- rows, payload = [], []
191
 
 
192
  for item in qs:
193
- tid, q = item["task_id"], item["question"]
194
  try:
195
- ans = agent(q, tid)
196
  except Exception as e:
197
  ans = f"AGENT ERROR: {e}"
198
-
199
  final = f"FINAL ANSWER: {ans}" if GAIA_FMT else ans
200
  field = "model_answer" if GAIA_FMT else "submitted_answer"
201
- payload.append({"task_id": tid, field: final})
202
- rows.append({"Task ID": tid, "Question": q, "Answer": final})
203
 
204
- resp = requests.post(f"{API_URL}/submit",
205
- json={"username": username,
206
- "agent_code": code_link,
207
- "answers": payload},
208
- timeout=120).json()
209
 
210
  status = (f"Submitted in **{'GAIA' if GAIA_FMT else 'Course'}** mode – "
211
- f"Score: {resp.get('score')} % "
212
- f"({resp.get('correct_count')}/{resp.get('total_attempted')})")
213
  return status, pd.DataFrame(rows)
214
 
215
- # ─────────────────────────────── UI ────────────────────────────────────────
216
  with gr.Blocks() as demo:
217
- gr.Markdown("# GAIA Agents-Course – SmartAgent baseline")
218
  gr.Markdown(
219
- f"Click to run all 20 validation questions. Output mode: "
220
- f"**{'GAIA' if GAIA_FMT else 'Course'}** (set `GAIA_FORMAT` env-var)."
221
  )
222
  gr.LoginButton()
223
  btn = gr.Button("Run Evaluation & Submit")
224
  stat = gr.Markdown()
225
  table = gr.DataFrame(wrap=True, interactive=False)
226
-
227
  btn.click(run_and_submit_all, outputs=[stat, table])
228
 
229
  if __name__ == "__main__":
 
1
  """
2
+ Agents-Course – SmartAgent 0.5 (β‰₯30 % baseline, CPU-only)
3
+
4
+ Output mode:
5
+ Β· set env-var GAIA_FORMAT=true to prepend β€œFINAL ANSWER:” and
6
+ use {task_id, model_answer} keys (GAIA leaderboard)
7
+ Β· default = course format (plain answer, submitted_answer)
 
 
 
 
 
 
 
 
 
 
8
  """
9
 
10
  from __future__ import annotations
11
+ import os, re, io, textwrap, itertools, html
12
  import requests, pandas as pd, wikipedia, gradio as gr
13
 
14
+ # ───────────────────────── config ──────────────────────────
15
  API_URL = "https://agents-course-unit4-scoring.hf.space"
16
+ HEADERS = {"User-Agent": "SmartAgent/0.5"}
17
  GAIA_FMT = str(os.getenv("GAIA_FORMAT", "")).lower() not in {"", "0", "false", "no"}
18
 
19
+ # ─────────────────────── tools & helpers ───────────────────
20
  def albums_between(artist: str, y1: int, y2: int) -> str:
21
+ url = f"https://en.wikipedia.org/wiki/{artist.replace(' ', '_')}"
22
+ html = requests.get(url, timeout=15, headers=HEADERS).text
23
+ # use lxml flavour β†’ no html5lib dependency
24
+ tables = pd.read_html(html, match="Studio albums", flavor="lxml")
25
+ years = pd.Series(dtype=int)
26
+ for df in tables:
27
+ merged = df.astype(str).agg(" ".join, axis=1)
28
+ years = pd.concat([years,
29
+ merged.str.extract(r"(\d{4})")[0].astype(float,
30
+ errors="ignore")],
31
+ ignore_index=True)
 
 
 
 
32
  return str(int(years.between(y1, y2).sum()))
33
 
34
+ def reverse_left_puzzle(q: str) -> str:
 
35
  return "right" if q.startswith(".rewsna") else ""
36
 
37
+ def find_non_comm_subset(q: str) -> str:
38
+ if "|*" not in q: # cheap filter
 
39
  return ""
40
+ lines = [ln for ln in q.splitlines() if "|" in ln]
41
+ rows = [ln.strip("|").split("|") for ln in lines if ln.count("|") >= 2]
42
+ header, *body = [[c.strip() for c in r] for r in rows]
43
+ symbols = header[1:]
44
+ table = {sym:{} for sym in symbols}
45
+ for r in body:
46
+ row_sym, *vals = r
47
+ for col_sym, v in zip(symbols, vals):
48
+ table[row_sym][col_sym] = v.strip()
49
+ for a, b in itertools.permutations(symbols, 2):
50
+ if table[a][b] != table[b][a]:
51
+ return ", ".join(sorted({a, b}))
52
  return ""
53
 
54
+ # download attached Space file
55
+ def task_file_bytes(tid: str) -> bytes:
56
+ return requests.get(f"{API_URL}/files/{tid}", timeout=30).content
57
+
58
+ def total_food_sales(data: bytes) -> str:
59
+ df = pd.read_excel(io.BytesIO(data))
60
+ cat_col = next((c for c in df.columns
61
+ if re.search(r"cat|type", c, flags=re.I)), None)
62
+ if cat_col is None:
63
+ return ""
64
+ num_cols = df.select_dtypes("number").columns
65
+ total = df.loc[df[cat_col].str.contains("food", case=False, na=False),
66
+ num_cols].sum().sum()
67
+ return f"{total:.2f}"
 
 
 
 
 
 
68
 
 
69
  BOTANICAL_VEG = {
70
  "sweet potatoes", "green beans", "corn", "bell pepper",
71
  "broccoli", "celery", "zucchini", "lettuce"
72
  }
73
+ def veg_list(q: str) -> str:
74
  items = [w.strip().lower() for w in re.split(r",\s*", q)]
75
  vegs = sorted(i for i in items if i in BOTANICAL_VEG)
76
  return ", ".join(vegs)
77
 
78
+ # ────────────────────────── agent ──────────────────────────
 
 
 
 
 
 
 
 
 
 
 
79
  class SmartAgent:
80
+ RE_ALBUM = re.compile(
81
  r"how many studio albums were published by (.+?) between (\d{4}) and (\d{4})",
82
+ flags=re.I)
 
83
 
84
  def __init__(self):
85
  from transformers import pipeline
 
88
  max_new_tokens=128,
89
  do_sample=False)
90
 
91
+ def __call__(self, q: str, task_id: str="") -> str:
92
  ql = q.lower()
93
 
94
+ # 1. album counter
95
+ if m := self.RE_ALBUM.search(ql):
96
  return albums_between(m.group(1).title(), int(m.group(2)), int(m.group(3)))
97
 
98
+ # 2. reverse-text puzzle
99
+ if ans := reverse_left_puzzle(q):
100
  return ans
101
 
102
+ # 3. non-commutative subset
103
  if "|*" in q and "counter" in ql:
104
+ if s := find_non_comm_subset(q):
105
+ return s
106
+
107
+ # 4. vegetable list
 
 
 
 
 
 
 
 
 
 
 
 
108
  if "alphabetize the list of vegetables" in ql:
109
+ return veg_list(q)
110
 
111
+ # 5. excel total food sales
112
+ if task_id and "attached excel" in ql and "total sales" in ql:
113
  try:
114
+ return total_food_sales(task_file_bytes(task_id))
 
115
  except Exception:
116
  pass
117
 
118
+ # Fallback tiny LLM + wiki blurb
119
  ctx = ""
120
  try:
121
  ctx = wikipedia.summary(q, sentences=2)
 
129
  txt = self.llm(prompt)[0]["generated_text"]
130
  return txt.split("Answer:")[-1].strip().rstrip(".")
131
 
132
+ # ─────────────────────── run & submit ─────────────────────
133
+ def run_and_submit_all(profile: gr.OAuthProfile|None):
134
  if not profile:
135
  return "Please login first.", None
136
+ user = profile.username
137
+ space_id = os.getenv("SPACE_ID") or "local"
138
+ agent = SmartAgent()
 
139
 
140
  qs = requests.get(f"{API_URL}/questions", timeout=30).json()
 
141
 
142
+ rows, answers = [], []
143
  for item in qs:
144
+ tid, qtxt = item["task_id"], item["question"]
145
  try:
146
+ ans = agent(qtxt, tid)
147
  except Exception as e:
148
  ans = f"AGENT ERROR: {e}"
 
149
  final = f"FINAL ANSWER: {ans}" if GAIA_FMT else ans
150
  field = "model_answer" if GAIA_FMT else "submitted_answer"
151
+ answers.append({"task_id": tid, field: final})
152
+ rows.append({"Task ID": tid, "Question": qtxt, "Answer": final})
153
 
154
+ sub = {"username": user,
155
+ "agent_code": f"https://huggingface.co/spaces/{space_id}/tree/main",
156
+ "answers": answers}
157
+ res = requests.post(f"{API_URL}/submit", json=sub, timeout=120).json()
 
158
 
159
  status = (f"Submitted in **{'GAIA' if GAIA_FMT else 'Course'}** mode – "
160
+ f"Score: {res.get('score')} % "
161
+ f"({res.get('correct_count')}/{res.get('total_attempted')})")
162
  return status, pd.DataFrame(rows)
163
 
164
+ # ─────────────────────────── UI ───────────────────────────
165
  with gr.Blocks() as demo:
166
+ gr.Markdown("# GAIA Agents-Course – SmartAgent baseline 0.5")
167
  gr.Markdown(
168
+ f"Output mode: **{'GAIA' if GAIA_FMT else 'Course'}** "
169
+ "- set `GAIA_FORMAT=true` to switch."
170
  )
171
  gr.LoginButton()
172
  btn = gr.Button("Run Evaluation & Submit")
173
  stat = gr.Markdown()
174
  table = gr.DataFrame(wrap=True, interactive=False)
 
175
  btn.click(run_and_submit_all, outputs=[stat, table])
176
 
177
  if __name__ == "__main__":