File size: 4,529 Bytes
4791c0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13fe947
 
 
4791c0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13fe947
 
 
 
4791c0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""Publish the trained quest-classification LoRA adapter to the Hub.

Uploads artifacts/quest-lora/ to a model repo and prints the commit revision to pin
in hackathon_advisor.quest_analysis (ADVISOR_QUEST_ADAPTER_REVISION).
"""
from __future__ import annotations

import argparse
import json
from pathlib import Path

from huggingface_hub import HfApi

DEFAULT_REPO = "build-small-hackathon/hackathon-advisor-quest-minicpm5-lora"


def model_card(recipe: dict, eval_report: dict) -> str:
    n = eval_report.get("n")
    exact = eval_report.get("quest_set_exact")
    f1 = eval_report.get("f1")
    return "\n".join(
        [
            "---",
            "base_model: openbmb/MiniCPM5-1B",
            "library_name: peft",
            "datasets:",
            "- build-small-hackathon/hackathon-advisor-quest-dataset",
            "tags:",
            "- lora",
            "- hackathon-advisor",
            "- quest-classification",
            "license: apache-2.0",
            "---",
            "",
            "# Hackathon Advisor — Quest Classification LoRA (MiniCPM5-1B)",
            "",
            "PEFT LoRA adapter that classifies a Build Small Hackathon project against 13 judging",
            "dimensions (6 merit badges + 2 tracks + 5 sponsor/special awards) from a two-segment",
            "README + app-file prompt, emitting strict JSON:",
            "",
            "```json",
            '{"matches":[{"quest":"...","confidence":0.0,"evidence":"...","source":"readme|app_file"}]}',
            "```",
            "",
            "Load it in the deployed Space by setting `ADVISOR_QUEST_ADAPTER_ID` to this repo.",
            "The backend revalidates every dashboard refresh and will not swap on schema failure.",
            "",
            "## Recipe",
            "",
            f"- Base model: `{recipe.get('base_model')}`",
            f"- Task: `{recipe.get('adapter_task')}`",
            f"- Method: {recipe.get('method')}",
            f"- Examples: {recipe.get('example_count')}",
            f"- Epochs: {recipe.get('epochs')}",
            f"- LoRA rank/alpha/dropout: {recipe.get('rank')}/{recipe.get('alpha')}/{recipe.get('dropout')}",
            f"- Max seq length: {recipe.get('max_seq_length')}",
            f"- GPU: {recipe.get('gpu')}",
            "",
            "## Dataset",
            "",
            "[`build-small-hackathon/hackathon-advisor-quest-dataset`]"
            "(https://huggingface.co/datasets/build-small-hackathon/hackathon-advisor-quest-dataset)"
            " — 156 chat-JSONL examples built from real `build-small-hackathon` Spaces: 108 teacher-",
            "labelled + adversarially-verified projects plus targeted augmentations (app-only,",
            "readme-only / missing app file, README↔app contradictions, empty matches, noisy",
            "metadata). All 13 quests covered.",
            "",
            f"## Full-dataset eval at training time: quest-set exact match {exact}/{n}, micro-F1 {f1}.",
            "",
            "Evaluated by reproducing the gold quest set for every example in the training dataset",
            "(the dataset is the spec — it is built from the real `build-small-hackathon` projects).",
            "",
        ]
    )


def main() -> None:
    parser = argparse.ArgumentParser(description="Publish the quest LoRA adapter.")
    parser.add_argument("--adapter-dir", default="artifacts/quest-lora", type=Path)
    parser.add_argument("--repo-id", default=DEFAULT_REPO)
    args = parser.parse_args()

    recipe_path = args.adapter_dir / "training-recipe.json"
    recipe = json.loads(recipe_path.read_text(encoding="utf-8")) if recipe_path.exists() else {}
    eval_path = args.adapter_dir / "self-eval.json"
    eval_report = json.loads(eval_path.read_text(encoding="utf-8")) if eval_path.exists() else {}
    (args.adapter_dir / "README.md").write_text(model_card(recipe, eval_report), encoding="utf-8")

    api = HfApi()
    api.create_repo(repo_id=args.repo_id, repo_type="model", exist_ok=True)
    commit = api.upload_folder(
        folder_path=str(args.adapter_dir),
        repo_id=args.repo_id,
        repo_type="model",
        commit_message="Publish Hackathon Advisor quest-classification MiniCPM5 LoRA",
        ignore_patterns=["self-eval.json"],
    )
    revision = getattr(commit, "oid", None) or getattr(commit, "commit_id", None) or str(commit)
    print(f"published {args.repo_id}")
    print(f"revision: {revision}")


if __name__ == "__main__":
    main()