Skip to content

Kimi K3 engine: native-MXFP4 expert streaming, KDA + gated NoPE MLA + AttnRes + LatentMoE (#658) - #676

Open
steve-m wants to merge 10 commits into
JustVugg:devfrom
steve-m:kimi-k3
Open

Kimi K3 engine: native-MXFP4 expert streaming, KDA + gated NoPE MLA + AttnRes + LatentMoE (#658)#676
steve-m wants to merge 10 commits into
JustVugg:devfrom
steve-m:kimi-k3

Conversation

@steve-m

@steve-m steve-m commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

This adds support for Kimi K3
(2.8T parameters, 104B active, 93 layers) as a new sibling engine
c/kimi_k3.c, following the one-engine-per-family pattern (olmoe.c,
inkling.c) — colibri.c is untouched, and the shared-header changes are
purely additive (make check stays green).

Closes #658. The engine implements exactly the split discussed there:

  • The routed experts (896/layer, top-16; 93% of the checkpoint's bytes) stay
    native MXFP4 — streamed straight from the original safetensors shards
    and computed on as-is via a new matmul_mxfp4 kernel (e2m1 nibbles +
    ue8m0 per-32 scales, scalar + AVX2). QAT weights are never re-encoded;
    there is deliberately no sub-4-bit expert option, since that would be
    double quantization of QAT weights.
  • The engine runs directly on the original HF snapshot (96 shards, as
    downloaded — nothing on disk is modified). The BF16 dense side is
    quantized into RAM at load time (K3_BITS=4|8|32, with separate knobs
    for the MLA projections and lm_head), which is the "convert the dense on
    the fly" option from the [Feature]: Kimi K3 Original Weights loading #658 thread. An optional repack
    (tools/k3_repack.py, below) trades a one-time disk copy for a 42 s cold
    start — also with the experts byte-identical.

K3 is architecturally unlike anything the repo currently runs, which is why
it gets its own engine rather than branches in the GLM one:

  • 69 KDA layers (Kimi Delta Attention — a gated delta-rule linear
    recurrence with per-channel bounded decay, short causal convolutions and a
    full-rank sigmoid output gate) interleaved 3:1 with 24 gated MLA
    layers
    ; NoPE — no positional encoding anywhere in the model.
  • AttnRes: the plain residual stream is replaced by learned softmax
    mixes over block-level snapshots of the stream (layers 0, 12, …, 84),
    applied twice per layer plus once before the head.
  • Stable LatentMoE: the routed experts run in a 3584-dim latent space
    behind shared down/up projections, plus two fused full-width shared
    experts, with the SiTU-GLU activation 4·tanh(g/4)·σ(g)·25·tanh(u/25).

Full architecture/format details and the operational manual are in the new
docs/kimi_k3.md.

It runs the full model

First full-93-layer runs on a Ryzen 9 3900X / 62 GiB / single PCIe-4 NVMe,
greedy, prompt "The theory of general relativity was developed by":

config resident RAM cold start prefill decode output
dense int8 56.9 GiB 42 s ~2.0 s/tok ~5.2 s/tok "…Einstein when he felt that the theory of special relativity was insufficient to describe the"
dense int4 (K3_BITS=4) 37.3 GiB 131 s ~2.0 s/tok ~5.2 s/tok "…Einstein in 1915, and it"
+ K3_TOPP=0.7 (opt-in) ~3.0 s/tok quality note below

25.8 GB of experts are streamed per token (worst case, and with K3's
Quantile-Balancing-flat routing the observed case): a token's expert misses
are read in parallel with O_DIRECT (K3_DIRECT=0 for buffered),
reaching 6.3 GB/s against the drive's measured 7.1 GB/s ceiling, and the
preads run on loader threads so expert j's matmuls overlap expert j+1's
read (K3_PIPE). The expert matmuls use int8-quantized activations with
integer dots by default (K3_IDOT; measured free at text level — see the
validation table). Per-token decode budget on the test box: un-hidden I/O
wait 2.5 s + expert compute 1.9 s + attention 0.9 s. Multi-drive splits
work via K3_DIRS (no duplication). Decode is expert-bandwidth-bound by
construction, so it scales with disk speed.

Chat works natively (--chat "msg" [--system "..."]): K3's XTML format
(from the checkpoint's own encoding_k3.py) uses four special tokens around
ordinary-text tags, with the assistant's preserved thinking as a
structural <think> channel (K3_THINK=0 for response-only). The C builder
is verified token-exact against encoding_k3.py + tiktoken, and the
full model uses the format natively — greedy "how many r in strawberry":
reasons in the think channel (spells the word, finds positions 3/8/9), opens
the response channel, answers "3 times". The CLI hides the markup and prints
[think]/[response] banners. (Single system+user turn for now; the format
also covers multi-turn and tool calls — see docs/kimi_k3.md.)

Prefill runs in K3_CHUNK-token chunks (default 32), layer-major: every
dense matmul batches over the chunk (each weight matrix streams from RAM
once per chunk instead of once per token), the MoE loads each unique expert
of the chunk once, and the lm_head runs only on the chunk's last token.
Sequential state (KDA recurrence, MLA cache, AttnRes) advances per token
inside each layer in the original order, so chunked results are
bit-identical to token-at-a-time (validation table). One measured
surprise worth recording: within a 32-token window the chunk's expert union
deduplicates 2.7× (9.6 GB streamed/token instead of 25.8) — K3's routing
is Quantile-Balancing-flat in its marginals but has strong temporal
locality across neighbouring tokens.

K3_TOPP=p (default off) keeps routed experts to cumulative gate weight p,
renormalized — at 0.7 it keeps ~8.1 of 16 experts and reaches ~3.0 s/token,
but real-text ppl moves 2.694 → 2.811 (ΔNLL +0.043; next-token accuracy
unchanged at 73.4%). That is a measurable trade, so it ships opt-in,
consistent with the quality-first stance in the #658 thread; any setting is
easy to gate with a K3_LOGITS A/B.

What's in the commits

  1. quant.h: MXFP4 matmul kernel. Verified against an independent numpy
    implementation of the compressed-tensors spec: rel err 1.2e-7; AVX2 ≡
    scalar.
  2. tok.h: a third pre-tokenizer family for Kimi's tiktoken pattern
    (leading \p{Han}-run rule, Han-masked letter classes), and a
    rank-BPE mode used when model.merges is empty: merge the adjacent
    pair whose concatenation has the lowest vocab id — tiktoken's own
    algorithm, exact by construction. (Merge-list recovery from ranks is
    provably lossy; the commit message has the counterexample.) Existing
    tokenizers keep the merges path bit-for-bit.
  3. c/kimi_k3.c + make kimi_k3 + docs/kimi_k3.md: the engine (~950
    lines). Per-layer expert LRU; one pread per expert (the six tensors of
    an expert are stored back-to-back in the shards — measured), issued in
    disk-offset order (experts are not id-ordered on disk — measured).
  4. tools/: k3_tokenizer.py (tiktoken.model → tokenizer.json + tiktoken
    cross-checks), k3_ref.py (independent numpy reference of the full
    layer stack), k3_repack.py (optional: one streaming read of the HF
    snapshot → pre-quantized container; experts byte-identical, verified).
  5. Streaming/RAM fixes from the full-model bring-up: parallel O_DIRECT
    expert reads (26–28 → 9.2–10.7 s/token), honest K3_EXPERT_GB
    accounting, and an explicit-K3_BITS=4 downcast for int8 containers
    (57 → 37 GiB resident, fits beside a desktop session).
  6. K3_LOGITS: per-prefill-position logit dump (the teacher-forced
    bit-width comparison below).
  7. quant.h: matmul_mxfp4_i8 — int8-activation MXFP4 matmul. The doubled
    e2m1 values are exact int8, so a 32-group reduces to
    maddubs(|w|, sign(x,w)) (the dot_i4i8 idiom) with per-group scales
    folded exactly. 3x on expert compute (5.8 → 1.9 s/token).
  8. K3_PIPE loader threads (overlap preads with compute) + K3_TOPP
    (opt-in expert pruning). Decode 9.4 → 5.2 s/token at defaults.
  9. Chunked prefill (K3_CHUNK, bit-identical by construction and by
    measurement) + AVX2 KDA state sweeps (attention 0.9 → ~0.5 s/token,
    ppl unchanged at 4 decimals). Prefill ~5.3 → 2.0 s/token.
  10. --chat: the XTML chat format with the preserved-thinking channel,
    token-exact vs the checkpoint's encoding_k3.py.

Validation

Everything was validated on real checkpoint weights, not synthetic
tensors:

what result
engine (f32) vs independent numpy reference, 4 tokens × 4 layers covering all four layer types (KDA+dense, KDA+MoE ×2, gated-MLA+MoE) rel-L2 ≤ 2.2e-6 every layer (f32 noise)
MXFP4 kernel vs numpy spec implementation rel err 1.2e-7; AVX2 ≡ scalar
C tokenizer vs tiktoken (--ctest, 18 adversarial cases: Chinese, kanji-vs-kana, Korean, CRLF, contractions, emoji, mixed-script boundaries) 18/18 exact
repacked container vs source every expert byte re-read and compared (--verify-full): identical; quantized tensors bit-identical to the engine's load-time quantization
full 93-layer generation coherent, factually correct (table above)
dense int8 vs int4, teacher-forced real-text logits (124 positions, K3_LOGITS) ppl 2.69 vs 2.71; next-token accuracy identical 73.4%; disagreements symmetric (2 wins each)
K3_IDOT int8-activation kernel vs float kernel, same protocol ppl 2.684 vs 2.694; argmax agreement 98.4%; mean KL 0.003 — free
chunked prefill K3_CHUNK=32 vs =1, 125-position logit streams bit-identical (max abs diff 0.0)
AVX2 KDA sweeps vs scalar, same protocol 100% argmax agreement; ppl 2.6841 unchanged
--chat prompt ids vs encoding_k3.py + tiktoken (±system, ±thinking) token-exact
make check on this branch green

An honest caveat on the reference: k3_ref.py is an independent
implementation, but it encodes the same reading of the released
modeling_kimi_linear.py + fla kernels as the C engine, so a shared
misreading of the modeling code would not be caught. Running HF+fla itself
needs triton/GPU and wasn't feasible here; the formulas were taken
line-by-line from the shipped modeling source and the fla
fused_recurrent_kda kernel (including two checkpoint quirks verified
empirically: A_log is stored [128] = per-head [96] zero-padded, and
dt_bias is per-channel [12288] despite the fla docstring saying [HV]).

One measured property reviewers should know: the AttnRes mix logits have
small margins (~0.02), so the architecture leverages weight-quantization
noise unusually strongly (load-time int8 drifts hidden states ~14% over four
layers on synthetic inputs, while a 1e-4 input perturbation does not
amplify at all). This affects only the dense side — the experts are
untouched QAT bytes, per #658.

That comparison has now been run on real text (teacher-forced over a
124-position factual passage, identical explicit ids, full logits per
position via the K3_LOGITS hook, --ngen 0):

  • perplexity on the passage: int8 2.69 vs int4 2.71
    (ΔNLL mean +0.006 nats, median +0.0007);
  • next-token accuracy against the text: identical 73.4% for both;
  • int8-vs-int4 argmax agreement 92.7% (9/124 differ) and the disagreements
    are symmetric: 2 positions where int8 is right and int4 wrong, 2 the
    other way, 5 where both are wrong — no directional quality loss;
  • mean KL(int8‖int4) 0.038 nats (p90 0.10): individual distributions do
    drift (the AttnRes signature, max |ΔNLL| 1.4 at one position) but the
    drift does not bias predictions.

So the synthetic hidden-state sensitivity does not surface as text-level
quality loss: dense int4 is a sound default (37 GiB resident), int8 the
belt-and-braces choice when RAM allows — and since both decode at the same
speed (I/O-bound), the choice is purely RAM. Caveats stated plainly: one
English passage, teacher-forced, and no f32 gold reference (the f32 dense
does not fit the 62 GiB test box, so this is a relative comparison —
consistent with the finer int8 grid, int8 remains the reference).

Scope notes / follow-ups

The originally-open WIP items are done and validated above (bits comparison,
chunked prefill + KDA SIMD, chat template). Deliberate bounds that remain,
listed in docs/kimi_k3.md:

  • CPU-only; decode is single-token (K3 has no MTP head to speculate with).
  • --chat renders a single system+user turn (the format itself covers
    multi-turn and tool calls).
  • A bf16-resident dense mode for large-RAM hosts could follow (the "leave
    dense untouched where RAM allows" arm of [Feature]: Kimi K3 Original Weights loading #658; K3_BITS=32 already gives
    an f32 superset of it at 2× the RAM, and the measured int8 delta on real
    text is within noise).

Interaction with other open PRs

colibri.c is untouched. quant.h/tok.h changes are self-contained
appends (rebased cleanly over the merged #654/#655); tok.h's rank-BPE mode
is gated on tokenizer features no existing model uses. No overlap with the
open #666/#667/#671 beyond the shared one-engine-per-family pattern.

Reproducing the numeric validation needs only shards 1–4 of the checkpoint:
k3_ref.py gen/run/cmp + K3_LAYERS=4 K3_BITS=32 K3_X0=… K3_TRACE=… (exact
commands in docs/kimi_k3.md).

steve-m added 6 commits July 28, 2026 13:20
…alar + AVX2

The native layout of compressed-tensors 'mxfp4-pack-quantized' checkpoints:
packed [O,I/2] u8 (low nibble = even column, bit3 = sign) + scales [O,I/32]
ue8m0, w = v * 2^(s-127). Computes on the vendor bytes directly - QAT weights
(Kimi K3 routed experts) are exactly the trained values, so they are streamed
and multiplied as-is, never re-encoded. AVX2 decodes a 16-byte group with one
pshufb over the doubled-e2m1 int8 LUT; the 0.5 un-doubling rides the group
scale. Scalar and SIMD paths use the same bit-trick exponent decode so both
agree on the s=0/255 edge cases. Verified against an independent numpy
implementation of the spec (rel err 1.2e-7 = f32 noise) and byte-conventions
against compressed-tensors' pack_fp4_to_uint8/unpack_fp4_from_uint8.
… vocabs

Two additions for Kimi K3's tokenizer (tiktoken vocab, fancy-regex pattern):

- kimi Split family (sniffed via \p{Han} in the pre_tokenizer regex): the
  o200k case-aware rules plus a leading [\p{Han}]+ rule, Han masked out of
  the letter classes (fancy-regex &&[^\p{Han}] intersections), and no '/'
  tail in the punctuation rule. A Han codepoint can only match the Han rule,
  so Han-free text tokenizes exactly like o200k minus the '/' tail.

- rank-BPE: when model.merges is empty, bpe_piece merges the adjacent pair
  whose CONCATENATION has the lowest vocab id - tiktoken's own algorithm,
  exact by construction. Merge-list recovery from ranks is provably lossy
  (the standard max-rank-minimizing reconstruction mis-merges 'newlines'
  as new+l+ines), which is why K3's generated tokenizer.json ships no merges.

tests/test_tok_kimi.c + tools/k3_tokenizer.py --ctest cross-check the C
encoder against tiktoken: exact on all 18 corpus cases (Chinese, kanji vs
kana, Korean, CRLF, contractions, emoji, mixed-script boundaries). Existing
families are untouched: merges-bearing tokenizers keep the merges path.
New sibling engine for Kimi K3 (2.8T / 104B active, 93 layers), following the
one-engine-per-family pattern (colibri.c untouched). Shares st.h / json.h /
tok.h / quant.h. make kimi_k3; ./kimi_k3 <model_dir> "prompt" --ngen N.

- 69 KDA layers: SiLU(causal-conv4) projections, L2-normed q/k, per-channel
  bounded decay gk = -5*sigmoid(exp(A_log[h])*(f_b(f_a x)+dt_bias)), delta
  rule S=(I-beta k k^T)Diag(e^gk)S + beta k v^T, per-head RMSNorm x sigmoid
  full-rank output gate. Checkpoint A_log [128] = per-head [96] zero-padded.
- 24 gated MLA layers, absorb form, NoPE (nothing is rotated; the 64 extra
  dims are cached raw), sigmoid output gate before o_proj.
- AttnRes: the residual stream is replaced by softmax mixes over block
  snapshots (layers 0,12,...,84) + the running prefix sum - twice per layer
  and once before the head, scored against res_norm.w*res_proj.w, raw-entry
  mixing in fp32.
- Stable LatentMoE: sigmoid router + score-correction bias, top-16,
  raw-score renormalization; latent 7168<->3584 projections around the
  experts; RMSNorm on the aggregate; 2 fused shared experts; SiTU-GLU
  (4 tanh(g/4) sigmoid(g) * 25 tanh(u/25)).
- Routed experts stream from the safetensors shards as native MXFP4 through
  a per-layer LRU (K3_EXPERT_GB): one pread per expert (the six tensors are
  stored back-to-back - measured), loads issued in disk-offset order
  (experts are NOT id-ordered in the shards - measured), multi-drive shard
  split via K3_DIRS (st_init_multi).
- Non-expert BF16 is quantized at load (K3_BITS int4-g64 / int8 / f32,
  K3_MLA_BITS, K3_HEAD_BITS) or read pre-quantized from a repacked
  container (U8 + .qs sidecars, tools/k3_repack.py) - container start
  measured 30.4s -> 0.6s on two layers.

Validation (docs/kimi_k3.md): with injected inputs and f32 weights the
engine matches an independent numpy reference (tools/k3_ref.py) on real
checkpoint weights across all four layer types to rel-L2 <= 2.2e-6;
container-vs-load-time quantization is bit-identical (layer-0 trace equal);
tokenizer exact vs tiktoken. Known limits: single-token stepping (sequential
prefill), CPU only, no chat template.
- k3_tokenizer.py: tiktoken.model -> tokenizer.json (byte-level vocab, EMPTY
  merges on purpose -> tok.h rank-BPE, kimi Split regex, 256 added tokens
  from tokenizer_config). --check replays rank-BPE in python vs tiktoken;
  --ctest drives tests/test_tok_kimi against tiktoken. 18/18 exact.

- k3_ref.py: independent numpy implementation of the K3 stack (KDA
  recurrence, gated NoPE MLA, AttnRes, LatentMoE with MXFP4 dequant) reading
  tensors straight from the HF shards; gen/run/cmp subcommands produce and
  compare the engine's K3_TRACE hidden-state dumps. This is the reference
  the engine is validated against (rel-L2 <= 2.2e-6 at f32 on real weights).

- k3_repack.py: one streaming read of the HF snapshot -> ideal ingestion
  container, so a disk-to-disk copy IS the conversion. Experts byte-identical
  in id order (six tensors back-to-back = the engine's slot layout, one pread
  per expert), big BF16 mats quantized with the ENGINE-EXACT algorithm
  (int8/row or int4-g64, --bits/--mla-bits/--head-bits) into U8 + .qs f32
  sidecars, sensitive smalls passed through, vision dropped, spec-valid
  safetensors (no holes) + regenerated index, per-shard resume, --verify-full
  re-reads every expert byte. Kills the engine's 10-15 min load-time
  quantization; quantized bytes verified bit-identical to the load-time path.
  ~1.56 TB source -> ~1.50 TB (int8) / ~1.48 TB (int4).
Full-model bring-up on a 62 GiB box exposed three streaming-path issues:

1. Expert loads were sequential buffered preads; with ~55 GiB of resident
   weights the page cache has no headroom and reclaim throttled reads to
   ~1.8 GB/s (drive: 7.1 GB/s O_DIRECT, 2.9 buffered — measured). A token's
   misses now load in parallel into working-set slots and read O_DIRECT
   through st.h's twin fds (aligned-window + tiny buffered tail; K3_DIRECT=0
   reverts to buffered + WILLNEED). Expert reads: 6.3 GB/s.
   Decode: 26-28 -> 9.2-10.7 s/token on the 93-layer model.
2. The LRU cap floor was topk, silently committing topk*92 slots (~26 GiB)
   regardless of K3_EXPERT_GB; experts are consumed one at a time, so the
   floor is now 1 and the budget is honored.
3. Explicit K3_BITS=4 on an int8 container downcasts to int4-g64 at load:
   ~35 vs ~57 GiB resident (fits next to a desktop session), same decode
   speed once I/O is fixed; unset K3_BITS keeps container bits (default
   unchanged).

93-layer sanity (greedy, repacked int8 container): 'The theory of general
relativity was developed by' -> int8: 'Einstein when he felt that the theory
of special relativity was insufficient...', int4: 'Einstein in 1915, and it'.
Init 42 s (int8) / 131 s (downcast pass).
Teacher-forced bit-width comparisons (with --ngen 0): run the same --ids
twice under different K3_BITS and diff the logit streams offline. Used for
the int8-vs-int4 dense-side quality evaluation on real text.
@JustVugg JustVugg added model-support Supporto a nuovi modelli enhancement New feature or request labels Jul 28, 2026
steve-m added 4 commits July 28, 2026 22:59
The float kernel converts every decoded weight nibble to float before the
FMA; at 25.8 GB of packed expert bytes per K3 token that path measured
~4.4 GB/s effective. The doubled e2m1 values are exact int8 (same LUT), so
a 32-group reduces to maddubs(|w|, sign(x,w)) + madd — the dot_i4i8 idiom —
with the group scale folded once per group:
    y = sum_g idot_g * 2^(e8-127)*0.5 * xscale_g
Activations quantize per 32-group to int8 (~0.4% group noise, aligned with
the weight groups so scale folding is exact). Unit test vs the float kernel:
rel-L2 3.5e-3 on random data. On the full model: teacher-forced real-text
ppl 2.684 vs 2.694 (float), argmax agreement 98.4%, KL mean 0.003 — free at
text level, 3x faster (expert compute 5.8 -> 1.9 s/token). Scalar fallback
included; I%32!=0 falls back to the float kernel.
Decode 9.4 -> 5.2 s/token (and 3.0 opt-in), on top of the O_DIRECT work:

- K3_PIPE (default 1): a small loader-thread pool (K3_LOAD_THREADS, 4) runs
  the expert preads while the compute loop chews already-loaded experts —
  expert j's matmuls overlap expert j+1's read. t_eload now reports the
  UN-hidden wait (2.5 s/token; pure I/O would be 3.8).
- K3_IDOT (default 1): expert matmuls via quant.h's new matmul_mxfp4_i8.
  Real-text gate: ppl 2.684 vs 2.694 float, agreement 98.4% — free.
- K3_TOPP (default 0 = off): drop the low-weight tail of the top-16 to
  cumulative weight p, renormalized (GLM TOPP semantics) — the only lever
  cutting I/O and compute together. At p=0.7 it keeps ~8.1/16 experts:
  3.0 s/token, but ppl 2.811 vs 2.694 (dNLL +0.043) with accuracy unchanged
  at 73.4% — measurable, so it stays OPT-IN; gate any setting with a
  K3_LOGITS A/B first.

Full-model numbers, greedy sanity text unchanged. make check green.
Prefill now runs layer-major over K3_CHUNK-token chunks (default 32):

- every dense matmul batches over the chunk, so each weight matrix streams
  from RAM once per chunk instead of once per token;
- the MoE builds the chunk's expert union per layer and loads each unique
  expert ONCE (position lists via counting sort), feeding the same pipelined
  block loader as decode. Measured on real text at C=32: 2.7x dedup —
  neighbouring tokens share experts far more than QB-flat routing predicts
  — 9.6 instead of 25.8 GB streamed per token;
- the lm_head runs only on the chunk's last token (K3_LOGITS still dumps
  every position);
- sequential state (KDA recurrence, MLA cache append, AttnRes bookkeeping)
  advances per token inside each layer in the original order, so chunked
  results are BIT-IDENTICAL to token-at-a-time: the 125-position
  teacher-forced logit streams at C=32 vs C=1 match exactly (max abs diff
  0.0). K3_TRACE forces C=1 (trace rows are token-major by contract).

The KDA delta-rule state sweeps get explicit AVX2 (mul+fmadd over the
128-wide state rows, scalar fallback when head_dim%8!=0): attention
0.9 -> ~0.5 s/token; vs the pre-SIMD logits: 100% argmax agreement,
ppl 2.6841 unchanged at 4 decimals.

Full-model prefill: ~5.3 -> 2.0 s/token (125-token prompt, C=32, int8
dense, K3_LOGITS on — pure generation prefill is slightly better still).
Decode unchanged (same C=1 path). make check green.
…hannel

K3 ships its chat renderer as encoding_k3.py: an XTML structure where only
<|open|>/<|close|>/<|sep|>/<|end_of_msg|> are special tokens and tag names /
attributes are ordinary text, encoded as standalone segments. A turn is

  <|open|>message role="user"<|sep|>TEXT<|close|>message<|sep|><|end_of_msg|>

and the generation prompt opens the assistant message plus its STRUCTURAL
thinking channel (<|open|>think<|sep|>; K3_THINK=0 opens <response> directly
— in thinking mode the channel exists in every assistant message, which is
the preserved-thinking property).

The C builder mirrors the reference segment-for-segment (segment boundaries
are token boundaries, so per-segment BPE matters) and is verified TOKEN-EXACT
against encoding_k3.py + tiktoken for system/no-system and thinking/
non-thinking prompts. The CLI hides the structure at decode and prints
[think]/[response] banners; <|end_of_msg|> is already the configured eos.

Full-model greedy check ('how many r in strawberry'): the model reasons in
the think channel (spells the word, finds positions 3/8/9), closes it, opens
response, answers '3 times' — native use of the format end to end.

Single system+user turn only for now (no multi-turn CLI, no tool-call
rendering); make check green.
@steve-m
steve-m marked this pull request as ready for review July 28, 2026 22:32
@JustVugg

Copy link
Copy Markdown
Owner

@steve-m when tests for you are ok i merge it!

@ZacharyZcR

Copy link
Copy Markdown
Contributor

I built the shared chat/API/Web integration on top of this PR in #684. The K3 multi-turn wire fixture matches Moonshot official encoding_k3.py exactly (77/77 token IDs), and the local C/Python regression suite passes. Full-model generation remains explicitly gated on a host large enough to load the snapshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request model-support Supporto a nuovi modelli

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants