Mushari440 Claude Opus 5 commited on
Commit
f79409d
·
1 Parent(s): 9dfe50b

Make the manage panel work across versions, and fix what it did wrong

Browse files

Three defects, all from the panel predating versioning.

Delete removed the queue entry as well as the score. The request is shared by every
version, so deleting a v8 score also destroyed the v7 row's metadata and left no way
to re-run without resubmitting. Delete is now two actions: 'Delete <version> score'
drops that version's file and keeps the queue entry, and 'Remove entirely' takes the
request plus every version's score. The old wording promised the second while the
button was reached from a single version.

Nothing showed which versions a model was actually on. The status badge is global --
one request serves all versions -- so a model reading FINISHED could have no score on
the board being viewed. Each row now carries a chip per version, and the panel header
counts how many are scored versus missing on the current one.

There was no way to start a version. The worker only claims PENDING / RUNNING / RERUN,
so after a sweep every model sits at FINISHED and a new version would evaluate nothing
at all. /api/requeue-all sets them back to RERUN, defaulting to only the models with no
score on the target version so a half-finished sweep resumes instead of restarting.

test_manage.py covers all of it against a stubbed Hub: per-version presence, that a
scoped delete leaves the other version and the request alone, that scope=all removes
both, and that requeue skips models already scored or already queued.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Files changed (3) hide show
  1. app.py +101 -20
  2. index.html +70 -9
  3. test_manage.py +95 -0
app.py CHANGED
@@ -286,11 +286,16 @@ def _results_paths(model: str, version: str | None = None) -> list[str]:
286
  class ModelRef(BaseModel):
287
  model: str
288
  version: str | None = None
 
 
 
 
 
289
 
290
 
291
  @app.get("/api/manage")
292
  def manage(request: Request, version: str | None = None):
293
- """Every request + whether a public/private score exists. Owner-only."""
294
  _require_owner(request, check_origin=False) # read-only GET
295
  api = HfApi(token=HF_TOKEN)
296
  have = {}
@@ -304,10 +309,16 @@ def manage(request: Request, version: str | None = None):
304
  rows = []
305
  for r in _list_requests():
306
  m = r.get("model", "")
307
- where = set()
308
- for path in _results_paths(m, version):
309
- where |= have.get(path, set())
310
- where = sorted(where)
 
 
 
 
 
 
311
  rows.append({
312
  "model": m,
313
  "status": r.get("status"),
@@ -315,10 +326,12 @@ def manage(request: Request, version: str | None = None):
315
  "submitted_by": r.get("submitted_by"),
316
  "source": r.get("source", "open"),
317
  "visibility": r.get("visibility", "public"),
318
- "result_in": where,
 
319
  })
320
  rows.sort(key=lambda x: x.get("submitted_time") or "", reverse=True)
321
- return {"models": rows, "owner": LEADERBOARD_OWNER}
 
322
 
323
 
324
  @app.post("/api/model/delete")
@@ -329,19 +342,25 @@ def model_delete(request: Request, body: ModelRef):
329
  if not MODEL_RE.match(model):
330
  raise HTTPException(400, "Bad model id.")
331
  api = HfApi(token=HF_TOKEN)
 
 
 
332
  removed = []
333
- for r in _list_requests():
334
- if r.get("model") == model and r.get("_path"):
335
- try:
336
- api.delete_file(path_in_repo=r["_path"], repo_id=REQUESTS_REPO,
337
- repo_type="dataset",
338
- commit_message=f"Remove request {model}")
339
- removed.append(f"request:{r['_path']}")
340
- except Exception as e:
341
- print(f"[delete] request {model}: {type(e).__name__}: {e}")
 
342
  version = _clean_version(body.version)
 
 
343
  for repo in (RESULTS_REPO, RESULTS_PRIVATE_REPO):
344
- for path in _results_paths(model, version):
345
  try:
346
  api.delete_file(path_in_repo=path, repo_id=repo,
347
  repo_type="dataset",
@@ -350,9 +369,13 @@ def model_delete(request: Request, body: ModelRef):
350
  except Exception:
351
  pass # simply not present there
352
  if not removed:
353
- raise HTTPException(404, f"Nothing found for {model}.")
 
 
 
 
354
  return {"ok": True, "removed": removed,
355
- "message": f"Deleted {model} ({len(removed)} file(s))."}
356
 
357
 
358
  @app.post("/api/model/rerun")
@@ -377,7 +400,65 @@ def model_rerun(request: Request, body: ModelRef):
377
  path_or_fileobj=json.dumps(payload, ensure_ascii=False, indent=1).encode("utf-8"),
378
  path_in_repo=hit["_path"], repo_id=REQUESTS_REPO, repo_type="dataset",
379
  commit_message=f"{model} -> RERUN")
380
- return {"ok": True, "message": f"{model} queued for re-evaluation (RERUN)."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
 
383
  @app.get("/api/private-results")
 
286
  class ModelRef(BaseModel):
287
  model: str
288
  version: str | None = None
289
+ # "version" removes only this version's score and leaves the queue entry, so the
290
+ # model can be re-evaluated. "all" removes the queue entry and every version's
291
+ # score. Deleting a v8 score used to destroy the shared request too, which took
292
+ # the v7 row's metadata with it and left no way to re-run without resubmitting.
293
+ scope: str = "version"
294
 
295
 
296
  @app.get("/api/manage")
297
  def manage(request: Request, version: str | None = None):
298
+ """Every request + where its scores live, per benchmark version. Owner-only."""
299
  _require_owner(request, check_origin=False) # read-only GET
300
  api = HfApi(token=HF_TOKEN)
301
  have = {}
 
309
  rows = []
310
  for r in _list_requests():
311
  m = r.get("model", "")
312
+ # Presence per version, so the panel can show at a glance which boards a
313
+ # model is actually on. `result_in` stays for the selected version because
314
+ # the older UI read it.
315
+ by_version = {}
316
+ for v in VERSIONS:
317
+ where = set()
318
+ for path in _results_paths(m, v):
319
+ where |= have.get(path, set())
320
+ if where:
321
+ by_version[v] = sorted(where)
322
  rows.append({
323
  "model": m,
324
  "status": r.get("status"),
 
326
  "submitted_by": r.get("submitted_by"),
327
  "source": r.get("source", "open"),
328
  "visibility": r.get("visibility", "public"),
329
+ "result_in": by_version.get(_clean_version(version), []),
330
+ "results_by_version": by_version,
331
  })
332
  rows.sort(key=lambda x: x.get("submitted_time") or "", reverse=True)
333
+ return {"models": rows, "owner": LEADERBOARD_OWNER,
334
+ "versions": VERSIONS, "version": _clean_version(version)}
335
 
336
 
337
  @app.post("/api/model/delete")
 
342
  if not MODEL_RE.match(model):
343
  raise HTTPException(400, "Bad model id.")
344
  api = HfApi(token=HF_TOKEN)
345
+ scope = (body.scope or "version").strip().lower()
346
+ if scope not in ("version", "all"):
347
+ raise HTTPException(400, "scope must be 'version' or 'all'.")
348
  removed = []
349
+ if scope == "all":
350
+ for r in _list_requests():
351
+ if r.get("model") == model and r.get("_path"):
352
+ try:
353
+ api.delete_file(path_in_repo=r["_path"], repo_id=REQUESTS_REPO,
354
+ repo_type="dataset",
355
+ commit_message=f"Remove request {model}")
356
+ removed.append(f"request:{r['_path']}")
357
+ except Exception as e:
358
+ print(f"[delete] request {model}: {type(e).__name__}: {e}")
359
  version = _clean_version(body.version)
360
+ targets = ([p for v in VERSIONS for p in _results_paths(model, v)]
361
+ if scope == "all" else _results_paths(model, version))
362
  for repo in (RESULTS_REPO, RESULTS_PRIVATE_REPO):
363
+ for path in dict.fromkeys(targets):
364
  try:
365
  api.delete_file(path_in_repo=path, repo_id=repo,
366
  repo_type="dataset",
 
369
  except Exception:
370
  pass # simply not present there
371
  if not removed:
372
+ raise HTTPException(
373
+ 404,
374
+ f"{model} has no score on {version}." if scope == "version"
375
+ else f"Nothing found for {model}.")
376
+ what = f"every version of {model}" if scope == "all" else f"{model} from {version}"
377
  return {"ok": True, "removed": removed,
378
+ "message": f"Deleted {what} ({len(removed)} file(s))."}
379
 
380
 
381
  @app.post("/api/model/rerun")
 
400
  path_or_fileobj=json.dumps(payload, ensure_ascii=False, indent=1).encode("utf-8"),
401
  path_in_repo=hit["_path"], repo_id=REQUESTS_REPO, repo_type="dataset",
402
  commit_message=f"{model} -> RERUN")
403
+ return {"ok": True,
404
+ "message": f"{model} queued for re-evaluation (RERUN). It lands on "
405
+ f"whichever version the worker is running (its RUN_ID)."}
406
+
407
+
408
+ class RequeueRef(BaseModel):
409
+ version: str | None = None
410
+ # Default to only what is missing, so re-running a half-finished sweep does not
411
+ # discard the models that already landed.
412
+ only_missing: bool = True
413
+
414
+
415
+ @app.post("/api/requeue-all")
416
+ def requeue_all(request: Request, body: RequeueRef):
417
+ """Set every queued model to RERUN so a whole version can be evaluated.
418
+
419
+ The worker only ever picks up PENDING / RUNNING / RERUN, so after a sweep every
420
+ model sits at FINISHED and a new version would evaluate nothing at all. This is
421
+ the switch that starts the next version's run."""
422
+ _require_owner(request)
423
+ version = _clean_version(body.version)
424
+ api = HfApi(token=HF_TOKEN)
425
+
426
+ have = set()
427
+ for repo in (RESULTS_REPO, RESULTS_PRIVATE_REPO):
428
+ try:
429
+ have |= {f for f in api.list_repo_files(repo, repo_type="dataset")
430
+ if f.endswith(".json")}
431
+ except Exception:
432
+ pass
433
+
434
+ queued, skipped = [], []
435
+ for r in _list_requests():
436
+ model = r.get("model")
437
+ if not model or not r.get("_path"):
438
+ continue
439
+ scored = any(p in have for p in _results_paths(model, version))
440
+ if body.only_missing and scored:
441
+ skipped.append(model)
442
+ continue
443
+ if str(r.get("status") or "").upper() in ("PENDING", "RUNNING", "RERUN"):
444
+ skipped.append(model) # already on the worker's list
445
+ continue
446
+ payload = {k: v for k, v in r.items() if k != "_path"}
447
+ payload["status"] = "RERUN"
448
+ try:
449
+ api.upload_file(
450
+ path_or_fileobj=json.dumps(payload, ensure_ascii=False, indent=1).encode("utf-8"),
451
+ path_in_repo=r["_path"], repo_id=REQUESTS_REPO, repo_type="dataset",
452
+ commit_message=f"{model} -> RERUN (requeue for {version})")
453
+ queued.append(model)
454
+ except Exception as e:
455
+ print(f"[requeue-all] {model}: {type(e).__name__}: {e}")
456
+ msg = (f"Queued {len(queued)} model(s) for {version}."
457
+ + (f" Skipped {len(skipped)} already scored or already queued." if skipped else ""))
458
+ if not queued:
459
+ msg = (f"Nothing to queue for {version} — "
460
+ f"{len(skipped)} model(s) are already scored or already in the queue.")
461
+ return {"ok": True, "queued": queued, "skipped": skipped, "message": msg}
462
 
463
 
464
  @app.get("/api/private-results")
index.html CHANGED
@@ -172,6 +172,13 @@ table.board tbody tr:hover td:nth-child(1),table.board tbody tr:hover td:nth-chi
172
  .mbtn:hover{border-color:var(--accent);color:var(--accent)}
173
  .mbtn.danger:hover{border-color:#e06666;color:#e06666}
174
  .b-private{background:rgba(120,120,200,.16);color:#9aa4e0}
 
 
 
 
 
 
 
175
  .prog{margin:2px 0 12px;padding:11px 13px;border-radius:12px;background:rgba(var(--cell-heat),.07);
176
  border:1px solid var(--border)}
177
  .prog .ptop{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;font-size:13px}
@@ -293,6 +300,12 @@ footer{margin-top:30px;text-align:center;color:var(--ink-mute);font-size:12px}
293
  <div class="grid2 one" id="managePanel" hidden>
294
  <div class="panel">
295
  <h3><span class="k">Manage</span> Your submitted models<span id="manageCount" class="qcount"></span></h3>
 
 
 
 
 
 
296
  <div id="manageList"><div class="mrow" style="color:var(--ink-mute)">Loading…</div></div>
297
  <div class="msg" id="manageMsg"></div>
298
  </div>
@@ -923,7 +936,13 @@ async function loadManage(){
923
  const d=await r.json();
924
  panel.hidden=false;
925
  const models=Array.isArray(d.models)?d.models:[];
 
926
  document.getElementById('manageCount').textContent=`${models.length} submitted`;
 
 
 
 
 
927
  if(!models.length){list.innerHTML='<div class="mrow" style="color:var(--ink-mute)">Nothing submitted yet.</div>';return;}
928
  list.innerHTML=models.map(m=>{
929
  const s=(m.status||'').toUpperCase();
@@ -931,13 +950,30 @@ async function loadManage(){
931
  const tags=[`<span class="badge ${cls}">${s}</span>`];
932
  if(m.visibility==='private')tags.push('<span class="badge b-private">PRIVATE</span>');
933
  if(m.source==='closed')tags.push('<span class="badge b-pending">API</span>');
 
 
 
 
 
 
 
 
 
 
 
934
  return `<div class="mrow" data-model="${esc(m.model)}">
935
  <span class="mname" title="${esc(m.model)}">${esc(m.model)}</span>
936
- ${tags.join('')}
937
  <button class="mbtn" data-act="rerun">Re-run</button>
938
- <button class="mbtn danger" data-act="delete">Delete</button>
 
939
  </div>`;}).join('');
940
  list.querySelectorAll('button[data-act]').forEach(b=>b.onclick=()=>manageAct(b));
 
 
 
 
 
941
  }catch(e){
942
  panel.hidden=true;
943
  list.innerHTML=`<div class="mrow" style="color:var(--ink-mute)">Could not load (${esc(String(e))}).</div>`;
@@ -946,21 +982,46 @@ async function loadManage(){
946
 
947
  async function manageAct(btn){
948
  const row=btn.closest('.mrow'), model=row.dataset.model, act=btn.dataset.act;
949
- if(act==='delete'&&!confirm(`Delete ${model}?\n\nThis removes its queue entry AND its score from the leaderboard. It cannot be undone �� only a fresh evaluation would bring it back.`))return;
 
 
 
 
 
 
 
950
  row.querySelectorAll('button').forEach(b=>b.disabled=true);
951
- btn.textContent=act==='delete'?'Deleting…':'Queuing…';
 
 
 
952
  try{
953
- const r=await fetch(`/api/model/${act}`,{method:'POST',credentials:'same-origin',
954
- headers:{'Content-Type':'application/json'},body:JSON.stringify({model,version:CURRENT.id})});
955
  const d=await r.json().catch(()=>({}));
956
  if(r.ok){mmsg(d.message||'Done.','ok');await loadManage();
957
- if(act==='delete')setTimeout(()=>location.reload(),1200);}
958
  else{mmsg(d.detail||d.message||('Error '+r.status),'err');
959
  row.querySelectorAll('button').forEach(b=>b.disabled=false);
960
- btn.textContent=act==='delete'?'Delete':'Re-run';}
961
  }catch(e){mmsg('Network error — please try again.','err');
962
  row.querySelectorAll('button').forEach(b=>b.disabled=false);
963
- btn.textContent=act==='delete'?'Delete':'Re-run';}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
964
  }
965
 
966
  // show the pinned-column edge shadow only while the table is scrolled right
 
172
  .mbtn:hover{border-color:var(--accent);color:var(--accent)}
173
  .mbtn.danger:hover{border-color:#e06666;color:#e06666}
174
  .b-private{background:rgba(120,120,200,.16);color:#9aa4e0}
175
+ .vchip{font:600 10px "IBM Plex Mono";padding:2px 7px;border-radius:6px;border:1px solid var(--border);
176
+ color:var(--ink-mute);white-space:nowrap}
177
+ .vchip.on{border-color:var(--accent);color:var(--accent)}
178
+ .vchip.here{box-shadow:inset 0 0 0 1px var(--accent)}
179
+ .mbar{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:9px 0 11px;
180
+ border-bottom:1px solid var(--border);margin-bottom:6px}
181
+ .mbar .grow{flex:1 1 auto}
182
  .prog{margin:2px 0 12px;padding:11px 13px;border-radius:12px;background:rgba(var(--cell-heat),.07);
183
  border:1px solid var(--border)}
184
  .prog .ptop{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;font-size:13px}
 
300
  <div class="grid2 one" id="managePanel" hidden>
301
  <div class="panel">
302
  <h3><span class="k">Manage</span> Your submitted models<span id="manageCount" class="qcount"></span></h3>
303
+ <div class="mbar" id="manageBar" hidden>
304
+ <span class="vchip here" id="mbarVer"></span>
305
+ <span class="grow" style="font-size:12px;color:var(--ink-mute)" id="mbarNote"></span>
306
+ <button class="mbtn" id="btnQueueMissing">Queue models missing here</button>
307
+ <button class="mbtn" id="btnQueueAll">Queue every model</button>
308
+ </div>
309
  <div id="manageList"><div class="mrow" style="color:var(--ink-mute)">Loading…</div></div>
310
  <div class="msg" id="manageMsg"></div>
311
  </div>
 
936
  const d=await r.json();
937
  panel.hidden=false;
938
  const models=Array.isArray(d.models)?d.models:[];
939
+ const versions=Array.isArray(d.versions)&&d.versions.length?d.versions:VERSIONS.map(v=>v.id);
940
  document.getElementById('manageCount').textContent=`${models.length} submitted`;
941
+ const missing=models.length-models.filter(m=>((m.results_by_version||{})[CURRENT.id]||[]).length).length;
942
+ document.getElementById('manageBar').hidden=false;
943
+ document.getElementById('mbarVer').textContent='managing '+CURRENT.id;
944
+ document.getElementById('mbarNote').textContent=
945
+ `${models.length-missing} scored on ${CURRENT.id}, ${missing} missing`;
946
  if(!models.length){list.innerHTML='<div class="mrow" style="color:var(--ink-mute)">Nothing submitted yet.</div>';return;}
947
  list.innerHTML=models.map(m=>{
948
  const s=(m.status||'').toUpperCase();
 
950
  const tags=[`<span class="badge ${cls}">${s}</span>`];
951
  if(m.visibility==='private')tags.push('<span class="badge b-private">PRIVATE</span>');
952
  if(m.source==='closed')tags.push('<span class="badge b-pending">API</span>');
953
+ // Where this model actually has a score. The status badge above is global —
954
+ // one request serves every version — so it says nothing about which board the
955
+ // model is on. These chips do.
956
+ const byv=m.results_by_version||{};
957
+ const chips=versions.map(v=>{
958
+ const on=Array.isArray(byv[v])&&byv[v].length;
959
+ const priv=on&&byv[v].includes('private')&&!byv[v].includes('public');
960
+ return `<span class="vchip${on?' on':''}${v===CURRENT.id?' here':''}" title="${
961
+ on?`scored on ${v}${priv?' (private)':''}`:`no score on ${v}`}">${v}${on?(priv?' ●':' ✓'):' —'}</span>`;
962
+ }).join('');
963
+ const hasHere=Array.isArray(byv[CURRENT.id])&&byv[CURRENT.id].length;
964
  return `<div class="mrow" data-model="${esc(m.model)}">
965
  <span class="mname" title="${esc(m.model)}">${esc(m.model)}</span>
966
+ ${tags.join('')}${chips}
967
  <button class="mbtn" data-act="rerun">Re-run</button>
968
+ <button class="mbtn danger" data-act="delete"${hasHere?'':' disabled title="no score on this version"'}>Delete ${esc(CURRENT.id)} score</button>
969
+ <button class="mbtn danger" data-act="purge">Remove entirely</button>
970
  </div>`;}).join('');
971
  list.querySelectorAll('button[data-act]').forEach(b=>b.onclick=()=>manageAct(b));
972
+ const bm=document.getElementById('btnQueueMissing'), ba=document.getElementById('btnQueueAll');
973
+ bm.textContent=`Queue ${missing} missing on ${CURRENT.id}`;
974
+ bm.disabled=!missing;
975
+ bm.onclick=()=>requeue(true,bm);
976
+ ba.onclick=()=>requeue(false,ba);
977
  }catch(e){
978
  panel.hidden=true;
979
  list.innerHTML=`<div class="mrow" style="color:var(--ink-mute)">Could not load (${esc(String(e))}).</div>`;
 
982
 
983
  async function manageAct(btn){
984
  const row=btn.closest('.mrow'), model=row.dataset.model, act=btn.dataset.act;
985
+ const label=btn.textContent;
986
+ // 'delete' drops only the score on the version being viewed. 'purge' removes the
987
+ // queue entry and every version's score — the request is shared across versions,
988
+ // so purging while looking at v8 also takes the v7 row down.
989
+ if(act==='delete'&&!confirm(
990
+ `Remove ${model}'s ${CURRENT.id} score?\n\nIts queue entry stays, so it can be evaluated again. Scores on other versions are untouched.`))return;
991
+ if(act==='purge'&&!confirm(
992
+ `Remove ${model} entirely?\n\nThis deletes its queue entry AND its score on EVERY version, ${CURRENT.id} included. It cannot be undone — only a fresh submission would bring it back.`))return;
993
  row.querySelectorAll('button').forEach(b=>b.disabled=true);
994
+ btn.textContent=act==='rerun'?'Queuing…':'Removing…';
995
+ const url=act==='rerun'?'/api/model/rerun':'/api/model/delete';
996
+ const body={model,version:CURRENT.id};
997
+ if(act!=='rerun')body.scope=(act==='purge')?'all':'version';
998
  try{
999
+ const r=await fetch(url,{method:'POST',credentials:'same-origin',
1000
+ headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
1001
  const d=await r.json().catch(()=>({}));
1002
  if(r.ok){mmsg(d.message||'Done.','ok');await loadManage();
1003
+ if(act!=='rerun')setTimeout(()=>location.reload(),1200);}
1004
  else{mmsg(d.detail||d.message||('Error '+r.status),'err');
1005
  row.querySelectorAll('button').forEach(b=>b.disabled=false);
1006
+ btn.textContent=label;}
1007
  }catch(e){mmsg('Network error — please try again.','err');
1008
  row.querySelectorAll('button').forEach(b=>b.disabled=false);
1009
+ btn.textContent=label;}
1010
+ }
1011
+
1012
+ async function requeue(onlyMissing,btn){
1013
+ const what=onlyMissing?`every model with no ${CURRENT.id} score`:`ALL models, including the ${CURRENT.id} ones already scored`;
1014
+ if(!confirm(`Queue ${what} for re-evaluation?\n\nThey run on whichever version the worker has as its RUN_ID — make sure that is ${CURRENT.id} before starting it.`))return;
1015
+ const label=btn.textContent; btn.disabled=true; btn.textContent='Queuing…';
1016
+ try{
1017
+ const r=await fetch('/api/requeue-all',{method:'POST',credentials:'same-origin',
1018
+ headers:{'Content-Type':'application/json'},
1019
+ body:JSON.stringify({version:CURRENT.id,only_missing:onlyMissing})});
1020
+ const d=await r.json().catch(()=>({}));
1021
+ mmsg(r.ok?(d.message||'Queued.'):(d.detail||d.message||('Error '+r.status)),r.ok?'ok':'err');
1022
+ if(r.ok)await loadManage();
1023
+ }catch(e){mmsg('Network error — please try again.','err');}
1024
+ btn.disabled=false; btn.textContent=label;
1025
  }
1026
 
1027
  // show the pinned-column edge shadow only while the table is scrolled right
test_manage.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Exercise the real manage/delete/requeue routes with the Hub stubbed out."""
2
+ import sys, types, json, importlib.util
3
+
4
+ import os
5
+ os.environ["HF_TOKEN"] = "stub-token"
6
+ os.environ["LEADERBOARD_OWNER"] = "Mushari440"
7
+
8
+ # ---- fake Hub state -------------------------------------------------------
9
+ FILES = {
10
+ "Mushari440/results": {
11
+ "v7/org/results_alpha.json", "v7/org/results_beta.json",
12
+ "v8/org/results_alpha.json",
13
+ },
14
+ "Mushari440/results-private": set(),
15
+ "Mushari440/requests": {"org/alpha.json", "org/beta.json", "org/gamma.json"},
16
+ }
17
+ REQ = {
18
+ "org/alpha.json": {"model": "org/alpha", "status": "FINISHED"},
19
+ "org/beta.json": {"model": "org/beta", "status": "FINISHED"},
20
+ "org/gamma.json": {"model": "org/gamma", "status": "PENDING"},
21
+ }
22
+ COMMITS = []
23
+
24
+ class FakeApi:
25
+ def __init__(self, *a, **k): pass
26
+ def list_repo_files(self, repo_id=None, repo_type=None, *a, **k):
27
+ return sorted(FILES.get(repo_id or a[0] if a else repo_id, set()))
28
+ def delete_file(self, path_in_repo=None, repo_id=None, **k):
29
+ if path_in_repo not in FILES.get(repo_id, set()):
30
+ raise RuntimeError("404")
31
+ FILES[repo_id].discard(path_in_repo)
32
+ if repo_id == "Mushari440/requests":
33
+ REQ.pop(path_in_repo, None) # the queue entry lives in both fakes
34
+ COMMITS.append(("delete", repo_id, path_in_repo))
35
+ def upload_file(self, path_or_fileobj=None, path_in_repo=None, repo_id=None, **k):
36
+ body = json.loads(path_or_fileobj.decode() if isinstance(path_or_fileobj, bytes) else path_or_fileobj)
37
+ REQ[path_in_repo] = body
38
+ COMMITS.append(("upload", repo_id, path_in_repo, body.get("status")))
39
+
40
+ spec = importlib.util.spec_from_file_location("spaceapp", os.path.join(os.path.dirname(__file__), "app.py"))
41
+ app = importlib.util.module_from_spec(spec)
42
+ sys.modules["spaceapp"] = app
43
+ spec.loader.exec_module(app)
44
+
45
+ app.HfApi = FakeApi
46
+ app.api = FakeApi()
47
+ app._require_owner = lambda *a, **k: "Mushari440"
48
+ app._list_requests = lambda: [dict(v, _path=k) for k, v in REQ.items()]
49
+
50
+ fails = []
51
+ def check(name, got, want):
52
+ ok = got == want
53
+ if not ok:
54
+ fails.append(name)
55
+ print(f" FAIL {name}\n got {got}\n want {want}")
56
+ else:
57
+ print(f" PASS {name}")
58
+
59
+ R = types.SimpleNamespace(headers={}, cookies={}, session={})
60
+
61
+ print("manage reports presence per version")
62
+ rows = {m["model"]: m for m in app.manage(R, version="v8")["models"]}
63
+ check("alpha scored on v7 and v8", sorted(rows["alpha" if "alpha" in rows else "org/alpha"]["results_by_version"]), ["v7", "v8"])
64
+ check("beta scored on v7 only", sorted(rows["org/beta"]["results_by_version"]), ["v7"])
65
+ check("gamma scored nowhere", rows["org/gamma"]["results_by_version"], {})
66
+ check("result_in follows the asked version", rows["org/beta"]["result_in"], [])
67
+
68
+ print("\ndelete scope='version' touches only that version")
69
+ app.model_delete(R, app.ModelRef(model="org/alpha", version="v8", scope="version"))
70
+ check("v8 score gone", "v8/org/results_alpha.json" in FILES["Mushari440/results"], False)
71
+ check("v7 score untouched", "v7/org/results_alpha.json" in FILES["Mushari440/results"], True)
72
+ check("request kept", "org/alpha.json" in REQ, True)
73
+
74
+ print("\ndelete scope='all' removes the model everywhere")
75
+ app.model_delete(R, app.ModelRef(model="org/beta", version="v7", scope="all"))
76
+ check("v7 score gone", "v7/org/results_beta.json" in FILES["Mushari440/results"], False)
77
+ check("request gone", "org/beta.json" in REQ, False)
78
+
79
+ print("\nrequeue only_missing skips models already scored on the target version")
80
+ FILES["Mushari440/results"] = {"v7/org/results_alpha.json", "v8/org/results_alpha.json"}
81
+ REQ["org/alpha.json"] = {"model": "org/alpha", "status": "FINISHED"}
82
+ REQ["org/delta.json"] = {"model": "org/delta", "status": "FINISHED"}
83
+ out = app.requeue_all(R, app.RequeueRef(version="v8", only_missing=True))
84
+ check("alpha skipped (already on v8)", "org/alpha" in out["skipped"], True)
85
+ check("delta queued", "org/delta" in out["queued"], True)
86
+ check("gamma skipped (already PENDING)", "org/gamma" in out["skipped"], True)
87
+ check("delta status is RERUN", REQ["org/delta.json"]["status"], "RERUN")
88
+
89
+ print("\nrequeue only_missing=False re-queues scored models too")
90
+ REQ["org/alpha.json"] = {"model": "org/alpha", "status": "FINISHED"}
91
+ out = app.requeue_all(R, app.RequeueRef(version="v8", only_missing=False))
92
+ check("alpha queued this time", "org/alpha" in out["queued"], True)
93
+
94
+ print("\nFAILURES:", fails or "none")
95
+ sys.exit(1 if fails else 0)