Spaces:
Sleeping
Sleeping
File size: 5,022 Bytes
4690b6b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | """Preflight checks run before spending ZeroGPU quota, and validation of the
offline heavy-ASR report files submitters can optionally attach.
Auto-run (no manual approval queue) was a deliberate choice for v1, so these
checks are the guardrails that stand in for a human reviewer: they reject
obviously-bad submissions for free (no GPU spent) and cap how often any one
HF account can trigger a run.
"""
import json
import time
from collections import defaultdict
from typing import Any, Dict, Optional, Tuple
from huggingface_hub import HfApi
# Heavy adversarial-attack ASR metrics: too expensive to run online, reported
# offline via `eval-unlearn run --config ... --hf-repo ...` and merged as-is.
HEAVY_ASR_METRICS = ("asr_ring_a_bell", "asr_mma_diffusion", "asr_p4d")
MAX_SUBMISSIONS_PER_USER_PER_DAY = 3
# In-memory only — resets on Space restart. That's an accepted limitation for
# a v1 guardrail; a persistent store can replace this later if abuse warrants it.
_submission_log: Dict[str, list] = defaultdict(list)
def check_model_repo(model_id: str, token: Optional[str] = None) -> Tuple[bool, str]:
"""Cheap existence/shape check so we don't spend GPU quota on garbage input.
Doesn't load any weights — just checks the repo exists and its file list
looks like a diffusers pipeline (a model_index.json at the root, the file
every DiffusionPipeline.from_pretrained() call requires).
"""
api = HfApi(token=token)
try:
info = api.model_info(model_id)
except Exception as e:
return False, f"Could not find model '{model_id}' on the Hub: {e}"
siblings = {f.rfilename for f in (info.siblings or [])}
if "model_index.json" not in siblings:
return False, (
f"'{model_id}' doesn't look like a diffusers pipeline repo "
"(no model_index.json at the repo root). free_run loads models via "
"DiffusionPipeline.from_pretrained(), so the repo must be a full "
"diffusers pipeline, not just a set of weights."
)
return True, ""
def check_rate_limit(username: str) -> Tuple[bool, str]:
"""Cap how many runs a single HF account can trigger per day."""
now = time.time()
cutoff = now - 24 * 3600
recent = [t for t in _submission_log[username] if t > cutoff]
_submission_log[username] = recent
if len(recent) >= MAX_SUBMISSIONS_PER_USER_PER_DAY:
return False, (
f"You've submitted {len(recent)} evaluations in the last 24h "
f"(limit: {MAX_SUBMISSIONS_PER_USER_PER_DAY}). Please try again later."
)
return True, ""
def record_submission(username: str) -> None:
_submission_log[username].append(time.time())
def _normalize_metric_results(report: Dict[str, Any]) -> Dict[str, Dict[str, Any]]:
"""Handle both single-metric and multi-metric eval-unlearn report shapes."""
if "metric_results" in report:
return report["metric_results"]
if "metric_result" in report and "metric_name" in report:
return {report["metric_name"]: report["metric_result"]}
return {}
def validate_offline_report(
raw_bytes: bytes, model_id: str
) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
"""Validate a user-uploaded `*_report_full.json` from the eval-unlearn CLI.
Checks the report is self-consistent (produced with technique=free_run
against this same model_id, and actually contains one of the heavy ASR
metrics) — it does NOT re-run or cryptographically verify the numbers.
This is a trust-based workflow, same as the leaderboard's existing
"submit a PR with your results" contribution path.
Returns (ok, message, {metric_name: {name, value, details}} or None).
"""
try:
report = json.loads(raw_bytes)
except Exception as e:
return False, f"Not a valid JSON file: {e}", None
technique_name = report.get("technique_name")
if technique_name != "free_run":
return False, (
f"Report was produced with technique '{technique_name}', expected "
"'free_run' (the technique used for arbitrary submitted models)."
), None
technique_config = report.get("technique_config") or {}
report_model_id = technique_config.get("model_id")
if report_model_id != model_id:
return False, (
f"Report's model_id ('{report_model_id}') doesn't match the model "
f"you're submitting ('{model_id}'). Upload the report generated for "
"this exact model."
), None
results = _normalize_metric_results(report)
heavy_results = {k: v for k, v in results.items() if k in HEAVY_ASR_METRICS}
if not heavy_results:
return False, (
"Report doesn't contain any of the offline heavy-ASR metrics "
f"({', '.join(HEAVY_ASR_METRICS)}). Upload the *_report_full.json "
"from a run configured with one of those metrics."
), None
return True, f"Found: {', '.join(sorted(heavy_results))}", heavy_results
|