255 lines
9.7 KiB
Python
255 lines
9.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Refresh (or check) the pinned GLM-5.2 target manifest and architecture snapshot.
|
|
|
|
Resolves revisions, shard sizes, and LFS SHA-256 digests from the Hugging Face
|
|
metadata API. It never downloads a weight payload: sizes and digests come from
|
|
``/api/models/.../paths-info``, which returns the LFS pointer metadata, and the only
|
|
files fetched in full are the small config/tokenizer/chat-template documents whose
|
|
bytes the snapshot hashes.
|
|
|
|
Usage::
|
|
|
|
python scripts/refresh_glm_target_manifest.py --check # CI: pinned bytes still resolve?
|
|
python scripts/refresh_glm_target_manifest.py --write # re-pin HEAD after human review
|
|
|
|
``--check`` validates the already locked revisions through Hugging Face's
|
|
revision-specific API. A normal new upstream commit does not mutate the target.
|
|
``--write`` intentionally follows moving HEAD and is therefore a reviewed target
|
|
change, never an automatic refresh.
|
|
|
|
This script requires network access and is not part of the default test suite.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
import urllib.request
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
DATA_DIR = Path(__file__).resolve().parent.parent / "packages/node/meshnet_node/glm_alpha/data"
|
|
MANIFEST_PATH = DATA_DIR / "target-manifest.json"
|
|
SNAPSHOT_PATH = DATA_DIR / "architecture-snapshot.json"
|
|
|
|
SOURCE_REPO = "zai-org/GLM-5.2"
|
|
GGUF_REPO = "unsloth/GLM-5.2-GGUF"
|
|
QUANT = "UD-IQ1_S"
|
|
FALLBACK_QUANT = "UD-IQ1_M"
|
|
SHARD_COUNT = 6
|
|
|
|
SNAPSHOT_FILES = (
|
|
"config.json",
|
|
"chat_template.jinja",
|
|
"generation_config.json",
|
|
"tokenizer_config.json",
|
|
)
|
|
|
|
TIMEOUT = 60
|
|
|
|
|
|
def _get(url: str) -> bytes:
|
|
with urllib.request.urlopen(url, timeout=TIMEOUT) as response: # noqa: S310 - fixed HTTPS host
|
|
return response.read()
|
|
|
|
|
|
def _get_json(url: str) -> Any:
|
|
return json.loads(_get(url))
|
|
|
|
|
|
def _post_json(url: str, payload: dict) -> Any:
|
|
request = urllib.request.Request(
|
|
url,
|
|
data=json.dumps(payload).encode("utf-8"),
|
|
headers={"Content-Type": "application/json"},
|
|
method="POST",
|
|
)
|
|
with urllib.request.urlopen(request, timeout=TIMEOUT) as response: # noqa: S310
|
|
return json.loads(response.read())
|
|
|
|
|
|
def _shard_paths(quant: str) -> list[str]:
|
|
return [
|
|
f"{quant}/GLM-5.2-{quant}-{index:05d}-of-{SHARD_COUNT:05d}.gguf"
|
|
for index in range(1, SHARD_COUNT + 1)
|
|
]
|
|
|
|
|
|
def _resolve_shards(revision: str, quant: str) -> list[dict]:
|
|
info = _post_json(
|
|
f"https://huggingface.co/api/models/{GGUF_REPO}/paths-info/{revision}",
|
|
{"paths": _shard_paths(quant)},
|
|
)
|
|
by_path = {entry["path"]: entry for entry in info}
|
|
shards = []
|
|
for index, path in enumerate(_shard_paths(quant), start=1):
|
|
entry = by_path.get(path)
|
|
if entry is None:
|
|
raise SystemExit(f"upstream is missing shard {path} at revision {revision}")
|
|
lfs = entry.get("lfs") or {}
|
|
oid = lfs.get("oid")
|
|
if not oid:
|
|
raise SystemExit(
|
|
f"{path} has no LFS oid; a non-LFS shard is not the published artifact"
|
|
)
|
|
shards.append(
|
|
{
|
|
"index": index,
|
|
"path": path,
|
|
"size_bytes": int(entry["size"]),
|
|
"sha256": oid,
|
|
"url": f"https://huggingface.co/{GGUF_REPO}/resolve/{revision}/{path}",
|
|
}
|
|
)
|
|
return shards
|
|
|
|
|
|
def build_documents(
|
|
*,
|
|
source_revision: str | None = None,
|
|
gguf_revision: str | None = None,
|
|
) -> tuple[dict, dict]:
|
|
"""Resolve documents at explicit pins, or at current HEAD for reviewed re-pinning."""
|
|
|
|
source_api = f"https://huggingface.co/api/models/{SOURCE_REPO}"
|
|
gguf_api = f"https://huggingface.co/api/models/{GGUF_REPO}"
|
|
if source_revision is not None:
|
|
source_api += f"/revision/{source_revision}"
|
|
if gguf_revision is not None:
|
|
gguf_api += f"/revision/{gguf_revision}"
|
|
source_info = _get_json(source_api)
|
|
gguf_info = _get_json(gguf_api)
|
|
source_rev = source_info["sha"]
|
|
gguf_rev = gguf_info["sha"]
|
|
if source_revision is not None and source_rev != source_revision:
|
|
raise SystemExit(
|
|
f"source revision endpoint returned {source_rev}, expected {source_revision}"
|
|
)
|
|
if gguf_revision is not None and gguf_rev != gguf_revision:
|
|
raise SystemExit(f"GGUF revision endpoint returned {gguf_rev}, expected {gguf_revision}")
|
|
|
|
shards = _resolve_shards(gguf_rev, QUANT)
|
|
total = sum(shard["size_bytes"] for shard in shards)
|
|
fallback = _resolve_shards(gguf_rev, FALLBACK_QUANT)
|
|
fallback_total = sum(shard["size_bytes"] for shard in fallback)
|
|
|
|
manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
|
manifest["source_model"]["revision"] = source_rev
|
|
manifest["source_model"]["last_modified"] = source_info.get("lastModified")
|
|
manifest["source_model"]["revision_url"] = (
|
|
f"https://huggingface.co/{SOURCE_REPO}/tree/{source_rev}"
|
|
)
|
|
manifest["gguf_artifact"]["revision"] = gguf_rev
|
|
manifest["gguf_artifact"]["last_modified"] = gguf_info.get("lastModified")
|
|
manifest["gguf_artifact"]["revision_url"] = (
|
|
f"https://huggingface.co/{GGUF_REPO}/tree/{gguf_rev}"
|
|
)
|
|
manifest["gguf_artifact"]["shards"] = shards
|
|
manifest["gguf_artifact"]["total_bytes"] = total
|
|
manifest["gguf_artifact"]["total_gib"] = round(total / 1024**3, 3)
|
|
manifest["gguf_artifact"]["total_gb"] = round(total / 1000**3, 3)
|
|
manifest["diagnostic_fallback"]["total_bytes"] = fallback_total
|
|
manifest["diagnostic_fallback"]["total_gib"] = round(fallback_total / 1024**3, 3)
|
|
manifest["diagnostic_fallback"]["total_gb"] = round(fallback_total / 1000**3, 3)
|
|
|
|
snapshot = json.loads(SNAPSHOT_PATH.read_text(encoding="utf-8"))
|
|
snapshot["source_revision"] = source_rev
|
|
source_files = []
|
|
config: dict[str, Any] = {}
|
|
for name in SNAPSHOT_FILES:
|
|
url = f"https://huggingface.co/{SOURCE_REPO}/resolve/{source_rev}/{name}"
|
|
body = _get(url)
|
|
source_files.append(
|
|
{
|
|
"path": name,
|
|
"size_bytes": len(body),
|
|
"sha256": hashlib.sha256(body).hexdigest(),
|
|
"url": url,
|
|
}
|
|
)
|
|
if name == "config.json":
|
|
config = json.loads(body)
|
|
snapshot["source_files"] = source_files
|
|
|
|
indexer_types = config["indexer_types"]
|
|
arch = snapshot["architecture"]
|
|
arch["num_hidden_layers"] = config["num_hidden_layers"]
|
|
arch["num_nextn_predict_layers"] = config["num_nextn_predict_layers"]
|
|
arch["total_artifact_layers"] = config["num_hidden_layers"] + config["num_nextn_predict_layers"]
|
|
arch["hidden_size"] = config["hidden_size"]
|
|
arch["n_routed_experts"] = config["n_routed_experts"]
|
|
arch["num_experts_per_tok"] = config["num_experts_per_tok"]
|
|
arch["n_shared_experts"] = config["n_shared_experts"]
|
|
arch["index_topk"] = config["index_topk"]
|
|
arch["index_head_dim"] = config["index_head_dim"]
|
|
arch["kv_lora_rank"] = config["kv_lora_rank"]
|
|
arch["qk_rope_head_dim"] = config["qk_rope_head_dim"]
|
|
arch["mla_cached_values_per_token_per_layer"] = (
|
|
config["kv_lora_rank"] + config["qk_rope_head_dim"]
|
|
)
|
|
arch["indexer_full_layers"] = sum(1 for role in indexer_types if role == "full")
|
|
arch["indexer_shared_layers"] = sum(1 for role in indexer_types if role == "shared")
|
|
arch["indexer_types_sha256"] = hashlib.sha256(
|
|
json.dumps(indexer_types, separators=(",", ":")).encode("utf-8")
|
|
).hexdigest()
|
|
arch["max_position_embeddings"] = config["max_position_embeddings"]
|
|
arch["vocab_size"] = config["vocab_size"]
|
|
arch["first_k_dense_replace"] = config["first_k_dense_replace"]
|
|
arch["dense_layers"] = config["first_k_dense_replace"]
|
|
arch["sparse_moe_layers"] = config["num_hidden_layers"] - config["first_k_dense_replace"]
|
|
|
|
return manifest, snapshot
|
|
|
|
|
|
def _dump(document: dict) -> str:
|
|
return json.dumps(document, indent=2, ensure_ascii=False) + "\n"
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument("--check", action="store_true", help="fail if the pins have drifted")
|
|
group.add_argument("--write", action="store_true", help="re-pin from upstream")
|
|
args = parser.parse_args()
|
|
|
|
if args.check:
|
|
pinned = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
|
manifest, snapshot = build_documents(
|
|
source_revision=pinned["source_model"]["revision"],
|
|
gguf_revision=pinned["gguf_artifact"]["revision"],
|
|
)
|
|
else:
|
|
# --write intentionally follows current HEAD and therefore requires review.
|
|
manifest, snapshot = build_documents()
|
|
|
|
if args.write:
|
|
MANIFEST_PATH.write_text(_dump(manifest), encoding="utf-8")
|
|
SNAPSHOT_PATH.write_text(_dump(snapshot), encoding="utf-8")
|
|
print(f"wrote {MANIFEST_PATH}")
|
|
print(f"wrote {SNAPSHOT_PATH}")
|
|
print("Re-pinning changes the alpha target. Update the alpha contract under review.")
|
|
return 0
|
|
|
|
drifted = False
|
|
for path, fresh in ((MANIFEST_PATH, manifest), (SNAPSHOT_PATH, snapshot)):
|
|
current = json.loads(path.read_text(encoding="utf-8"))
|
|
if current != fresh:
|
|
drifted = True
|
|
print(f"DRIFT: {path.name} no longer matches upstream", file=sys.stderr)
|
|
if drifted:
|
|
print(
|
|
"\nPinned revision metadata no longer matches the locked files. Treat this as "
|
|
"artifact-integrity drift; do not heal or re-pin without human review.",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
print("target manifest and architecture snapshot match upstream")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|