CoolFace
Modelpublic

patdev/k3-a40-bootstrap

sourceHugging Faceotherupdated 20d agoView on Hugging Face
0likes1.3kdownloads
fix_dspark_arch.py134 linesDownload Raw Back to root
1#!/usr/bin/env python32"""Rewrite a Kimi-K3 DSpark GGUF so llama.cpp will load it.3 4Lucebox/Kimi-K3-DSpark-Q8_0-GGUF was produced by convert_dspark_to_gguf.py v1,5which stamps `general.architecture = "dflash-draft"`. llama.cpp registers the6architecture as plain `"dflash"` (LLM_ARCH_DFLASH), so loading fails with:7 8    error loading model: unknown model architecture: 'dflash-draft'9 10Everything else in the file is already correct -- the tensors are named11`dflash.dspark.markov.w1`, `dflash.fc.weight`, `blk.N.attn_q.weight`, exactly12what LLM_TENSOR_DSPARK_* expects. Only the architecture string and the KV keys13namespaced under it need renaming.14 15Usage: fix_dspark_arch.py IN.gguf OUT.gguf [--from dflash-draft] [--to dflash]16Exit codes: 0 rewritten, 3 nothing to do, 1 failure.17"""18from __future__ import annotations19 20import argparse21import sys22 23from gguf import GGUFReader, GGUFWriter24from gguf.constants import GGUFValueType25 26 27def field_value(field):28    """Return a plain Python value for a GGUFReader field."""29    # Newer gguf exposes .contents(); fall back to manual decoding.30    contents = getattr(field, "contents", None)31    if callable(contents):32        return contents()33    if field.types and field.types[0] == GGUFValueType.STRING:34        return str(bytes(field.parts[field.data[0]]), encoding="utf-8")35    return field.parts[field.data[0]].tolist()[0]36 37 38def main() -> int:39    ap = argparse.ArgumentParser()40    ap.add_argument("src")41    ap.add_argument("dst")42    ap.add_argument("--from", dest="old", default="dflash-draft")43    ap.add_argument("--to", dest="new", default="dflash")44    a = ap.parse_args()45 46    reader = GGUFReader(a.src)47    arch_field = reader.get_field("general.architecture")48    if arch_field is None:49        print("FAIL: no general.architecture in source", file=sys.stderr)50        return 151    arch = field_value(arch_field)52    print(f"source architecture: {arch!r}")53    if arch == a.new:54        print("already correct, nothing to do")55        return 356    if arch != a.old:57        print(f"FAIL: unexpected architecture {arch!r} (expected {a.old!r})", file=sys.stderr)58        return 159 60    writer = GGUFWriter(a.dst, a.new)61 62    renamed = 063    for key, field in reader.fields.items():64        # GGUFReader surfaces the file header as pseudo-fields (GGUF.version,65        # GGUF.tensor_count, GGUF.kv_count). Copying them as real KV pairs66        # makes GGUFWriter emit a second copy of the header, and the result67        # fails to reopen with "Duplicate GGUF.version already in list".68        if key.startswith("GGUF."):69            continue70        if key == "general.architecture":71            continue  # GGUFWriter already wrote it as a.new72        new_key = key73        if key.startswith(a.old + "."):74            new_key = a.new + key[len(a.old):]75            renamed += 176        if not field.types:77            continue78        vtype = field.types[0]79        if vtype == GGUFValueType.ARRAY:80            # Arrays carry their element type as the second entry.81            sub = field.types[1]82            if sub == GGUFValueType.STRING:83                vals = [str(bytes(field.parts[i]), encoding="utf-8") for i in field.data]84            else:85                vals = [field.parts[i].tolist()[0] for i in field.data]86            writer.add_key_value(new_key, vals, GGUFValueType.ARRAY, sub_type=sub)87        else:88            writer.add_key_value(new_key, field_value(field), vtype)89 90    for t in reader.tensors:91        # No raw_shape. For a quantized raw_dtype, add_tensor_info treats the92        # shape it is given as a BYTE shape and runs quant_shape_from_byte_shape93        # on it; handing it the logical shape makes it try to divide 163840 by94        # the Q8_0 type size of 34. reader tensors already carry the byte shape95        # in .data, which is exactly what it wants.96        writer.add_tensor(t.name, t.data, raw_dtype=t.tensor_type)97 98    writer.write_header_to_file()99    writer.write_kv_data_to_file()100    writer.write_tensors_to_file()101    writer.close()102    print(f"renamed {renamed} '{a.old}.*' keys, copied {len(reader.tensors)} tensors")103 104    # Verify rather than assume: a silently truncated or mis-shaped rewrite105    # would only surface much later, as a confusing runtime failure.106    check = GGUFReader(a.dst)107    got = field_value(check.get_field("general.architecture"))108    if got != a.new:109        print(f"FAIL: output architecture is {got!r}", file=sys.stderr)110        return 1111    if len(check.tensors) != len(reader.tensors):112        print(f"FAIL: {len(check.tensors)} tensors written, {len(reader.tensors)} expected",113              file=sys.stderr)114        return 1115    # Compare shape AND dtype AND byte size. Byte size alone would accept a116    # tensor that was reshaped into something the model cannot use.117    def sig(ts):118        return {t.name: (tuple(t.shape.tolist()), int(t.tensor_type), int(t.n_bytes)) for t in ts}119 120    src_sig, dst_sig = sig(reader.tensors), sig(check.tensors)121    if src_sig != dst_sig:122        only = set(src_sig) ^ set(dst_sig)123        changed = [k for k in set(src_sig) & set(dst_sig) if src_sig[k] != dst_sig[k]]124        print(f"FAIL: tensor mismatch; name-diff={sorted(only)[:5]}", file=sys.stderr)125        for k in changed[:5]:126            print(f"  {k}: {src_sig[k]} -> {dst_sig[k]}", file=sys.stderr)127        return 1128    print(f"verified: architecture={got}, {len(check.tensors)} tensors, shapes/dtypes/sizes identical")129    return 0130 131 132if __name__ == "__main__":133    raise SystemExit(main())134