-
Notifications
You must be signed in to change notification settings - Fork 16
feat(site): Prism v2.1-only public leaderboard #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,11 +11,6 @@ use futures::future::join_all; | |
| use serde::Deserialize; | ||
| use serde_json::{json, Value}; | ||
|
|
||
| use crate::prism_enrich::{ | ||
| enrich_leaderboard_row_from_detail_with_zone, enrich_submission_from_detail_with_zone, | ||
| map_benchmarks, pin_id_from_payload, prism_reference_baselines, | ||
| prism_submission_detail_with_zone, | ||
| }; | ||
| use crate::state::SiteState; | ||
| use crate::upstream::{self, DESIGN, PRISM}; | ||
| use site_data::map::{ | ||
|
|
@@ -25,6 +20,11 @@ use site_data::map::{ | |
| prism_bpb_leaderboard, prism_submission, prism_telemetry, prism_window, | ||
| submission_matches_query, uid_index_from_hotkeys, | ||
| }; | ||
| use site_prism::{ | ||
| enrich_leaderboard_row_from_detail_with_zone, enrich_submission_from_detail_with_zone, | ||
| infer_recipe_era_with_live, map_benchmarks, payload_is_v21_contest, pin_id_from_payload, | ||
| prism_reference_baselines, prism_submission_detail_with_zone, | ||
| }; | ||
| use site_types::coding_arena; | ||
| use site_types::page_slice; | ||
| use site_types::{ | ||
|
|
@@ -454,17 +454,30 @@ async fn prism_leaderboard_json( | |
| .collect(); | ||
| ids.truncate(PRISM_CHAMPION_DETAIL_FANOUT); | ||
| let details = fetch_prism_details(st, &ids).await; | ||
| let live_recipe = fetch_prism_recipe(st) | ||
| .await | ||
| .as_ref() | ||
| .and_then(|r| r.get("version")) | ||
| .and_then(Value::as_str) | ||
| .map(str::to_owned); | ||
| let live = live_recipe.as_deref(); | ||
| for row in &mut board { | ||
| if let Some(id) = row.submission_id.as_ref() { | ||
| if let Some(fan) = details.get(id) { | ||
| enrich_leaderboard_row_from_detail_with_zone(row, &fan.detail, fan.zone_a.as_ref()); | ||
| row.recipe_era = Some(infer_recipe_era_with_live(&fan.detail, live)); | ||
| if row.recipe_era == Some(RecipeEra::V21) { | ||
| row.run.weight_eligible = Some(true); | ||
| } | ||
| } | ||
| } | ||
| if row.recipe_era.is_none() { | ||
| // Historical 1.x champions without AutoModel signals → legacy tab. | ||
| row.recipe_era = Some(RecipeEra::Legacy); | ||
| } | ||
| } | ||
| // Public board is the live v2.1 contest only. Empty + burn is honest. | ||
| board.retain(|r| r.recipe_era == Some(RecipeEra::V21)); | ||
| decorate_leaderboard(st, &mut board); | ||
| if let Some(needle) = q.filter(|s| !s.trim().is_empty()) { | ||
| board.retain(|r| leaderboard_matches_query(r, needle)); | ||
|
|
@@ -478,6 +491,10 @@ async fn prism_leaderboard_json( | |
| "pageCount": page_out.page_count, | ||
| "epoch": epoch, | ||
| "metric": "score", | ||
| "competitionId": "prism-v2.1", | ||
| "scoringGeneration": 21, | ||
| "recipeVersion": live_recipe.as_deref().unwrap_or("2.1.0"), | ||
| "waitingForFirst": page_out.total == 0, | ||
|
Comment on lines
+494
to
+497
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win Do not publish a recipe version that was never read. When 🛡️ Proposed fix- "recipeVersion": live_recipe.as_deref().unwrap_or("2.1.0"),
+ "recipeVersion": live_recipe,🤖 Prompt for AI Agents |
||
| "updatedAt": now_iso(), | ||
| }) | ||
| } | ||
|
|
@@ -597,9 +614,20 @@ async fn get_submissions( | |
| let mut ids: Vec<String> = items.iter().map(|s| s.id.clone()).collect(); | ||
| ids.truncate(PRISM_CHAMPION_DETAIL_FANOUT); | ||
| let details = fetch_prism_details(&st, &ids).await; | ||
| let live_recipe = fetch_prism_recipe(&st) | ||
| .await | ||
| .as_ref() | ||
| .and_then(|r| r.get("version")) | ||
| .and_then(Value::as_str) | ||
| .map(str::to_owned); | ||
| let live = live_recipe.as_deref(); | ||
| for item in &mut items { | ||
| if let Some(fan) = details.get(&item.id) { | ||
| enrich_submission_from_detail_with_zone(item, &fan.detail, fan.zone_a.as_ref()); | ||
| item.recipe_era = Some(infer_recipe_era_with_live(&fan.detail, live)); | ||
| if item.recipe_era == Some(RecipeEra::V21) { | ||
| item.run.weight_eligible = Some(true); | ||
| } | ||
| } else if item.recipe_era.is_none() { | ||
| // Pre-2.0 / unknown → legacy so era tabs are not empty. | ||
| item.recipe_era = Some(RecipeEra::Legacy); | ||
|
|
@@ -739,19 +767,39 @@ async fn get_prism_window(State(st): State<SiteState>) -> impl IntoResponse { | |
| ids.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal)); | ||
| ids.truncate(PRISM_WINDOW_TELEMETRY_FANOUT); | ||
| let mut telemetry = HashMap::new(); | ||
| let mut v21_ids = std::collections::HashSet::new(); | ||
| for row in &rows { | ||
| if payload_is_v21_contest(row) { | ||
| if let Some(id) = row.get("id").and_then(Value::as_str) { | ||
| v21_ids.insert(id.to_owned()); | ||
| } | ||
| } | ||
| } | ||
| for (_, id) in ids { | ||
| if let Some(detail) = | ||
| upstream::get_json_opt(&st, PRISM, &format!("/v1/submissions/{id}")).await | ||
| { | ||
| if payload_is_v21_contest(&detail) { | ||
| v21_ids.insert(id.clone()); | ||
| } | ||
| if let Some(t) = prism_telemetry(&detail) { | ||
| telemetry.insert(id, t); | ||
| } | ||
| } | ||
| } | ||
| let v21_rows: Vec<Value> = rows | ||
| .into_iter() | ||
| .filter(|r| { | ||
| r.get("id") | ||
| .and_then(Value::as_str) | ||
| .is_some_and(|id| v21_ids.contains(id)) | ||
| || payload_is_v21_contest(r) | ||
| }) | ||
| .collect(); | ||
| Json(prism_window( | ||
| recipe.as_ref(), | ||
| status.as_ref(), | ||
| &rows, | ||
| &v21_rows, | ||
| &telemetry, | ||
| )) | ||
| } | ||
|
|
@@ -1033,6 +1081,9 @@ mod tests { | |
| mount_design_site_mocks(design).await; | ||
| mount_prism_list_mocks(prism).await; | ||
| mount_prism_detail_mock(prism).await; | ||
| // Re-mount the list after per-id stubs so later GETs (`?limit=`) still | ||
| // hit the collection (wiremock first-match can steal `/v1/submissions*`). | ||
| mount_prism_list_collection(prism).await; | ||
| } | ||
|
|
||
| async fn mount_design_site_mocks(design: &MockServer) { | ||
|
|
@@ -1097,6 +1148,22 @@ mod tests { | |
| }))) | ||
| .mount(prism) | ||
| .await; | ||
| Mock::given(method("GET")) | ||
| .and(path("/v1/recipe")) | ||
| .respond_with(ResponseTemplate::new(200).set_body_json(json!({ | ||
| "version": "2.1.0", | ||
| "competition_id": "prism-v2.1", | ||
| "scoring_generation": 21, | ||
| "dataset_ref": "HuggingFaceFW/fineweb-edu@sample/10BT", | ||
| "max_params": 1_000_000_000_u64, | ||
| "train_hours_cap": 4.0 | ||
| }))) | ||
| .mount(prism) | ||
| .await; | ||
| mount_prism_list_collection(prism).await; | ||
| } | ||
|
|
||
| async fn mount_prism_list_collection(prism: &MockServer) { | ||
| Mock::given(method("GET")) | ||
| .and(path("/v1/submissions")) | ||
| .respond_with(ResponseTemplate::new(200).set_body_json(json!({ | ||
|
|
@@ -1111,6 +1178,17 @@ mod tests { | |
| "score": {"kind":"score","value": 900}, | ||
| "created_at_ms": 1_700_000_000_000_u64, | ||
| "updated_at_ms": 1_700_000_000_000_u64 | ||
| }, { | ||
| "id": "sub-old20", | ||
| "miner_hotkey": "dd".repeat(32), | ||
| "epoch": 2, | ||
| "status": "terminated", | ||
| "label": "old-2.0", | ||
| "bpb": 0.9, | ||
| "n_params": 350_000_000_u64, | ||
| "score": {"kind":"score","value": 800}, | ||
| "created_at_ms": 1_700_000_000_000_u64, | ||
| "updated_at_ms": 1_700_000_000_000_u64 | ||
| }, { | ||
| "id": "sub-running", | ||
| "miner_hotkey": "cc".repeat(32), | ||
|
|
@@ -1157,9 +1235,15 @@ mod tests { | |
| "n_params": 12_000_000_u64, | ||
| "score": {"kind":"score","value": 900}, | ||
| "metrics": { | ||
| "recipe": "2.0.0", | ||
| "recipe": "2.1.0", | ||
| "competition_id": "prism-v2.1", | ||
| "scoring_generation": 21, | ||
| "bpb": 1.25, | ||
| "tokens_seen": 2048, | ||
| "org.g7.throughput_toks_s": {"value": 1200.0}, | ||
| "org.diag.mfu_achieved": {"value": 0.23}, | ||
| "org.diag.flops_attested": {"value": 1.5e18}, | ||
| "bits_per_byte": {"value": 0.88}, | ||
| "wall_clock_seconds": 12.0, | ||
| "gpu_type": "SIM", | ||
| "n_params": 12_000_000_u64, | ||
|
|
@@ -1205,6 +1289,29 @@ mod tests { | |
| }))) | ||
| .mount(prism) | ||
| .await; | ||
| Mock::given(method("GET")) | ||
| .and(path("/v1/submissions/sub-old20")) | ||
| .respond_with(ResponseTemplate::new(200).set_body_json(json!({ | ||
| "submission": { | ||
| "id": "sub-old20", | ||
| "miner_hotkey": "dd".repeat(32), | ||
| "epoch": 2, | ||
| "status": "terminated", | ||
| "label": "old-2.0", | ||
| "bpb": 0.9, | ||
| "n_params": 350_000_000_u64, | ||
| "score": {"kind":"score","value": 800}, | ||
| "created_at_ms": 1_700_000_000_000_u64, | ||
| "metrics": { | ||
| "recipe": "2.0.0", | ||
| "bpb": 0.9, | ||
| "org.g2.hellaswag_acc": {"value": 0.99} | ||
| } | ||
| }, | ||
| "events": [] | ||
| }))) | ||
| .mount(prism) | ||
| .await; | ||
| } | ||
|
|
||
| #[tokio::test] | ||
|
|
@@ -1328,14 +1435,25 @@ mod tests { | |
|
|
||
| let (s, v) = call(app.clone(), "/v1/site/arenas/prism/submissions").await; | ||
| assert_eq!(s, StatusCode::OK, "{v}"); | ||
| assert_eq!(v["total"], 2, "default scope=all includes in-flight: {v}"); | ||
| assert_eq!(v["items"][0]["recipeEra"], "automodel"); | ||
| assert_eq!( | ||
| v["total"], 3, | ||
| "default scope=all includes in-flight + closed 2.0: {v}" | ||
| ); | ||
| assert_eq!(v["items"][0]["recipeEra"], "v21"); | ||
| assert_eq!(v["items"][0]["pinId"], "automodel@v0.5.0"); | ||
| assert_eq!(v["items"][0]["weightEligible"], true); | ||
| assert_eq!(v["items"][0]["competitionId"], "prism-v2.1"); | ||
| assert_eq!(v["items"][0]["tokens"], 2048.0); | ||
| assert_eq!(v["items"][0]["tokensPerSec"], 1200.0); | ||
| assert_eq!(v["items"][0]["mfu"], 0.23); | ||
| assert_eq!(v["items"][0]["benchmarks"]["hellaswag"], 0.31); | ||
| assert_eq!(v["items"][0]["evalGroups"].as_array().unwrap().len(), 2); | ||
| assert_eq!(v["items"][1]["id"], "sub-running"); | ||
| assert_eq!(v["items"][1]["status"], "pending"); | ||
| assert_eq!(v["items"][1]["recipeEra"], "legacy"); | ||
| assert_eq!(v["items"][1]["id"], "sub-old20"); | ||
| assert_eq!(v["items"][1]["recipeEra"], "automodel"); | ||
| assert_eq!(v["items"][1]["weightEligible"], false); | ||
| assert_eq!(v["items"][2]["id"], "sub-running"); | ||
| assert_eq!(v["items"][2]["status"], "pending"); | ||
| assert_eq!(v["items"][2]["recipeEra"], "legacy"); | ||
|
|
||
| // scope=champions keeps Score>0 gallery; default scope=all includes in-flight. | ||
| let (s, v) = call( | ||
|
|
@@ -1344,18 +1462,26 @@ mod tests { | |
| ) | ||
| .await; | ||
| assert_eq!(s, StatusCode::OK, "{v}"); | ||
| assert_eq!(v["total"], 1, "{v}"); | ||
| assert_eq!( | ||
| v["total"], 2, | ||
| "scope=champions should keep Score>0 rows (v21 + closed 2.0): {v}" | ||
| ); | ||
|
|
||
| let (s, v) = call(app.clone(), "/v1/site/arenas/prism/leaderboard").await; | ||
| assert_eq!(s, StatusCode::OK, "{v}"); | ||
| assert_eq!(v["competitionId"], "prism-v2.1"); | ||
| assert_eq!(v["scoringGeneration"], 21); | ||
| assert_eq!(v["waitingForFirst"], false); | ||
| assert_eq!(v["total"], 1, "closed 2.0 champion must not rank: {v}"); | ||
| assert_eq!(v["items"][0]["submissionId"], "sub1"); | ||
| assert_eq!(v["items"][0]["recipeEra"], "automodel"); | ||
| assert_eq!(v["items"][0]["recipeEra"], "v21"); | ||
| assert_eq!(v["items"][0]["weightEligible"], true); | ||
| assert_eq!(v["items"][0]["benchmarks"]["piqa"], 0.62); | ||
|
|
||
| let (s, v) = call(app.clone(), "/v1/site/arenas/prism/submissions/sub1").await; | ||
| assert_eq!(s, StatusCode::OK, "{v}"); | ||
| assert_eq!(v["id"], "sub1"); | ||
| assert_eq!(v["recipeEra"], "automodel"); | ||
| assert_eq!(v["recipeEra"], "v21"); | ||
| assert_eq!(v["pinId"], "automodel@v0.5.0"); | ||
| assert_eq!(v["eval"]["status"], "scored"); | ||
| assert_eq!(v["eval"]["groups"].as_array().unwrap().len(), 2); | ||
|
|
@@ -1523,7 +1649,7 @@ mod tests { | |
| Mock::given(method("GET")) | ||
| .and(path("/v1/recipe")) | ||
| .respond_with(ResponseTemplate::new(200).set_body_json(json!({ | ||
| "version": "1.0.1", | ||
| "version": "2.1.0", | ||
| "dataset_ref": "ds@pin", | ||
| "pin_hex": "abc" | ||
| }))) | ||
|
|
@@ -1540,8 +1666,8 @@ mod tests { | |
| .and(path("/v1/submissions")) | ||
| .respond_with(ResponseTemplate::new(200).set_body_json(json!({ | ||
| "submissions": [ | ||
| {"id":"x1","status":"terminated","bpb":2.0,"label":"a"}, | ||
| {"id":"x2","status":"terminated","bpb":1.0,"label":"b"} | ||
| {"id":"x1","status":"terminated","bpb":2.0,"label":"a","metrics":{"recipe":"2.1.0"}}, | ||
| {"id":"x2","status":"terminated","bpb":1.0,"label":"b","metrics":{"recipe":"2.1.0"}} | ||
| ] | ||
| }))) | ||
| .mount(&prism) | ||
|
|
@@ -1553,6 +1679,8 @@ mod tests { | |
| "id": "x2", | ||
| "bpb": 1.0, | ||
| "metrics": { | ||
| "recipe": "2.1.0", | ||
| "competition_id": "prism-v2.1", | ||
| "bpb": 1.0, | ||
| "n_params": 12_000_000_u64, | ||
| "telemetry": { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,6 @@ | |
| #![allow(clippy::doc_markdown)] | ||
|
|
||
| mod handlers; | ||
| mod prism_enrich; | ||
| mod state; | ||
| mod upstream; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,7 +53,11 @@ pub async fn get_json( | |
| .map_err(|e| UpstreamError::Transport(e.to_string()))?; | ||
| let status = resp.status(); | ||
| if !status.is_success() { | ||
| st.registry.record_failure(backend.id); | ||
| // Optional fan-out (diff / zone-A) often 404s; do not mark the | ||
| // challenge backend unhealthy or later list/leaderboard reads go dark. | ||
| if status.as_u16() != 404 { | ||
| st.registry.record_failure(backend.id); | ||
| } | ||
|
Comment on lines
+56
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Limit the 404 exception to optional endpoints.
🤖 Prompt for AI Agents |
||
| return Err(UpstreamError::Transport(format!( | ||
| "upstream {challenge_id} {path} → {status}" | ||
| ))); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
v2.1 classification depends on the bounded detail fan-out. Both Prism handlers truncate
idstoPRISM_CHAMPION_DETAIL_FANOUT(24) and then deriverecipe_eraonly from the fetched detail payloads. Rows beyond that limit keepLegacy. Classify membership from the upstream list row withpayload_is_v21_contest, asget_prism_windowalready does, and use the detail fan-out only for enrichment.crates/site-api/src/handlers.rs#L457-L480: build a v2.1 id set fromrowsbefore the loop, and applyretainagainst that set so eligible champions past index 24 stay on the board andtotal/pageCountare not capped at 24.crates/site-api/src/handlers.rs#L617-L630: apply the same list-row classification before theLegacyfallback so submissions past index 24 are not mislabeled, and extract the shared "fetch live recipe, classify era, setweight_eligible" helper used by both handlers.📍 Affects 1 file
crates/site-api/src/handlers.rs#L457-L480(this comment)crates/site-api/src/handlers.rs#L617-L630🤖 Prompt for AI Agents