Download testing/convert_oracle.py from PYTHAI/bankml: direct link, hf CLI and curl.
- Browser
- Download file 7.32 kB
-
https://huggingface.co/spaces/PYTHAI/bankml/resolve/main/testing/convert_oracle.py
- Command line
-
hf download hf://spaces/PYTHAI/bankml/testing/convert_oracle.py
-
curl -L -o convert_oracle.py https://huggingface.co/spaces/PYTHAI/bankml/resolve/main/testing/convert_oracle.py
7.32 kB
| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: MIT OR Apache-2.0 | |
| """The oracle of `bankml convert` (O5): llama.cpp b11192's own converter on the same safetensors directory. | |
| convert_oracle.py --record DIR OUT.gguf llama.cpp's `convert_hf_to_gguf.py DIR --outtype f16 --outfile OUT.gguf` | |
| (needs the b11192 source tree in $BANKML_LLAMA_SRC and its Python | |
| requirements: torch CPU, transformers, numpy, safetensors) | |
| convert_oracle.py --bankml DIR ORACLE.gguf `bankml convert DIR` and the sha256 of both files: identical or not | |
| convert_oracle.py --compare A.gguf B.gguf where two GGUFs differ: header, each key (order, type, value), each | |
| tensor (name, order, type, shape, offset, sha256 of its data) | |
| convert_oracle.py --chkhsh DIR llama.cpp's pre-tokenizer hash of DIR's tokenizer (its get_vocab_base_pre | |
| rule, through transformers) and bankml's vocabulary+merges sha256: the | |
| evidence a KNOWN_TOKENIZERS entry in convert.rs needs | |
| convert_oracle.py --names OUT.jsonl gguf-py's name heuristics on a corpus of model ids, for the Rust test | |
| oracle_name_heuristics (BANKML_NAMES_ORACLE=OUT.jsonl) | |
| The directory's name matters: llama.cpp names the model from it (general.name, basename, finetune, size label), so | |
| record and compare the same directory. Byte identity of the two files is the bar; --compare explains a miss. | |
| """ | |
| import ast, hashlib, json, os, re, subprocess, sys | |
| from pathlib import Path | |
| HERE = Path(__file__).resolve().parent | |
| ROOT = HERE.parent | |
| SRC = Path(os.environ.get("BANKML_LLAMA_SRC", "upstream/llama.cpp")) | |
| def sha256(p): | |
| h = hashlib.sha256() | |
| with open(p, "rb") as f: | |
| for b in iter(lambda: f.read(1 << 20), b""): | |
| h.update(b) | |
| return h.hexdigest() | |
| def gguf_mod(): | |
| sys.path.insert(0, str(SRC / "gguf-py")) | |
| import gguf # noqa: E402 | |
| return gguf | |
| def record(d, out): | |
| conv = SRC / "convert_hf_to_gguf.py" | |
| if not conv.is_file(): | |
| sys.exit(f"no {conv}: set BANKML_LLAMA_SRC to a llama.cpp b11192 source tree") | |
| subprocess.run([sys.executable, str(conv), d, "--outtype", "f16", "--outfile", out], check=True) | |
| print(f"{sha256(out)} {out} (llama.cpp convert_hf_to_gguf.py --outtype f16)") | |
| def bankml(d, oracle): | |
| out = str(Path(oracle).with_suffix(".bankml.gguf")) | |
| exe = os.environ.get("BANKML", str(ROOT / "target/release/bankml")) | |
| subprocess.run([exe, "convert", d, "-o", out], check=True) | |
| a, b = sha256(out), sha256(oracle) | |
| print(f"bankml {a}\nllama.cpp {b}\n{'IDENTICAL' if a == b else 'DIFFERENT (run --compare)'}") | |
| if a == b: | |
| os.remove(out) | |
| sys.exit(0 if a == b else 1) | |
| def compare(a, b): | |
| g = gguf_mod() | |
| ra, rb = g.GGUFReader(a), g.GGUFReader(b) | |
| def kv(r): | |
| out = [] | |
| for f in r.fields.values(): | |
| v = [bytes(f.parts[i]).hex() for i in f.data] | |
| out.append((f.name, [int(t) for t in f.types], hashlib.sha256("".join(v).encode()).hexdigest()[:16])) | |
| return out | |
| ka, kb = kv(ra), kv(rb) | |
| bad = 0 | |
| for i in range(max(len(ka), len(kb))): | |
| x = ka[i] if i < len(ka) else None | |
| y = kb[i] if i < len(kb) else None | |
| if x != y: | |
| bad += 1 | |
| print(f"key #{i}: {x} != {y}") | |
| for i in range(max(len(ra.tensors), len(rb.tensors))): | |
| x = ra.tensors[i] if i < len(ra.tensors) else None | |
| y = rb.tensors[i] if i < len(rb.tensors) else None | |
| d = lambda t: None if t is None else (t.name, int(t.tensor_type), [int(s) for s in t.shape], int(t.data_offset), hashlib.sha256(bytes(t.data)).hexdigest()[:16]) | |
| if d(x) != d(y): | |
| bad += 1 | |
| print(f"tensor #{i}: {d(x)} != {d(y)}") | |
| print(f"{len(ka)} / {len(kb)} keys, {len(ra.tensors)} / {len(rb.tensors)} tensors, {bad} differences") | |
| sys.exit(1 if bad else 0) | |
| def chkhsh(d): | |
| src = (SRC / "conversion" / "base.py").read_text() | |
| chktxt = ast.literal_eval(re.search(r"\n chktxt = (.*)\n", src).group(1)) | |
| from transformers import AutoTokenizer | |
| tok = AutoTokenizer.from_pretrained(d) | |
| h = hashlib.sha256(str(tok.encode(chktxt)).encode()).hexdigest() | |
| names = re.findall(r'if chkhsh == "([0-9a-f]{64})":\n\s+# ref: (\S+)\n\s+res = "([^"]+)"', src) | |
| hit = [n for n in names if n[0] == h] | |
| t = json.load(open(Path(d) / "tokenizer.json")) | |
| m = t["model"] | |
| inv = {v: k for k, v in m["vocab"].items()} | |
| for a in t.get("added_tokens", []): | |
| inv[a["id"]] = a["content"] | |
| n = json.load(open(Path(d) / "config.json")).get("vocab_size", 32000) | |
| vm = hashlib.sha256() | |
| for i in range(n): | |
| vm.update((inv.get(i, f"[PAD{i}]")).encode() + b"\n") | |
| vm.update(b"\n") | |
| for x in m["merges"]: | |
| x = x if isinstance(x, str) else " ".join("".join(chr(ord(c) + 256) if c == " " else c for c in p) for p in x) | |
| vm.update((x + "\n").encode()) | |
| print(json.dumps({"dir": d, "chkhsh": h, "llama.cpp pre": hit[0][2] if hit else None, "ref": hit[0][1] if hit else None, | |
| "vocab_merges_sha256": vm.hexdigest(), "pre_tokenizer": t.get("pre_tokenizer")}, indent=1)) | |
| NAMES = """smollm2-135m-instruct mindx-gen39 Mixtral-8x7B-Instruct-v0.1 meta-llama/Meta-Llama-3-8B bigscience/bloom-7b1-petals | |
| Qwen/Qwen2.5-0.5B-Instruct ./my-model ../x/model-7B HuggingFaceTB/SmolLM2-135M-Instruct HuggingFaceTB/SmolLM-135M | |
| prism-ml/Bonsai-8B-gguf Qwen/Qwen3-8B Qwen/Qwen3-0.6B-Base microsoft/Phi-3-mini-4k-instruct google/gemma-2-2b-it | |
| mistralai/Mistral-7B-Instruct-v0.3 deepseek-ai/DeepSeek-V2-Lite-Chat tiiuae/falcon-7b EleutherAI/pythia-1.4b-deduped | |
| TheBloke/Llama-2-13B-chat-GPTQ stabilityai/stablelm-2-1_6b openai-community/gpt2-xl allenai/OLMo-7B-0724-Instruct-hf | |
| ibm-granite/granite-3.0-2b-instruct unsloth/Llama-3.2-1B-Instruct mistralai/Mixtral-8x22B-v0.1 Qwen/Qwen1.5-MoE-A2.7B | |
| model-32k-chat merged checkpoint gen39 mindx-gen40-v2 mindX-Gen41-135M-lora Llama-3-70B-Instruct-AWQ q4_k_m-model | |
| some-model-F16 Model-bf16-v1.2.3 tiny-llama-1.1b-chat-v1.0 x-small-mini-medium iter12-model A3B-thing model-7b1""".split() | |
| def names(out): | |
| g = gguf_mod() | |
| from gguf.metadata import Metadata | |
| with open(out, "w") as f: | |
| for i in NAMES: | |
| for total in (0, 134_515_008, 8_190_000_000, -2_000_000): | |
| parts = Metadata.get_model_id_components(i, total) | |
| f.write(json.dumps({"id": i, "total": total, "parts": list(parts), "title": Metadata.id_to_title(i), | |
| "size": g.size_label(abs(total), abs(total), 0, 0) if total else None}) + "\n") | |
| print(f"{len(NAMES) * 4} cases → {out}") | |
| if __name__ == "__main__": | |
| a = sys.argv[1:] | |
| if len(a) == 3 and a[0] == "--record": | |
| record(a[1], a[2]) | |
| elif len(a) == 3 and a[0] == "--bankml": | |
| bankml(a[1], a[2]) | |
| elif len(a) == 3 and a[0] == "--compare": | |
| compare(a[1], a[2]) | |
| elif len(a) == 2 and a[0] == "--chkhsh": | |
| chkhsh(a[1]) | |
| elif len(a) == 2 and a[0] == "--names": | |
| names(a[1]) | |
| else: | |
| sys.exit(__doc__) | |