Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Commit ·
a5fbf6b
1
Parent(s): b60e9d6
added email section
Browse files- app.py +15 -2
- constants.py +1 -0
- init.py +10 -1
- job_queue.py +64 -13
app.py
CHANGED
|
@@ -213,6 +213,7 @@ def _model_hub_page_link(model_id: str) -> str:
|
|
| 213 |
def submit_model(
|
| 214 |
model_id: str,
|
| 215 |
submission_notes: str,
|
|
|
|
| 216 |
extra_requirements: str,
|
| 217 |
setup_script: str,
|
| 218 |
custom_script: str,
|
|
@@ -255,6 +256,11 @@ def submit_model(
|
|
| 255 |
if not on_hub:
|
| 256 |
return styled_error(f"Model '{model_id}' {err_msg}")
|
| 257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
script_hint = ""
|
| 259 |
if (custom_script or "").strip():
|
| 260 |
dep_warn = job_queue.custom_script_deprecated_api_warning(custom_script)
|
|
@@ -275,6 +281,7 @@ def submit_model(
|
|
| 275 |
model_id,
|
| 276 |
family_id,
|
| 277 |
submission_notes=submission_notes or "",
|
|
|
|
| 278 |
extra_requirements=extra_requirements or "",
|
| 279 |
setup_script=setup_script or "",
|
| 280 |
custom_script=custom_script or "",
|
|
@@ -613,6 +620,11 @@ with gr.Blocks(title=APP_TITLE, theme=_theme, css=LEADERBOARD_CSS) as demo:
|
|
| 613 |
lines=2,
|
| 614 |
max_length=4000,
|
| 615 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 616 |
|
| 617 |
status_output = gr.HTML()
|
| 618 |
gr.Markdown(
|
|
@@ -635,8 +647,8 @@ with gr.Blocks(title=APP_TITLE, theme=_theme, css=LEADERBOARD_CSS) as demo:
|
|
| 635 |
)
|
| 636 |
return extra, setup_out, custom
|
| 637 |
|
| 638 |
-
def _submit_and_refresh(model_id, notes, reqs, setup, script, recipe, gated):
|
| 639 |
-
status = submit_model(model_id, notes, reqs, setup, script, recipe, gated)
|
| 640 |
try:
|
| 641 |
nxt = job_queue.next_up_html()
|
| 642 |
except Exception as exc:
|
|
@@ -660,6 +672,7 @@ with gr.Blocks(title=APP_TITLE, theme=_theme, css=LEADERBOARD_CSS) as demo:
|
|
| 660 |
inputs=[
|
| 661 |
model_input,
|
| 662 |
notes_input,
|
|
|
|
| 663 |
requirements_input,
|
| 664 |
setup_script_input,
|
| 665 |
script_input,
|
|
|
|
| 213 |
def submit_model(
|
| 214 |
model_id: str,
|
| 215 |
submission_notes: str,
|
| 216 |
+
contact_email: str,
|
| 217 |
extra_requirements: str,
|
| 218 |
setup_script: str,
|
| 219 |
custom_script: str,
|
|
|
|
| 256 |
if not on_hub:
|
| 257 |
return styled_error(f"Model '{model_id}' {err_msg}")
|
| 258 |
|
| 259 |
+
try:
|
| 260 |
+
job_queue.sanitize_contact_email(contact_email)
|
| 261 |
+
except ValueError:
|
| 262 |
+
return styled_error("Please enter a valid email address.")
|
| 263 |
+
|
| 264 |
script_hint = ""
|
| 265 |
if (custom_script or "").strip():
|
| 266 |
dep_warn = job_queue.custom_script_deprecated_api_warning(custom_script)
|
|
|
|
| 281 |
model_id,
|
| 282 |
family_id,
|
| 283 |
submission_notes=submission_notes or "",
|
| 284 |
+
contact_email=contact_email or "",
|
| 285 |
extra_requirements=extra_requirements or "",
|
| 286 |
setup_script=setup_script or "",
|
| 287 |
custom_script=custom_script or "",
|
|
|
|
| 620 |
lines=2,
|
| 621 |
max_length=4000,
|
| 622 |
)
|
| 623 |
+
email_input = gr.Textbox(
|
| 624 |
+
label="Contact email (optional)",
|
| 625 |
+
placeholder="you@example.com",
|
| 626 |
+
max_length=254,
|
| 627 |
+
)
|
| 628 |
|
| 629 |
status_output = gr.HTML()
|
| 630 |
gr.Markdown(
|
|
|
|
| 647 |
)
|
| 648 |
return extra, setup_out, custom
|
| 649 |
|
| 650 |
+
def _submit_and_refresh(model_id, notes, email, reqs, setup, script, recipe, gated):
|
| 651 |
+
status = submit_model(model_id, notes, email, reqs, setup, script, recipe, gated)
|
| 652 |
try:
|
| 653 |
nxt = job_queue.next_up_html()
|
| 654 |
except Exception as exc:
|
|
|
|
| 672 |
inputs=[
|
| 673 |
model_input,
|
| 674 |
notes_input,
|
| 675 |
+
email_input,
|
| 676 |
requirements_input,
|
| 677 |
setup_script_input,
|
| 678 |
script_input,
|
constants.py
CHANGED
|
@@ -248,6 +248,7 @@ client = Client("__SPACE_ID__")
|
|
| 248 |
result = client.predict(
|
| 249 |
"openai/whisper-tiny", # model id
|
| 250 |
"", # optional notes
|
|
|
|
| 251 |
"", # extra requirements
|
| 252 |
"", # setup script
|
| 253 |
"", # custom evaluator
|
|
|
|
| 248 |
result = client.predict(
|
| 249 |
"openai/whisper-tiny", # model id
|
| 250 |
"", # optional notes
|
| 251 |
+
"", # contact email
|
| 252 |
"", # extra requirements
|
| 253 |
"", # setup script
|
| 254 |
"", # custom evaluator
|
init.py
CHANGED
|
@@ -63,6 +63,7 @@ CSV_FIELDS = [
|
|
| 63 |
"eval_rtf",
|
| 64 |
"num_params",
|
| 65 |
"submission_notes",
|
|
|
|
| 66 |
"submitted_at",
|
| 67 |
]
|
| 68 |
|
|
@@ -111,6 +112,7 @@ def leaderboard_row_from_eval_result(
|
|
| 111 |
result: dict,
|
| 112 |
submitted_at_iso: str,
|
| 113 |
submission_notes: str = "",
|
|
|
|
| 114 |
) -> dict:
|
| 115 |
"""Build a full CSV row; scenario keys missing from `result` are left empty (future metrics)."""
|
| 116 |
row = {k: "" for k in CSV_FIELDS}
|
|
@@ -156,6 +158,7 @@ def leaderboard_row_from_eval_result(
|
|
| 156 |
if k in result and result[k] is not None and result[k] != "":
|
| 157 |
row[k] = str(result[k])
|
| 158 |
row["submission_notes"] = (submission_notes or "").strip()[:4000]
|
|
|
|
| 159 |
row["submitted_at"] = submitted_at_iso
|
| 160 |
return row
|
| 161 |
|
|
@@ -164,6 +167,7 @@ def merge_leaderboard_row_from_eval_result(
|
|
| 164 |
result: dict,
|
| 165 |
submitted_at_iso: str,
|
| 166 |
submission_notes: str = "",
|
|
|
|
| 167 |
) -> None:
|
| 168 |
"""
|
| 169 |
Merge evaluation output into an existing leaderboard row (partial re-eval).
|
|
@@ -186,7 +190,7 @@ def merge_leaderboard_row_from_eval_result(
|
|
| 186 |
break
|
| 187 |
|
| 188 |
patch = leaderboard_row_from_eval_result(
|
| 189 |
-
result, submitted_at_iso, submission_notes=submission_notes
|
| 190 |
)
|
| 191 |
|
| 192 |
if match_idx is None:
|
|
@@ -204,6 +208,8 @@ def merge_leaderboard_row_from_eval_result(
|
|
| 204 |
row["num_params"] = patch["num_params"]
|
| 205 |
if (submission_notes or "").strip():
|
| 206 |
row["submission_notes"] = patch["submission_notes"]
|
|
|
|
|
|
|
| 207 |
normalize_legacy_csv_row(row)
|
| 208 |
rows[match_idx] = row
|
| 209 |
|
|
@@ -286,6 +292,7 @@ def load_raw_results(*, refresh: bool = False) -> list[dict]:
|
|
| 286 |
if not row.get("eval_family"):
|
| 287 |
row["eval_family"] = "N/A"
|
| 288 |
row.setdefault("submission_notes", "")
|
|
|
|
| 289 |
normalize_legacy_csv_row(row)
|
| 290 |
_raw_results_cache = results
|
| 291 |
return [dict(r) for r in results]
|
|
@@ -482,6 +489,7 @@ def raw_rows_for_version(version: str) -> list[dict]:
|
|
| 482 |
if not row.get("eval_family"):
|
| 483 |
row["eval_family"] = "N/A"
|
| 484 |
row.setdefault("submission_notes", "")
|
|
|
|
| 485 |
normalize_legacy_csv_row(row)
|
| 486 |
out.append(row)
|
| 487 |
return out
|
|
@@ -636,6 +644,7 @@ def raw_rows_for_version(version: str) -> list[dict]:
|
|
| 636 |
if not row.get("eval_family"):
|
| 637 |
row["eval_family"] = "N/A"
|
| 638 |
row.setdefault("submission_notes", "")
|
|
|
|
| 639 |
normalize_legacy_csv_row(row)
|
| 640 |
out.append(row)
|
| 641 |
return out
|
|
|
|
| 63 |
"eval_rtf",
|
| 64 |
"num_params",
|
| 65 |
"submission_notes",
|
| 66 |
+
"contact_email",
|
| 67 |
"submitted_at",
|
| 68 |
]
|
| 69 |
|
|
|
|
| 112 |
result: dict,
|
| 113 |
submitted_at_iso: str,
|
| 114 |
submission_notes: str = "",
|
| 115 |
+
contact_email: str = "",
|
| 116 |
) -> dict:
|
| 117 |
"""Build a full CSV row; scenario keys missing from `result` are left empty (future metrics)."""
|
| 118 |
row = {k: "" for k in CSV_FIELDS}
|
|
|
|
| 158 |
if k in result and result[k] is not None and result[k] != "":
|
| 159 |
row[k] = str(result[k])
|
| 160 |
row["submission_notes"] = (submission_notes or "").strip()[:4000]
|
| 161 |
+
row["contact_email"] = (contact_email or "").strip()[:254]
|
| 162 |
row["submitted_at"] = submitted_at_iso
|
| 163 |
return row
|
| 164 |
|
|
|
|
| 167 |
result: dict,
|
| 168 |
submitted_at_iso: str,
|
| 169 |
submission_notes: str = "",
|
| 170 |
+
contact_email: str = "",
|
| 171 |
) -> None:
|
| 172 |
"""
|
| 173 |
Merge evaluation output into an existing leaderboard row (partial re-eval).
|
|
|
|
| 190 |
break
|
| 191 |
|
| 192 |
patch = leaderboard_row_from_eval_result(
|
| 193 |
+
result, submitted_at_iso, submission_notes=submission_notes, contact_email=contact_email
|
| 194 |
)
|
| 195 |
|
| 196 |
if match_idx is None:
|
|
|
|
| 208 |
row["num_params"] = patch["num_params"]
|
| 209 |
if (submission_notes or "").strip():
|
| 210 |
row["submission_notes"] = patch["submission_notes"]
|
| 211 |
+
if (contact_email or "").strip():
|
| 212 |
+
row["contact_email"] = patch["contact_email"]
|
| 213 |
normalize_legacy_csv_row(row)
|
| 214 |
rows[match_idx] = row
|
| 215 |
|
|
|
|
| 292 |
if not row.get("eval_family"):
|
| 293 |
row["eval_family"] = "N/A"
|
| 294 |
row.setdefault("submission_notes", "")
|
| 295 |
+
row.setdefault("contact_email", "")
|
| 296 |
normalize_legacy_csv_row(row)
|
| 297 |
_raw_results_cache = results
|
| 298 |
return [dict(r) for r in results]
|
|
|
|
| 489 |
if not row.get("eval_family"):
|
| 490 |
row["eval_family"] = "N/A"
|
| 491 |
row.setdefault("submission_notes", "")
|
| 492 |
+
row.setdefault("contact_email", "")
|
| 493 |
normalize_legacy_csv_row(row)
|
| 494 |
out.append(row)
|
| 495 |
return out
|
|
|
|
| 644 |
if not row.get("eval_family"):
|
| 645 |
row["eval_family"] = "N/A"
|
| 646 |
row.setdefault("submission_notes", "")
|
| 647 |
+
row.setdefault("contact_email", "")
|
| 648 |
normalize_legacy_csv_row(row)
|
| 649 |
out.append(row)
|
| 650 |
return out
|
job_queue.py
CHANGED
|
@@ -62,6 +62,7 @@ class Job:
|
|
| 62 |
error: str | None = None
|
| 63 |
result: dict[str, Any] | None = None
|
| 64 |
submission_notes: str = ""
|
|
|
|
| 65 |
extra_requirements: str = ""
|
| 66 |
setup_script: str = ""
|
| 67 |
custom_script: str = ""
|
|
@@ -108,6 +109,7 @@ _JOBS_CSV_FIELDS = [
|
|
| 108 |
"updated_at",
|
| 109 |
"error",
|
| 110 |
"submission_notes",
|
|
|
|
| 111 |
"extra_requirements",
|
| 112 |
"setup_script_b64",
|
| 113 |
"custom_script_b64",
|
|
@@ -149,6 +151,19 @@ def sanitize_custom_script(text: str) -> str:
|
|
| 149 |
return s
|
| 150 |
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
def sanitize_setup_script(text: str) -> str:
|
| 153 |
"""Trim and cap one-time setup script (shell or Python) for storage."""
|
| 154 |
s = (text or "").strip()
|
|
@@ -327,6 +342,7 @@ def _persist_jobs() -> None:
|
|
| 327 |
"updated_at": j.updated_at or j.created_at,
|
| 328 |
"error": (j.error or "").replace("\n", " ")[:2000],
|
| 329 |
"submission_notes": (j.submission_notes or "").replace("\n", " ")[:4000],
|
|
|
|
| 330 |
"extra_requirements": (j.extra_requirements or "").replace("\r\n", "\n")[:8000],
|
| 331 |
"setup_script_b64": _encode_script_b64(j.setup_script or ""),
|
| 332 |
"custom_script_b64": _encode_script_b64(j.custom_script or ""),
|
|
@@ -399,6 +415,7 @@ def _load_persisted_jobs_once() -> None:
|
|
| 399 |
continue
|
| 400 |
err = (row.get("error") or "").strip() or None
|
| 401 |
notes = (row.get("submission_notes") or "").strip()
|
|
|
|
| 402 |
extra_req = (row.get("extra_requirements") or "").strip()
|
| 403 |
setup = _decode_script_b64(row.get("setup_script_b64") or "")
|
| 404 |
script = _decode_script_b64(row.get("custom_script_b64") or "")
|
|
@@ -434,6 +451,7 @@ def _load_persisted_jobs_once() -> None:
|
|
| 434 |
updated_at=updated,
|
| 435 |
error=err,
|
| 436 |
submission_notes=notes,
|
|
|
|
| 437 |
extra_requirements=extra_req,
|
| 438 |
setup_script=setup,
|
| 439 |
custom_script=script,
|
|
@@ -580,6 +598,7 @@ def _merge_eval_result_to_leaderboard(
|
|
| 580 |
result: dict,
|
| 581 |
submitted_at_iso: str,
|
| 582 |
submission_notes: str,
|
|
|
|
| 583 |
*,
|
| 584 |
replace_existing: bool = False,
|
| 585 |
merge_partial: bool = False,
|
|
@@ -595,7 +614,10 @@ def _merge_eval_result_to_leaderboard(
|
|
| 595 |
|
| 596 |
if merge_partial:
|
| 597 |
merge_leaderboard_row_from_eval_result(
|
| 598 |
-
result,
|
|
|
|
|
|
|
|
|
|
| 599 |
)
|
| 600 |
return
|
| 601 |
|
|
@@ -607,7 +629,10 @@ def _merge_eval_result_to_leaderboard(
|
|
| 607 |
rows.pop(i)
|
| 608 |
|
| 609 |
new_row = leaderboard_row_from_eval_result(
|
| 610 |
-
result,
|
|
|
|
|
|
|
|
|
|
| 611 |
)
|
| 612 |
normalize_legacy_csv_row(new_row)
|
| 613 |
rows.append(new_row)
|
|
@@ -682,20 +707,36 @@ def _load_artifact_json_from_bucket(artifact_path: str) -> dict:
|
|
| 682 |
pass
|
| 683 |
|
| 684 |
|
| 685 |
-
def
|
| 686 |
-
data: dict, artifact_path: str,
|
| 687 |
-
) -> str:
|
| 688 |
-
notes = (
|
| 689 |
-
|
| 690 |
-
|
|
|
|
| 691 |
space_job_id = str(data.get("job_id") or "").strip()
|
| 692 |
with _jobs_lock:
|
| 693 |
for j in _jobs.values():
|
| 694 |
if space_job_id and j.id == space_job_id:
|
| 695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
if (j.remote_artifact_path or "").strip() == artifact_path:
|
| 697 |
-
|
| 698 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 699 |
|
| 700 |
|
| 701 |
def _mark_job_done_for_artifact(
|
|
@@ -770,7 +811,9 @@ def import_artifact_to_leaderboard(
|
|
| 770 |
if not model_id:
|
| 771 |
return False, "Artifact result is missing model_id."
|
| 772 |
|
| 773 |
-
notes =
|
|
|
|
|
|
|
| 774 |
rows = load_raw_results()
|
| 775 |
existing = [i for i, r in enumerate(rows) if (r.get("model_id") or "").strip() == model_id]
|
| 776 |
|
|
@@ -790,7 +833,9 @@ def import_artifact_to_leaderboard(
|
|
| 790 |
rows.pop(i)
|
| 791 |
|
| 792 |
submitted_at = _now_iso()
|
| 793 |
-
new_row = leaderboard_row_from_eval_result(
|
|
|
|
|
|
|
| 794 |
normalize_legacy_csv_row(new_row)
|
| 795 |
rows.append(new_row)
|
| 796 |
_leaderboard_sort_rows_inplace(rows)
|
|
@@ -950,6 +995,7 @@ def _remote_start_queued_job(job_id: str, jobs_token: str) -> None:
|
|
| 950 |
mid = j.model_id
|
| 951 |
fid = j.family_id
|
| 952 |
notes = j.submission_notes or ""
|
|
|
|
| 953 |
j.status = JobStatus.running
|
| 954 |
j.progress_done = 0
|
| 955 |
j.progress_total = 0
|
|
@@ -994,6 +1040,7 @@ def _remote_tick_job(job_id: str, jobs_token: str) -> None:
|
|
| 994 |
mid = j.model_id
|
| 995 |
fid = j.family_id
|
| 996 |
notes = j.submission_notes or ""
|
|
|
|
| 997 |
|
| 998 |
# Grace window absorbs the brief gap between the worker script returning
|
| 999 |
# rc=0 (and uploading the JSON artifact) and the Hub flipping the job's
|
|
@@ -1035,6 +1082,7 @@ def _remote_tick_job(job_id: str, jobs_token: str) -> None:
|
|
| 1035 |
result,
|
| 1036 |
_now_iso(),
|
| 1037 |
notes,
|
|
|
|
| 1038 |
replace_existing=replace_lb and not partial,
|
| 1039 |
merge_partial=partial,
|
| 1040 |
)
|
|
@@ -1157,6 +1205,7 @@ def enqueue(
|
|
| 1157 |
family_id: str,
|
| 1158 |
submission_notes: str = "",
|
| 1159 |
*,
|
|
|
|
| 1160 |
extra_requirements: str = "",
|
| 1161 |
setup_script: str = "",
|
| 1162 |
custom_script: str = "",
|
|
@@ -1199,6 +1248,7 @@ def enqueue(
|
|
| 1199 |
created = _now_iso()
|
| 1200 |
notes_clean = (submission_notes or "").strip()[:4000]
|
| 1201 |
try:
|
|
|
|
| 1202 |
from recipes.registry import apply_recipe_to_submission
|
| 1203 |
|
| 1204 |
req_lines = parse_requirements_lines(extra_requirements)
|
|
@@ -1231,6 +1281,7 @@ def enqueue(
|
|
| 1231 |
created_at=created,
|
| 1232 |
updated_at=created,
|
| 1233 |
submission_notes=notes_clean,
|
|
|
|
| 1234 |
extra_requirements=extra_req_clean,
|
| 1235 |
setup_script=setup_clean,
|
| 1236 |
custom_script=script_clean,
|
|
|
|
| 62 |
error: str | None = None
|
| 63 |
result: dict[str, Any] | None = None
|
| 64 |
submission_notes: str = ""
|
| 65 |
+
contact_email: str = ""
|
| 66 |
extra_requirements: str = ""
|
| 67 |
setup_script: str = ""
|
| 68 |
custom_script: str = ""
|
|
|
|
| 109 |
"updated_at",
|
| 110 |
"error",
|
| 111 |
"submission_notes",
|
| 112 |
+
"contact_email",
|
| 113 |
"extra_requirements",
|
| 114 |
"setup_script_b64",
|
| 115 |
"custom_script_b64",
|
|
|
|
| 151 |
return s
|
| 152 |
|
| 153 |
|
| 154 |
+
def sanitize_contact_email(text: str) -> str:
|
| 155 |
+
"""Trim and validate an optional submitter email address."""
|
| 156 |
+
s = (text or "").strip()[:254]
|
| 157 |
+
if not s:
|
| 158 |
+
return ""
|
| 159 |
+
if s.count("@") != 1:
|
| 160 |
+
raise ValueError("Invalid email address.")
|
| 161 |
+
local, domain = s.split("@", 1)
|
| 162 |
+
if not local or not domain or "." not in domain:
|
| 163 |
+
raise ValueError("Invalid email address.")
|
| 164 |
+
return s
|
| 165 |
+
|
| 166 |
+
|
| 167 |
def sanitize_setup_script(text: str) -> str:
|
| 168 |
"""Trim and cap one-time setup script (shell or Python) for storage."""
|
| 169 |
s = (text or "").strip()
|
|
|
|
| 342 |
"updated_at": j.updated_at or j.created_at,
|
| 343 |
"error": (j.error or "").replace("\n", " ")[:2000],
|
| 344 |
"submission_notes": (j.submission_notes or "").replace("\n", " ")[:4000],
|
| 345 |
+
"contact_email": (j.contact_email or "").replace("\n", " ")[:254],
|
| 346 |
"extra_requirements": (j.extra_requirements or "").replace("\r\n", "\n")[:8000],
|
| 347 |
"setup_script_b64": _encode_script_b64(j.setup_script or ""),
|
| 348 |
"custom_script_b64": _encode_script_b64(j.custom_script or ""),
|
|
|
|
| 415 |
continue
|
| 416 |
err = (row.get("error") or "").strip() or None
|
| 417 |
notes = (row.get("submission_notes") or "").strip()
|
| 418 |
+
email = (row.get("contact_email") or "").strip()[:254]
|
| 419 |
extra_req = (row.get("extra_requirements") or "").strip()
|
| 420 |
setup = _decode_script_b64(row.get("setup_script_b64") or "")
|
| 421 |
script = _decode_script_b64(row.get("custom_script_b64") or "")
|
|
|
|
| 451 |
updated_at=updated,
|
| 452 |
error=err,
|
| 453 |
submission_notes=notes,
|
| 454 |
+
contact_email=email,
|
| 455 |
extra_requirements=extra_req,
|
| 456 |
setup_script=setup,
|
| 457 |
custom_script=script,
|
|
|
|
| 598 |
result: dict,
|
| 599 |
submitted_at_iso: str,
|
| 600 |
submission_notes: str,
|
| 601 |
+
contact_email: str = "",
|
| 602 |
*,
|
| 603 |
replace_existing: bool = False,
|
| 604 |
merge_partial: bool = False,
|
|
|
|
| 614 |
|
| 615 |
if merge_partial:
|
| 616 |
merge_leaderboard_row_from_eval_result(
|
| 617 |
+
result,
|
| 618 |
+
submitted_at_iso,
|
| 619 |
+
submission_notes=submission_notes,
|
| 620 |
+
contact_email=contact_email,
|
| 621 |
)
|
| 622 |
return
|
| 623 |
|
|
|
|
| 629 |
rows.pop(i)
|
| 630 |
|
| 631 |
new_row = leaderboard_row_from_eval_result(
|
| 632 |
+
result,
|
| 633 |
+
submitted_at_iso,
|
| 634 |
+
submission_notes=submission_notes,
|
| 635 |
+
contact_email=contact_email,
|
| 636 |
)
|
| 637 |
normalize_legacy_csv_row(new_row)
|
| 638 |
rows.append(new_row)
|
|
|
|
| 707 |
pass
|
| 708 |
|
| 709 |
|
| 710 |
+
def _submission_fields_for_artifact_import(
|
| 711 |
+
data: dict, artifact_path: str, notes_override: str, email_override: str
|
| 712 |
+
) -> tuple[str, str]:
|
| 713 |
+
notes = (notes_override or "").strip()[:4000]
|
| 714 |
+
email = (email_override or "").strip()[:254]
|
| 715 |
+
if notes and email:
|
| 716 |
+
return notes, email
|
| 717 |
space_job_id = str(data.get("job_id") or "").strip()
|
| 718 |
with _jobs_lock:
|
| 719 |
for j in _jobs.values():
|
| 720 |
if space_job_id and j.id == space_job_id:
|
| 721 |
+
if not notes:
|
| 722 |
+
notes = (j.submission_notes or "").strip()[:4000]
|
| 723 |
+
if not email:
|
| 724 |
+
email = (j.contact_email or "").strip()[:254]
|
| 725 |
+
return notes, email
|
| 726 |
if (j.remote_artifact_path or "").strip() == artifact_path:
|
| 727 |
+
if not notes:
|
| 728 |
+
notes = (j.submission_notes or "").strip()[:4000]
|
| 729 |
+
if not email:
|
| 730 |
+
email = (j.contact_email or "").strip()[:254]
|
| 731 |
+
return notes, email
|
| 732 |
+
return notes, email
|
| 733 |
+
|
| 734 |
+
|
| 735 |
+
def _submission_notes_for_artifact_import(
|
| 736 |
+
data: dict, artifact_path: str, override: str
|
| 737 |
+
) -> str:
|
| 738 |
+
notes, _ = _submission_fields_for_artifact_import(data, artifact_path, override, "")
|
| 739 |
+
return notes
|
| 740 |
|
| 741 |
|
| 742 |
def _mark_job_done_for_artifact(
|
|
|
|
| 811 |
if not model_id:
|
| 812 |
return False, "Artifact result is missing model_id."
|
| 813 |
|
| 814 |
+
notes, email = _submission_fields_for_artifact_import(
|
| 815 |
+
data, artifact_path, submission_notes, ""
|
| 816 |
+
)
|
| 817 |
rows = load_raw_results()
|
| 818 |
existing = [i for i, r in enumerate(rows) if (r.get("model_id") or "").strip() == model_id]
|
| 819 |
|
|
|
|
| 833 |
rows.pop(i)
|
| 834 |
|
| 835 |
submitted_at = _now_iso()
|
| 836 |
+
new_row = leaderboard_row_from_eval_result(
|
| 837 |
+
result, submitted_at, submission_notes=notes, contact_email=email
|
| 838 |
+
)
|
| 839 |
normalize_legacy_csv_row(new_row)
|
| 840 |
rows.append(new_row)
|
| 841 |
_leaderboard_sort_rows_inplace(rows)
|
|
|
|
| 995 |
mid = j.model_id
|
| 996 |
fid = j.family_id
|
| 997 |
notes = j.submission_notes or ""
|
| 998 |
+
email = j.contact_email or ""
|
| 999 |
j.status = JobStatus.running
|
| 1000 |
j.progress_done = 0
|
| 1001 |
j.progress_total = 0
|
|
|
|
| 1040 |
mid = j.model_id
|
| 1041 |
fid = j.family_id
|
| 1042 |
notes = j.submission_notes or ""
|
| 1043 |
+
email = j.contact_email or ""
|
| 1044 |
|
| 1045 |
# Grace window absorbs the brief gap between the worker script returning
|
| 1046 |
# rc=0 (and uploading the JSON artifact) and the Hub flipping the job's
|
|
|
|
| 1082 |
result,
|
| 1083 |
_now_iso(),
|
| 1084 |
notes,
|
| 1085 |
+
email,
|
| 1086 |
replace_existing=replace_lb and not partial,
|
| 1087 |
merge_partial=partial,
|
| 1088 |
)
|
|
|
|
| 1205 |
family_id: str,
|
| 1206 |
submission_notes: str = "",
|
| 1207 |
*,
|
| 1208 |
+
contact_email: str = "",
|
| 1209 |
extra_requirements: str = "",
|
| 1210 |
setup_script: str = "",
|
| 1211 |
custom_script: str = "",
|
|
|
|
| 1248 |
created = _now_iso()
|
| 1249 |
notes_clean = (submission_notes or "").strip()[:4000]
|
| 1250 |
try:
|
| 1251 |
+
email_clean = sanitize_contact_email(contact_email)
|
| 1252 |
from recipes.registry import apply_recipe_to_submission
|
| 1253 |
|
| 1254 |
req_lines = parse_requirements_lines(extra_requirements)
|
|
|
|
| 1281 |
created_at=created,
|
| 1282 |
updated_at=created,
|
| 1283 |
submission_notes=notes_clean,
|
| 1284 |
+
contact_email=email_clean,
|
| 1285 |
extra_requirements=extra_req_clean,
|
| 1286 |
setup_script=setup_clean,
|
| 1287 |
custom_script=script_clean,
|