Spaces:
Sleeping
Sleeping
Sync submission checker docs
Browse files- PLAN.md +1 -0
- SUBMISSION.md +1 -0
- scripts/check_submission_ready.py +156 -0
PLAN.md
CHANGED
|
@@ -113,6 +113,7 @@ All times are `America/Los_Angeles` / PDT unless noted.
|
|
| 113 |
- 2026-06-14 01:27: addressed the submission-readiness review by adding hero-name parsing, making Draft Lab demoable, adding tests, preparing a dataset card, declaring Apache-2.0 licensing, and aligning the Modal dependency version.
|
| 114 |
- 2026-06-14 02:36: continued demo-readiness hardening by adding role-aware recommendation filtering, Bayesian win-rate shrinkage for low samples, pre-game item timing labels, and a repeatable public Space API check script.
|
| 115 |
- 2026-06-14 02:41: replaced the generic generated adapter README with a DOTA2Tuned-specific model card covering intended behavior, evidence discipline, usage, evaluation notes, limitations, and public links.
|
|
|
|
| 116 |
|
| 117 |
## Adapter Eval Notes
|
| 118 |
|
|
|
|
| 113 |
- 2026-06-14 01:27: addressed the submission-readiness review by adding hero-name parsing, making Draft Lab demoable, adding tests, preparing a dataset card, declaring Apache-2.0 licensing, and aligning the Modal dependency version.
|
| 114 |
- 2026-06-14 02:36: continued demo-readiness hardening by adding role-aware recommendation filtering, Bayesian win-rate shrinkage for low samples, pre-game item timing labels, and a repeatable public Space API check script.
|
| 115 |
- 2026-06-14 02:41: replaced the generic generated adapter README with a DOTA2Tuned-specific model card covering intended behavior, evidence discipline, usage, evaluation notes, limitations, and public links.
|
| 116 |
+
- 2026-06-14 02:57: added a final submission-readiness checker that verifies public URLs, Space runtime, Hub model/dataset cards, git cleanliness, and all public Gradio API endpoints while listing the human-only video/social/submission steps separately.
|
| 117 |
|
| 118 |
## Adapter Eval Notes
|
| 119 |
|
SUBMISSION.md
CHANGED
|
@@ -80,6 +80,7 @@ uv run python -c "from app import demo; print(type(demo).__name__, len(demo.bloc
|
|
| 80 |
uv run dota2tuned modal-smoke
|
| 81 |
uv run dota2tuned modal-ask "Suggest one mid hero against Phantom Assassin and Witch Doctor. Include one caveat." --context "Use only grounded advice. Mention that sample sizes and patch context matter." --max-new-tokens 160
|
| 82 |
uv run python scripts/check_public_space.py
|
|
|
|
| 83 |
curl -L -sS -o /dev/null -w '%{http_code}\n' https://build-small-hackathon-dota2tuned.hf.space
|
| 84 |
curl -sS -o /dev/null -w '%{http_code}\n' https://dracufeuer--dota2tuned-ui.modal.run
|
| 85 |
git status --short --branch
|
|
|
|
| 80 |
uv run dota2tuned modal-smoke
|
| 81 |
uv run dota2tuned modal-ask "Suggest one mid hero against Phantom Assassin and Witch Doctor. Include one caveat." --context "Use only grounded advice. Mention that sample sizes and patch context matter." --max-new-tokens 160
|
| 82 |
uv run python scripts/check_public_space.py
|
| 83 |
+
uv run python scripts/check_submission_ready.py
|
| 84 |
curl -L -sS -o /dev/null -w '%{http_code}\n' https://build-small-hackathon-dota2tuned.hf.space
|
| 85 |
curl -sS -o /dev/null -w '%{http_code}\n' https://dracufeuer--dota2tuned-ui.modal.run
|
| 86 |
git status --short --branch
|
scripts/check_submission_ready.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
import subprocess
|
| 7 |
+
import sys
|
| 8 |
+
from datetime import UTC, datetime
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
from typing import Any
|
| 11 |
+
from urllib.error import HTTPError, URLError
|
| 12 |
+
from urllib.request import Request, urlopen
|
| 13 |
+
|
| 14 |
+
from check_public_space import DEFAULT_SPACE, run_checks
|
| 15 |
+
from huggingface_hub import HfApi
|
| 16 |
+
|
| 17 |
+
DEFAULT_MODEL_REPO = "build-small-hackathon/dota2tuned-qwen3-4b-2507-lora"
|
| 18 |
+
DEFAULT_DATASET_REPO = "build-small-hackathon/dota2tuned-data"
|
| 19 |
+
DEFAULT_MODAL_URL = "https://dracufeuer--dota2tuned-ui.modal.run"
|
| 20 |
+
DEFAULT_GITHUB_REPO = "https://github.com/1ncompleteness/DOTA2Tuned"
|
| 21 |
+
|
| 22 |
+
MANUAL_ITEMS = [
|
| 23 |
+
"Record short demo video.",
|
| 24 |
+
"Publish social post.",
|
| 25 |
+
"Submit Space link, demo video link, and social post link by June 15, 2026.",
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _http_status(url: str, *, timeout: int = 30) -> dict[str, Any]:
|
| 30 |
+
request = Request(url, headers={"User-Agent": "DOTA2Tuned submission checker"})
|
| 31 |
+
try:
|
| 32 |
+
with urlopen(request, timeout=timeout) as response:
|
| 33 |
+
return {"ok": 200 <= response.status < 400, "status": response.status}
|
| 34 |
+
except HTTPError as exc:
|
| 35 |
+
return {"ok": False, "status": exc.code, "error": str(exc)}
|
| 36 |
+
except URLError as exc:
|
| 37 |
+
return {"ok": False, "status": None, "error": str(exc.reason)}
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _git_status() -> dict[str, Any]:
|
| 41 |
+
result = subprocess.run(
|
| 42 |
+
["git", "status", "--short", "--branch"],
|
| 43 |
+
check=False,
|
| 44 |
+
capture_output=True,
|
| 45 |
+
text=True,
|
| 46 |
+
)
|
| 47 |
+
lines = result.stdout.strip().splitlines()
|
| 48 |
+
changed = [line for line in lines[1:] if line.strip()]
|
| 49 |
+
return {
|
| 50 |
+
"ok": result.returncode == 0 and not changed,
|
| 51 |
+
"returncode": result.returncode,
|
| 52 |
+
"status": lines,
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _repo_has_file(api: HfApi, repo_id: str, repo_type: str, filename: str) -> dict[str, Any]:
|
| 57 |
+
try:
|
| 58 |
+
if repo_type == "model":
|
| 59 |
+
info = api.model_info(repo_id)
|
| 60 |
+
elif repo_type == "dataset":
|
| 61 |
+
info = api.dataset_info(repo_id)
|
| 62 |
+
else:
|
| 63 |
+
raise ValueError(f"unsupported repo_type: {repo_type}")
|
| 64 |
+
files = {sibling.rfilename for sibling in info.siblings}
|
| 65 |
+
return {"ok": filename in files, "file": filename, "repo": repo_id}
|
| 66 |
+
except Exception as exc: # pragma: no cover - network smoke script
|
| 67 |
+
return {"ok": False, "file": filename, "repo": repo_id, "error": repr(exc)}
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _space_runtime(api: HfApi, space_id: str) -> dict[str, Any]:
|
| 71 |
+
try:
|
| 72 |
+
info = api.space_info(space_id)
|
| 73 |
+
runtime = getattr(info, "runtime", None)
|
| 74 |
+
stage = getattr(runtime, "stage", str(runtime))
|
| 75 |
+
return {"ok": stage == "RUNNING", "stage": stage, "space": space_id}
|
| 76 |
+
except Exception as exc: # pragma: no cover - network smoke script
|
| 77 |
+
return {"ok": False, "space": space_id, "error": repr(exc)}
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def run_submission_checks(args: argparse.Namespace) -> dict[str, Any]:
|
| 81 |
+
token = os.getenv("HF_TOKEN")
|
| 82 |
+
api = HfApi(token=token)
|
| 83 |
+
space_id = args.space_repo
|
| 84 |
+
checks: dict[str, Any] = {
|
| 85 |
+
"git_clean": _git_status(),
|
| 86 |
+
"space_http": _http_status(args.space_url),
|
| 87 |
+
"modal_http": _http_status(args.modal_url),
|
| 88 |
+
"github_http": _http_status(args.github_repo),
|
| 89 |
+
"space_runtime": _space_runtime(api, space_id),
|
| 90 |
+
"model_card": _repo_has_file(api, args.model_repo, "model", "README.md"),
|
| 91 |
+
"dataset_card": _repo_has_file(api, args.dataset_repo, "dataset", "README.md"),
|
| 92 |
+
}
|
| 93 |
+
if not args.skip_public_api:
|
| 94 |
+
public_api = run_checks(args.space_url)
|
| 95 |
+
checks["public_space_api"] = {
|
| 96 |
+
"ok": all(item["status"] == "ok" for item in public_api["checks"]),
|
| 97 |
+
"checks": public_api["checks"],
|
| 98 |
+
}
|
| 99 |
+
return {
|
| 100 |
+
"generated_at": datetime.now(UTC).isoformat(),
|
| 101 |
+
"machine_checks": checks,
|
| 102 |
+
"manual_remaining": MANUAL_ITEMS,
|
| 103 |
+
"links": {
|
| 104 |
+
"space": args.space_url,
|
| 105 |
+
"github": args.github_repo,
|
| 106 |
+
"model": f"https://huggingface.co/{args.model_repo}",
|
| 107 |
+
"dataset": f"https://huggingface.co/datasets/{args.dataset_repo}",
|
| 108 |
+
"modal": args.modal_url,
|
| 109 |
+
},
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def _machine_ok(payload: dict[str, Any]) -> bool:
|
| 114 |
+
return all(check.get("ok") is True for check in payload["machine_checks"].values())
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def main() -> None:
|
| 118 |
+
parser = argparse.ArgumentParser(description="Run final DOTA2Tuned submission checks.")
|
| 119 |
+
parser.add_argument("--space-url", default=DEFAULT_SPACE)
|
| 120 |
+
parser.add_argument("--space-repo", default="build-small-hackathon/dota2tuned")
|
| 121 |
+
parser.add_argument("--model-repo", default=DEFAULT_MODEL_REPO)
|
| 122 |
+
parser.add_argument("--dataset-repo", default=DEFAULT_DATASET_REPO)
|
| 123 |
+
parser.add_argument("--modal-url", default=DEFAULT_MODAL_URL)
|
| 124 |
+
parser.add_argument("--github-repo", default=DEFAULT_GITHUB_REPO)
|
| 125 |
+
parser.add_argument(
|
| 126 |
+
"--output",
|
| 127 |
+
default="submission_evidence/final_submission_check.json",
|
| 128 |
+
help="Path for JSON evidence output.",
|
| 129 |
+
)
|
| 130 |
+
parser.add_argument(
|
| 131 |
+
"--skip-public-api",
|
| 132 |
+
action="store_true",
|
| 133 |
+
help="Skip Gradio endpoint checks when only public artifact status is needed.",
|
| 134 |
+
)
|
| 135 |
+
args = parser.parse_args()
|
| 136 |
+
|
| 137 |
+
payload = run_submission_checks(args)
|
| 138 |
+
output_path = Path(args.output)
|
| 139 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 140 |
+
output_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
| 141 |
+
|
| 142 |
+
print("Machine checks:")
|
| 143 |
+
for name, check in payload["machine_checks"].items():
|
| 144 |
+
detail = check.get("stage") or check.get("status") or check.get("file") or ""
|
| 145 |
+
print(f"- {name}: {'ok' if check.get('ok') else 'fail'} {detail}")
|
| 146 |
+
print("\nManual remaining:")
|
| 147 |
+
for item in payload["manual_remaining"]:
|
| 148 |
+
print(f"- {item}")
|
| 149 |
+
print(f"\nWrote {output_path}")
|
| 150 |
+
|
| 151 |
+
if not _machine_ok(payload):
|
| 152 |
+
sys.exit(1)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
if __name__ == "__main__":
|
| 156 |
+
main()
|