johnnychiang commited on
Commit
9ccc416
·
verified ·
1 Parent(s): 87712f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -162
app.py CHANGED
@@ -61,7 +61,7 @@ def sanitize_answer(ans: str) -> str:
61
 
62
 
63
  # -----------------------------
64
- # Utils: collect strings
65
  # -----------------------------
66
  def _collect_strings(x: Any) -> List[str]:
67
  out: List[str] = []
@@ -95,6 +95,7 @@ def extract_file_ids_from_item(item: Dict[str, Any]) -> List[str]:
95
  if isinstance(vv, str) and vv:
96
  ids.append(vv)
97
 
 
98
  seen = set()
99
  out: List[str] = []
100
  for x in ids:
@@ -112,7 +113,6 @@ def _normalize_to_full_url(s: str, api_url: str) -> Optional[str]:
112
  return s
113
  if s.startswith("/"):
114
  return api_url.rstrip("/") + s
115
- # relative-ish
116
  if s.startswith(("files/", "file/", "static/", "assets/", "attachments/", "media/", "raw/", "api/")):
117
  return api_url.rstrip("/") + "/" + s
118
  return None
@@ -124,7 +124,6 @@ def extract_file_urls_from_item(item: Dict[str, Any], api_url: str) -> List[str]
124
  u = _normalize_to_full_url(s, api_url)
125
  if u:
126
  urls.append(u)
127
- # dedup
128
  seen = set()
129
  out = []
130
  for u in urls:
@@ -136,7 +135,7 @@ def extract_file_urls_from_item(item: Dict[str, Any], api_url: str) -> List[str]
136
 
137
  def extract_filenames_from_question(q: str) -> List[str]:
138
  names = re.findall(
139
- r"attached (?:a file called|the recipe as|as)\s+([A-Za-z0-9 _\-\.\(\)]+?\.(?:mp3|xlsx|xls|py|txt))",
140
  q,
141
  flags=re.I,
142
  )
@@ -145,7 +144,6 @@ def extract_filenames_from_question(q: str) -> List[str]:
145
  n = n.strip().strip('"').strip("'")
146
  if n:
147
  out.append(n)
148
- # dedup
149
  seen = set()
150
  res = []
151
  for x in out:
@@ -155,8 +153,13 @@ def extract_filenames_from_question(q: str) -> List[str]:
155
  return res
156
 
157
 
 
 
 
 
 
158
  # -----------------------------
159
- # Download & save helpers
160
  # -----------------------------
161
  def _save_stream_to_tmp(resp: requests.Response, file_tag: str) -> Optional[Path]:
162
  try:
@@ -202,60 +205,7 @@ def _try_download_urls(urls: List[str], tag: str) -> Tuple[Optional[Path], List[
202
 
203
 
204
  # -----------------------------
205
- # OpenAPI discovery
206
- # -----------------------------
207
- def discover_openapi_paths(api_url: str) -> List[str]:
208
- """
209
- If openapi exists, extract candidate path templates that look like file download endpoints.
210
- """
211
- probes = [
212
- f"{api_url}/openapi.json",
213
- f"{api_url}/openapi",
214
- f"{api_url}/swagger.json",
215
- f"{api_url}/api/openapi.json",
216
- ]
217
- candidates: List[str] = []
218
- for p in probes:
219
- try:
220
- r = _http_get(p, timeout=20, stream=False)
221
- if r.status_code != 200:
222
- continue
223
- data = r.json()
224
- paths = data.get("paths", {}) if isinstance(data, dict) else {}
225
- for path in paths.keys():
226
- low = path.lower()
227
- if any(k in low for k in ["file", "files", "attachment", "download", "asset", "media"]):
228
- candidates.append(path)
229
- except Exception:
230
- continue
231
-
232
- # dedup
233
- seen = set()
234
- out = []
235
- for x in candidates:
236
- if x not in seen:
237
- out.append(x)
238
- seen.add(x)
239
- return out
240
-
241
-
242
- def build_openapi_url_candidates(api_url: str, fid: str, openapi_paths: List[str]) -> List[str]:
243
- urls = []
244
- for path in openapi_paths:
245
- # Replace common params
246
- u = path
247
- u = u.replace("{file_id}", fid).replace("{fileId}", fid).replace("{id}", fid).replace("{attachment_id}", fid)
248
- if "{" in u and "}" in u:
249
- # still has unknown template vars
250
- continue
251
- if not u.startswith("/"):
252
- u = "/" + u
253
- urls.append(api_url.rstrip("/") + u)
254
- return urls
255
-
256
-
257
- # -----------------------------
258
- # Base64-in-item extraction (IMPORTANT)
259
  # -----------------------------
260
  _B64_KEYS = {
261
  "data", "content", "blob", "bytes", "file_bytes", "filebytes", "b64", "base64",
@@ -268,13 +218,10 @@ def looks_like_base64(s: str) -> bool:
268
  t = s.strip()
269
  if len(t) < 200:
270
  return False
271
- # allow data:...;base64,....
272
  if t.startswith("data:") and "base64," in t:
273
  return True
274
- # base64 charset check (loose)
275
  if re.fullmatch(r"[A-Za-z0-9+/=\s]+", t) is None:
276
  return False
277
- # must have padding-ish or length multiple-ish (loose)
278
  return True
279
 
280
 
@@ -291,15 +238,13 @@ def decode_base64_to_file(b64s: str, filename_hint: str) -> Optional[Path]:
291
  out_dir = Path("/tmp/gaia_files")
292
  out_dir.mkdir(parents=True, exist_ok=True)
293
 
294
- # infer suffix by magic if missing
295
  name = filename_hint or "attachment"
296
  if "." not in name:
297
- # quick magic guesses
298
  if raw[:2] == b"PK":
299
  name += ".xlsx"
300
  elif raw[:3] == b"ID3" or raw[:2] == b"\xff\xfb":
301
  name += ".mp3"
302
- elif raw[:1] == b"#" or b"import" in raw[:200]:
303
  name += ".py"
304
  else:
305
  name += ".bin"
@@ -313,9 +258,6 @@ def decode_base64_to_file(b64s: str, filename_hint: str) -> Optional[Path]:
313
 
314
 
315
  def extract_base64_files_from_item(item: Any, filename_hint: str) -> Tuple[List[Path], List[str]]:
316
- """
317
- Deep-scan dict/list for base64 strings under likely keys.
318
- """
319
  found_paths: List[Path] = []
320
  debug: List[str] = []
321
 
@@ -323,7 +265,6 @@ def extract_base64_files_from_item(item: Any, filename_hint: str) -> Tuple[List[
323
  if isinstance(x, dict):
324
  for k, v in x.items():
325
  kh = f"{key_hint}.{k}" if key_hint else str(k)
326
- # if key suggests file-like, attempt decode when value is b64-ish string
327
  if isinstance(v, str) and (k.lower() in _B64_KEYS or "base64" in k.lower() or "b64" in k.lower()):
328
  if looks_like_base64(v):
329
  p = decode_base64_to_file(v, filename_hint)
@@ -336,16 +277,13 @@ def extract_base64_files_from_item(item: Any, filename_hint: str) -> Tuple[List[
336
  elif isinstance(x, list):
337
  for i, y in enumerate(x):
338
  walk(y, f"{key_hint}[{i}]")
339
- else:
340
- # not traversable
341
- return
342
 
343
  walk(item)
344
  return found_paths, debug
345
 
346
 
347
  # -----------------------------
348
- # Rule solvers (known correct)
349
  # -----------------------------
350
  def solve_reversed_sentence(q: str) -> Optional[str]:
351
  if "rewsna eht sa" in q and '"tfel"' in q:
@@ -483,8 +421,7 @@ def solve_python_final_numeric(file_path: Path) -> Optional[str]:
483
  class BasicAgent:
484
  def __init__(self, api_url: str):
485
  self.api_url = api_url.rstrip("/")
486
- self.openapi_paths = discover_openapi_paths(self.api_url)
487
- print("BasicAgent initialized. openapi_paths:", len(self.openapi_paths))
488
 
489
  def __call__(self, question: str, item: Dict[str, Any]) -> Tuple[str, str]:
490
  q = (question or "").strip()
@@ -500,40 +437,96 @@ class BasicAgent:
500
  except Exception:
501
  pass
502
 
503
- is_attachment_task = any(k in ql for k in ["attached excel", "attached python", "attached a file", "i've attached", ".mp3", ".xlsx", ".py"])
504
  if not is_attachment_task:
505
  return "", ""
506
 
507
- # filename hint
 
508
  filenames = extract_filenames_from_question(q)
509
- filename_hint = filenames[0] if filenames else "attachment"
510
-
511
- # 1) try base64 inside item (NEW)
512
- paths, dbg = extract_base64_files_from_item(item, filename_hint=filename_hint)
513
- debug_lines.extend(dbg)
514
- for fp in paths:
515
- ans = self._solve_from_file(q, fp)
516
- if ans:
517
- return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
518
-
519
- # 2) try URLs found in item
520
- urls = extract_file_urls_from_item(item, api_url=self.api_url)
521
- for u in urls:
522
- fp, dbg2 = _try_download_urls([u], tag=filename_hint)
523
- debug_lines.extend(dbg2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  if fp:
525
  ans = self._solve_from_file(q, fp)
526
  if ans:
527
  return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
528
 
529
- # 3) try ids in item
530
  file_ids = extract_file_ids_from_item(item)
531
- # fallback: task_id also as id
532
- tid = item.get("task_id")
533
- if isinstance(tid, str) and tid:
534
- file_ids.append(tid)
535
-
536
- # dedup ids
537
  seen = set()
538
  file_ids2 = []
539
  for x in file_ids:
@@ -541,80 +534,44 @@ class BasicAgent:
541
  file_ids2.append(x); seen.add(x)
542
 
543
  for fid in file_ids2:
544
- fp, dbg3 = self._download_by_id(fid, filename_hint)
545
- debug_lines.extend(dbg3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  if fp:
547
  ans = self._solve_from_file(q, fp)
548
  if ans:
549
  return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
550
 
551
- # last: add item keys snapshot (so you can see where file is)
552
  if DEBUG_ATTACH:
553
  try:
554
  keys = sorted(list(item.keys()))
555
  debug_lines.append("ITEM_KEYS: " + ", ".join(keys))
 
 
556
  except Exception:
557
  pass
558
 
559
  return "", "\n".join(debug_lines).strip() if DEBUG_ATTACH else ""
560
 
561
- def _download_by_id(self, fid: str, tag: str) -> Tuple[Optional[Path], List[str]]:
562
- fid = fid.strip()
563
- api = self.api_url
564
-
565
- candidates = []
566
-
567
- # openapi discovered paths
568
- candidates += build_openapi_url_candidates(api, fid, self.openapi_paths)
569
-
570
- # common REST-ish
571
- candidates += [
572
- f"{api}/files/{fid}",
573
- f"{api}/files/{fid}/download",
574
- f"{api}/file/{fid}",
575
- f"{api}/download/{fid}",
576
- f"{api}/get_file/{fid}",
577
- f"{api}/assets/{fid}",
578
- f"{api}/static/{fid}",
579
- f"{api}/attachments/{fid}",
580
- f"{api}/media/{fid}",
581
- f"{api}/raw/{fid}",
582
- ]
583
-
584
- # API prefix (very common)
585
- candidates += [
586
- f"{api}/api/files/{fid}",
587
- f"{api}/api/files/{fid}/download",
588
- f"{api}/api/file/{fid}",
589
- f"{api}/api/download/{fid}",
590
- f"{api}/api/attachments/{fid}",
591
- f"{api}/api/media/{fid}",
592
- ]
593
-
594
- # HF/Gradio-style file serving (common on Spaces)
595
- candidates += [
596
- f"{api}/file={fid}",
597
- f"{api}/gradio_api/file={fid}",
598
- f"{api}/gradio_api/file={fid}&download=1",
599
- ]
600
-
601
- # query styles
602
- candidates += [
603
- f"{api}/download?file_id={fid}",
604
- f"{api}/api/download?file_id={fid}",
605
- f"{api}/files?file_id={fid}",
606
- f"{api}/api/files?file_id={fid}",
607
- ]
608
-
609
- # dedup preserve order
610
- seen = set()
611
- cand2 = []
612
- for u in candidates:
613
- if u not in seen:
614
- cand2.append(u); seen.add(u)
615
-
616
- return _try_download_urls(cand2, tag)
617
-
618
  def _solve_from_file(self, q: str, fp: Path) -> Optional[str]:
619
  suf = fp.suffix.lower()
620
  ql = q.lower()
@@ -625,7 +582,7 @@ class BasicAgent:
625
  if ("attached python code" in ql) or (suf in [".py", ".txt"]):
626
  return solve_python_final_numeric(fp)
627
 
628
- # mp3 tasks are intentionally skipped (no audio pipeline)
629
  return None
630
 
631
 
@@ -704,7 +661,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None = None):
704
  # -----------------------------
705
  with gr.Blocks() as demo:
706
  gr.Markdown("# Basic Agent Evaluation Runner (No Paid Model)")
707
- gr.Markdown("✅ This version tries **base64-in-item** + **openapi discovery** + more HF Space download patterns.\n\nDebug column shows what happened.")
708
  gr.LoginButton()
709
  run_button = gr.Button("Run Evaluation & Submit All Answers")
710
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=14, interactive=False)
 
61
 
62
 
63
  # -----------------------------
64
+ # Utils
65
  # -----------------------------
66
  def _collect_strings(x: Any) -> List[str]:
67
  out: List[str] = []
 
95
  if isinstance(vv, str) and vv:
96
  ids.append(vv)
97
 
98
+ # dedup
99
  seen = set()
100
  out: List[str] = []
101
  for x in ids:
 
113
  return s
114
  if s.startswith("/"):
115
  return api_url.rstrip("/") + s
 
116
  if s.startswith(("files/", "file/", "static/", "assets/", "attachments/", "media/", "raw/", "api/")):
117
  return api_url.rstrip("/") + "/" + s
118
  return None
 
124
  u = _normalize_to_full_url(s, api_url)
125
  if u:
126
  urls.append(u)
 
127
  seen = set()
128
  out = []
129
  for u in urls:
 
135
 
136
  def extract_filenames_from_question(q: str) -> List[str]:
137
  names = re.findall(
138
+ r"(?:attached a file called|attached the recipe as|attached a file|file called)\s+([A-Za-z0-9 _\-\.\(\)]+?\.(?:mp3|xlsx|xls|py|txt))",
139
  q,
140
  flags=re.I,
141
  )
 
144
  n = n.strip().strip('"').strip("'")
145
  if n:
146
  out.append(n)
 
147
  seen = set()
148
  res = []
149
  for x in out:
 
153
  return res
154
 
155
 
156
+ def url_quote_filename(name: str) -> str:
157
+ # minimal url-encoding for spaces
158
+ return name.replace(" ", "%20")
159
+
160
+
161
  # -----------------------------
162
+ # Download helpers
163
  # -----------------------------
164
  def _save_stream_to_tmp(resp: requests.Response, file_tag: str) -> Optional[Path]:
165
  try:
 
205
 
206
 
207
  # -----------------------------
208
+ # Base64-in-item extraction (備用)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  # -----------------------------
210
  _B64_KEYS = {
211
  "data", "content", "blob", "bytes", "file_bytes", "filebytes", "b64", "base64",
 
218
  t = s.strip()
219
  if len(t) < 200:
220
  return False
 
221
  if t.startswith("data:") and "base64," in t:
222
  return True
 
223
  if re.fullmatch(r"[A-Za-z0-9+/=\s]+", t) is None:
224
  return False
 
225
  return True
226
 
227
 
 
238
  out_dir = Path("/tmp/gaia_files")
239
  out_dir.mkdir(parents=True, exist_ok=True)
240
 
 
241
  name = filename_hint or "attachment"
242
  if "." not in name:
 
243
  if raw[:2] == b"PK":
244
  name += ".xlsx"
245
  elif raw[:3] == b"ID3" or raw[:2] == b"\xff\xfb":
246
  name += ".mp3"
247
+ elif b"import" in raw[:200]:
248
  name += ".py"
249
  else:
250
  name += ".bin"
 
258
 
259
 
260
  def extract_base64_files_from_item(item: Any, filename_hint: str) -> Tuple[List[Path], List[str]]:
 
 
 
261
  found_paths: List[Path] = []
262
  debug: List[str] = []
263
 
 
265
  if isinstance(x, dict):
266
  for k, v in x.items():
267
  kh = f"{key_hint}.{k}" if key_hint else str(k)
 
268
  if isinstance(v, str) and (k.lower() in _B64_KEYS or "base64" in k.lower() or "b64" in k.lower()):
269
  if looks_like_base64(v):
270
  p = decode_base64_to_file(v, filename_hint)
 
277
  elif isinstance(x, list):
278
  for i, y in enumerate(x):
279
  walk(y, f"{key_hint}[{i}]")
 
 
 
280
 
281
  walk(item)
282
  return found_paths, debug
283
 
284
 
285
  # -----------------------------
286
+ # Deterministic solvers (你已經答對的)
287
  # -----------------------------
288
  def solve_reversed_sentence(q: str) -> Optional[str]:
289
  if "rewsna eht sa" in q and '"tfel"' in q:
 
421
  class BasicAgent:
422
  def __init__(self, api_url: str):
423
  self.api_url = api_url.rstrip("/")
424
+ print("BasicAgent initialized.")
 
425
 
426
  def __call__(self, question: str, item: Dict[str, Any]) -> Tuple[str, str]:
427
  q = (question or "").strip()
 
437
  except Exception:
438
  pass
439
 
440
+ is_attachment_task = any(k in ql for k in ["attached excel", "attached python", "i've attached", ".mp3", ".xlsx", ".py"])
441
  if not is_attachment_task:
442
  return "", ""
443
 
444
+ task_id = str(item.get("task_id", "")).strip()
445
+ file_name = str(item.get("file_name", "")).strip() # <<<<<< 你缺的就是用它
446
  filenames = extract_filenames_from_question(q)
447
+ filename_hint = filenames[0] if filenames else (file_name or "attachment")
448
+
449
+ # 0) 先嘗試打題目詳情 (很多系統附件藏在這裡)
450
+ detail_candidates = [
451
+ f"{self.api_url}/question/{task_id}",
452
+ f"{self.api_url}/questions/{task_id}",
453
+ f"{self.api_url}/task/{task_id}",
454
+ f"{self.api_url}/tasks/{task_id}",
455
+ f"{self.api_url}/api/question/{task_id}",
456
+ f"{self.api_url}/api/questions/{task_id}",
457
+ ]
458
+ detail_json = None
459
+ for u in detail_candidates:
460
+ try:
461
+ r = _http_get(u, timeout=20, stream=False)
462
+ debug_lines.append(f"{r.status_code} {u}")
463
+ if r.status_code == 200 and "application/json" in (r.headers.get("content-type","").lower()):
464
+ detail_json = r.json()
465
+ debug_lines.append("DETAIL_OK: got json")
466
+ break
467
+ except Exception as e:
468
+ debug_lines.append(f"ERR {u} :: {type(e).__name__}: {e}")
469
+
470
+ # 1) base64 in detail/item
471
+ for src_name, src in [("DETAIL", detail_json), ("ITEM", item)]:
472
+ if src:
473
+ paths, dbg = extract_base64_files_from_item(src, filename_hint=filename_hint)
474
+ debug_lines.extend([f"{src_name}::{x}" for x in dbg])
475
+ for fp in paths:
476
+ ans = self._solve_from_file(q, fp)
477
+ if ans:
478
+ return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
479
+
480
+ # 2) url strings in detail/item
481
+ for src_name, src in [("DETAIL", detail_json), ("ITEM", item)]:
482
+ if src:
483
+ urls = extract_file_urls_from_item(src, api_url=self.api_url)
484
+ if urls:
485
+ fp, dbg2 = _try_download_urls(urls, tag=filename_hint)
486
+ debug_lines.extend([f"{src_name}::{x}" for x in dbg2])
487
+ if fp:
488
+ ans = self._solve_from_file(q, fp)
489
+ if ans:
490
+ return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
491
+
492
+ # 3) 用 file_name 組路徑(你目前最缺的)
493
+ # (你的 debug 顯示 item 就只有這個線索)
494
+ if file_name:
495
+ fn_q = url_quote_filename(file_name)
496
+ fn_candidates = [
497
+ # direct filename
498
+ f"{self.api_url}/static/{fn_q}",
499
+ f"{self.api_url}/files/{fn_q}",
500
+ f"{self.api_url}/assets/{fn_q}",
501
+ f"{self.api_url}/media/{fn_q}",
502
+ f"{self.api_url}/raw/{fn_q}",
503
+ f"{self.api_url}/api/static/{fn_q}",
504
+ f"{self.api_url}/api/files/{fn_q}",
505
+ f"{self.api_url}/api/assets/{fn_q}",
506
+ f"{self.api_url}/api/media/{fn_q}",
507
+ # task_id + filename (常見)
508
+ f"{self.api_url}/files/{task_id}/{fn_q}",
509
+ f"{self.api_url}/files/{task_id}/download/{fn_q}",
510
+ f"{self.api_url}/download/{task_id}/{fn_q}",
511
+ f"{self.api_url}/api/files/{task_id}/{fn_q}",
512
+ f"{self.api_url}/api/download/{task_id}/{fn_q}",
513
+ # query style
514
+ f"{self.api_url}/download?task_id={task_id}&file_name={fn_q}",
515
+ f"{self.api_url}/download?task_id={task_id}&filename={fn_q}",
516
+ f"{self.api_url}/api/download?task_id={task_id}&file_name={fn_q}",
517
+ f"{self.api_url}/api/download?task_id={task_id}&filename={fn_q}",
518
+ ]
519
+ fp, dbg3 = _try_download_urls(fn_candidates, tag=file_name)
520
+ debug_lines.extend(dbg3)
521
  if fp:
522
  ans = self._solve_from_file(q, fp)
523
  if ans:
524
  return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
525
 
526
+ # 4) id-based fallback(保留)
527
  file_ids = extract_file_ids_from_item(item)
528
+ if task_id:
529
+ file_ids.append(task_id)
 
 
 
 
530
  seen = set()
531
  file_ids2 = []
532
  for x in file_ids:
 
534
  file_ids2.append(x); seen.add(x)
535
 
536
  for fid in file_ids2:
537
+ candidates = [
538
+ f"{self.api_url}/files/{fid}",
539
+ f"{self.api_url}/files/{fid}/download",
540
+ f"{self.api_url}/file/{fid}",
541
+ f"{self.api_url}/download/{fid}",
542
+ f"{self.api_url}/get_file/{fid}",
543
+ f"{self.api_url}/assets/{fid}",
544
+ f"{self.api_url}/static/{fid}",
545
+ f"{self.api_url}/attachments/{fid}",
546
+ f"{self.api_url}/media/{fid}",
547
+ f"{self.api_url}/raw/{fid}",
548
+ f"{self.api_url}/api/files/{fid}",
549
+ f"{self.api_url}/api/files/{fid}/download",
550
+ f"{self.api_url}/api/file/{fid}",
551
+ f"{self.api_url}/api/download/{fid}",
552
+ f"{self.api_url}/file={fid}",
553
+ f"{self.api_url}/gradio_api/file={fid}",
554
+ f"{self.api_url}/download?file_id={fid}",
555
+ f"{self.api_url}/api/download?file_id={fid}",
556
+ ]
557
+ fp, dbg4 = _try_download_urls(candidates, tag=filename_hint)
558
+ debug_lines.extend(dbg4)
559
  if fp:
560
  ans = self._solve_from_file(q, fp)
561
  if ans:
562
  return sanitize_answer(ans), "\n".join(debug_lines) if DEBUG_ATTACH else ""
563
 
 
564
  if DEBUG_ATTACH:
565
  try:
566
  keys = sorted(list(item.keys()))
567
  debug_lines.append("ITEM_KEYS: " + ", ".join(keys))
568
+ if file_name:
569
+ debug_lines.append(f"ITEM_FILE_NAME: {file_name}")
570
  except Exception:
571
  pass
572
 
573
  return "", "\n".join(debug_lines).strip() if DEBUG_ATTACH else ""
574
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  def _solve_from_file(self, q: str, fp: Path) -> Optional[str]:
576
  suf = fp.suffix.lower()
577
  ql = q.lower()
 
582
  if ("attached python code" in ql) or (suf in [".py", ".txt"]):
583
  return solve_python_final_numeric(fp)
584
 
585
+ # mp3 tasks 仍然 skip(你目前沒做音訊辨識)
586
  return None
587
 
588
 
 
661
  # -----------------------------
662
  with gr.Blocks() as demo:
663
  gr.Markdown("# Basic Agent Evaluation Runner (No Paid Model)")
664
+ gr.Markdown("✅ This version tries: **question detail endpoints** + **file_name path patterns** + url/base64 scan.\n\nDebug欄會顯示嘗試過哪些網址。")
665
  gr.LoginButton()
666
  run_button = gr.Button("Run Evaluation & Submit All Answers")
667
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=14, interactive=False)