Skip to content

add GLM-5.3-Flash (GLM5-Next) support - #27773

Open
timkhronos wants to merge 31 commits into
ggml-org:masterfrom
timkhronos:GLM5.3-Flash
Open

add GLM-5.3-Flash (GLM5-Next) support#27773
timkhronos wants to merge 31 commits into
ggml-org:masterfrom
timkhronos:GLM5.3-Flash

Conversation

@timkhronos

@timkhronos timkhronos commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Overview

Add support for GLM -5.3-flash a 320B hybrid model, supporting both text and vision.

Additional information

Architecture

GLM 5.3 flash mixes 34 KDA linear layers with 11 DSA laters, with mHC and Deepseek style Moe. Most of the parts are already in llama.cpp so I reused whatever I could:

  • KDA layers reuse the Kimi-K3 implementation
  • Attention layers are nope only MLA
  • For mHC I reused the Deepseek V4 implementation. I moved the build_hc helpers from the DSV4 graph into graph_context so both models can share them.
  • Moe and swiglu clamping follow DSV4.

What I implemented new:

  • Here, the DSA indexer scores pools of 4 consecutive token, and always keeps the incomplete tail. I implemented this on top of the existing DSA cache. The indexer cache stores key | gate per token and pooling happens in the graph. No new backend ops have been added.
  • llama_memory_hybrid_dsa: recurrent state + DSA cache, cloned from hybrid ISWA. Rebased onto llama_memory_hybrid_idx instead of the earlier ISWA clone.
  • Vision Tower: the encoder is the same family as glmv4 with per head qk-norm, clamped Swiglu and no post conv norm. It reuses glm4v projector with a swiglu_limit key and an optional image token budget. Added as glm5v as GLM 5.3 Flash requires a different pre processing method than what glm4v uses.
  • Small, precision sensitive tensors (indexer, mHC mixers, KDA gates, MLA low rank paths, roughly 1GB total) are kept unquantized.

Tests

  • Logits match transformers on a small random model across full prefill, small ubatches and single token decode while sparse selection is active, covering both scatter and gather.
  • Vision embeddings match to ~1e-5.
  • The converted model generates coherently and correctly, and vision is working as expected.

Limitations

Quantized GGUFs converted with this PR are available here.

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: YES, AI was used in an assistive capacity, and helped figure out and solve several conversion issues, and helped validate the correctness of the implementation.

@github-actions github-actions Bot added model Model specific mtmd Related to multimodal functionality (video/image/audio) conversion labels Aug 26, 2026
Comment thread tools/mtmd/clip.cpp Outdated
Comment thread tools/mtmd/clip-model.h Outdated
Comment thread tools/mtmd/clip-impl.h Outdated
@danielhanchen

danielhanchen commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Hey @timkhronos great work on the PR! A few requests if possible:

  1. You're using glm5-next for general.architecture, whilst model: add GLM-5-Next (GLM-5.3-Flash) #27754 and model : add GLM-5.3-Flash (glm5next) #27752 uses glm5next - Qwen3-Next for eg does qwen3next - I'm unsure what the convention is @ngxson but adopting glm5next might be more generalized? If glm5-next is accepted, a simple first shard rewrite for our uploads should suffice.

  2. The bigger issue is blk.N.indexer.kpool_ape / kpool_gate vs blk.N.indexer_compressor_ape / _gate. deepseek4 mainline already uses blk.N.indexer_compressor_ape and blk.N.indexer_compressor_gate but your PR changes it - the ones we uploaded uses deepseek4's convention. If this PR is accepted, we have to provide a script to rewrite all tensor names or folks have to re-download. If not, can you add aliases so the ones we published works - thanks in advance. Seems like it's more complex than I expected.

Tagging @ngxson for visibility as well.

I re-checked and if (1) + (2) is applied, the quants we uploaded work fine (+ the small shard-1 rewrite) and KLD / PPL are correct under this PR.

Seems like a simple alias isn't possible actually :( It breaks the quants made with this PR

@danielhanchen

Copy link
Copy Markdown
Contributor

Hmmm https://github.com/timkhronos/llama.cpp/pull/9/changes would alias the tensors but it looks a bit problematic hmmm

@Sciguy429

Copy link
Copy Markdown

Throwing up some performance numbers here from the lower end of consumer hardware (128GB DDR5 + 24GB VRAM (4090)).

avar6 has some freshly converted imatrix quants from this PR up as of now if anyone else wants to give them a go: https://huggingface.co/avar6/GLM-5.3-Flash-BF16-gguf

For the IQ3_S, I am getting roughly 300t/s prefill at 256K context and 2048 b/ub size. Generation speed starts off at around 9t/s and drops down considerably by mid window (~128K) to around 6t/s. This seems to track with the 'pooled indexer keys' issue. The model is fully coherent and seems to be working fine. I don't have PPL/KL numbers at the moment as I still need to generate a logit dump.

I have noticed an interesting memory quirk, which I haven't seen before. This is the only model I have ever seen have inconsistent checkpoint sizes. As the context fills the checkpoints grow alarmingly fast in size. At ~90K they are already up to nearly 1.6GB. I don't know if this is an expected behavior for this model arch, or if this is a something which needs to be looked into.

Also, something of note for you @danielhanchen which I found last night while looking over the three PRs for this arch. The vision towers between this PR and yours differ as well. This PR reuses the name clip.vision.projector_type = "glm4v" while you built a new one clip.vision.projector_type = "glm5next". Likely not much of an issue given how easy it is to regenerate mmproj files, but it will need to be delt with as well.

@danielhanchen

Copy link
Copy Markdown
Contributor

Yes I'll re-do the vision! This is fine!

@timkhronos I confirmed timkhronos#9 works fine and does not break your GGUFs. We will however need to do a cheap shard-1 update so that should be fine

@danielhanchen

Copy link
Copy Markdown
Contributor

@timkhronos I saw you changed the tensor naming - but my solution I provided was to allow everyone's quants to work - now your own ones you uploaded don't work haha.

We still need to provide the shard rewrite for the naming (glm5-next) which we're fine with, but now the DeepSeek convention means you yourself have to reupload all shards or do a tensor rename inplace with a script - was this your intention?

@timkhronos

timkhronos commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

@danielhanchen Hey!

I ended up going with the the indexer_compressor naming scheme, as it is closer to what's already there, and I was meaning to ask Avar to reconvert anyways, as his ggufs were made when we were missing quantization protection for some crucial tensors so they are not ideal.

Your vision projectors will need reconverting though most likely, and your main model ggufs might be missing the index_share_for_mtp_iteration key as well.

@danielhanchen

Copy link
Copy Markdown
Contributor

@timkhronos Hey! I made some shard rewrites to https://huggingface.co/unsloth/GLM-5.3-Flash-GGUF/tree/main/Shard_Rewrite for in preparation!

@timkhronos

Copy link
Copy Markdown
Contributor Author

@ngxson I think 74bb0e3 should fix it

Comment thread conversion/glm.py Outdated
Comment thread conversion/glm.py Outdated
Comment thread conversion/glm.py Outdated
Comment thread conversion/glm.py Outdated
Comment thread src/models/glm5-next.cpp Outdated
timkhronos and others added 3 commits August 31, 2026 16:33
@github-actions github-actions Bot added the testing Everything test related label Aug 31, 2026
danielhanchen added a commit to unslothai/unsloth that referenced this pull request Sep 1, 2026
* studio: keep MTP on for GLM-5.3-Flash

Auto drops embedded MTP for any MLA model, because llama.cpp's MLA/DSA MTP
path duplicates the target KV and recomputes the sparse indexer every draft
step. That is measured on GLM-5.2 and it holds there.

glm5next does not work that way. Its NextN block is an ordinary DSA layer with
its own one-layer KV cache, so it pays neither cost, and the gate was turning
off a speedup rather than avoiding a slowdown. On the b10715-mix-86bd2d3
prebuilt, UD-IQ1_S on one B200 at --spec-draft-n-max 3, generation goes from
61.1 to 80.2 tok/s with draft acceptance 0.634.

Name the exempt architectures instead of widening the gate, so an MLA model
nobody has benchmarked still defaults to the safe side. Only the unhyphenated
spelling is listed: ggml-org/llama.cpp#27773 ports the same model as
"glm5-next" without building the NextN graph at all, and promoting MTP there
would be wrong.

Also add glm5next to the architectures whose target KV skips the MTP block. Its
trunk context installs a filter returning il < n_layer() && !is_recr(il), so
blk.45 gets no target KV and reserving for it only costs context.

* studio: do not charge GLM-5.3-Flash for a duplicated target KV

* studio: tighten comments on the GLM-5.3-Flash MTP change

* studio: reserve KDA recurrent rollback copies for MTP

* studio: route both rollback reserves through one helper

* studio: final comment tightening on the MTP reserve change
Comment thread conversion/glm.py Outdated
Comment thread conversion/glm.py Outdated
Comment thread conversion/glm.py Outdated
@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member

@ggerganov You may want to review kpool.

Comment thread conversion/glm.py Outdated
Comment thread conversion/glm.py Outdated
Comment thread src/models/glm5-next.cpp Outdated
@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member

@timkhronos Sorry, I think we need to create a full n_head_kv array after all, will fix the fix. :P

Edit: #28173

Comment thread src/llama-memory-hybrid-idx.h Outdated
// glm5-next, complete pools of kpool consecutive positions per sequence, scored as whole pools
// Cache sequence-private complete pools and re-pool only changed pools.
uint32_t get_n_kpool (uint32_t kpool) const; // Padded pool count, where the last pool is always unused.
uint32_t get_n_kpool_new(uint32_t kpool, const llama_ubatch * ubatch) const; // Exact count of new pools.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of passing ubatches from the graph and mutating constant state, can we initialize the kpool states upon llama_memory_hybrid_idx_context construction?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I moved the state init out of the graph into apply(). Ubatch i's pools span tokens that only enter the cells once ubatch i applies, so I think construction time init could get a bit messy. I think apply() is the first point where the layout can be read from the real cells, and it should still precede all graph access.

Comment thread src/models/glm5-next.cpp
Comment thread src/llama-memory-hybrid-idx.h Outdated
Comment on lines +651 to +653
if (mem->get_mem_idx() != nullptr && mem->get_kpool() > 0) {
kpool_states.push_back(kpool_build_state(nullptr));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It is not clear what this does when the ubatch argumetn is nullptr.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That was building a layout only state for the full cache context, so get n kpools has something to report during graph reserve and state ops. I split it into an explicit kpool_build_layout, and kpool_build_state takes the ubatch by ref now.

Comment thread src/llama-memory-hybrid-idx.h Outdated
Comment on lines +93 to +95
// Sequence edits invalidate cached relative pools.
bool kpool_is_dirty () const { return kpool_dirty; }
void kpool_clear_dirty() { kpool_dirty = false; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is confusing. From the code, it looks like the kpools are owned by the llama_memory_hybrid_idx_context which lives only during the processing of the logical batch. They seem to be recreated for each logical batch - is this not correct? A sequence edit cannot occur inbetween ubatches of a logical batch. Or am I missing something?

Basically, it is not clear to me what is the lifetime of the kpools - try to clarify this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So basically, there are two things with different lifetimes. The per ubatch pool layout (kpool_state) is owned by the context and is rebuilt every logical batch.

But the pooled key values are not per batch, but are scattered into the idx_cache, and persist across batches, so each batch repools only the pools it's ubatches touch. Sequence edits between batches shifts the pool grid, which invalidates the cached values, but without touching the layout, and that is what the flag on the memory tracks. It is cleared only once the first ubatch of the next batch succeeds. I renamed it to kpool_cache_stale and split the state builder so the layout only build is explicit. Hope it reads a bit clearer this way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just in case you forgot to push the commit - it's not visible atm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry, It should be in 8c28939 now, I misscopied my Acctoken and didn't notice it rejected.

Comment thread tests/test-llama-archs.cpp Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conversion model Model specific mtmd Related to multimodal functionality (video/image/audio) testing Everything test related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants