Skip to content

kimi-k3 : full-size model fixes and the MoonViT-3d vision tower - #48

Open
danielhanchen wants to merge 2 commits into
kimi-k3-text-basefrom
kimi-k3-fullsize-vision
Open

kimi-k3 : full-size model fixes and the MoonViT-3d vision tower#48
danielhanchen wants to merge 2 commits into
kimi-k3-text-basefrom
kimi-k3-fullsize-vision

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Stacked on top of ggml-org#26185 (Kimi-K3 text support), which is the base branch
here. Only the two Kimi-K3 commits are included, so the diff is 14 files rather than the
merged nightly tree.

1. Fixes for loading and running the full-size model

The 1T-parameter checkpoint exposes issues the small test model does not:

  • n_expert_used is read per layer instead of once, so the dense prefix and the MoE layers
    no longer have to agree.
  • The KV cache is sized from the actual number of KDA layers rather than the total layer
    count, which was over-allocating on a 92-layer model.
  • conversion/base.py keeps the expert tensors in their source order, avoiding a repack
    that needed the whole layer resident.

2. MoonViT-3d vision tower (image path)

Adds the vision encoder as a new mtmd model:

  • tools/mtmd/models/kimik3.cpp builds the graph. Patch embedding is 3d, so the temporal
    axis is folded into the batch for the image case.
  • clip_hparams::n_embd_head is added because MoonViT's head dimension is not
    n_embd / n_head. It defaults to 0 and the existing n_embd / n_head path is used
    whenever it is unset, so every other mmproj model is unaffected. I verified this against
    the mmproj files for the other supported architectures.
  • conversion/kimivl.py gains KimiK3VisionModel to write the mmproj.

Audio is not included. Only the image path is implemented.

Testing

  • Both commits build clean.
  • The full-size model loads and generates correctly across the four published quants.
  • mmproj files for the other architectures were checked to confirm the n_embd_head change
    does not alter their behaviour.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: efc8bc38f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread conversion/kimivl.py
assert self.hparams_vision is not None
n_head = self.hparams_vision["vt_num_attention_heads"]

if "wqkv" in name and "weight" in name:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fold the Conv3D patch embedding before serialization

For Kimi-K3 checkpoints, vision_tower.patch_embed.proj.weight is a rank-5 Conv3D kernel. This method lets it fall through to MmprojModel.modify_tensors(), which serializes it as a five-dimensional v.patch_embd.weight; the GGUF loader rejects tensors exceeding GGML_MAX_DIMS == 4, and clip_graph::build_inp() only invokes ggml_conv_2d. Fold or squeeze the temporal dimension for the supported single-image path before yielding this tensor.

Useful? React with 👍 / 👎.

Comment thread conversion/kimivl.py
Comment on lines +241 to +244
if "mm_projector.proj.0." in name:
name = name.replace(".proj.0.", ".proj.linear_1.")
elif "mm_projector.proj.2." in name:
name = name.replace(".proj.2.", ".proj.linear_2.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Map the required projector post-norm tensor

When converting the published PatchMergerV2 layout, the required mm_projector.post_norm.weight passes filter_tensors() but is neither renamed here nor covered by the MMPROJ tensor aliases, so super().modify_tensors() raises Can not map tensor and conversion cannot finish. This tensor must be mapped to mm.post_norm.weight, which the new PROJECTOR_TYPE_KIMIK3 loader requires.

Useful? React with 👍 / 👎.

Comment thread conversion/kimivl.py
Comment on lines +213 to +216
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
name, _ = item
if not name.startswith(("vision_tower.", "mm_projector.")):
return None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude unused temporal position weights from image conversion

The MoonViT-3d state dict also contains vision_tower.patch_embed.pos_emb.time_weight, and this broad prefix filter retains it even though the image-only graph deliberately omits the temporal position term. There is no MMPROJ tensor-map entry or runtime tensor for this parameter, so it reaches super().modify_tensors() and aborts conversion with Can not map tensor; explicitly discard it for the supported t == 1 path.

Useful? React with 👍 / 👎.

@ngxson ngxson left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vision model impl looks clean, thanks!

Comment thread tools/mtmd/clip.cpp

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you mind pushing the n_embd_head change to a dedicated PR, so that I can run a test & make sure it doesn't break existing models?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or I can do that if you prefer)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ngxson will check!

I was planning to make this as a PR to pwilkin's fork as well haha

Or if you wanna take over that also works - but I shall re-check! I also did multiple images and Kimi works - https://unsloth.ai/docs/models/kimi-k3#run-kimi-k3-in-llama.cpp

I'll double check other models

TheChuckster added a commit to TheChuckster/glm-cpu-kit that referenced this pull request Jul 30, 2026
unsloth published the sub-4-bit tiers on 2026-07-29 and all four fit:
UD-IQ1_S 594.0, UD-IQ1_M 648.9, UD-IQ2_XXS 711.1, UD-Q2_K_XL 861.3 GB
against 1133 GB of RAM. The pre-staged row needed no correction - the
subdir and prefix it guessed (UD-Q2_K_XL / Kimi-K3-UD-Q2_K_XL) are
exactly what shipped, 19 shards - so its `?` becomes a real count and
only kimi-k3-ik stays pending, ubergarm having published nothing.

That closes the quant gate and leaves the engine as the sole blocker,
now characterised properly rather than as "no arch":

- ik has no kimi-k3 arch; mainline's #26185 is open and conflicted;
  unsloth built these against their own fork (unslothai/llama.cpp#48),
  which their README tells you to use.
- Before any of that matters, LLAMA_MAX_EXPERTS is 512 and K3 has 896,
  so it trips an arch-generic assert in load_hparams before a kimi-k3
  hook could run - ik src/llama-hparams.cpp:9, asserted at :165.
  Mainline's PR to raise it (#26192) was closed unmerged. Recorded as
  step 0 in porting/k3, with the caveat that the constant sizes stack
  arrays, so "one line" needs checking rather than assuming.

Two findings from Moonshot's README invalidate opts that are right for
K2.x, so the K3 rows change from --reasoning off to
--reasoning-format deepseek:

- K3 always thinks. There is no enable_thinking equivalent; effort is a
  top-level reasoning_effort field llama.cpp has no flag for, defaulting
  to max. So the K2-correct flag is simply meaningless here.
- K3 was trained in preserved-thinking-history mode: multi-turn and tool
  calls require the complete assistant message replayed, including
  reasoning_content AND tool_calls. That is precisely the shape ik #1605
  400s on. For K2 you can avoid emitting it; for K3 it is mandatory, so
  #1605 is promoted from caveat to blocking for agentic K3.

Sampling: Moonshot's generation_config sets only max_length and eos, so
the rows assert no temperature or top_p rather than inventing them.

The registry now warns that downloading is safe but serving is not:
download and verify will succeed, then the load will fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Graph node budget, LLM_TYPE for the 93-layer config, four hparams promoted to
required, and an E8M0 NaN check during the MXFP4 repack. Details in the PR
description.

Assisted-by: Claude Code
New kimik3 projector type, its graph builder and the mmproj converter. Also adds
an optional clip.%s.attention.head_dim so build_vit stops deriving d_head from
n_embd, which is wrong whenever a tower's qkv width differs from n_embd.

Assisted-by: Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

llama.cpp/tools/mtmd/clip.cpp

Lines 1490 to 1491 in daef2b3

hparams.image_min_pixels = min_pixels;
hparams.image_max_pixels = max_pixels;

P1 Badge Honor custom token limits when loading Kimi-K3

When a Kimi-K3 user supplies --image-min-tokens or --image-max-tokens, clip_ctx stores the override in custom_image_*_tokens, but this branch directly replaces the effective limits with the GGUF metadata and never applies those custom values. The converter always emits both metadata keys, so the set_limit_image_tokens() fallback that honors the overrides is unreachable; a requested memory cap can therefore be ignored and images can still expand to the checkpoint's 4096-token default. Apply the custom limits after loading the metadata or reuse the existing limit helper.

AGENTS.md reference: AGENTS.md:L80-L80

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

danielhanchen added a commit that referenced this pull request Aug 2, 2026
…head (#49)

The nightly has been red since 2026-07-31. pwilkin force-pushed
ggml-org#26185 at 21:16Z that day, eleven minutes before the schedule
fired, and the pin in this file still named cf67f0d -- a merge commit
that the rebase wrote out of the branch. The membership gate in the
resolve step caught it and refused, so every build job was skipped.
Runs 30666643530 and 30718507279 both died there.

Two more things were wrong once that was unblocked:

  - Inkling was pinned to d69b7e6, which is the commit three separate
    reporters got garbage output from (CPU, Mac, and ROCm with -fa on).
    ggml-org#25731 fixed all of those in 02142bb; the nightly was
    shipping the broken one.

  - The rebase left ggml-org#26185 unmergeable against current master,
    and Kimi-K3 and Inkling register in the same arch, model and mtmd
    tables. Two independent heads cannot both land, so one of them has
    to know about the other.

#48 now carries that. Its base branch was advanced to the
rebased ggml-org#26185 head, then merged with b10223 and with Inkling,
resolving four collisions:

  src/llama-arch.cpp          KIMI_K3 and INKLING share a fallthrough
  src/llama-model.cpp         same, in the ROPE_TYPE_NONE group
  tests/test-llama-archs.cpp  master's MINIMAX_M3 vs KIMI_K3
  tools/mtmd/                 CMake source list and the graph registry

All four are additive; both arches belong in every group. The two
Kimi-K3 commits were rebased on top, so #48 is still a
10-file stacked diff against its base.

ggml-org#26185 stays listed, last. Its head is now an ancestor of
#48, so the resolver merges it as a no-op and the release
manifest still names it.

Verified against b10223: the four merge in this order with zero
unmerged paths, and the merged tree builds with -DGGML_CUDA=ON. On the
merged tree test-chat passes and test-llama-archs -a kimi-k3 is OK on
both CUDA and CPU (NMSE 8.77e-08); it fails to even load the model
without this repin.
danielhanchen added a commit that referenced this pull request Aug 3, 2026
The 2026-08-02 schedule (run 30767267935) failed in resolve:

  ggml-org#25731 (02142bb) does not merge cleanly onto b10229
  + the PRs listed before it

Not the pin rot that broke the previous two nights -- all four pins
passed the membership gate this time. Upstream added LLM_ARCH_DEEPSEEK4
to the same fallthrough group in src/llama-arch.cpp that Inkling
registers in, between b10226 and b10229, and the resolver has no
conflict resolution.

Fixed on the branch rather than here: b10229 is merged into add-inkling
and both cases are kept, since both arches belong in that group. That
also clears ggml-org#25731's own mergeable state against master, which
had gone red for the same reason.

No other pin moves. #48 needed nothing -- it already carries
b10223 and the new arch does not collide with Kimi-K3.

Verified against b10229: the four merge in order with zero unmerged
paths, and the merged tree builds with -DGGML_CUDA=ON. test-chat passes
and test-llama-archs -a kimi-k3 is OK on CUDA and CPU (NMSE 8.77e-08).
The new Inkling head also merge-tree's clean against upstream master,
which is six commits past b10229, so the next tag should resolve too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants