Skip to content

Commit 4c09e16

Browse files
authored
Surface unranked leaderboard submissions (#70)
1 parent 886932d commit 4c09e16

2 files changed

Lines changed: 183 additions & 2 deletions

File tree

src/cmd/submissions.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub async fn list_submissions(
2626

2727
// Print each submission
2828
for sub in submissions {
29-
let status = if sub.done { "done" } else { "pending" };
29+
let status = list_status(sub.done, sub.runs.is_empty());
3030

3131
// Collect all GPU types and best score from runs
3232
let gpus: Vec<&str> = sub.runs.iter().map(|r| r.gpu_type.as_str()).collect();
@@ -77,8 +77,16 @@ pub async fn show_submission(cli_id: String, submission_id: i64, no_code: bool)
7777
println!("Submitted: {}", sub.submission_time);
7878
println!(
7979
"Status: {}",
80-
if sub.done { "done" } else { "pending" }
80+
detail_status(sub.done, sub.job.as_ref())
8181
);
82+
if let Some(error) = sub
83+
.job
84+
.as_ref()
85+
.and_then(|job| job.error.as_deref())
86+
.filter(|error| !error.is_empty())
87+
{
88+
println!("Job error: {}", error);
89+
}
8290

8391
if !sub.runs.is_empty() {
8492
println!("\nRuns:");
@@ -173,6 +181,22 @@ fn format_score(score: Option<f64>) -> String {
173181
.unwrap_or_else(|| "-".to_string())
174182
}
175183

184+
fn list_status(done: bool, has_no_ranked_runs: bool) -> &'static str {
185+
if !done {
186+
"pending"
187+
} else if has_no_ranked_runs {
188+
"not ranked"
189+
} else {
190+
"done"
191+
}
192+
}
193+
194+
fn detail_status(done: bool, job: Option<&crate::models::SubmissionJobStatus>) -> String {
195+
job.and_then(|job| job.status.as_deref())
196+
.unwrap_or(if done { "done" } else { "pending" })
197+
.to_string()
198+
}
199+
176200
#[cfg(test)]
177201
mod tests {
178202
use super::*;
@@ -203,6 +227,13 @@ mod tests {
203227
assert_eq!(format_score(None), "-");
204228
}
205229

230+
#[test]
231+
fn test_list_status_marks_done_without_ranked_runs() {
232+
assert_eq!(list_status(false, true), "pending");
233+
assert_eq!(list_status(true, true), "not ranked");
234+
assert_eq!(list_status(true, false), "done");
235+
}
236+
206237
#[test]
207238
fn test_truncate() {
208239
assert_eq!(truncate("short", 19), "short");

src/service/mod.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,9 @@ async fn submit_solution_background<P: AsRef<Path>>(
10901090
}
10911091

10921092
if details.done {
1093+
if let Some(error) = leaderboard_completion_error(&details, submission_mode, gpu) {
1094+
return Err(anyhow!(error));
1095+
}
10931096
return format_submission_details(&details);
10941097
}
10951098

@@ -1133,6 +1136,86 @@ fn leaderboard_score_summary(details: &SubmissionDetails) -> Option<String> {
11331136
}
11341137
}
11351138

1139+
fn same_gpu(run_gpu: &str, requested_gpu: &str) -> bool {
1140+
run_gpu.eq_ignore_ascii_case(requested_gpu)
1141+
}
1142+
1143+
fn has_qualifying_leaderboard_result(details: &SubmissionDetails, gpu: &str) -> bool {
1144+
let has_public_score = details.runs.iter().any(|run| {
1145+
run.mode == "leaderboard"
1146+
&& !run.secret
1147+
&& same_gpu(&run.runner, gpu)
1148+
&& run.passed
1149+
&& run.score.is_some()
1150+
});
1151+
1152+
let has_secret_leaderboard_pass = details.runs.iter().any(|run| {
1153+
run.mode == "leaderboard" && run.secret && same_gpu(&run.runner, gpu) && run.passed
1154+
});
1155+
1156+
let has_secret_failure = details
1157+
.runs
1158+
.iter()
1159+
.any(|run| run.secret && same_gpu(&run.runner, gpu) && !run.passed);
1160+
1161+
has_public_score && has_secret_leaderboard_pass && !has_secret_failure
1162+
}
1163+
1164+
fn leaderboard_completion_error(
1165+
details: &SubmissionDetails,
1166+
submission_mode: &str,
1167+
gpu: &str,
1168+
) -> Option<String> {
1169+
if !submission_mode.eq_ignore_ascii_case("leaderboard") {
1170+
return None;
1171+
}
1172+
1173+
if has_qualifying_leaderboard_result(details, gpu) {
1174+
return None;
1175+
}
1176+
1177+
if let Some(error) = details
1178+
.job
1179+
.as_ref()
1180+
.and_then(|job| job.error.as_deref())
1181+
.filter(|error| !error.is_empty())
1182+
{
1183+
return Some(format!("Submission {}: {}", details.id, error));
1184+
}
1185+
1186+
let secret_failed = details
1187+
.runs
1188+
.iter()
1189+
.any(|run| run.secret && same_gpu(&run.runner, gpu) && !run.passed);
1190+
1191+
if secret_failed {
1192+
return Some(format!(
1193+
"Submission {} failed secret validation on {}; it will not appear on the leaderboard",
1194+
details.id, gpu
1195+
));
1196+
}
1197+
1198+
let has_public_leaderboard_score = details.runs.iter().any(|run| {
1199+
run.mode == "leaderboard"
1200+
&& !run.secret
1201+
&& same_gpu(&run.runner, gpu)
1202+
&& run.passed
1203+
&& run.score.is_some()
1204+
});
1205+
1206+
if has_public_leaderboard_score {
1207+
Some(format!(
1208+
"Submission {} did not pass secret leaderboard validation on {}; it will not appear on the leaderboard",
1209+
details.id, gpu
1210+
))
1211+
} else {
1212+
Some(format!(
1213+
"Submission {} completed without a leaderboard score on {}; it will not appear on the leaderboard",
1214+
details.id, gpu
1215+
))
1216+
}
1217+
}
1218+
11361219
fn format_submission_details(details: &SubmissionDetails) -> Result<String> {
11371220
let runs: Vec<Value> = details
11381221
.runs
@@ -1150,11 +1233,19 @@ fn format_submission_details(details: &SubmissionDetails) -> Result<String> {
11501233
})
11511234
.collect();
11521235

1236+
let job = details.job.as_ref().map(|job| {
1237+
serde_json::json!({
1238+
"status": job.status,
1239+
"error": job.error,
1240+
})
1241+
});
1242+
11531243
let json = serde_json::to_string_pretty(&serde_json::json!({
11541244
"submission_id": details.id,
11551245
"leaderboard": details.leaderboard_name,
11561246
"file_name": details.file_name,
11571247
"done": details.done,
1248+
"job": job,
11581249
"runs": runs,
11591250
}))
11601251
.map_err(|e| anyhow!("Failed to format submission result: {}", e))?;
@@ -1697,6 +1788,12 @@ mod tests {
16971788
}
16981789
}
16991790

1791+
fn failed_run(mode: &str, secret: bool, score: Option<f64>) -> SubmissionRun {
1792+
let mut r = run(mode, secret, score);
1793+
r.passed = false;
1794+
r
1795+
}
1796+
17001797
fn details(runs: Vec<SubmissionRun>) -> SubmissionDetails {
17011798
SubmissionDetails {
17021799
id: 1,
@@ -1762,4 +1859,57 @@ mod tests {
17621859
// format_submission_details still works, just without a summary header.
17631860
assert!(format_submission_details(&d).unwrap().starts_with('{'));
17641861
}
1862+
1863+
#[test]
1864+
fn test_leaderboard_completion_error_none_for_qualified_submission() {
1865+
let d = details(vec![
1866+
run("leaderboard", false, Some(0.0066)),
1867+
run("leaderboard", true, Some(0.0018)),
1868+
]);
1869+
1870+
assert!(leaderboard_completion_error(&d, "leaderboard", "B200").is_none());
1871+
}
1872+
1873+
#[test]
1874+
fn test_leaderboard_completion_error_for_secret_failure() {
1875+
let d = details(vec![
1876+
run("leaderboard", false, Some(0.0066)),
1877+
failed_run("benchmark", true, None),
1878+
]);
1879+
1880+
let error = leaderboard_completion_error(&d, "leaderboard", "B200")
1881+
.expect("expected secret validation failure");
1882+
1883+
assert!(error.contains("failed secret validation"));
1884+
assert!(error.contains("will not appear on the leaderboard"));
1885+
}
1886+
1887+
#[test]
1888+
fn test_leaderboard_completion_error_for_missing_secret_leaderboard() {
1889+
let d = details(vec![run("leaderboard", false, Some(0.0066))]);
1890+
1891+
let error = leaderboard_completion_error(&d, "leaderboard", "B200")
1892+
.expect("expected missing secret validation failure");
1893+
1894+
assert!(error.contains("did not pass secret leaderboard validation"));
1895+
assert!(error.contains("will not appear on the leaderboard"));
1896+
}
1897+
1898+
#[test]
1899+
fn test_leaderboard_completion_error_for_missing_public_score() {
1900+
let d = details(vec![run("leaderboard", false, None)]);
1901+
1902+
let error = leaderboard_completion_error(&d, "leaderboard", "B200")
1903+
.expect("expected missing score failure");
1904+
1905+
assert!(error.contains("completed without a leaderboard score"));
1906+
assert!(error.contains("will not appear on the leaderboard"));
1907+
}
1908+
1909+
#[test]
1910+
fn test_leaderboard_completion_error_ignores_non_leaderboard_modes() {
1911+
let d = details(vec![run("test", false, None)]);
1912+
1913+
assert!(leaderboard_completion_error(&d, "test", "B200").is_none());
1914+
}
17651915
}

0 commit comments

Comments
 (0)