Skip to content

[MODEL] support qwen3.5 series - #19468

Merged
ggerganov merged 7 commits into
ggml-org:masterfrom
JJJYmmm:add_qwen35
Feb 10, 2026
Merged

ggerganov merged 7 commits into
ggml-org:masterfrom
JJJYmmm:add_qwen35

Conversation

@JJJYmmm

@JJJYmmm JJJYmmm commented Feb 9, 2026 •

Copy link
Copy Markdown
Contributor

This PR adds model support for the upcoming Qwen3.5 models, including both dense and MoE variants. It has been verified with preview checkpoints from the Qwen Team in both vision and pure text modes (with or without mmproj file).

As mentioned here, this PR was planned to be a follow-up PR after #19435 or #19456 by @pwilkin. However, it seems there is still a lot to do without the real checkpoints (such as vision support, vocab, weight mapping, and forward logic), so I'm sharing my implementation here for reference. 🫡

Some differences compared to #19435 or #19456:

  • Qwen3.5 uses a different pre-tokenizer
  • Qwen3.5 uses partial IMRoPE
  • Qwen3.5 has fixed the qkvzba order—thanks to @ngxson in model: try to improve Qwen3 Next #18683. I saw it last month and we decided to simplify it in the official weights! Now they are split into qkv, z, b, and a.
  • Qwen3.5 has dense/MoE variants, allowing us to simplify the forward logic (no need to check the mlp type of the current layer).

@pwilkin Sorry for the work overlap, and I really appreciate your work on Qwen3Next! It has been a huge stepping stone for this PR.

Reference HF implementation: huggingface/transformers#43830

@pwilkin

pwilkin commented Feb 9, 2026

Copy link
Copy Markdown
Member

@JJJYmmm no worries, glad if it helped even a little :)

@ngxson ngxson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I haven't reviewed the actual implementation, but I assume it's mostly qwen3 next + imrope (please correct if I'm wrong)

Don't hesitate to ping if you need helps to add the vision support!

Comment thread convert_hf_to_gguf_update.py Resolved
Comment thread src/llama-model.cpp Resolved
Comment thread src/llama-model.cpp Resolved
@JJJYmmm

JJJYmmm commented Feb 9, 2026

Copy link
Copy Markdown
Contributor Author

@JJJYmmm no worries, glad if it helped even a little :)

It definitely helped a lot 🙏

Comment thread gguf-py/gguf/tensor_mapping.py Resolved
Comment thread gguf-py/gguf/constants.py Resolved
Comment thread src/llama-model.cpp Resolved
Comment thread src/models/qwen35.cpp Resolved
Comment thread src/models/qwen35.cpp Resolved
Comment thread convert_hf_to_gguf.py Resolved
Comment thread convert_hf_to_gguf.py Resolved

@ggerganov ggerganov left a comment

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.

The libllama changes are good. I will follow up with a PR to deduplicate and optimize the delta net stuff across the Qwen family.

Comment thread src/models/qwen35.cpp
Comment on lines +673 to +695

// if head keys and value keys are different, repeat to force tensors into matching shapes
if (num_k_heads != num_v_heads) {
GGML_ASSERT(num_v_heads % num_k_heads == 0);
int64_t repeat_factor = num_v_heads / num_k_heads;

// repeat interleave: reshape to (repeat part, 1, remaining part), do repeat, then reshape back
ggml_tensor * q_reshaped = ggml_reshape_3d(ctx0, q_conv, head_k_dim, 1, num_k_heads * n_seq_tokens * n_seqs);
ggml_tensor * k_reshaped = ggml_reshape_3d(ctx0, k_conv, head_k_dim, 1, num_k_heads * n_seq_tokens * n_seqs);

// Repeat along the third dimension (the new dimension with size 1)
ggml_tensor * q_repeated =
ggml_repeat_4d(ctx0, q_reshaped, head_k_dim, repeat_factor, num_k_heads * n_seq_tokens * n_seqs, 1);
ggml_tensor * k_repeated =
ggml_repeat_4d(ctx0, k_reshaped, head_k_dim, repeat_factor, num_k_heads * n_seq_tokens * n_seqs, 1);

// Reshape back to merge the head and repeat dimensions
// From [head_dim, num_k_heads, repeat_factor, n_seq_tokens * n_seqs]
// Back to [head_dim, num_k_heads * repeat_factor, n_seq_tokens, n_seqs]
q_conv = ggml_reshape_4d(ctx0, q_repeated, head_k_dim, num_k_heads * repeat_factor, n_seq_tokens, n_seqs);
k_conv = ggml_reshape_4d(ctx0, k_repeated, head_k_dim, num_k_heads * repeat_factor, n_seq_tokens, n_seqs);
}

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.

Btw, this is an issue inherited from the Qwen3 Next implementation. Basically we are doing redundant repeats here because the V weights are not arranged correctly during conversion. Ideally this whole section should not be needed and we should only rely on broadcasts.

Basically, we want to broadcast Q and K into V. With the current order of the V heads, we need an interleaved broadcast. However the ggml binary ops perform tiled broadcast. Hence the need for this rearrangement.

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.

If it is too difficult to figure out how to fix this for the new Qwen3.5 models, we'll have to do it in a follow-up PR. But it's going to be a breaking change. Or maybe we can try to gate it with some flag.

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 would try to fix it in the qwen3.5 gguf conversion since it is not released.

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.

Since it's a new arch we should not need a flag as such then.

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.

just reorder the related v weights to avoid repeat_interleave🫡 4248c93

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.

Nice. Just make sure results are still as expected after this change.

From here, I think we will be able to remove the explicit repeats - this is something for next PR. Thanks a lot!

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.

Yes, I tested it with the new generated weights, and it works just as before. 🫡

Comment thread convert_hf_to_gguf.py Resolved
Comment thread convert_hf_to_gguf.py Resolved
Comment thread convert_hf_to_gguf.py Resolved
Comment thread src/llama-arch.cpp Resolved
@CISC

CISC commented Feb 10, 2026

Copy link
Copy Markdown
Member

@JJJYmmm https://github.com/ggml-org/llama.cpp/actions/runs/21854651667/job/63068757680?pr=19468

@ngxson ngxson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The changes outside of cgraph looks good to me (sorry don't have too much time to look deeper, but I believe the review from @ggerganov and @CISC should already be enough)

Looking forward to the vision support PR then!

@JJJYmmm

JJJYmmm commented Feb 10, 2026

Copy link
Copy Markdown
Contributor Author

The changes outside of cgraph looks good to me (sorry don't have too much time to look deeper, but I believe the review from @ggerganov and @CISC should already be enough)

Looking forward to the vision support PR then!

@ngxson This pr includes the vision part and I test it too! (qwen3.5 uses the same vit as qwen3vl and deepstack was removed temporally)🤗

@ngxson

ngxson commented Feb 10, 2026

Copy link
Copy Markdown
Collaborator

@JJJYmmm ah yeah right, thanks for the clarification. Yes I already acknowledge that the deep stack is removed (confirmed by transformers implementation)

Look good to me then, thanks again!

@ngxson

ngxson commented Feb 10, 2026

Copy link
Copy Markdown
Collaborator

It seems to me that all review comments are resolved, and @JJJYmmm also confirmed that the model is still working fine with last commit. So, I'll let @ggerganov to decide when to merge this.

@ggerganov
ggerganov merged commit fc0fe40 into ggml-org:master Feb 10, 2026
79 of 82 checks passed
liparetejas pushed a commit to liparetejas/llama.cpp that referenced this pull request Feb 23, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
electimon pushed a commit to electimon/llama.cpp that referenced this pull request Mar 24, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
Seunghhon pushed a commit to Seunghhon/llama.cpp that referenced this pull request Apr 26, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
my-other-github-account pushed a commit to my-other-github-account/llama.cpp that referenced this pull request May 15, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
fukuro-kun pushed a commit to fukuro-kun/fukuro-llama-cpp-turboquant that referenced this pull request Jul 5, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
MrLordCat referenced this pull request in MrLordCat/llama.cpp-rdna-lab Jul 16, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
zommiommy pushed a commit to zommiommy/llama.cpp that referenced this pull request Aug 18, 2026
* support qwen3.5 series

* remove deepstack for now, and some code clean

* code clean

* add FULL_ATTENTION_INTERVAL metadata

* code clean

* reorder v heads for linear attention to avoid expensive interleaved repeat
baylitoo added a commit to baylitoo/slm_benchmark that referenced this pull request Aug 26, 2026
… support already landed

The previous version wrongly concluded llama.cpp had no vision support
for Qwen3.5 at all, citing a reverted early PR (#19435) and misreading
issue #19917 as an open capability gap.

Verified directly against the primary sources this time:
- ggml-org/llama.cpp#19468 (merged 2026-02-10) explicitly includes and
  tests Qwen3.5 vision support.
- Issue #19917 is closed -- it reports the standard 'missing --mmproj'
  hint text, a usage error, not a missing feature.
- bartowski/ATH-MaaS_OvisOCR2-GGUF ships a matching mmproj for this
  exact 0.8B checkpoint. Its GGUF general.architecture is 'qwen35',
  already in ARCH_TO_FAMILY -- the existing mmproj-upgrade path already
  promotes it to a vision family with zero new detection code.
- The mmproj-upgrade default (lfm2_vl) forces JSON-schema output, wrong
  for this model's free-text Markdown OCR; vision_ocr is the right
  family pick -- an operator choice at deploy, not a new family.
- The one real llama.cpp correctness bug in this arch's history
  (#19683) is closed, CUDA-only, and MoE-only -- irrelevant to CPU
  dense-model serving.

Added RUNTIME_NOTES['qwen35'] (cites #19468, warns the mmproj must
come from the same checkpoint size -- projector output dimension
differs across Qwen3.5 variants) plus a test covering both the plain
text and mmproj-upgraded vision verdicts.

The two transformers-runtime fixes from the previous commit are
unaffected -- they were found while chasing the (ultimately wrong)
trust_remote_code theory, but are real, general fixes independent of
which family this specific model turned out to need.
baylitoo added a commit to baylitoo/slm_benchmark that referenced this pull request Aug 26, 2026
…m-code VLMs (#252)

* fix(transformers): correct vision detection + honest errors for custom-code VLMs

Investigating support for ATH-MaaS/OvisOCR2 (an Ovis-family OCR model on a
Qwen3.5 base) surfaced two real gaps in the last-resort transformers
serving path, independent of that specific model:

- HfTransformersBackend inferred vision capability from which AutoModel
  constructor happened to succeed (AutoModelForImageTextToText vs a
  AutoModelForCausalLM fallback). A custom-code VLM whose auto_map
  registers only under AutoModelForCausalLM (a common, older pattern —
  the Ovis family included) was silently misclassified as text-only even
  though its processor is genuinely multimodal. Fixed by deriving
  vision from the loaded processor's own shape (does it carry an
  image_processor, the standard ProcessorMixin composition every VLM
  processor uses regardless of which AutoModel class loaded it).
- The chat-completions route only caught ValueError from a backend
  failure; any other exception (an AttributeError/TypeError from a
  custom model whose calling convention diverges from the standard
  apply_chat_template(images=...) contract this shim assumes
  generically) reached the caller as an unhandled 500 with no
  actionable message. Now every backend exception comes back
  OpenAI-shaped, with the real error text.

Added a case study to docs/model-onboarding.md documenting why OvisOCR2
needs the transformers_trust_remote_code path rather than a GGUF or a
new family: Qwen3.5's hybrid Gated DeltaNet architecture has llama.cpp
text-only support as of a few weeks ago (with an open GPU correctness
bug), and Ovis's actual vision stack has no llama.cpp implementation at
all — so every community GGUF quantization of this repo can only be
the text backbone, never a working OCR model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(docs): correct the OvisOCR2 architecture case study

The previous version assumed, from the product name alone, that
ATH-MaaS/OvisOCR2 shared AIDC-AI's unrelated 'Ovis' architecture
(custom AIMv2 vision tower, needing trust_remote_code). The repo's
actual config.json (fetched directly) shows architectures:
['Qwen3_5ForConditionalGeneration'], model_type 'qwen3_5', a native
vision_config, and no auto_map -- this is ATH-MaaS's own fine-tune of
Qwen's native Qwen3.5-VL, not AIDC-AI's Ovis.

Corrected conclusion: no trust_remote_code needed; the real gate is
transformers version currency -- Qwen3_5ForConditionalGeneration
(confirmed as a real, ~2000-line, standard-registration implementation)
lives only on transformers' unreleased main branch as of this writing.
The llama.cpp vision gap conclusion is unchanged and now more precisely
sourced: ggml-org/llama.cpp#19917 ("Qwen3.5 image input is not
supported") is a direct match for this exact vision_config, not an
inference about an unrelated architecture.

The two code fixes in the previous commit (processor-shape vision
detection, honest backend error surfacing) are unaffected -- they are
general fixes, not specific to which architecture family this model
turned out to be.

* fix(docs): OvisOCR2 is a rung-1 model, not rung-3 -- llama.cpp vision support already landed

The previous version wrongly concluded llama.cpp had no vision support
for Qwen3.5 at all, citing a reverted early PR (#19435) and misreading
issue #19917 as an open capability gap.

Verified directly against the primary sources this time:
- ggml-org/llama.cpp#19468 (merged 2026-02-10) explicitly includes and
  tests Qwen3.5 vision support.
- Issue #19917 is closed -- it reports the standard 'missing --mmproj'
  hint text, a usage error, not a missing feature.
- bartowski/ATH-MaaS_OvisOCR2-GGUF ships a matching mmproj for this
  exact 0.8B checkpoint. Its GGUF general.architecture is 'qwen35',
  already in ARCH_TO_FAMILY -- the existing mmproj-upgrade path already
  promotes it to a vision family with zero new detection code.
- The mmproj-upgrade default (lfm2_vl) forces JSON-schema output, wrong
  for this model's free-text Markdown OCR; vision_ocr is the right
  family pick -- an operator choice at deploy, not a new family.
- The one real llama.cpp correctness bug in this arch's history
  (#19683) is closed, CUDA-only, and MoE-only -- irrelevant to CPU
  dense-model serving.

Added RUNTIME_NOTES['qwen35'] (cites #19468, warns the mmproj must
come from the same checkpoint size -- projector output dimension
differs across Qwen3.5 variants) plus a test covering both the plain
text and mmproj-upgraded vision verdicts.

The two transformers-runtime fixes from the previous commit are
unaffected -- they were found while chasing the (ultimately wrong)
trust_remote_code theory, but are real, general fixes independent of
which family this specific model turned out to need.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

examples model Model specific python python script changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants