model: add Kimi-K3 text model - #26185
Conversation
ngxson
left a comment
There was a problem hiding this comment.
may need to shorten comments too, IMO some/most comments are too verbose
| std::vector<ggml_tensor *> ckpts; | ||
| ggml_tensor * stack_cache = nullptr; | ||
| int stack_cache_n = -1; | ||
|
|
There was a problem hiding this comment.
I'd suggest renaming:
- ckpts --> resi (short for residual stream, same naming mentioned in the paper)
- drop the
_cachesince technically there is no cache here,resi_stackshould be enough
also, it might be cleaner if these are grouped into a new struct and explicitly pass it like this:
struct attn_resi; // private struct, defined inside cpp file
void res_push(attn_resi r, int64_t n_embd, int64_t n_tokens);
ggml_tensor * res_stack(attn_resi r, int64_t n_embd, int64_t n_tokens);| layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A, i), {n_head}, TENSOR_NOT_REQUIRED); | ||
| if (!layer.ssm_a) { | ||
| layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A, i), {1, n_head, 1, 1}, TENSOR_NOT_REQUIRED); | ||
| } | ||
| if (!layer.ssm_a) { | ||
| layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A, i), {1, n_head}, 0); | ||
| } |
There was a problem hiding this comment.
these might not be necessary, I suppose for compat?
There was a problem hiding this comment.
Yeah, those are artifacts from the mock model runs, for the real model I'll purge them.
|
https://huggingface.co/inference-optimization/Kimi-K3-0.18B potentially useful, depending on how faithful the model is reconstructed in 0.18B . |
|
@Green-Sky I wouldn't put it up without checking parity with a mock model 😄 |
Isnt that a mock model? |
|
Yeah, that's what I'm saying, already built one of my own for parity testing purposes when doing the PR. |
|
@GrEarl please put your comment inside a collapsible block, it takes up too much space & make the discussion hard to keep track |
|
Sorry for the long text. reposting it collapsed and trimmed. Ran this branch against the actual Kimi-K3 checkpoint. One thing needs fixing; the rest is context.
|
|
Tested this branch today with a converted K3 checkpoint. Everything loaded successfully and generation worked as expected. Conversion (
Runtime
Two observations that may help others:
Thank you. |
|
@GrEarl 's late-edition Q2 works There are stray template elements (or special tokens?) in the output, but the reasoning parser still detects begin/end and eos token ends the turn properly. e.g. ---snip--- (newlines added to avoid side-scrolling) It would be helpful if llama warmed up the experts. It doesn't anymore, or doesn't with this model. The warmup run does not force all experts on. I know this is also a problem for DSv4 warmup. Many long stories about cats or doubly-linked list impls are needed to get 2^7*7 mmap experts warmed up. The no-imatrix (I think I can safely assume) Q2 quant is coherent, and seems quite capable at a glance. |
|
FYI: Here's another chat template PR on HF: https://huggingface.co/moonshotai/Kimi-K3/discussions/66 |
This is the template I used. |
|
Tested Method: I generated a random-init shrunk K3 (90M params, f32) that keeps the full structure: hybrid KDA/MLA with the real Hardware is deliberately ancient: 2x Xeon E5-2609v2 (AVX only, no AVX2), 512GB DDR3, CPU-only build so far. Build is clean on this ISA, conversion works (tensor mapping, res_norm x res_proj fusion, expert merge, tokenizer; transformers git-main / 5.15.0.dev0), and llama-server generates at ~55 tok/s on the tiny model. Two problems hit along the way:
Fixture generator, CPU shim, and reference outputs: https://gist.github.com/SolshineCode/3115760b0c3b655563a3102ba897c426. I can upstream the fixture into the test suite if useful, or defer to @200lz if their fixtures already cover this. Once a real quant exists I can also run a full-scale streamed validation on this box (512GB RAM, mmap + AI usage disclosure: the test harness and this report were built with Claude running on my machine; the numbers are from real runs I can rerun on request. |
|
@SolshineCode Excellent validation—thank you for sharing the fixture and the CPU reference path. My work does not duplicate your end-to-end fixture. I analyzed the released checkpoint schema using all 96 official safetensors headers, without downloading tensor payloads. The production checkpoint confirms:
Please do not defer the execution fixture to me—your fixture covers an area I have not implemented and would be valuable upstream. I can instead review the PR against the full released tensor vocabulary and contribute a compact schema-level regression test, if useful, covering the exceptional final MLA layer, the dense-to-MoE boundary, expert-set invariants, and I’ll first inspect the current PR branch to avoid duplicating existing tests. |
|
Ran the fixture against the CUDA build as well: kimi-k3-text @ cf11c4c, CUDA 12.4 with -DCMAKE_CUDA_ARCHITECTURES=52, 2x Tesla M40 (compute 5.2), -ngl 99 --tensor-split 1,1. All 3 prompts match the reference token for token, ~109 t/s vs ~55 CPU-only. One flag: load prints resolve_fused_ops: layer 3 is assigned to device CUDA0 but Flash Attention is assigned to device CPU (usually due to missing support), so at least one attention op lacks an sm_52 kernel. Logs: https://gist.github.com/SolshineCode/3115760b0c3b655563a3102ba897c426 (sm52_results.md) @200lz thanks, schema-level checks are exactly what my fixture doesn't do, so those complement each other well. I cross-checked your header-derived layer map against the released config's linear_attn_config and they match exactly (your 0-indexed MLA 3, 7, ..., 91, 92 is the config's 1-based full_attn_layers). The fixture already encodes both structural exceptions you mention: it ends with consecutive MLA layers like 91+92, and it has the dense layer 0 to MoE boundary. I'll upstream it as an execution test to sit alongside your schema regression test, in whatever form the maintainers prefer. |
|
@SolshineCode Thanks for cross-checking the released layer map and for confirming the two structural exceptions in the fixture. That separation sounds ideal: your fixture can cover execution and token-level parity, while I’ll focus on compact schema regression coverage derived from the official checkpoint. I’ll review the current PR tests and prepare the smallest non-duplicative schema test proposal, especially around the final MLA layer, the dense/MoE boundary, expert-set invariants, and tensor-name mapping. |
|
@pwilkin I completed a read-only test-gap review of this PR against the full released K3 checkpoint schema. The external execution fixture provides strong CPU/CUDA parity coverage, but the PR currently has no dedicated K3 schema regression test in CI. I would like to contribute a compact converter-level test covering:
The test would use synthetic config/tensor metadata only—no model weights, runtime execution, or overlap with the existing fixture. Would you prefer this added to #26185, or submitted as a small follow-up PR after merge? |
|
Can we run this text model on dual RTX 3090 and 128GB DDR4 memory and 5800x3d cpu on Windows 11? |
No, the lowest size, if by some miracle Aes or Bart make a Q1-2 with imatrix quant that is still coherent, that would even be 700GB |
|
Quality topic I also acknowledge that this is work in progress, also this maybe my local issue. Shared just for fyi Looking forward to hear other's experience |
|
Okay so I Tried to reproduce the multi-turn corruption @Tonoken3 reported, using my tiny fixture so iteration is seconds instead of minutes (for testing). On cf67f0d I can't repro it at small scale: with confirmed partial prefix reuse (timings.prompt_n 53 vs 343 fresh), both single divergent-turn rollback and 6 turns of accumulated reuse give token-identical greedy outputs vs a fresh server, on CPU and on CUDA (compute 5.2). So at least on this branch tip the basic KDA rollback path looks right, and the remaining surface is long histories (state checkpoint exhaustion), specific serving flags, batch effects, newer CUDA archs, or something fixed since your test. Repro harness and results are in the gist: https://gist.github.com/SolshineCode/3115760b0c3b655563a3102ba897c426 (repro_kda_cache_reuse_chat.py, kda_cache_results.md), if useful for pinning it down on a rig that does repro instead of mine. |
The nightly died in resolve: #40 carried MiniMax-M3 plus Inkling, upstream master now ships its own MiniMax-M3, and the two collided in src/models/models.h and tests/test-backend-ops.cpp. Every build job was skipped. Drop the two MiniMax entries (ggml-org#24523 is closed anyway, so it was already inert) and repin on the four PRs we actually want: ggml-org#24423 DiffusionGemma ggml-org#25731 TML Inkling #44 Kimi-K3 fixes + MoonViT-3d vision tower ggml-org#26185 Kimi-K3 text ggml-org#25731 and #44 both had their conflicts against current master fixed on their own branches, and #44 also carries the Inkling merge: the two archs land in the same arch/model/mtmd registries, so they cannot go in as two independent heads and one of them has to know about the other. ggml-org#26185 is last on purpose. Its head is an ancestor of #44, so by the time the resolver gets there the merge is a no-op; listed before #44 it conflicts on tests/test-llama-archs.cpp. It stays in the set so the release manifest names it. Verified locally against b10173: the four merge in this order with zero unmerged paths, and the merged tree builds with -DGGML_CUDA=OFF -DLLAMA_CURL=OFF -DLLAMA_BUILD_TESTS=ON. Assisted-by: Claude Code
|
@pwilkin I added mmproj + some fixes to unslothai#48 which stacks on top of yours - feel free to merge commits / take them :) |
|
@danielhanchen thanks :) the only question I have is about the conversion fixes - judging from this thread people have already converted the model and verified it works correctly using this code, so I'm wondering about those claims (the KV cache I'll look into). |
|
@createthis I observe the same wrt. warmup. Warmup should avoid top-k for _exps selection and does not, so it only loads 16/896. Without this, it takes a very long time to warmup due to coupon-collector problem. It's additionally important since as I understand, this model made efforts to improve exp distribution and avoid hot spots (improving expert-parallel workload distrib., and probably much to the dismay of the REAP-enjoyers) |
|
@usrlocalben I can confirm, I measured possible REAP/REAM opportunities and my Opus was like "nah, better quant with a codebook instead". Very informationally dense. That's why it doesn't quant so well. |
Hybrid KDA (linear) + MLA (full) attention as in Kimi-Linear-48B, plus five things that architecture does not have: 1. cross-layer residual attention (attn_res_block_size) 2. latent MoE (routed experts run at n_expert_latent) 3. situ activation (replaces SwiGLU everywhere) 4. MLA output gate (sigmoid gate before o_proj) 5. full-rank KDA gate (single ssm_g instead of ssm_g_a/ssm_g_b) K3's text_config reports KimiLinearForCausalLM - the older 48B architecture - so get_model_architecture routes on the top-level name instead. The KDA decay gate has two forms, selected by linear_attn_config's gate_lower_bound. It is not a clamp: when set it swaps the activation entirely (fla/ops/kda/gate.py), from -exp(A_log)*softplus(x) to lower_bound*sigmoid(exp(A_log)*x). K3 sets it to -5.0; kimi-linear leaves it unset, so that path is unchanged. Cross-layer residuals reuse ggml_dsv4_hc_pre for the weighted sum. That op is CPU + CUDA only, so Metal/Vulkan will fall back per-node until those kernels exist. The routed experts ship as compressed-tensors "mxfp4-pack-quantized". That is bit-compatible with ggml's MXFP4 - same E2M1 code assignment, same E8M0 scale byte, only the nibble positions within a block differ - so they are repacked rather than dequantized, losslessly and without a ~5.5 TB bf16 round-trip. The repack is built lazily because gguf_writer holds every added tensor until the final write. DeepSeek-V4 was already doing the identical bit-shuffling, so it now shares the helper. Verified against Moonshot's own code path (transformers + fla's Triton KDA kernels) on a tiny model exercising every K3-specific feature. Final-position logits vs the fp32 reference: 6.7e-05 rel / corr 1.00000000 for both the chunked and the recurrent delta-net path. MXFP4 blocks dequantize to the source weights with 0.0e+00 error. Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- `_res_parts` buffers (kind, tensor) pairs, not bare tensors - `get_tensors` must return an Iterator, matching ModelBase - LazyBase's `func` takes one argument, so pass the expert loaders through `args` instead of the closure - borrowing KimiLinearModel.set_vocab from an unrelated TextModel is deliberate and safe, but not expressible in the signature No behaviour change: the MXFP4 repack still dequantizes to the source weights with 0.0e+00 error and end-to-end logits are unchanged (8.386e-03 rel, corr 0.99996630). Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Boris Dvorkin <b_dvorkin@niuitmo.ru>
K3's assistant output is an XTML-ish tagged format built by the template's
open_tag/close_tag macros. Two properties break generic parsing:
1. The generation prompt ends with open_tag('think'), so the completion
starts inside the think section with no opening marker in the output
(thinking_forced_open).
2. Only <|open|>/<|close|>/<|sep|>/<|end_of_msg|> are special tokens; tag
names ("think", "response", "message") are ordinary text tokens.
Adds common_chat_params_init_kimi_k3 (PEG_NATIVE) with detection on the
marker trio, reasoning extraction, response unwrapping, and tool-call
parsing of the tools/call/argument tag structure with argument types
taken from the tool schema. Includes the K3 chat template fixture and 9
test-chat cases derived from real generations of the full 2.8T model.
Verified end-to-end against Kimi-K3-Q2_K (GrEarl/Kimi-K3-GGUF) on 8x B200:
content, reasoning_content, streaming deltas, and tool_calls all correct;
finish_reason stop/tool_calls as appropriate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-role message-start markers for token-level span splitting. User and assistant messages carry only the role attribute, so their full opener (through <|sep|>) is used; system and tool messages continue with more attributes (type=/tool=/index=), so those delimiters stop after the role's closing quote. Verified against the K3 tiktoken vocabulary that the closing quote is always a standalone token across all attribute variants, so the token-level prefix match stays exact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Upstream publishes no image for this branch: it lives in the contributor's fork and ghcr.io/ggml-org/llama.cpp only carries upstream branches, so the kimi-k3-text tag answers 404 there. This builds it until PR ggml-org#26185 merges. Both architectures build on native runners rather than under QEMU, because an emulated arm64 C++ build of llama.cpp costs hours instead of minutes.
|
|
||
| auto attn_out = build_delta_net(Qcur, Kcur, Vcur, g1, beta, state, il); | ||
|
|
||
| ggml_tensor * output = ggml_cont(ctx0, attn_out.first); |
There was a problem hiding this comment.
IIRC, the build_* should always cont the result before returning, right?
There was a problem hiding this comment.
I'm not familiar with the gated delta net details, but from what I see in code the build_delta_net() output is permuted in some cases, so needs cont before reshape.
|
Reconverted and requantized model after yesterdays changes, ran perplexity test with 8k ubatch to speed things up, the result: Nice. |
|
Did some limited multi-stream lineage-bench runs:
|
| {%- macro json_sorted(value) -%} | ||
| {#- Minja は tojson(sort_keys=true) を実装していない。K3 参照実装は | ||
| deep_sort_dict() の後に compact JSON 化するため、dictsort と再帰マクロで | ||
| 同じバイト列を作る。配列の順序は保持し、mapping の各階層だけソートする。 -#} |
There was a problem hiding this comment.
Why there are Japanese comments in the chat template?
There was a problem hiding this comment.
lmao, it's hilarious that there is (almost) no mentions to minja in the llama.cpp code base anymore, yet models are trained on old dataset still assumes that llama.cpp uses minja
There was a problem hiding this comment.
@fairydreaming guess that's the person who posted the parser changes to my branch, I didn't check the template 😀
Bring the kimi-k3 feature branch to master. Adds the Kimi-K3 (2.78T hybrid KDA-delta-net + MLA + latent-MoE) text model (upstream PR ggml-org#26185) on top of the CPU-TP + MTP fork, and — the headline — makes K3's attention shard across TP ranks so its attention-dominated decode SCALES: 2-node 4-rank decode 1.0 -> 2.0 t/s with --tp-attn (2x), vs an attention-replicated regression without it. Contents: - Kimi-K3 model + converter + chat template (merged, clean, LLAMA_MAX_EXPERTS->1024) - per-context n_expert_used override + self-spec-accept/self-spec-simple tools (measured: reduced-expert self-draft does NOT win on K3 — draft cost ratio 0.78) - fix: build_moe_ffn expert aggregation honors a reduced per-context n_expert_used - feat: --tp-attn for K3 (tp_kda_plan_for gate-tensor head-sharding, ATTN_KV_B / K3-gated ATTN_GATE roles, wo all-reduce in build_kda_layer/build_mla_layer); recurrent state shards automatically via the n_head division. Design reviewed adversarially before build; verified byte-correct + scaling on-node. - docs: cpu-tensor-parallel.md results + docs/development/kimi-k3-tensor-parallel.md






Hybrid KDA (linear) + MLA (full) attention as in Kimi-Linear-48B, plus five things that architecture does not have:
Reuses DeepSeek4's HC_PRE for the cross-layer residual weighted sum. Supports repack for the MXFP4 weights in conversion.
Now need someone to actually convert and test :)