[Model] Support Qwen3.5 text-only dense and MoE models (#50210)
Signed-off-by: Perkz Zheng <PerkzZheng@users.noreply.github.com> Co-authored-by: Perkz Zheng <PerkzZheng@users.noreply.github.com>
Perkz Zheng committed
Jul 29, 2026 at 16:21 UTC
ad5d29db702ce5bce78d9d3b2da1a76eab72e9c2
5 files changed
+62
tests/models/registry.py
+2
@@ -497,6 +497,8 @@ _TEXT_GENERATION_EXAMPLE_MODELS = {
497
"Qwen2MoeForCausalLM": _HfExamplesInfo("Qwen/Qwen1.5-MoE-A2.7B-Chat"),
498
"Qwen3ForCausalLM": _HfExamplesInfo("Qwen/Qwen3-8B"),
499
"Qwen3MoeForCausalLM": _HfExamplesInfo("Qwen/Qwen3-30B-A3B"),
500
+ "Qwen3_5ForCausalLM": _HfExamplesInfo("codecho/Qwen3.5-0.8B-text-only"),
501
+ "Qwen3_5MoeForCausalLM": _HfExamplesInfo("codecho/Qwen3.5-35B-A3B-text-only"),
502
"MellumForCausalLM": _HfExamplesInfo("JetBrains/Mellum2-12B-A2.5B-Base"),
503
"Qwen3NextForCausalLM": _HfExamplesInfo(
504
"Qwen/Qwen3-Next-80B-A3B-Instruct",
vllm/model_executor/models/config.py
+16
@@ -768,6 +768,20 @@ class Qwen3_5ForConditionalGenerationConfig(VerifyAndUpdateConfig):
768
)
769
770
771
+class Qwen3_5ForCausalLMConfig(Qwen3_5ForConditionalGenerationConfig):
772
+ @staticmethod
773
+ def verify_and_update_config(vllm_config: "VllmConfig") -> None:
774
+ Qwen3_5ForConditionalGenerationConfig.verify_and_update_config(vllm_config)
775
+
776
+ # Text-only Qwen3.5 models use one-dimensional positions. Remove the
777
+ # M-RoPE fields inherited from the multimodal configuration.
778
+ hf_text_config = vllm_config.model_config.hf_text_config
779
+ rope_parameters = getattr(hf_text_config, "rope_parameters", None)
780
+ if rope_parameters is not None:
781
+ rope_parameters.pop("mrope_section", None)
782
+ rope_parameters.pop("mrope_interleaved", None)
783
+
784
+
785
class ColQwen3_5Config(Qwen3_5ForConditionalGenerationConfig):
786
"""Apply the attention contract declared by a ColQwen3.5 checkpoint."""
787
@@ -884,7 +898,9 @@ MODELS_CONFIG_MAP: dict[str, type[VerifyAndUpdateConfig]] = {
898
"Qwen2ForRewardModel": Qwen2ForRewardModelConfig,
899
"Qwen3ForSequenceClassification": Qwen3ForSequenceClassificationConfig,
900
"Qwen3VLForSequenceClassification": Qwen3VLForSequenceClassificationConfig,
901
+ "Qwen3_5ForCausalLM": Qwen3_5ForCausalLMConfig,
902
"Qwen3_5ForConditionalGeneration": Qwen3_5ForConditionalGenerationConfig,
903
+ "Qwen3_5MoeForCausalLM": Qwen3_5ForCausalLMConfig,
904
"Qwen3_5MoeForConditionalGeneration": Qwen3_5ForConditionalGenerationConfig,
905
"UnlimitedOCRForCausalLM": UnlimitedOCRForCausalLMConfig,
906
"VoyageQwen3BidirectionalEmbedModel": VoyageQwen3BidirectionalEmbedModelConfig,
vllm/model_executor/models/qwen3_5.py
+40
@@ -282,6 +282,7 @@ class Qwen3_5Model(Qwen3NextModel):
282
class Qwen3_5ForCausalLMBase(
283
nn.Module,
284
HasInnerState,
285
+ IsHybrid,
286
SupportsEagle3,
287
SupportsLoRA,
288
SupportsPP,
@@ -361,6 +362,45 @@ class Qwen3_5ForCausalLMBase(
362
363
return hidden_states
364
365
+ @classmethod
366
+ def get_mamba_state_dtype_from_config(
367
+ cls,
368
+ vllm_config: "VllmConfig",
369
+ ) -> tuple[torch.dtype, torch.dtype]:
370
+ return MambaStateDtypeCalculator.gated_delta_net_state_dtype(
371
+ vllm_config.model_config.dtype,
372
+ vllm_config.cache_config.mamba_cache_dtype,
373
+ vllm_config.cache_config.mamba_ssm_cache_dtype,
374
+ )
375
+
376
+ @classmethod
377
+ def get_mamba_state_shape_from_config(
378
+ cls, vllm_config: "VllmConfig"
379
+ ) -> tuple[tuple[int, int], tuple[int, int]]:
380
+ parallel_config = vllm_config.parallel_config
381
+ hf_config = vllm_config.model_config.hf_text_config
382
+ tp_size = parallel_config.tensor_parallel_size
383
+ num_spec = (
384
+ vllm_config.speculative_config.num_speculative_tokens
385
+ if vllm_config.speculative_config
386
+ else 0
387
+ )
388
+ return MambaStateShapeCalculator.gated_delta_net_state_shape(
389
+ tp_size,
390
+ hf_config.linear_num_key_heads,
391
+ hf_config.linear_num_value_heads,
392
+ hf_config.linear_key_head_dim,
393
+ hf_config.linear_value_head_dim,
394
+ hf_config.linear_conv_kernel_dim,
395
+ num_spec,
396
+ )
397
+
398
+ @classmethod
399
+ def get_mamba_state_copy_func(
400
+ cls,
401
+ ) -> tuple[MambaStateCopyFunc, MambaStateCopyFunc]:
402
+ return MambaStateCopyFuncCalculator.gated_delta_net_state_copy_func()
403
+
404
def compute_logits(
405
self,
406
hidden_states: torch.Tensor,
vllm/model_executor/models/registry.py
+2
@@ -195,6 +195,8 @@ _TEXT_GENERATION_MODELS = {
195
"Qwen2MoeForCausalLM": ("qwen2_moe", "Qwen2MoeForCausalLM"),
196
"Qwen3ForCausalLM": ("qwen3", "Qwen3ForCausalLM"),
197
"Qwen3MoeForCausalLM": ("qwen3_moe", "Qwen3MoeForCausalLM"),
198
+ "Qwen3_5ForCausalLM": ("qwen3_5", "Qwen3_5ForCausalLM"),
199
+ "Qwen3_5MoeForCausalLM": ("qwen3_5", "Qwen3_5MoeForCausalLM"),
200
"RWForCausalLM": ("falcon", "FalconForCausalLM"),
201
"SarvamMoEForCausalLM": ("sarvam", "SarvamMoEForCausalLM"),
202
"SarvamMLAForCausalLM": ("sarvam", "SarvamMLAForCausalLM"),
vllm/transformers_utils/config.py
+2
@@ -124,7 +124,9 @@ _CONFIG_REGISTRY: dict[str, type[PretrainedConfig]] = LazyConfigDict(
124
qwen3_asr="Qwen3ASRConfig",
125
qwen3_next="Qwen3NextConfig",
126
qwen3_5="Qwen3_5Config",
127
+ qwen3_5_text="Qwen3_5TextConfig",
128
qwen3_5_moe="Qwen3_5MoeConfig",
129
+ qwen3_5_moe_text="Qwen3_5MoeTextConfig",
130
laguna="LagunaConfig",
131
lfm2_moe="Lfm2MoeConfig",
132
**{"unlimited-ocr": "UnlimitedOCRConfig"},