p7 probe: prove --ckpt-repo chooses the publisher's weight source
Browse files
kernels/p7_probe_publish_source.py
CHANGED
|
@@ -8,18 +8,23 @@ deleted the failure arrives as a confusing 404 instead of a wrong answer.
|
|
| 8 |
Why a 404 cannot be the test. A traceback prints the failing *source line*, which contains the literal
|
| 9 |
`{a.ckpt_repo}`, not the URL it resolved to. "It died" therefore says nothing about which repo it asked for,
|
| 10 |
and a probe asserting on that text would pass whether or not the fix works. So the decisive read is the
|
| 11 |
-
publisher's own `ckpt latest.json -> {...}` print, which names the repo it fetched -- that needs a repo
|
| 12 |
-
answers. The kernel
|
| 13 |
-
`latest.json = {"step": 0, "path_in_repo": "final"}`, reads that
|
| 14 |
-
deletes the repo in a `finally`.
|
| 15 |
-
would 404, and the assertion fails for the right reason.
|
| 16 |
|
| 17 |
Four cases, free CPU, nothing published:
|
| 18 |
1 static -- the fetch is built from the argument; no fetch is built from the constant.
|
| 19 |
2 help -- `--help` lists `--ckpt-repo`.
|
| 20 |
-
3 positive-- the new file, told to read the temp repo, prints that repo's name and step 0
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
"""
|
| 24 |
import hashlib
|
| 25 |
import io
|
|
@@ -33,37 +38,44 @@ CODE = "Cion-lab/ounce100m-code"
|
|
| 33 |
NEW_REV = "dd1b6b94cc90ae6deff94e0990d58da64c6637f1"
|
| 34 |
NEW_SHA = "d1f3c4eb2b80cd4bff35c1b1b90a67ec6204dd753e09f354673d0812cc231e01"
|
| 35 |
OLD_REV = "55f23ef5d0e8e6a60adad23bf8a2ffffc6bc7580"
|
|
|
|
| 36 |
TMP = "Cion-lab/ounce100m-p7-probe-tmp"
|
|
|
|
| 37 |
|
| 38 |
|
| 39 |
-
def fetch(rev):
|
| 40 |
-
url = f"https://huggingface.co/{CODE}/resolve/{rev}/
|
| 41 |
return urllib.request.urlopen(url, timeout=120).read()
|
| 42 |
|
| 43 |
|
| 44 |
def report(R, why=""):
|
| 45 |
print("PROBE_JSON_BEGIN\n" + json.dumps(R, indent=1) + "\nPROBE_JSON_END", flush=True)
|
| 46 |
-
|
|
|
|
| 47 |
print(f"VERDICT PUBLISH_SOURCE {'PASS' if ok else 'FAIL'} {why}", flush=True)
|
| 48 |
return ok
|
| 49 |
|
| 50 |
|
| 51 |
R = {}
|
| 52 |
try:
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
import ounce100m_credentials
|
| 55 |
ounce100m_credentials.install(verify=True)
|
| 56 |
from huggingface_hub import HfApi
|
| 57 |
api = HfApi()
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
| 62 |
open(newp, "wb").write(nb)
|
| 63 |
-
|
| 64 |
-
R["fetched"] = f"{NEW_REV[:10]} sha_ok={got == NEW_SHA}"
|
| 65 |
-
assert got == NEW_SHA, "the pinned publisher bytes are not what this probe was written against"
|
| 66 |
-
open(oldp, "wb").write(fetch(OLD_REV))
|
| 67 |
|
| 68 |
src = nb.decode("utf-8")
|
| 69 |
R["case1_static_arg_wired"] = "datasets/{a.ckpt_repo}/resolve/main/latest.json" in src
|
|
@@ -72,24 +84,22 @@ try:
|
|
| 72 |
R["case2_help_lists_flag"] = "--ckpt-repo" in subprocess.run(
|
| 73 |
[sys.executable, newp, "--help"], capture_output=True, text=True).stdout
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
| 77 |
api.upload_file(path_or_fileobj=io.BytesIO(json.dumps({"step": 0, "path_in_repo": "final"}).encode()),
|
| 78 |
-
path_in_repo="latest.json", repo_id=TMP, repo_type="dataset", token=
|
| 79 |
made = True
|
| 80 |
|
| 81 |
r = subprocess.run([sys.executable, newp, "--dry-run", "--ckpt-repo", TMP],
|
| 82 |
capture_output=True, text=True)
|
| 83 |
out = r.stdout + r.stderr
|
| 84 |
R["case3_names_the_repo_it_was_given"] = f"'repo': '{TMP}'" in out
|
| 85 |
-
R["case3_read_the_pointer"] = "'step': 0" in out
|
| 86 |
-
|
| 87 |
-
# and it is what proves no upload was attempted.
|
| 88 |
-
R["case3_stopped_before_upload"] = r.returncode != 0 and "upload" not in out.lower()
|
| 89 |
-
|
| 90 |
ro = subprocess.run([sys.executable, oldp, "--dry-run", "--ckpt-repo", TMP],
|
| 91 |
capture_output=True, text=True)
|
| 92 |
R["case4_old_file_rejects_the_flag"] = ro.returncode == 2 and "unrecognized arguments" in ro.stderr
|
|
|
|
| 93 |
except Exception as e:
|
| 94 |
R["aborted"] = f"{type(e).__name__}: {str(e)[:200]}"
|
| 95 |
finally:
|
|
@@ -98,6 +108,6 @@ finally:
|
|
| 98 |
api.delete_repo(repo_id=TMP, repo_type="dataset")
|
| 99 |
R["temp_repo_deleted"] = True
|
| 100 |
except Exception as e:
|
| 101 |
-
R["temp_repo_deleted"] = f"FAILED {type(e).__name__} --
|
| 102 |
|
| 103 |
sys.exit(0 if report(R, R.get("aborted", "")) else 1)
|
|
|
|
| 8 |
Why a 404 cannot be the test. A traceback prints the failing *source line*, which contains the literal
|
| 9 |
`{a.ckpt_repo}`, not the URL it resolved to. "It died" therefore says nothing about which repo it asked for,
|
| 10 |
and a probe asserting on that text would pass whether or not the fix works. So the decisive read is the
|
| 11 |
+
publisher's own `ckpt latest.json -> {...}` print, which names the repo it fetched -- that needs a repo which
|
| 12 |
+
answers. The kernel creates a throwaway **private** dataset repo holding nothing but
|
| 13 |
+
`latest.json = {"step": 0, "path_in_repo": "final"}`, reads that name back out of the publisher's stdout, and
|
| 14 |
+
deletes the repo in a `finally`.
|
|
|
|
| 15 |
|
| 16 |
Four cases, free CPU, nothing published:
|
| 17 |
1 static -- the fetch is built from the argument; no fetch is built from the constant.
|
| 18 |
2 help -- `--help` lists `--ckpt-repo`.
|
| 19 |
+
3 positive-- the new file, told to read the temp repo, prints that repo's name and step 0, and stops before
|
| 20 |
+
any upload (the temp repo has no `final/`, which is the expected place for it to stop).
|
| 21 |
+
4 contrast-- the old file at REV `55f23ef5` rejects `--ckpt-repo` outright (argparse rc 2), so case 3 is
|
| 22 |
+
evidence about this change and not about something that was always true.
|
| 23 |
+
|
| 24 |
+
Layout matters: the publisher resolves `ounce100m_credentials` from its own grandparent directory
|
| 25 |
+
(`sys.path.insert(0, dirname(dirname(__file__)))`), and a subprocess is a fresh interpreter that inherits
|
| 26 |
+
nothing this kernel puts on `sys.path` -- so both copies go exactly where Phase 5 put them,
|
| 27 |
+
`<work>/build/publish_model.py` beside `<work>/ounce100m_credentials.py`. (E-056 was this same bug.)
|
| 28 |
"""
|
| 29 |
import hashlib
|
| 30 |
import io
|
|
|
|
| 38 |
NEW_REV = "dd1b6b94cc90ae6deff94e0990d58da64c6637f1"
|
| 39 |
NEW_SHA = "d1f3c4eb2b80cd4bff35c1b1b90a67ec6204dd753e09f354673d0812cc231e01"
|
| 40 |
OLD_REV = "55f23ef5d0e8e6a60adad23bf8a2ffffc6bc7580"
|
| 41 |
+
CRED_SHA = "6525f62f03f2d73650a1eb4f70fcb52d1194caad4ca88b2d8bd8fd54f88339b6"
|
| 42 |
TMP = "Cion-lab/ounce100m-p7-probe-tmp"
|
| 43 |
+
WORK = "/kaggle/working"
|
| 44 |
|
| 45 |
|
| 46 |
+
def fetch(rev, path):
|
| 47 |
+
url = f"https://huggingface.co/{CODE}/resolve/{rev}/{path}"
|
| 48 |
return urllib.request.urlopen(url, timeout=120).read()
|
| 49 |
|
| 50 |
|
| 51 |
def report(R, why=""):
|
| 52 |
print("PROBE_JSON_BEGIN\n" + json.dumps(R, indent=1) + "\nPROBE_JSON_END", flush=True)
|
| 53 |
+
oks = [v for k, v in R.items() if k.startswith("case")]
|
| 54 |
+
ok = bool(oks) and all(oks)
|
| 55 |
print(f"VERDICT PUBLISH_SOURCE {'PASS' if ok else 'FAIL'} {why}", flush=True)
|
| 56 |
return ok
|
| 57 |
|
| 58 |
|
| 59 |
R = {}
|
| 60 |
try:
|
| 61 |
+
for d in ("build", "build_old"):
|
| 62 |
+
os.makedirs(os.path.join(WORK, d), exist_ok=True)
|
| 63 |
+
cb = fetch(NEW_REV, "ounce100m_credentials.py")
|
| 64 |
+
assert hashlib.sha256(cb).hexdigest() == CRED_SHA, "credentials module is not the Phase 5 bytes"
|
| 65 |
+
open(os.path.join(WORK, "ounce100m_credentials.py"), "wb").write(cb)
|
| 66 |
+
sys.path.insert(0, WORK)
|
| 67 |
import ounce100m_credentials
|
| 68 |
ounce100m_credentials.install(verify=True)
|
| 69 |
from huggingface_hub import HfApi
|
| 70 |
api = HfApi()
|
| 71 |
|
| 72 |
+
nb = fetch(NEW_REV, "build/publish_model.py")
|
| 73 |
+
R["pinned_bytes_ok"] = hashlib.sha256(nb).hexdigest() == NEW_SHA
|
| 74 |
+
assert R["pinned_bytes_ok"], "the pinned publisher is not the bytes this probe was written against"
|
| 75 |
+
newp = os.path.join(WORK, "build", "publish_model.py")
|
| 76 |
+
oldp = os.path.join(WORK, "build_old", "publish_model.py")
|
| 77 |
open(newp, "wb").write(nb)
|
| 78 |
+
open(oldp, "wb").write(fetch(OLD_REV, "build/publish_model.py"))
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
src = nb.decode("utf-8")
|
| 81 |
R["case1_static_arg_wired"] = "datasets/{a.ckpt_repo}/resolve/main/latest.json" in src
|
|
|
|
| 84 |
R["case2_help_lists_flag"] = "--ckpt-repo" in subprocess.run(
|
| 85 |
[sys.executable, newp, "--help"], capture_output=True, text=True).stdout
|
| 86 |
|
| 87 |
+
tok = os.environ["HF_TOKEN"]
|
| 88 |
+
api.create_repo(repo_id=TMP, repo_type="dataset", private=True, exist_ok=True, token=tok)
|
| 89 |
api.upload_file(path_or_fileobj=io.BytesIO(json.dumps({"step": 0, "path_in_repo": "final"}).encode()),
|
| 90 |
+
path_in_repo="latest.json", repo_id=TMP, repo_type="dataset", token=tok)
|
| 91 |
made = True
|
| 92 |
|
| 93 |
r = subprocess.run([sys.executable, newp, "--dry-run", "--ckpt-repo", TMP],
|
| 94 |
capture_output=True, text=True)
|
| 95 |
out = r.stdout + r.stderr
|
| 96 |
R["case3_names_the_repo_it_was_given"] = f"'repo': '{TMP}'" in out
|
| 97 |
+
R["case3_read_the_pointer"] = "'step': 0" in out
|
| 98 |
+
R["case3_stopped_before_upload"] = r.returncode != 0 and "uploading" not in out
|
|
|
|
|
|
|
|
|
|
| 99 |
ro = subprocess.run([sys.executable, oldp, "--dry-run", "--ckpt-repo", TMP],
|
| 100 |
capture_output=True, text=True)
|
| 101 |
R["case4_old_file_rejects_the_flag"] = ro.returncode == 2 and "unrecognized arguments" in ro.stderr
|
| 102 |
+
R["observed_tail"] = out.strip().splitlines()[-1][:160] if out.strip() else ""
|
| 103 |
except Exception as e:
|
| 104 |
R["aborted"] = f"{type(e).__name__}: {str(e)[:200]}"
|
| 105 |
finally:
|
|
|
|
| 108 |
api.delete_repo(repo_id=TMP, repo_type="dataset")
|
| 109 |
R["temp_repo_deleted"] = True
|
| 110 |
except Exception as e:
|
| 111 |
+
R["temp_repo_deleted"] = f"FAILED {type(e).__name__} -- delete {TMP} by hand"
|
| 112 |
|
| 113 |
sys.exit(0 if report(R, R.get("aborted", "")) else 1)
|