| |
| """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", |
| } |
| |
| 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("<I", self.buf, self.pos)[0]; self.pos += 4; return v |
|
|
| def u64(self): |
| self.need(8); v = struct.unpack_from("<Q", self.buf, self.pos)[0]; self.pos += 8; return v |
|
|
| def i64(self): |
| self.need(8); v = struct.unpack_from("<q", self.buf, self.pos)[0]; self.pos += 8; return v |
|
|
| def f32(self): |
| self.need(4); v = struct.unpack_from("<f", self.buf, self.pos)[0]; self.pos += 4; return v |
|
|
| def f64(self): |
| self.need(8); v = struct.unpack_from("<d", self.buf, self.pos)[0]; self.pos += 8; return v |
|
|
| def s(self): |
| n = self.u64(); self.need(n) |
| v = self.buf[self.pos:self.pos + n].decode("utf-8", errors="replace"); self.pos += n |
| return v |
|
|
| def value(self, t): |
| if t == 0: self.need(1); v = self.buf[self.pos]; self.pos += 1; return v |
| if t == 1: self.need(1); v = struct.unpack_from("<b", self.buf, self.pos)[0]; self.pos += 1; return v |
| if t == 2: self.need(2); v = struct.unpack_from("<H", self.buf, self.pos)[0]; self.pos += 2; return v |
| if t == 3: self.need(2); v = struct.unpack_from("<h", self.buf, self.pos)[0]; self.pos += 2; return v |
| if t == 4: return self.u32() |
| if t == 5: self.need(4); v = struct.unpack_from("<i", self.buf, self.pos)[0]; self.pos += 4; return v |
| if t == 6: return self.f32() |
| if t == 7: self.need(1); v = self.buf[self.pos] != 0; self.pos += 1; return v |
| if t == 8: return self.s() |
| if t == 9: |
| et = self.u32(); n = self.u64() |
| return [self.value(et) for _ in range(n)] |
| if t == 10: return self.u64() |
| if t == 11: return self.i64() |
| if t == 12: return self.f64() |
| raise ValueError(f"bad gguf kv type {t}") |
|
|
|
|
| def parse_header(url: str, window: int = 4 << 20, want_kv: bool = False): |
| buf = fetch_range(url, 0, window) |
| while True: |
| try: |
| return _parse(buf, want_kv) |
| except EOFError: |
| window *= 4 |
| if window > (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] |
| 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) |
|
|