Kuberwastaken/Kimi-K3-GGUF
<p align="center"> <img src="assets/banner.png" alt="Kimi-K3 → GGUF" width="100%"> </p>
<p align="center"> <a href="https://huggingface.co/moonshotai/Kimi-K3"><img src="https://img.shields.io/badge/base%20model-moonshotai%2FKimi--K3-1783FF?style=flat-square" alt="base model"></a> <img src="https://img.shields.io/badge/format-GGUF-1783FF?style=flat-square" alt="GGUF"> <img src="https://img.shields.io/badge/precision-MXFP4%20lossless-2ea043?style=flat-square" alt="lossless"> <img src="https://img.shields.io/badge/verified-1.419%20TiB%20%C2%B7%200%20mismatches-2ea043?style=flat-square" alt="verified"> <img src="https://img.shields.io/badge/llama.cpp-not%20yet%20supported-d29922?style=flat-square" alt="llama.cpp status"> <a href="https://huggingface.co/moonshotai/Kimi-K3/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Kimi--K3-8a94a3?style=flat-square" alt="license"></a> </p>
What this is
GGUF conversion assets for [`moonshotai/Kimi-K3`](https://huggingface.co/moonshotai/Kimi-K3) — Moonshot AI's 2.8T-parameter (104B active) multimodal MoE.
This repo hosts the complete vision encoder, the full tokenizer and architecture metadata, and a converter proven lossless across the entire 1.419 TiB model. It does not re-host the expert weights: those already live in Moonshot's repo, and the converter streams from there so you get identical bytes without a second terabyte sitting on the Hub.
[!WARNING] Nothing here runs yet. llama.cpp has nokimi_k3architecture, so no GGUF of this model — from anyone — will load inllama-clitoday. These files are correct, verified and ready for the moment support lands. See What's blocking it; the gap is smaller than the model's size suggests.
Contents
Setup
pip install numpy gguf huggingface_hub
hf download Kuberwastaken/Kimi-K3-GGUF --local-dir k3gguf
cd k3ggufThat's enough to use the vision encoder, tokenizer and manifest. To build the full model you additionally need the metadata files from the source repo:
hf download moonshotai/Kimi-K3 --local-dir meta \
config.json model.safetensors.index.json tokenizer_config.json tiktoken.modelRequirements for a full conversion
There is no fp16 intermediate at any point. A dequantise-then-requantise pipeline would need ~5.6 TB of scratch; this needs one shard.
Operating guide
Convert the whole model
python convert_kimi_k3.py --meta ./meta --out-dir ./out --shards 1-94Output is Kimi-K3-MXFP4-000NN-of-00094.gguf. Weights stream from the Hub automatically; pass --src ./local-copy if you already have the safetensors.
Convert a piece at a time
Source shard k holds *exactly layer k−1***, and no tensor spans two shards. The 94 units are therefore fully independent — interrupt, resume, or spread them across machines freely.
python convert_kimi_k3.py --meta ./meta --out-dir ./out --shards 2 # layer 1
python convert_kimi_k3.py --meta ./meta --out-dir ./out --shards 2,5,9 # a few
python convert_kimi_k3.py --meta ./meta --out-dir ./out --shards 40-60 # a rangeOptions
In the cloud
modal_run.py fans the same work across 94 containers (~$4 of Modal credits, ~5 min wall-clock). Create a huggingface secret holding HF_TOKEN first — 94 unauthenticated containers will hit Hub rate limits.
modal secret create huggingface HF_TOKEN=hf_...
modal run modal_run.py::pilot # one layer, verified, no upload
modal run modal_run.py::convert_all # all 94
modal run modal_run.py::build_manifest # all 94 + SHA-256 manifestRebuild the small artifacts
python convert_vision.py --src ./meta --out mmproj-Kimi-K3-BF16.gguf
python convert_tokenizer.py --src ./meta --out Kimi-K3-tokenizer.ggufconvert_vision.py needs shards 95 and 96 (the projector and vision tower).
Why the conversion is lossless
Kimi-K3 ships natively in MXFP4 (compressed-tensors, mxfp4-pack-quantized, group size 32, E8M0 uint8 scales). GGML's block_mxfp4 stores exactly the same information:
source: packed nibbles [K/2 bytes] + E8M0 scale [K/32 bytes]
ggml: block_mxfp4 { uint8 e; uint8 qs[16]; } × K/32For one expert tensor that is 5,505,024 + 344,064 = 5,849,088 bytes on both sides — identical. The conversion is a nibble permutation (the source packs consecutive pairs 2k/2k+1; GGML packs j/j+16) with the scale byte copied verbatim. No dequantisation, no intermediate, no precision loss.
The scale byte transfers untouched because GGML's half-scale × doubled-kvalues convention is algebraically identical to the OCP MX interpretation: E8M0_TO_FP32_HALF(e) × (2·e2m1) == 2^(e-127) × e2m1.
[!NOTE] One tensor's values do change.A_log→ssm_ais stored pre-transformed as-exp(A_log), matching llama.cpp'skimi_linear.py. Every other tensor in the model is bit-preserved.
Verification
Nothing above is asserted without a check:
Two independent implementations are compared for the MXFP4 path: a NumPy reference written from the compressed-tensors semantics, and llama.cpp's own gguf.quants.MXFP4 dequantizer. They agree bit-for-bit.
Verify your own conversion
MANIFEST.json records the SHA-256 of the canonical GGML bytes for all 2,760 tensors, with ggml type and ne:
"blk.1.ffn_down_exps.weight": { "sha256": "…", "type": 39, "ne": [3072, 3584, 896] }So you never have to take this on faith:
import hashlib, json
from gguf import GGUFReader
man = json.load(open("MANIFEST.json"))["tensors"]
for t in GGUFReader("out/Kimi-K3-MXFP4-00002-of-00094.gguf").tensors:
assert hashlib.sha256(t.data.tobytes()).hexdigest() == man[t.name]["sha256"], t.name
print("match")Composition: 1,978 BF16 + 506 F32 + 276 MXFP4 (stacked experts, 92 layers × 3) = 2,760 tensors, 1,559,972,708,032 bytes. The ~846 MiB difference from the source index is the vision tower, which lives in the mmproj file instead.
Warnings and gotchas
[!WARNING] It will not load in llama.cpp yet.general.architectureiskimi-k3, which yields an honest "unknown architecture" error. Declaringkimi-linearwould make llama.cpp attempt a graph that cannot represent this model.
- You still need the disk. GGUF has no remote-pointer mechanism — tensor bytes live inside the file. Streaming avoids re-hosting, not downloading.
- Don't run 94 containers unauthenticated. Set
HF_TOKENor you'll hit Hub rate limits partway through. - `--scratch` on a slow disk will dominate runtime. Each layer stages a ~5.2 GiB expert memmap.
- Tensor names are a considered guess for K3-only tensors. AttnRes and LatentMoE have no upstream precedent yet; whoever lands the
kimi_k3PR picks the final names. Everything with akimi-linearequivalent already uses it. Full table in `TENSOR_MAP.md`. - `gguf-py` shape trap. Passing a
uint8buffer withraw_dtypemakes gguf-py treatraw_shapeas a byte shape and divide by the type size, silently halving the declared row length. Pass a 2-byte view for BF16.
What's blocking it
llama.cpp has no kimi_k3 architecture — but it already ships LLM_ARCH_KIMI_LINEAR with a full llama_model_kimi_linear implementation, and K3's text_config.architectures is literally ["KimiLinearForCausalLM"]. Kimi Delta Attention and MLA are already implemented upstream.
The remaining delta is narrower than 2.8T parameters suggests:
- AttnRes — extra residual pathway (
attn_res_*,ffn_res_*,output_attn_res_*),attn_res_block_size = 12 - LatentMoE — routed experts run in a 3,584-dim latent space, not model space
- `situ` activation — β = 4.0, linear β = 25.0
- 2 shared experts (Kimi-Linear has 1) and 896 routed, 16 active
- output gate on every layer (
mla_use_output_gate) - NoPE —
mla_use_nope = true, norope_thetaanywhere in the config - the multimodal wrapper (
KimiK3ForConditionalGeneration)
Tensor names here deliberately follow the existing kimi-linear conventions so that delta stays as small as possible.
Model facts
Credits
Model and logo by [Moonshot AI](https://huggingface.co/moonshotai) — the weights this repo converts live at [`moonshotai/Kimi-K3`](https://huggingface.co/moonshotai/Kimi-K3). Conversion conventions follow llama.cpp's kimi_linear.py and gguf-py. Licensed under the upstream Kimi-K3 License.
<p align="center"> Built with ♥ by Kuber Mehta (<a href="https://kuber.studio">kuberwastaken</a>) </p>
