eyad-silx commited on
Commit
95aa2b3
Β·
verified Β·
1 Parent(s): c128a6c

push artifacts/scripts/hf_push.py

Browse files
Files changed (1) hide show
  1. artifacts/scripts/hf_push.py +152 -0
artifacts/scripts/hf_push.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Push Ingot (SN97) build-pod artifacts to HF repo eyad-silx/progress.
3
+
4
+ Modes: --small (docs/logs/scripts), --big (GGUFs + QAT states), default = both.
5
+ Only uploads new/changed files (size+mtime_ns fingerprint), records sha256 in
6
+ state for the MANIFEST. State: /root/.hf_push_state.json. Log: /root/hf_push.log.
7
+ """
8
+ import argparse, glob, hashlib, json, os, sys
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
11
+ from huggingface_hub import HfApi
12
+
13
+ REPO = "eyad-silx/progress"
14
+ TOKEN = Path("/root/.hf_token").read_text().strip()
15
+ STATE_P = Path("/root/.hf_push_state.json")
16
+ LOG_P = Path("/root/hf_push.log")
17
+
18
+ README = """# Ingot (SN97) challenger build β€” progress proof
19
+
20
+ Automated backup of build/train/eval artifacts from the H100 pod (RunPod, 1x H100 NVL).
21
+ Pushed by `hf_push.py` β€” checks hourly and uploads any new/changed artifact.
22
+
23
+ - `king-ix/` β€” incumbent king GGUF (Ingot-IX-27B-Q2) + provenance files (baseline)
24
+ - `chal-identity/`, `chal-v1/`, `chal-n2/`, `chal-norms/` β€” challenger GGUF iterations
25
+ (scale-refit / QAT variants; same 27B Q2 architecture as the king)
26
+ - `states/` β€” QAT state checkpoints (`t0..tN.safetensors`)
27
+ - `artifacts/` β€” `HANDOFF.md`, build/train/eval scripts, run logs, sample draws,
28
+ measurement results (`measure*.jsonl`, `pick*.jsonl`, `drawn.jsonl`)
29
+ - `MANIFEST.md` β€” size + sha256 + upload time of every artifact (regenerated each push)
30
+ - `PROGRESS.md` β€” push history
31
+
32
+ The GGUFs are ~6.8 GB each (PQ2_0 quantized 27B linear-attention models).
33
+ """
34
+
35
+ def now():
36
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
37
+
38
+ def log(evt, **kw):
39
+ line = json.dumps({"t": now(), "event": evt, **kw})
40
+ with open(LOG_P, "a") as f:
41
+ f.write(line + "\n")
42
+ print(line, flush=True)
43
+
44
+ def sha256(p):
45
+ h = hashlib.sha256()
46
+ with open(p, "rb") as f:
47
+ for chunk in iter(lambda: f.read(1 << 24), b""):
48
+ h.update(chunk)
49
+ return h.hexdigest()
50
+
51
+ def load_state():
52
+ return json.loads(STATE_P.read_text()) if STATE_P.exists() else {}
53
+
54
+ def save_state(s):
55
+ STATE_P.write_text(json.dumps(s, indent=1, sort_keys=True))
56
+
57
+ def small_tracks():
58
+ t = [("/root/HANDOFF.md", "artifacts/HANDOFF.md")]
59
+ for pat, dest in [("/root/*.py", "artifacts/scripts/"),
60
+ ("/root/*.sh", "artifacts/scripts/"),
61
+ ("/root/*.jsonl", "artifacts/"),
62
+ ("/root/*.json", "artifacts/"),
63
+ ("/root/*.log", "artifacts/logs/")]:
64
+ for p in sorted(glob.glob(pat)):
65
+ name = os.path.basename(p)
66
+ if name in ("hf_push.log", "hf_push_nohup.log"):
67
+ continue
68
+ t.append((p, dest + name))
69
+ for extra in ("README.md", "LICENSE", "NOTICE.txt"):
70
+ p = f"/root/models/king-ix/{extra}"
71
+ if os.path.exists(p):
72
+ t.append((p, f"king-ix/{extra}"))
73
+ return t
74
+
75
+ def big_tracks():
76
+ # priority order: newest challenger first (pod lifetime is uncertain)
77
+ t = [("/root/models/chal-identity/Ingot-IX-27B-Q2.gguf", "chal-identity/Ingot-IX-27B-Q2.gguf"),
78
+ ("/root/models/chal-v1/chal-v1.gguf", "chal-v1/chal-v1.gguf"),
79
+ ("/root/models/chal-n2/chal-norms.gguf", "chal-n2/chal-norms.gguf"),
80
+ ("/root/models/chal-norms/chal-norms.gguf", "chal-norms/chal-norms.gguf"),
81
+ ("/root/models/king-ix/Ingot-IX-27B-Q2.gguf", "king-ix/Ingot-IX-27B-Q2.gguf")]
82
+ for p in sorted(glob.glob("/root/states/*.safetensors")):
83
+ t.append((p, "states/" + os.path.basename(p)))
84
+ return t
85
+
86
+ def main():
87
+ ap = argparse.ArgumentParser()
88
+ ap.add_argument("--small", action="store_true")
89
+ ap.add_argument("--big", action="store_true")
90
+ args = ap.parse_args()
91
+ if args.small or args.big:
92
+ tracks = (small_tracks() if args.small else []) + (big_tracks() if args.big else [])
93
+ else:
94
+ tracks = small_tracks() + big_tracks()
95
+
96
+ state = load_state()
97
+ api = HfApi(token=TOKEN)
98
+ results = []
99
+ for local, repo in tracks:
100
+ if not os.path.exists(local):
101
+ continue
102
+ st = os.stat(local)
103
+ fp = f"{st.st_size}:{st.st_mtime_ns}"
104
+ prev = state.get(repo)
105
+ if prev and prev["fp"] == fp:
106
+ results.append((repo, "unchanged"))
107
+ continue
108
+ try:
109
+ d = sha256(local)
110
+ api.upload_file(path_or_fileobj=local, path_in_repo=repo, repo_id=REPO,
111
+ repo_type="model", commit_message=f"push {repo}")
112
+ state[repo] = {"fp": fp, "sha256": d, "size": st.st_size, "uploaded_at": now()}
113
+ save_state(state)
114
+ log("uploaded", local=local, repo=repo, sha256=d, size=st.st_size)
115
+ results.append((repo, "uploaded"))
116
+ except Exception as e:
117
+ log("error", local=local, repo=repo, err=repr(e)[:500])
118
+ results.append((repo, "FAILED"))
119
+
120
+ # regenerate docs (manifest / progress / readme) from state
121
+ try:
122
+ manifest = ["# Manifest β€” eyad-silx/progress", "",
123
+ "| repo path | size (bytes) | sha256 | uploaded (UTC) |",
124
+ "|---|---|---|---|"]
125
+ for repo in sorted(state):
126
+ v = state[repo]
127
+ manifest.append(f"| `{repo}` | {v['size']:,} | `{v['sha256']}` | {v['uploaded_at']} |")
128
+ Path("/tmp/MANIFEST.md").write_text("\n".join(manifest) + "\n")
129
+ api.upload_file(path_or_fileobj="/tmp/MANIFEST.md", path_in_repo="MANIFEST.md",
130
+ repo_id=REPO, repo_type="model", commit_message="update MANIFEST.md")
131
+
132
+ hist = [json.loads(l) for l in LOG_P.read_text().splitlines()[-80:] if l.strip()]
133
+ prog = ["# Progress β€” push history", "", f"Last run: {now()}", ""]
134
+ prog += [f"- `{h['t']}` **{h['event']}** {h.get('repo', h.get('local', ''))}" for h in hist]
135
+ Path("/tmp/PROGRESS.md").write_text("\n".join(prog) + "\n")
136
+ api.upload_file(path_or_fileobj="/tmp/PROGRESS.md", path_in_repo="PROGRESS.md",
137
+ repo_id=REPO, repo_type="model", commit_message="update PROGRESS.md")
138
+
139
+ Path("/tmp/README_REPO.md").write_text(README)
140
+ api.upload_file(path_or_fileobj="/tmp/README_REPO.md", path_in_repo="README.md",
141
+ repo_id=REPO, repo_type="model", commit_message="README")
142
+ except Exception as e:
143
+ log("error", step="docs", err=repr(e)[:500])
144
+
145
+ n_up = sum(1 for r in results if r[1] == "uploaded")
146
+ n_un = sum(1 for r in results if r[1] == "unchanged")
147
+ n_fail = sum(1 for r in results if r[1] == "FAILED")
148
+ log("run_done", uploaded=n_up, unchanged=n_un, failed=n_fail)
149
+ sys.exit(1 if n_fail else 0)
150
+
151
+ if __name__ == "__main__":
152
+ main()