Repository navigation
model : add K2 Horizon dense and MoVA support - #29535
Conversation
The K2-Horizon regex had no arm in unicode_regex_split_custom and fell through to the
general std::regex fallback, which fails two ways.
On MSVC std::regex rejects \p{...}, so no K2-Horizon GGUF loads on Windows at all:
llama-quantize, llama-imatrix and llama-perplexity all abort with
regex_error(error_escape) before a token is produced.
Where the fallback does compile it is still wrong. unicode_regex_split collapses each
codepoint to a single byte naming its Unicode category before matching, and U+200C/U+200D
are category Control, which has no entry in k_ucat_cpt, so both become the 0xD0 fallback
byte. The literal and alternatives in K2's regex can then never match and
every ZWNJ or ZWJ ends a letter run.
The splitter is the existing llama3 one with a single rule widened, since K2's regex
differs from llama3's only in that a letter run also takes marks, ZWNJ and ZWJ.
tests/test-unicode.cpp gains a case for this: it fails before the change with
[Amy] [ZWNJ khaham] and passes after with the run intact.
…ed-tests tests: expand K2 Horizon unicode splitter coverage
Assisted-by: Codex
…nizer unicode : add the K2-Horizon pre-tokenizer splitter
Assisted-by: Codex
Includes the K2 Horizon implementation from ifm-ai/llama.cpp with converter, tensor-parallel and model save/reload fixes. Assisted-by: Codex
Assisted-by: Codex
Assisted-by: Codex
|
Hi @bitalov, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
Assisted-by: Codex
Constrain final JSON after reasoning, accept flexible JSON tool envelopes, enforce XML dialects, and handle repeated or alternate thinking markers. Load YaRN beta metadata instead of retaining the default values. Add schema, streaming, continuation, and model reload regressions. Validate CUDA and CPU builds and 0.9B, 4B, and MoVA conversation/tool round trips. Assisted-by: Codex
|
I think I found what's going on (with help from Claude). K2's group norm starts with a reshape, so the view it creates still points to the embeddings, which are on the CPU. That view then ends up in a Meta split, and since the backend expects a Meta buffer there, it asserts. |
Yeah, this is not the right fix, though, not sure what would be right myself, @ggerganov? |
|
Which part of the graph is that? |
It happens right at the start of the first layer, before the Q/K/V projections even run. On line 241, the embeddings get passed into the group RMS norm: cur = k2_horizon_group_rms_norm(ctx0, inpL, layer.attn_norm, hparams.n_norm_groups, hparams.f_norm_rms_eps);The first thing that helper does is this reshape on line 130: cur = ggml_reshape_3d(ctx, cur, n_embd / n_groups, n_groups, n_tokens); |
|
Hm, not sure. @JohannesGaessler how should we handle CPU buffers reaching the meta backend? |
|
I would expect this to be a bug in the ggml backend scheduler. It should not be possible to reach that assert with a tensor allocated on a buffer for which In any case, for this PR I would suggest that you simply add the new arch to |
Resolves the conflicts with upstream ggml-org#29535 (K2 Horizon) and ggml-org#29442 (cuBLAS ldc and F16/BF16 src0 chunking): - Inkling and K2 Horizon both register: parser decl, parser source, model mapping case, EOG token, output tensor name. model.unembed moves a few lines up the OUTPUT tuple, away from upstream's embedding_projection, so pins that carry that line still merge. - LLAMA_VOCAB_PRE_TYPE_INKLING moves to 62, since upstream claimed 61 for K2_HORIZON. The id is internal; the GGUF carries tokenizer.ggml.pre as a string. - ggml_cuda_mul_mat_cublas_impl keeps the F32_PEDANTIC GemmEx path and passes upstream's ldc to both F32 branches; the pedantic compute-type override stays ahead of upstream's F16/BF16 src0 chunking, which it cannot overlap (pedantic only applies to F32 src0).
b11146 predates ggml-org/llama.cpp#29535 (K2 Horizon dense and MoVA, merged 2026-10-06 as 4625240). b11503 is 49 commits ahead of that merge (GitHub compare: ahead/0 behind). Asset sizes and SHA256 come from the release's GitHub digests; the macOS arm64 archive was downloaded and re-hashed. llama-server --help of that archive still lists every flag Pippa passes (--jinja, --reasoning, --swa-full, --cache-type-k/-v, --chat-template-kwargs, --ctx-checkpoints, --cache-ram, --alias, --slot-save-path, --no-webui, sampling flags, LLAMA_API_KEY). The dylib set build-app.sh walks via @rpath is unchanged (ggml 0.26.0, llama 0.6.0), the LICENSE file is identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at merge past b11453, drop unslothai#247, pin unslothai#251 Upstream K2 Horizon (ggml-org#29535), the glm5-next gather removal (ggml-org#30042) and the GLM5-Next MTP graph (ggml-org#29928) broke the Inkling, unslothai#243 and unslothai#241 pins. Each branch now carries a merge of upstream master. unslothai#247 is in every tag from b11452 on. unslothai#251 adds --moe-cache-mib auto on top of ggml-org#29887.
276 upstream commits. Conflicts and how they were resolved: - K2-Horizon: upstream added its own port of the same ifm-ai code (ggml-org#29535), with the chat parser, the unicode splitter and tests. Upstream's model, converter and names are taken; GGUF names are identical, so existing K2-Horizon GGUFs still load. The fork keeps only the K2-Horizon-36B tokenizer hash, which upstream dropped. LLAMA_VOCAB_PRE_TYPE_K2_HORIZON moves from 60 to 61 to follow upstream (it is not stored in GGUFs). - MoE: upstream moved the selective expert copy out of the scheduler into a llama_context copy callback (ggml-org#29943) and added a GPU cache for host experts (ggml-org#29887). The fork's partial mul_mat_id (id < 0 rows, used by LLAMA_MOE_HOT) now skips negative ids in that callback. build_lora_mm_id takes both the fork's partial/ids_s and upstream's MoE cache slots; build_moe_ffn_exps passes the slots on the untiered path only, so the MoE cache and LLAMA_MOE_HOT tiering do not stack on one layer. - mmvq: upstream's shared-expert fusion and the fork's negative-id early return combined; the shared-expert block never takes the early return. - server: upstream's rejection sampling and checkpoint replay paths gained the fork's on_sample hook, so speculative logprobs cover them too. - qwen4exp / build_attn: turbo Q pre-rotation kept on top of upstream's reworked QSA mask and the new training path. - top-k.cu: upstream's CCCL >= 3.4.3 gate; the fork's hipcub arm was dead since HIP stopped defining GGML_CUDA_USE_CUB. - ggml-cuda init_tensor: upstream's async memset on owned buffers, the fork's host-side padding clear on externally owned ones. - tests/test-turbo-kv.cpp, tests/test-moe-tier.cpp: moved to common_batch and llama_process, as upstream removed common_batch_add. Tested on gfx1100: ctest 76/78 (the two failures are environmental: the ggml-vocabs clone has git-lfs pointers, and system python has no jinja2; test-jinja-py passes with it). test-llama-archs 506/507; the qwen2moe "Meta" failure is a seed-dependent flake that the pre-merge build shows too. Server speculative/logprobs pytests 18/18. Needle at 12k tokens with turbo4 KV: K2-Horizon-36B, Qwen3.8-Flash-Next, Qwen3.8-27B (plus MTP).
* model: K2 Horizon gguf conversion code
* model: loading hparams and tensors in k2-horizon.cpp
* model: K2 Horizon compute graph
* model: K2 Horizon compute graph adjustment and registering tokenizers
* model: K2 Horizon chat template and accomodate safetensors naming
* unicode : add the K2-Horizon pre-tokenizer splitter
The K2-Horizon regex had no arm in unicode_regex_split_custom and fell through to the
general std::regex fallback, which fails two ways.
On MSVC std::regex rejects \p{...}, so no K2-Horizon GGUF loads on Windows at all:
llama-quantize, llama-imatrix and llama-perplexity all abort with
regex_error(error_escape) before a token is produced.
Where the fallback does compile it is still wrong. unicode_regex_split collapses each
codepoint to a single byte naming its Unicode category before matching, and U+200C/U+200D
are category Control, which has no entry in k_ucat_cpt, so both become the 0xD0 fallback
byte. The literal and alternatives in K2's regex can then never match and
every ZWNJ or ZWJ ends a letter run.
The splitter is the existing llama3 one with a single rule widened, since K2's regex
differs from llama3's only in that a letter run also takes marks, ZWNJ and ZWJ.
tests/test-unicode.cpp gains a case for this: it fails before the change with
[Amy] [ZWNJ khaham] and passes after with the run intact.
* tests: expand K2 Horizon unicode splitter coverage
* unicode: handle K2 Horizon case folding and empty input
Assisted-by: Codex
* jinja : support sequence indices in selectattr and rejectattr
Assisted-by: Codex
* model : add K2 Horizon dense and MoVA support
Includes the K2 Horizon implementation from ifm-ai/llama.cpp with converter, tensor-parallel and model save/reload fixes.
Assisted-by: Codex
* chat : support K2 Horizon reasoning and tool calls
Assisted-by: Codex
* conversion: remove obsolete K2 Aurora alias
Assisted-by: Codex
* k2-horizon: enforce response schemas and load YaRN betas
Constrain final JSON after reasoning, accept flexible JSON tool envelopes,
enforce XML dialects, and handle repeated or alternate thinking markers.
Load YaRN beta metadata instead of retaining the default values.
Add schema, streaming, continuation, and model reload regressions. Validate
CUDA and CPU builds and 0.9B, 4B, and MoVA conversation/tool round trips.
Assisted-by: Codex
* renaming template fixture
* adressing cisc follows ups
* desloppify the parser / adress aldehir comments
* clean test-chat
* remove fallback : model trained mostly on high anyway
* fix k2 attn_v_exp tn splitting and metal fusion baseline
* k2-horizon : forward expand views before sums
* k2-horizon: copy embds before group norm to fix TP
* disable tesnor parallelism
---------
Co-authored-by: Ryandito Diandaru <ryandito.diandaru@mbzuai.ac.ae>
Co-authored-by: WestWaters <mario.papaleo2013@gmail.com>
Co-authored-by: Natani L. Mayday <71436458+TaskPuppyNatani@users.noreply.github.com>
Co-authored-by: West <100190545+WestWaters@users.noreply.github.com>
Co-authored-by: aaryamonvikram <aaryamonvikram@gmail.com>
Co-authored-by: aaryamonvikram <96529820+aaryamonvikram@users.noreply.github.com>
This PR adds support for K2 Horizon, both the dense and MoVA variants. It covers HF-to-GGUF conversion, tokenizer support, model loading, and the inference graph. Everything is built on existing GGML operators, so there are no new kernels.
Along the way, a few supporting changes were needed:
Numeric indexing in
selectattrandrejectattr. The K2 chat template processes tool schemas withdict | items | rejectattr('0', 'equalto', '$ref'), which needs to reach the first element of each key/value pair. The existing filters couldn't do that, so I added support for it.A dedicated K2 chat parser. The auto-parser figures out reasoning and tool-call formats by rendering sample conversations. Some of those samples include assistant turns without
reasoning_content, and the K2 template rejects them, so discovery fails. The dedicated parser handles K2's reasoning and tool calls, its end-of-turn marker, and the case wheretool_choice=requiredjumps straight to a tool call.Template capability detection in
common/jinja/caps.cpp. The same missing field can make capability probes fail, which then wrongly reports that tools, object arguments, or parallel calls aren't supported. Now, if a probe fails, it retries once with an emptyreasoning_contentadded to any assistant turns that lack it. This does touch shared capability detection, but when I compared results across 75 templates, only the K2 templates changed.Multi-GPU support with
-sm tensor. Value-expert weights follow the same split rule as the value weights. Q/K normalization weights are loaded per head, and the MoVA expert sum uses 3D views so the tensor-parallel backend can keep track of the split. None of this requires a change to the GGUF format.MoVA save/reload support. The saver now writes the value-expert metadata needed to load saved models back in.
AI usage disclosure: YES - Codex reviewed the changes, made targeted fixes, and ran additional validation.