#!/usr/bin/env python3 """Repair the engram metadata of a DeepSeek-V4.1 GGUF that was written before the converter fix. Two things went wrong in those files and both live in the header of the first shard: 1. the four engram keys carry a hardcoded `deepseek4.` prefix, so a `deepseek41` model looks for `deepseek41.engram.head_count` and finds nothing 2. the five constants the hash actually needs are absent, because gguf-py's add_array() maps every Python int to INT32, the 47 bit multipliers raised struct.error, and a broad except downgraded that to a warning Tensor data is untouched. Existing key/value pairs are re-emitted byte for byte, apart from the four that get renamed, so nothing this script does not understand can be corrupted by it. python fix_gguf_engram_kv.py shard1.gguf out.gguf --model-dir /path/to/DeepSeek-V4.1-Flash """ import argparse import os import struct import sys GGUF_MAGIC = b"GGUF" # value type tags T_UINT32 = 4 T_INT32 = 5 T_STRING = 8 T_ARRAY = 9 T_UINT64 = 10 FIXED = {0: 1, 1: 1, 2: 2, 3: 2, 4: 4, 5: 4, 6: 4, 7: 1, 10: 8, 11: 8, 12: 8} def _is_prime(n: int) -> bool: if n < 2: return False for p in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37): if n % p == 0: return n == p i = 41 while i * i <= n: if n % i == 0 or n % (i + 2) == 0: return False i += 6 return True def _next_prime(start: int, seen: set) -> int: c = start + 1 while not _is_prime(c) or c in seen: c += 1 return c def build_token_map(model_dir): """Case folded, accent stripped vocabulary, exactly as the reference builds it.""" from tokenizers import Regex, normalizers from transformers import AutoTokenizer tok = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True) sentinel = "" # private use char, so a lone space survives Strip() norm = normalizers.Sequence([ normalizers.NFKC(), normalizers.NFD(), normalizers.StripAccents(), normalizers.Lowercase(), normalizers.Replace(Regex(r"[ \t\r\n]+"), " "), normalizers.Replace(Regex(r"^ $"), sentinel), normalizers.Strip(), normalizers.Replace(sentinel, " "), ]) backend = tok.backend_tokenizer key_to_new, lookup = {}, [0] * len(tok) for tid in range(len(tok)): text = backend.decode([tid], skip_special_tokens=False) if "�" in text: key = backend.id_to_token(tid) else: normalized = norm.normalize_str(text) key = normalized if normalized else text new = key_to_new.get(key) if new is None: new = len(key_to_new) key_to_new[key] = new lookup[tid] = new return lookup, len(key_to_new) def build_constants(model_dir, layer_ids, max_ngram, n_heads, vocab_size, pad_raw): import numpy as np token_map, compressed = build_token_map(model_dir) max_long = np.iinfo(np.int64).max bound = max(1, (max_long // compressed) // 2) mults = [] for lid in layer_ids: rng = np.random.default_rng(10007 * lid) mults.extend(int(v) * 2 + 1 for v in rng.integers(0, bound, size=(max_ngram,), dtype=np.int64)) primes, seen = [], set() for _ in layer_ids: for _ in range(max_ngram - 1): cur = vocab_size - 1 for _ in range(n_heads): cur = _next_prime(cur, seen) seen.add(cur) primes.append(cur) per_layer = (max_ngram - 1) * n_heads offsets = [] for l in range(len(layer_ids)): acc = 0 for b in range(per_layer): offsets.append(acc) acc += primes[l * per_layer + b] return { "multipliers": mults, "primes": primes, "offsets": offsets, "token_map": token_map, "pad_id": token_map[pad_raw], "compressed_vocab": compressed, } def kv_uint32(v): return struct.pack(" {len(header)} bytes, copying " f"{(src_size - data_start)/1e9:.1f} GB of tensor data") f.seek(data_start) with open(args.dst, "wb") as out: out.write(header) while True: chunk = f.read(64 << 20) if not chunk: break out.write(chunk) print(f" wrote {args.dst} ({os.path.getsize(args.dst)/1e9:.1f} GB)") if __name__ == "__main__": main()