From dbccc5ae328d7b9168bc33b278a9125cfd89cc69 Mon Sep 17 00:00:00 2001 From: "Rui \"Garry\" Gao" Date: Mon, 27 Jul 2026 21:42:35 +0800 Subject: [PATCH] [Model] Enable EVS for Qwen3.5 (#48912) Signed-off-by: Rui "Garry" Gao --- vllm/model_executor/models/qwen3_5.py | 44 +++++++++++++++++++-------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/vllm/model_executor/models/qwen3_5.py b/vllm/model_executor/models/qwen3_5.py index bcd2576b74..4707633753 100644 --- a/vllm/model_executor/models/qwen3_5.py +++ b/vllm/model_executor/models/qwen3_5.py @@ -53,6 +53,7 @@ from vllm.model_executor.layers.vocab_parallel_embedding import ( ) from vllm.multimodal import MULTIMODAL_REGISTRY from vllm.sequence import IntermediateTensors +from vllm.tokenizers.registry import cached_tokenizer_from_config from vllm.transformers_utils.configs.qwen3_5 import Qwen3_5Config, Qwen3_5TextConfig from vllm.transformers_utils.configs.qwen3_5_moe import ( Qwen3_5MoeConfig, @@ -397,8 +398,7 @@ class Qwen3_5MoeForCausalLM(Qwen3_5ForCausalLMBase, QwenNextMixtureOfExperts): dummy_inputs=Qwen3VLDummyInputsBuilder, ) class Qwen3_5ForConditionalGeneration(Qwen3VLForConditionalGeneration, IsHybrid): - # Qwen3.5 does not support multimodal pruning (EVS). - supports_multimodal_pruning = False + supports_multimodal_pruning = True packed_modules_mapping = Qwen3VLForConditionalGeneration.packed_modules_mapping | { "in_proj_qkvz": ["in_proj_qkv", "in_proj_z"], @@ -416,8 +416,21 @@ class Qwen3_5ForConditionalGeneration(Qwen3VLForConditionalGeneration, IsHybrid) self.model_config = vllm_config.model_config self.multimodal_config = multimodal_config self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data" - # Qwen3.5 does not support multimodal pruning (EVS). - self.is_multimodal_pruning_enabled = False + self.is_multimodal_pruning_enabled = ( + multimodal_config.is_multimodal_pruning_enabled() + ) + self.video_pruning_rate = self.multimodal_config.video_pruning_rate + self._tokenizer = cached_tokenizer_from_config(vllm_config.model_config) + + # attributes needed by EVS-related functions inherited from Qwen3-VL + self.use_deepstack = hasattr(config.vision_config, "deepstack_visual_indexes") + self.deepstack_num_level = ( + len(config.vision_config.deepstack_visual_indexes) + if self.use_deepstack + else 0 + ) + self.visual_dim = config.vision_config.out_hidden_size + self.multiscale_dim = self.visual_dim * self.deepstack_num_level with self._mark_tower_model(vllm_config, {"image", "video"}): self.visual = Qwen3_VisionTransformer( @@ -462,12 +475,6 @@ class Qwen3_5ForConditionalGeneration(Qwen3VLForConditionalGeneration, IsHybrid) return inputs_embeds - def recompute_mrope_positions(self, *args, **kwargs): - raise NotImplementedError( - "Qwen3.5 does not support multimodal pruning (EVS). " - "recompute_mrope_positions should never be called." - ) - def forward( self, input_ids: torch.Tensor, @@ -628,8 +635,21 @@ class Qwen3_5MoeForConditionalGeneration( self.model_config = vllm_config.model_config self.multimodal_config = multimodal_config self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data" - # Qwen3.5 does not support multimodal pruning (EVS). - self.is_multimodal_pruning_enabled = False + self.is_multimodal_pruning_enabled = ( + multimodal_config.is_multimodal_pruning_enabled() + ) + self.video_pruning_rate = self.multimodal_config.video_pruning_rate + self._tokenizer = cached_tokenizer_from_config(vllm_config.model_config) + + # attributes needed by EVS-related functions inherited from Qwen3-VL + self.use_deepstack = hasattr(config.vision_config, "deepstack_visual_indexes") + self.deepstack_num_level = ( + len(config.vision_config.deepstack_visual_indexes) + if self.use_deepstack + else 0 + ) + self.visual_dim = config.vision_config.out_hidden_size + self.multiscale_dim = self.visual_dim * self.deepstack_num_level with self._mark_tower_model(vllm_config, {"image", "video"}): self.visual = Qwen3_VisionTransformer(