#!/usr/bin/env python3 """Parse GGUF headers from HuggingFace via HTTP range requests (no full download). Produces a JSON tensor map per shard: name, shape, ggml type, absolute byte range within the shard file. This lets us (a) recover unsloth's per-tensor quant-type assignment (their importance map), and (b) later download exactly the expert tensors we need for the hybrid checkpoint build. """ import json import struct import sys import urllib.request GGML_TYPES = { 0: "F32", 1: "F16", 2: "Q4_0", 3: "Q4_1", 6: "Q5_0", 7: "Q5_1", 8: "Q8_0", 9: "Q8_1", 10: "Q2_K", 11: "Q3_K", 12: "Q4_K", 13: "Q5_K", 14: "Q6_K", 15: "Q8_K", 16: "IQ2_XXS", 17: "IQ2_XS", 18: "IQ3_XXS", 19: "IQ1_S", 20: "IQ4_NL", 21: "IQ3_S", 22: "IQ2_S", 23: "IQ4_XS", 24: "I8", 25: "I16", 26: "I32", 27: "I64", 28: "F64", 29: "IQ1_M", 30: "BF16", 34: "TQ1_0", 35: "TQ2_0", 39: "MXFP4", } # bytes per block / elements per block GGML_BLOCK = { "F32": (4, 1), "F16": (2, 1), "BF16": (2, 1), "Q8_0": (34, 32), "Q4_0": (18, 32), "Q4_1": (20, 32), "Q5_0": (22, 32), "Q5_1": (24, 32), "Q2_K": (84, 256), "Q3_K": (110, 256), "Q4_K": (144, 256), "Q5_K": (176, 256), "Q6_K": (210, 256), "IQ1_S": (50, 256), "IQ1_M": (56, 256), "IQ2_XXS": (66, 256), "IQ2_XS": (74, 256), "IQ2_S": (82, 256), "IQ3_XXS": (98, 256), "IQ3_S": (110, 256), "IQ4_NL": (18, 32), "IQ4_XS": (136, 256), "I8": (1, 1), "I16": (2, 1), "I32": (4, 1), "I64": (8, 1), "F64": (8, 1), "MXFP4": (17, 32), } def fetch_range(url: str, start: int, length: int) -> bytes: req = urllib.request.Request(url) req.add_header("Range", f"bytes={start}-{start + length - 1}") with urllib.request.urlopen(req) as r: return r.read() class Cursor: def __init__(self, buf: bytes): self.buf = buf self.pos = 0 def need(self, n): if self.pos + n > len(self.buf): raise EOFError(f"header larger than fetched window ({len(self.buf)} bytes)") def u32(self): self.need(4); v = struct.unpack_from(" (1 << 30): raise buf = fetch_range(url, 0, window) def _parse(buf: bytes, want_kv: bool): c = Cursor(buf) magic = c.u32() assert magic == 0x46554747, f"not gguf: {magic:#x}" version = c.u32() assert version in (2, 3), version n_tensors = c.u64() n_kv = c.u64() kvs = {} alignment = 32 for _ in range(n_kv): k = c.s() t = c.u32() v = c.value(t) if k == "general.alignment": alignment = v if want_kv and not (isinstance(v, list) and len(v) > 64): kvs[k] = v tensors = [] for _ in range(n_tensors): name = c.s() nd = c.u32() dims = [c.u64() for _ in range(nd)] ty = GGML_TYPES.get(c.u32(), "?") off = c.u64() tensors.append({"name": name, "dims": dims, "type": ty, "off": off}) data_start = (c.pos + alignment - 1) // alignment * alignment for t in tensors: bpb, epb = GGML_BLOCK[t["type"]] nelem = 1 for d in t["dims"]: nelem *= d nbytes = nelem // epb * bpb t["abs_off"] = data_start + t["off"] t["nbytes"] = nbytes return {"n_tensors": n_tensors, "data_start": data_start, "tensors": tensors, "kv": kvs} if __name__ == "__main__": base = sys.argv[1] # e.g. https://huggingface.co/unsloth/GLM-5.2-GGUF/resolve/main/UD-Q2_K_XL/GLM-5.2-UD-Q2_K_XL nshards = int(sys.argv[2]) out = sys.argv[3] all_shards = {} for i in range(1, nshards + 1): url = f"{base}-{i:05d}-of-{nshards:05d}.gguf" h = parse_header(url, want_kv=(i == 1)) all_shards[i] = {"url": url, "data_start": h["data_start"], "tensors": h["tensors"], "kv": h.get("kv", {})} print(f"shard {i}: {h['n_tensors']} tensors", file=sys.stderr) json.dump(all_shards, open(out, "w")) print(f"wrote {out}", file=sys.stderr)