[Mypy Fix] Mypy fix for "vllm/model_executor/models/[gG]" (#53616)

Signed-off-by: yewentao256 <zhyanwentao@126.com>

Wentao Ye committed Aug 25, 2026 at 12:47 UTC 70021a0081cc10d8862f7458c36353b72ea9b5f4
26 files changed +350 -123
tools/pre_commit/mypy.py
-1
@@ -112,7 +112,6 @@ SEPARATE_GROUPS = [
112 ]
113
114 EXCLUDE = [
115 - r"vllm/model_executor/models/[gG]",
115 r"vllm/model_executor/models/[hH]",
116 r"vllm/model_executor/models/[iI]",
117 r"vllm/model_executor/models/[jJ]",
vllm/model_executor/models/deepseek_v2.py
+2 -2
@@ -457,7 +457,7 @@ class DeepseekV2Attention(nn.Module):
457 qk_nope_head_dim: int,
458 qk_rope_head_dim: int,
459 v_head_dim: int,
460 - q_lora_rank: int,
460 + q_lora_rank: int | None,
461 kv_lora_rank: int,
462 max_position_embeddings: int = 8192,
463 cache_config: CacheConfig | None = None,
@@ -495,7 +495,7 @@ class DeepseekV2Attention(nn.Module):
495 )
496 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
497 self.q_b_proj = ColumnParallelLinear(
498 - q_lora_rank,
498 + self.q_lora_rank,
499 self.num_heads * self.qk_head_dim,
500 bias=False,
501 quant_config=quant_config,
vllm/model_executor/models/gemma.py
+1
@@ -311,6 +311,7 @@ class GemmaModel(nn.Module):
311 hidden_states = self.embed_input_ids(input_ids)
312 residual = None
313 else:
314 + assert intermediate_tensors is not None
315 hidden_states = intermediate_tensors["hidden_states"]
316 residual = intermediate_tensors["residual"]
317 for layer in islice(self.layers, self.start_layer, self.end_layer):
vllm/model_executor/models/gemma3_mm.py
+2
@@ -852,7 +852,9 @@ class Gemma3ForConditionalGeneration(
852 def encoder_eager_forward(
853 self,
854 mm_kwargs: dict[str, Any],
855 + path: str = "default",
856 ) -> torch.Tensor:
857 image_input = self._parse_and_validate_image_input(**mm_kwargs)
858 + assert image_input is not None
859 results = self._process_image_input(image_input)
860 return torch.cat(results, dim=0)
vllm/model_executor/models/gemma3n.py
+1 -1
@@ -77,7 +77,7 @@ class Gemma3nAltUp(nn.Module):
77 altup_num_inputs: int,
78 altup_coef_clip: float,
79 altup_active_idx: int,
80 - quant_config: QuantizationConfig,
80 + quant_config: QuantizationConfig | None,
81 prefix: str,
82 ):
83 super().__init__()
vllm/model_executor/models/gemma3n_mm.py
+31 -10
@@ -1,7 +1,7 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import inspect
4 -from collections.abc import Iterable, Mapping, Sequence
4 +from collections.abc import Iterable, Mapping, Sequence, Sized
5 from typing import Annotated, Any, Literal
6
7 import torch
@@ -18,7 +18,7 @@ from transformers.models.gemma3n import (
18 from transformers.models.siglip import SiglipImageProcessorFast
19
20 from vllm.config import ModelConfig, SpeechToTextConfig, VllmConfig
21 -from vllm.config.multimodal import BaseDummyOptions
21 +from vllm.config.multimodal import AudioDummyOptions, BaseDummyOptions
22 from vllm.config.speech_to_text import SpeechToTextParams
23 from vllm.inputs import MultiModalDataDict, PromptType, TextPrompt
24 from vllm.logger import init_logger
@@ -115,8 +115,10 @@ def batch_audio_features(
115 The `(bn, s_max, f)` features and their `(bn, s_max)` mask.
116 """
117 if isinstance(input_features, torch.Tensor):
118 + assert isinstance(input_features_mask, torch.Tensor)
119 return input_features.squeeze(1), input_features_mask.squeeze(1)
120
121 + assert isinstance(input_features_mask, list)
122 max_len = max(features.shape[0] for features in input_features)
123 batched_features = input_features[0].new_zeros(
124 (len(input_features), max_len, input_features[0].shape[-1])
@@ -248,6 +250,7 @@ class Gemma3nDummyInputsBuilder(BaseDummyInputsBuilder[Gemma3nProcessingInfo]):
250
251 image_overrides = mm_options.get("image")
252 audio_overrides = mm_options.get("audio")
253 + assert audio_overrides is None or isinstance(audio_overrides, AudioDummyOptions)
254
255 return {
256 "image": self._get_dummy_images(
@@ -293,10 +296,14 @@ class Gemma3nMultiModalProcessor(BaseMultiModalProcessor[Gemma3nProcessingInfo])
296 # output (and thus its cache entry) is independent of the other
297 # items in the batch. `batch_audio_features` re-pads the
298 # per-item features so audio_tower can still run batched.
296 - num_frames = [
297 - self._get_num_audio_frames_alone(len(audio))
298 - for audio in mm_data["audio"]
299 - ]
299 + audios = mm_data["audio"]
300 + if not isinstance(audios, Sequence):
301 + raise TypeError("audio data must be a sequence")
302 + num_frames = []
303 + for audio in audios:
304 + if not isinstance(audio, Sized):
305 + raise TypeError("each audio item must have a length")
306 + num_frames.append(self._get_num_audio_frames_alone(len(audio)))
307 processed_data["input_features_padded"] = [
308 features[:n]
309 for features, n in zip(processed_data.pop("input_features"), num_frames)
@@ -685,7 +692,9 @@ class Gemma3nForConditionalGeneration(
692 )
693
694 def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
688 - mm_input_by_modality = {}
695 + mm_input_by_modality: dict[
696 + str, Gemma3nImageInputs | Gemma3nAudioInputs | None
697 + ] = {}
698
699 # Preserve the order of modalities if there are multiple of them
700 # from the order of kwargs.
@@ -852,7 +861,13 @@ class Gemma3nForConditionalGeneration(
861 # select a chunk of pre-allocated PLEs. During normal execution,
862 # `embed_input_ids` is called before forward, hence this slice
863 # will contain PLEs computed from the actual input_ids.
855 - per_layer_inputs = self.per_layer_embeddings[: inputs_embeds.shape[0]]
864 + if inputs_embeds is not None:
865 + num_tokens = inputs_embeds.shape[0]
866 + elif intermediate_tensors is not None:
867 + num_tokens = intermediate_tensors["hidden_states"].shape[0]
868 + else:
869 + raise ValueError("inputs_embeds is required on the first PP rank")
870 + per_layer_inputs = self.per_layer_embeddings[:num_tokens]
871
872 hidden_states = self.language_model.model(
873 input_ids,
@@ -913,9 +928,15 @@ class Gemma3nForConditionalGeneration(
928 prompt += " this audio"
929
930 # We assume the language is a valid ISO 639-1 code.
916 - full_lang_name = cls.supported_languages.get(language, "")
931 + full_lang_name = (
932 + cls.supported_languages.get(language, "") if language is not None else ""
933 + )
934 # Translation only for now
918 - full_lang_name_to = cls.supported_languages.get(to_language, "")
935 + full_lang_name_to = (
936 + cls.supported_languages.get(to_language, "")
937 + if to_language is not None
938 + else ""
939 + )
940
941 if task_type == "transcribe" and full_lang_name:
942 prompt += f" into {full_lang_name}"
vllm/model_executor/models/gemma4.py
+31 -7
@@ -352,15 +352,19 @@ class Gemma4MoE(nn.Module):
352 )
353
354 # MoERunner experts with custom Gemma4 routing
355 + intermediate_size = getattr(
356 + config,
357 + "moe_intermediate_size",
358 + getattr(config, "expert_intermediate_size", None),
359 + )
360 + if intermediate_size is None:
361 + raise ValueError("Gemma4 MoE requires an expert intermediate size")
362 +
363 self.experts = FusedMoEFactory(
364 num_experts=config.num_experts,
365 top_k=config.top_k_experts,
366 hidden_size=config.hidden_size,
359 - intermediate_size=getattr(
360 - config,
361 - "moe_intermediate_size",
362 - getattr(config, "expert_intermediate_size", None),
363 - ),
367 + intermediate_size=intermediate_size,
368 renormalize=True,
369 quant_config=quant_config,
370 prefix=f"{prefix}.experts",
@@ -624,6 +628,11 @@ class Gemma4DecoderLayer(nn.Module):
628 self.enable_moe_block = getattr(config, "enable_moe_block", False) or getattr(
629 config, "use_second_mlp_block", False
630 )
631 + self.router: Gemma4Router | None
632 + self.moe: Gemma4MoE | None
633 + self.post_feedforward_layernorm_1: RMSNorm | None
634 + self.post_feedforward_layernorm_2: RMSNorm | None
635 + self.pre_feedforward_layernorm_2: RMSNorm | None
636 if self.enable_moe_block:
637 self.router = Gemma4Router(
638 config,
@@ -652,6 +661,9 @@ class Gemma4DecoderLayer(nn.Module):
661 self.pre_feedforward_layernorm_2 = None
662
663 # Per-Layer Embedding (PLE) components — present in each decoder layer
664 + self.per_layer_input_gate: ReplicatedLinear | None
665 + self.per_layer_projection: ReplicatedLinear | None
666 + self.post_per_layer_input_norm: RMSNorm | None
667 if (
668 self.hidden_size_per_layer_input is not None
669 and self.hidden_size_per_layer_input > 0
@@ -716,6 +728,11 @@ class Gemma4DecoderLayer(nn.Module):
728 hidden_states = self.mlp(hidden_states)
729
730 if self.enable_moe_block:
731 + assert self.post_feedforward_layernorm_1 is not None
732 + assert self.pre_feedforward_layernorm_2 is not None
733 + assert self.router is not None
734 + assert self.moe is not None
735 + assert self.post_feedforward_layernorm_2 is not None
736 hidden_states_1 = self.post_feedforward_layernorm_1(hidden_states)
737
738 hidden_states_2 = self.pre_feedforward_layernorm_2(residual)
@@ -731,6 +748,8 @@ class Gemma4DecoderLayer(nn.Module):
748
749 # Apply PLE (Per-Layer Embedding) if configured
750 if per_layer_input is not None and self.per_layer_input_gate is not None:
751 + assert self.per_layer_projection is not None
752 + assert self.post_per_layer_input_norm is not None
753 gate = self.per_layer_input_gate(hidden_states)
754 gate = torch.nn.functional.gelu(gate, approximate="tanh")
755 gated_per_layer = gate * per_layer_input
@@ -865,6 +884,7 @@ class Gemma4SelfDecoderLayers(nn.Module):
884 """
885 if self.per_layer_model_projection is None:
886 return None
887 + assert self.per_layer_projection_norm is not None
888 per_layer_projection = self.per_layer_model_projection(inputs_embeds)
889 per_layer_projection = per_layer_projection * self.per_layer_projection_scale
890 per_layer_projection = per_layer_projection.reshape(
@@ -971,6 +991,9 @@ class Gemma4Model(nn.Module, EagleModelMixin):
991 )
992
993 # Per-Layer Embedding (PLE) components
994 + self.embed_tokens_per_layer: VocabParallelEmbedding | None
995 + self.per_layer_model_projection: ColumnParallelLinear | None
996 + self.per_layer_projection_norm: RMSNorm | None
997 if (
998 self.hidden_size_per_layer_input is not None
999 and self.hidden_size_per_layer_input > 0
@@ -1472,9 +1495,10 @@ class Gemma4Model(nn.Module, EagleModelMixin):
1495 else:
1496 if name.endswith(".bias") and name not in params_dict:
1497 continue
1475 - name = maybe_remap_kv_scale_name(name, params_dict)
1476 - if name is None:
1498 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
1499 + if remapped_name is None:
1500 continue
1501 + name = remapped_name
1502 if is_pp_missing_parameter(name, self):
1503 continue
1504 # Skip if name doesn't exist in params_dict (e.g., individual
vllm/model_executor/models/gemma4_dspark.py
+5 -1
@@ -89,7 +89,11 @@ class Gemma4DSparkAttention(Gemma4MTPAttention):
89 ) -> tuple[torch.Tensor, torch.Tensor]:
90 k, _ = self.k_proj(hidden_states)
91 k_normed = self.k_norm(k.unflatten(-1, (self.num_kv_heads, self.head_dim)))
92 - v_src = k if self.use_k_eq_v else self.v_proj(hidden_states)[0]
92 + if self.use_k_eq_v:
93 + v_src = k
94 + else:
95 + assert self.v_proj is not None
96 + v_src = self.v_proj(hidden_states)[0]
97 v_normed = self.v_norm(v_src.unflatten(-1, (self.num_kv_heads, self.head_dim)))
98 return k_normed.flatten(-2, -1), v_normed.flatten(-2, -1)
99
vllm/model_executor/models/gemma4_mm.py
+58 -14
@@ -35,7 +35,11 @@ from transformers.models.gemma4.configuration_gemma4 import (
35
36 from vllm.config import VllmConfig
37 from vllm.config.model import get_served_model_name
38 -from vllm.config.multimodal import BaseDummyOptions, VideoDummyOptions
38 +from vllm.config.multimodal import (
39 + AudioDummyOptions,
40 + BaseDummyOptions,
41 + VideoDummyOptions,
42 +)
43 from vllm.inputs import MultiModalDataDict
44 from vllm.logger import init_logger
45 from vllm.model_executor.layers.layernorm import RMSNorm
@@ -189,6 +193,7 @@ class Gemma4VideoInputs(TensorSchema):
193 torch.Tensor,
194 TensorShape("bn", "np", 2),
195 ]
196 + video_frame_counts: Annotated[torch.Tensor, TensorShape("nv")]
197
198
199 # ---------------------------------------------------------------------------
@@ -490,8 +495,10 @@ class Gemma4DummyInputsBuilder(BaseDummyInputsBuilder[Gemma4ProcessingInfo]):
495 image_overrides = mm_options.get("image") if mm_options else None
496 audio_overrides = mm_options.get("audio") if mm_options else None
497 video_overrides = mm_options.get("video") if mm_options else None
498 + assert audio_overrides is None or isinstance(audio_overrides, AudioDummyOptions)
499 + assert video_overrides is None or isinstance(video_overrides, VideoDummyOptions)
500
494 - data: MultiModalDataDict = {
501 + data: dict[str, Any] = {
502 "image": self._get_dummy_images(
503 width=img_width,
504 height=img_height,
@@ -592,6 +599,7 @@ class Gemma4MultiModalProcessor(BaseMultiModalProcessor[Gemma4ProcessingInfo]):
599 # same vision tower, matching transformers processing_gemma4.py.
600 video_outputs: dict[str, Any] = {}
601 if videos := mm_data.pop("videos", []):
602 + assert isinstance(videos, list)
603 processor = self.info.get_hf_processor()
604
605 all_video_pixel_values: list[torch.Tensor] = []
@@ -858,8 +866,29 @@ class Gemma4MultiModalProcessor(BaseMultiModalProcessor[Gemma4ProcessingInfo]):
866
867 def get_replacement_video(item_idx: int):
868 out_item = out_mm_kwargs["video"][item_idx]
861 - timestamps = out_item["video_timestamps"].data
862 - num_soft = out_item["video_num_soft_tokens"].data
869 + timestamps_data = out_item["video_timestamps"].data
870 + if isinstance(timestamps_data, torch.Tensor):
871 + timestamps_data = timestamps_data.tolist()
872 + if not isinstance(timestamps_data, list):
873 + raise TypeError("video timestamps must be a list")
874 + timestamps: list[float] = []
875 + for timestamp in timestamps_data:
876 + if not isinstance(timestamp, (int, float)):
877 + raise TypeError("each video timestamp must be numeric")
878 + timestamps.append(float(timestamp))
879 +
880 + num_soft_data = out_item["video_num_soft_tokens"].data
881 + if isinstance(num_soft_data, torch.Tensor):
882 + num_soft_data = num_soft_data.tolist()
883 + if not isinstance(num_soft_data, list):
884 + raise TypeError("video soft-token counts must be a list")
885 + num_soft: list[int] = []
886 + for count in num_soft_data:
887 + if not isinstance(count, int):
888 + raise TypeError(
889 + "each video soft-token count must be an integer"
890 + )
891 + num_soft.append(count)
892 return self.info.get_video_repl(
893 timestamps=timestamps,
894 num_soft_tokens_per_frame=num_soft,
@@ -1038,6 +1067,7 @@ class Gemma4ForConditionalGeneration(
1067 # (Marlin, FP8, …) require dimensions divisible by 64, which
1068 # the vision tower (intermediate_size=4304) does not satisfy.
1069 # TODO(mgoin): remove this by fixing kernel padding.
1070 + tower_quant: QuantizationConfig | None
1071 if quant_config and quant_config.get_name() in [
1072 "bitsandbytes",
1073 "torchao",
@@ -1068,6 +1098,7 @@ class Gemma4ForConditionalGeneration(
1098 )
1099
1100 # ---- Audio tower (variants with audio_config) ----
1101 + self.embed_audio: Gemma4MultimodalEmbedder | None
1102 if config.audio_config is not None:
1103 with self._mark_tower_model(vllm_config, "audio"):
1104 self.audio_tower = AutoModel.from_config(config=config.audio_config)
@@ -1177,22 +1208,24 @@ class Gemma4ForConditionalGeneration(
1208
1209 def _parse_and_validate_video_input(
1210 self, **kwargs: object
1180 - ) -> dict[str, torch.Tensor] | None:
1211 + ) -> Gemma4VideoInputs | None:
1212 pixel_values_videos = kwargs.pop("pixel_values_videos", None)
1213 pixel_position_ids_videos = kwargs.pop("pixel_position_ids_videos", None)
1214 video_frame_counts = kwargs.pop("video_frame_counts", None)
1215 if pixel_values_videos is None:
1216 return None
1186 - return {
1187 - "pixel_values_videos": pixel_values_videos,
1188 - "pixel_position_ids_videos": pixel_position_ids_videos,
1189 - "video_frame_counts": video_frame_counts,
1190 - }
1217 + return Gemma4VideoInputs(
1218 + pixel_values_videos=pixel_values_videos,
1219 + pixel_position_ids_videos=pixel_position_ids_videos,
1220 + video_frame_counts=video_frame_counts,
1221 + )
1222
1223 def _parse_and_validate_multimodal_inputs(
1224 self, **kwargs: object
1225 ) -> dict[str, Gemma4ImageInputs | Gemma4AudioInputs | Gemma4VideoInputs | None]:
1195 - mm_input_by_modality = {}
1226 + mm_input_by_modality: dict[
1227 + str, Gemma4ImageInputs | Gemma4AudioInputs | Gemma4VideoInputs | None
1228 + ] = {}
1229 for input_key in list(kwargs):
1230 if (
1231 input_key in ("pixel_values", "image_embeds")
@@ -1419,7 +1452,7 @@ class Gemma4ForConditionalGeneration(
1452
1453 def _process_video_input(
1454 self,
1422 - video_input: dict[str, torch.Tensor],
1455 + video_input: Gemma4VideoInputs,
1456 ) -> list[torch.Tensor]:
1457 """Batch-encode video frames through the vision tower.
1458
@@ -1543,6 +1576,8 @@ class Gemma4ForConditionalGeneration(
1576 audio_input["input_features_mask"],
1577 )
1578
1579 + assert self.audio_tower is not None
1580 + assert self.embed_audio is not None
1581 # Run audio tower — mask convention: True=valid, False=padding.
1582 audio_outputs = self.audio_tower(input_features, input_features_mask)
1583 if isinstance(audio_outputs, tuple):
@@ -1576,14 +1611,17 @@ class Gemma4ForConditionalGeneration(
1611 if multimodal_input is None:
1612 continue
1613 if modality == "image":
1614 + assert isinstance(multimodal_input, Gemma4ImageInputs)
1615 multimodal_embeddings.extend(
1616 self._process_image_input(multimodal_input)
1617 )
1618 elif modality == "video":
1619 + assert isinstance(multimodal_input, Gemma4VideoInputs)
1620 multimodal_embeddings.extend(
1621 self._process_video_input(multimodal_input)
1622 )
1623 elif modality == "audio":
1624 + assert isinstance(multimodal_input, Gemma4AudioInputs)
1625 multimodal_embeddings.extend(
1626 self._process_audio_input(multimodal_input)
1627 )
@@ -2204,13 +2242,15 @@ class Gemma4ForConditionalGeneration(
2242 mm_kwargs: MultiModalKwargsItem | None,
2243 num_mm_embeds: int,
2244 ) -> tuple[int, int | None]:
2245 + tower_tokens: int | None = None
2246 + connector_tokens: int | None = None
2247 if modality in ("image", "video"):
2248 vision_config = self.config.vision_config
2249 pooling_k2 = vision_config.pooling_kernel_size**2
2250
2251 if modality == "image":
2252 pixel_values_key = "pixel_values"
2213 - max_soft_tokens = vision_config.default_output_length
2253 + max_soft_tokens = int(vision_config.default_output_length)
2254 mm_processor_kwargs = getattr(
2255 getattr(self, "multimodal_config", None),
2256 "mm_processor_kwargs",
@@ -2241,7 +2281,7 @@ class Gemma4ForConditionalGeneration(
2281 * pooling_k2
2282 )
2283
2244 - if modality == "audio":
2284 + elif modality == "audio":
2285 tower_tokens = num_mm_embeds
2286 connector_tokens = num_mm_embeds
2287
@@ -2255,6 +2295,10 @@ class Gemma4ForConditionalGeneration(
2295 tower_tokens = audio_tokens
2296 connector_tokens = audio_tokens
2297
2298 + else:
2299 + raise ValueError(f"Unsupported modality: {modality}")
2300 +
2301 + assert tower_tokens is not None
2302 return tower_tokens, connector_tokens
2303
2304 @classmethod
vllm/model_executor/models/gemma4_mtp.py
+10 -3
@@ -345,7 +345,9 @@ class Gemma4MultiTokenPredictor(nn.Module):
345 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
346 super().__init__()
347
348 - config = vllm_config.speculative_config.draft_model_config.hf_config
348 + speculative_config = vllm_config.speculative_config
349 + assert speculative_config is not None
350 + config = speculative_config.draft_model_config.hf_config
351 text_config = _get_text_config(config)
352 quant_config = get_draft_quant_config(vllm_config)
353 self.config = text_config
@@ -467,7 +469,9 @@ class Gemma4MTP(nn.Module):
469
470 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
471 super().__init__()
470 - config = vllm_config.speculative_config.draft_model_config.hf_config
472 + speculative_config = vllm_config.speculative_config
473 + assert speculative_config is not None
474 + config = speculative_config.draft_model_config.hf_config
475 text_config = _get_text_config(config)
476 self.quant_config = get_draft_quant_config(vllm_config)
477 self.config = config
@@ -496,6 +500,7 @@ class Gemma4MTP(nn.Module):
500 soft_cap=getattr(text_config, "final_logit_softcapping", None),
501 )
502
503 + self.masked_embedding: Gemma4MTPMaskedEmbedder | None
504 if getattr(config, "use_ordered_embeddings", False):
505 num_centroids = getattr(config, "num_centroids", 2048)
506 top_k = getattr(config, "centroid_intermediate_top_k", 32)
@@ -516,7 +521,7 @@ class Gemma4MTP(nn.Module):
521 else:
522 self.masked_embedding = None
523
519 - draft_cfg = vllm_config.speculative_config.draft_model_config
524 + draft_cfg = speculative_config.draft_model_config
525 gen_cfg = draft_cfg.try_get_generation_config()
526 self._suppress_token_ids = gen_cfg.get("suppress_tokens") if gen_cfg else None
527
@@ -545,6 +550,7 @@ class Gemma4MTP(nn.Module):
550 def _get_full_lm_head_weight(self) -> torch.Tensor:
551 if self._stable_full_lm_head_weight is not None:
552 return self._stable_full_lm_head_weight
553 + assert self.masked_embedding is not None
554 lm_head_weight = self.lm_head.weight
555 tp_size = get_tensor_model_parallel_world_size()
556 if tp_size > 1:
@@ -579,6 +585,7 @@ class Gemma4MTP(nn.Module):
585 hidden_states: torch.Tensor,
586 ) -> torch.Tensor:
587 """Sparse argmax via centroids masking. Returns token IDs directly."""
588 + assert self.masked_embedding is not None
589 return self.masked_embedding.get_top_tokens(
590 hidden_states,
591 self._get_full_lm_head_weight(),
vllm/model_executor/models/gemma4_unified.py
+12 -16
@@ -44,6 +44,7 @@ from vllm.model_executor.models.gemma4_mm import (
44 Gemma4MultimodalEmbedder,
45 Gemma4MultiModalProcessor,
46 Gemma4ProcessingInfo,
47 + Gemma4VideoInputs,
48 _get_max_soft_tokens,
49 )
50 from vllm.model_executor.models.module_mapping import MultiModelKeys
@@ -265,22 +266,16 @@ class Gemma4UnifiedForConditionalGeneration(Gemma4ForConditionalGeneration):
266 self.audio_tower = None
267
268 # ---- Encoder-free vision embedder ----
268 - self.vision_embedder = (
269 - Gemma4UnifiedVisionEmbedder(
270 - config.vision_config,
271 - quant_config=quant_config,
272 - prefix=maybe_prefix(prefix, "vision_embedder"),
273 - )
274 - if config.vision_config is not None
275 - else None
269 + if config.vision_config is None:
270 + raise ValueError("Gemma4 Unified requires a vision configuration")
271 + self.vision_embedder = Gemma4UnifiedVisionEmbedder(
272 + config.vision_config,
273 + quant_config=quant_config,
274 + prefix=maybe_prefix(prefix, "vision_embedder"),
275 )
277 - self.embed_vision = (
278 - Gemma4MultimodalEmbedder(
279 - config.vision_config,
280 - config.text_config,
281 - )
282 - if config.vision_config is not None
283 - else None
276 + self.embed_vision = Gemma4MultimodalEmbedder(
277 + config.vision_config,
278 + config.text_config,
279 )
280
281 # ---- Encoder-free audio embedder ----
@@ -382,7 +377,7 @@ class Gemma4UnifiedForConditionalGeneration(Gemma4ForConditionalGeneration):
377
378 def _process_video_input(
379 self,
385 - video_input: dict[str, torch.Tensor],
380 + video_input: Gemma4VideoInputs,
381 ) -> list[torch.Tensor]:
382 """Project video frames to LM space, one frame at a time.
383
@@ -429,6 +424,7 @@ class Gemma4UnifiedForConditionalGeneration(Gemma4ForConditionalGeneration):
424 audio_input["input_features_mask"],
425 )
426
427 + assert self.embed_audio is not None
428 target_dtype = self.embed_audio.embedding_projection.weight.dtype
429 audio_features = self.embed_audio(input_features.to(target_dtype))
430 per_audio: list[torch.Tensor] = []
vllm/model_executor/models/glm4_1v.py
+43 -15
@@ -1393,9 +1393,15 @@ class Glm4vProcessingInfo(BaseProcessingInfo):
1393 return list(timestamps_list)
1394
1395 def _get_video_frame_embed_token_id(self, hf_processor: object) -> int:
1396 - if isinstance(hf_processor, Glm4vProcessor) or TRANSFORMERS_WITH_GA:
1397 - return hf_processor.image_token_id
1398 - return hf_processor.video_token_id
1396 + attr = (
1397 + "image_token_id"
1398 + if isinstance(hf_processor, Glm4vProcessor) or TRANSFORMERS_WITH_GA
1399 + else "video_token_id"
1400 + )
1401 + token_id = getattr(hf_processor, attr, None)
1402 + if not isinstance(token_id, int):
1403 + raise ValueError(f"Processor has no valid {attr}")
1404 + return token_id
1405
1406 def _construct_video_placeholder(
1407 self,
@@ -1479,6 +1485,7 @@ class Glm4vDummyInputsBuilder(BaseDummyInputsBuilder[Glm4vProcessingInfo]):
1485
1486 image_overrides = mm_options.get("image")
1487 video_overrides = mm_options.get("video")
1488 + assert video_overrides is None or isinstance(video_overrides, VideoDummyOptions)
1489
1490 return {
1491 "image": self._get_dummy_images(
@@ -1519,7 +1526,7 @@ class Glm4vDummyInputsBuilder(BaseDummyInputsBuilder[Glm4vProcessingInfo]):
1526 )
1527 videos = [v.copy() for v in videos]
1528
1522 - video_items = []
1529 + video_items: list[VideoItem] = []
1530 for video in videos:
1531 video_num_frames = video.shape[0]
1532 video_metadata = {
@@ -1611,16 +1618,14 @@ class Glm4vMultiModalProcessor(BaseMultiModalProcessor[Glm4vProcessingInfo]):
1618 processed_data.update(passthrough_data)
1619 return processed_data
1620
1614 - if (
1615 - "videos" in mm_data
1616 - and isinstance(mm_data["videos"], list)
1617 - and len(mm_data["videos"]) > 0
1618 - ):
1621 + videos = mm_data.get("videos")
1622 + if isinstance(videos, list) and videos:
1623 video_grid_thw_lst = []
1624 pixel_values_videos_lst = []
1625 frame_embed_token_id = self.info._get_video_frame_embed_token_id(processor)
1626 swap_video_frame_tokens = frame_embed_token_id == processor.image_token_id
1623 - for item in mm_data.pop("videos", []):
1627 + mm_data.pop("videos")
1628 + for item in videos:
1629 video_array, metadata = item
1630
1631 # don't update mm_kwargs inplace
@@ -1794,6 +1799,7 @@ class Glm4vForConditionalGeneration(
1799 self.config = config
1800 self.model_config = vllm_config.model_config
1801 self.multimodal_config = multimodal_config
1802 + assert multimodal_config is not None
1803 self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
1804 self.is_multimodal_pruning_enabled = (
1805 multimodal_config.is_multimodal_pruning_enabled()
@@ -1850,6 +1856,7 @@ class Glm4vForConditionalGeneration(
1856 image_embeds=image_embeds,
1857 image_grid_thw=image_grid_thw,
1858 )
1859 + raise AssertionError("image input must contain pixels or embeddings")
1860
1861 def _parse_and_validate_video_input(
1862 self, **kwargs: object
@@ -1874,6 +1881,7 @@ class Glm4vForConditionalGeneration(
1881 video_embeds=video_embeds,
1882 video_grid_thw=video_grid_thw,
1883 )
1884 + raise AssertionError("video input must contain pixels or embeddings")
1885
1886 def _process_image_input(
1887 self, image_input: Glm4vImageInputs
@@ -1996,12 +2004,12 @@ class Glm4vForConditionalGeneration(
2004 def _get_grid_thw_by_modality(
2005 self,
2006 mm_kwargs: dict[str, Any],
1999 - ) -> list[tuple[int, int, int]]:
2007 + ) -> list[list[int]]:
2008 grid_thw_key = f"{self.get_input_modality(mm_kwargs)}_grid_thw"
2009 grid_thw = mm_kwargs[grid_thw_key]
2010 if not isinstance(grid_thw, list):
2011 grid_thw = grid_thw.tolist()
2004 - return grid_thw
2012 + return [list(row) for row in grid_thw]
2013
2014 def get_encoder_cudagraph_item_specs(
2015 self,
@@ -2188,7 +2196,7 @@ class Glm4vForConditionalGeneration(
2196 return self.visual(pixel_values, grid_thw)
2197
2198 def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
2191 - mm_input_by_modality = {}
2199 + mm_input_by_modality: dict[str, Glm4vImageInputs | Glm4vVideoInputs | None] = {}
2200
2201 # Preserve the order of modalities if there are multiple of them
2202 # from the order of kwargs.
@@ -2223,9 +2231,17 @@ class Glm4vForConditionalGeneration(
2231 for modality in mm_input_by_modality:
2232 multimodal_input = mm_input_by_modality[modality]
2233 if modality == "image":
2234 + assert isinstance(
2235 + multimodal_input,
2236 + (Glm4vImagePixelInputs, Glm4vImageEmbeddingInputs),
2237 + )
2238 image_embeddings = self._process_image_input(multimodal_input)
2239 multimodal_embeddings += tuple(image_embeddings)
2240 if modality == "video":
2241 + assert isinstance(
2242 + multimodal_input,
2243 + (Glm4vVideoPixelInputs, Glm4vVideoEmbeddingInputs),
2244 + )
2245 video_embeddings = self._process_video_input(multimodal_input)
2246 multimodal_embeddings += tuple(video_embeddings)
2247 return multimodal_embeddings
@@ -2238,14 +2254,26 @@ class Glm4vForConditionalGeneration(
2254 for mm_feature in sorted(mm_features, key=lambda f: f.mm_position.offset):
2255 embed_ranges = mm_feature.mm_position.extract_embeds_range()
2256 if mm_feature.modality == "image":
2241 - t, h, w = mm_feature.data["image_grid_thw"].data.tolist()
2257 + feature_data = mm_feature.data
2258 + assert feature_data is not None
2259 + grid_item = feature_data.get("image_grid_thw")
2260 + assert grid_item is not None
2261 + grid_data = grid_item.data
2262 + assert isinstance(grid_data, torch.Tensor)
2263 + t, h, w = grid_data.tolist()
2264 assert t == 1, f"Image must have 1 frame, got {t}"
2265 assert len(embed_ranges) == 1
2266 offset, end = embed_ranges[0]
2267 assert end - offset + 1 == h * w // spatial_merge_size**2
2268 yield offset, t, h // spatial_merge_size, w // spatial_merge_size
2269 elif mm_feature.modality == "video":
2248 - t, h, w = mm_feature.data["video_grid_thw"].data.tolist()
2270 + feature_data = mm_feature.data
2271 + assert feature_data is not None
2272 + grid_item = feature_data.get("video_grid_thw")
2273 + assert grid_item is not None
2274 + grid_data = grid_item.data
2275 + assert isinstance(grid_data, torch.Tensor)
2276 + t, h, w = grid_data.tolist()
2277 llm_grid_h = h // spatial_merge_size
2278 llm_grid_w = w // spatial_merge_size
2279 num_tokens_per_frame = llm_grid_h * llm_grid_w
vllm/model_executor/models/glm4_moe.py
+2
@@ -502,6 +502,8 @@ class Glm4MoeModel(nn.Module):
502
503
504 class Glm4MixtureOfExperts(MixtureOfExperts):
505 + moe_mlp_layers: list[Glm4MoE]
506 +
507 def extract_moe_parameters(self, example_moe: Glm4MoE | None) -> None:
508 if example_moe is None:
509 raise RuntimeError("No Glm4MoE layer found in model.layers.")
vllm/model_executor/models/glm4_moe_lite.py
+10 -10
@@ -74,7 +74,6 @@ from .utils import (
74 PPMissingLayer,
75 get_spec_layer_idx_from_weight_name,
76 is_pp_missing_parameter,
77 - make_empty_intermediate_tensors_factory,
77 make_layers,
78 maybe_prefix,
79 )
@@ -132,6 +131,7 @@ class Glm4MoeLiteDecoderLayer(nn.Module):
131 v_head_dim = getattr(config, "v_head_dim", 0)
132 kv_lora_rank = getattr(config, "kv_lora_rank", 0)
133
134 + attn_cls: Callable[..., Glm4MoeLiteMLAAttention | Glm4MoeLiteAttention]
135 if model_config.use_mla:
136 attn_cls = Glm4MoeLiteMLAAttention
137 else:
@@ -145,7 +145,7 @@ class Glm4MoeLiteDecoderLayer(nn.Module):
145 qk_nope_head_dim=qk_nope_head_dim,
146 qk_rope_head_dim=qk_rope_head_dim,
147 v_head_dim=v_head_dim,
148 - q_lora_rank=config.q_lora_rank if hasattr(config, "q_lora_rank") else None,
148 + q_lora_rank=getattr(config, "q_lora_rank", None),
149 kv_lora_rank=kv_lora_rank,
150 max_position_embeddings=max_position_embeddings,
151 cache_config=cache_config,
@@ -266,9 +266,6 @@ class Glm4MoeLiteModel(nn.Module):
266 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
267 else:
268 self.norm = PPMissingLayer()
269 - self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
270 - ["hidden_states", "residual"], config.hidden_size
271 - )
269
270 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
271 return self.embed_tokens(input_ids)
@@ -454,7 +451,9 @@ class Glm4MoeLiteModel(nn.Module):
451 # param and delegate to its expert-aware weight_loader
452 # with expert_id.
453 for mapping in expert_params_mapping:
457 - param_name, weight_name, expert_id, shard_id = mapping
454 + param_name, weight_name, mapping_expert_id, mapping_shard_id = (
455 + mapping
456 + )
457 if weight_name not in chunk_name:
458 continue
459
@@ -480,8 +479,8 @@ class Glm4MoeLiteModel(nn.Module):
479 param,
480 weight_to_load,
481 name_mapped,
483 - shard_id=shard_id,
484 - expert_id=expert_id,
482 + shard_id=mapping_shard_id,
483 + expert_id=mapping_expert_id,
484 return_success=True,
485 )
486 if success:
@@ -502,9 +501,10 @@ class Glm4MoeLiteModel(nn.Module):
501 continue
502
503 # Remapping the name of FP8 kv-scale.
505 - name = maybe_remap_kv_scale_name(name, params_dict)
506 - if name is None:
504 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
505 + if remapped_name is None:
506 continue
507 + name = remapped_name
508
509 if is_pp_missing_parameter(name, self):
510 continue
vllm/model_executor/models/glm4_moe_lite_mtp.py
+12 -7
@@ -85,7 +85,9 @@ class Glm4MoeLiteMultiTokenPredictorLayer(nn.Module):
85 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
86 super().__init__()
87
88 - config = vllm_config.speculative_config.draft_model_config.hf_config
88 + speculative_config = vllm_config.speculative_config
89 + assert speculative_config is not None
90 + config = speculative_config.draft_model_config.hf_config
91 self.config = config
92 quant_config = vllm_config.quant_config
93
@@ -235,7 +237,7 @@ class Glm4MoeLiteMTP(nn.Module, SupportsPP, Glm4MixtureOfExperts):
237 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
238 return self.model.embed_input_ids(input_ids)
239
238 - def forward(
240 + def forward( # type: ignore[override]
241 self,
242 input_ids: torch.Tensor | None,
243 positions: torch.Tensor,
@@ -370,7 +372,9 @@ class Glm4MoeLiteMTP(nn.Module, SupportsPP, Glm4MixtureOfExperts):
372 # with expert_id.
373 is_expert_weight = False
374 for mapping in expert_params_mapping:
373 - param_name, weight_name, expert_id, shard_id = mapping
375 + param_name, weight_name, mapping_expert_id, mapping_shard_id = (
376 + mapping
377 + )
378 if weight_name not in chunk_name:
379 continue
380
@@ -393,8 +397,8 @@ class Glm4MoeLiteMTP(nn.Module, SupportsPP, Glm4MixtureOfExperts):
397 param,
398 weight_to_load,
399 name_mapped,
396 - shard_id=shard_id,
397 - expert_id=expert_id,
400 + shard_id=mapping_shard_id,
401 + expert_id=mapping_expert_id,
402 return_success=True,
403 )
404 if success:
@@ -414,9 +418,10 @@ class Glm4MoeLiteMTP(nn.Module, SupportsPP, Glm4MixtureOfExperts):
418 if name.endswith(".bias") and name not in params_dict:
419 continue
420
417 - name = maybe_remap_kv_scale_name(name, params_dict)
418 - if name is None:
421 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
422 + if remapped_name is None:
423 continue
424 + name = remapped_name
425
426 # According to DeepSeek-V3 Technical Report, MTP modules
427 # shares embedding layer. We only load the first weights.
vllm/model_executor/models/glm4_moe_mtp.py
+1
@@ -93,6 +93,7 @@ class Glm4MoeMultiTokenPredictorLayer(nn.Module):
93 self.shared_head = SharedHead(
94 config=config, prefix=prefix, quant_config=quant_config
95 )
96 + assert parallel_config is not None
97 self.enable_eplb = parallel_config.enable_eplb
98 self.mtp_block = Glm4MoeDecoderLayer(
99 config=config,
vllm/model_executor/models/glm4v.py
+19 -2
@@ -582,7 +582,13 @@ class GLM4VForCausalLM(
582 for mm_feature in sorted(mm_features, key=lambda f: f.mm_position.offset):
583 offset = mm_feature.mm_position.offset
584 if mm_feature.modality == "image":
585 - t, h, w = mm_feature.data["image_grid_thw"].data.tolist()
585 + feature_data = mm_feature.data
586 + assert feature_data is not None
587 + grid_item = feature_data.get("image_grid_thw")
588 + assert grid_item is not None
589 + grid_data = grid_item.data
590 + assert isinstance(grid_data, torch.Tensor)
591 + t, h, w = grid_data.tolist()
592 assert t == 1, f"Image must have 1 frame, got {t}"
593 yield offset, t, h // spatial_merge_size, w // spatial_merge_size
594 else:
@@ -625,7 +631,18 @@ class GLM4VForCausalLM(
631 mrope_position_delta = (llm_positions.max() + 1 - len(input_tokens)).item()
632 return torch.from_numpy(llm_positions), mrope_position_delta
633
628 - embed_input_ids = SupportsMultiModal.embed_input_ids
634 + def embed_input_ids(
635 + self,
636 + input_ids: torch.Tensor,
637 + multimodal_embeddings: MultiModalEmbeddings | None = None,
638 + is_multimodal: torch.Tensor | None = None,
639 + ) -> torch.Tensor:
640 + return SupportsMultiModal.embed_input_ids(
641 + self,
642 + input_ids,
643 + multimodal_embeddings,
644 + is_multimodal=is_multimodal,
645 + )
646
647 def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
648 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/glm_ocr.py
+2
@@ -326,7 +326,9 @@ class GlmOcrVisionTransformer(Glm4vVisionTransformer):
326 self,
327 x: torch.Tensor,
328 grid_thw: torch.Tensor | list[list[int]],
329 + encoder_metadata: dict[str, torch.Tensor] | None = None,
330 ) -> torch.Tensor:
331 + del encoder_metadata
332 if isinstance(grid_thw, list):
333 grid_thw = torch.tensor(grid_thw, dtype=torch.int32)
334
vllm/model_executor/models/glm_ocr_mtp.py
+7 -4
@@ -58,7 +58,9 @@ class GlmOcrMultiTokenPredictorLayer(nn.Module):
58 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
59 nn.Module.__init__(self)
60
61 - config = vllm_config.speculative_config.draft_model_config.hf_config.text_config
61 + speculative_config = vllm_config.speculative_config
62 + assert speculative_config is not None
63 + config = speculative_config.draft_model_config.hf_config.text_config
64 self.config = config
65 quant_config = vllm_config.quant_config
66
@@ -144,7 +146,7 @@ class GlmOcrMTP(nn.Module, SupportsPP):
146 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
147 return self.model.embed_input_ids(input_ids)
148
147 - def forward(
149 + def forward( # type: ignore[override]
150 self,
151 input_ids: torch.Tensor,
152 positions: torch.Tensor,
@@ -195,9 +197,10 @@ class GlmOcrMTP(nn.Module, SupportsPP):
197
198 if "scale" in name or "zero_point" in name:
199 # Remapping the name of FP8 kv-scale or zero point.
198 - name = maybe_remap_kv_scale_name(name, params_dict)
199 - if name is None:
200 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
201 + if remapped_name is None:
202 continue
203 + name = remapped_name
204
205 for param_name, weight_name, shard_id in stacked_params_mapping:
206 if weight_name not in name:
vllm/model_executor/models/glmasr.py
+15 -4
@@ -11,7 +11,7 @@ from transformers.models.glmasr import GlmAsrConfig, GlmAsrProcessor
11 from transformers.models.whisper import WhisperFeatureExtractor
12
13 from vllm.config import ModelConfig, SpeechToTextConfig, VllmConfig
14 -from vllm.config.multimodal import BaseDummyOptions
14 +from vllm.config.multimodal import AudioDummyOptions, BaseDummyOptions
15 from vllm.config.speech_to_text import SpeechToTextParams
16 from vllm.distributed.parallel_state import get_tensor_model_parallel_world_size
17 from vllm.inputs import ModalityData, MultiModalDataDict, PromptType, TokensPrompt
@@ -710,6 +710,7 @@ class GlmAsrDummyInputsBuilder(BaseDummyInputsBuilder[GlmAsrProcessingInfo]):
710 sampling_rate = feature_extractor.sampling_rate
711 num_audios = mm_counts.get("audio", 0)
712 audio_overrides = mm_options.get("audio")
713 + assert audio_overrides is None or isinstance(audio_overrides, AudioDummyOptions)
714
715 max_audio_len = getattr(
716 self.info.get_hf_processor(), "max_audio_len", DEFAULT_MAX_AUDIO_LEN_S
@@ -793,8 +794,13 @@ class GlmAsrMultiModalProcessor(BaseMultiModalProcessor["GlmAsrProcessingInfo"])
794 )
795 processed_data["feature_attention_mask"] = mask
796
796 - audio = mm_data.get("audio", [])
797 - audio_list = [audio] if audio and not isinstance(audio, list) else audio
797 + audio = mm_data.get("audio")
798 + if audio is None:
799 + audio_list: list[Any] = []
800 + elif isinstance(audio, list):
801 + audio_list = audio
802 + else:
803 + audio_list = [audio]
804 if audio_list:
805 processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
806
@@ -878,6 +884,7 @@ class GlmAsrMultiModalProcessor(BaseMultiModalProcessor["GlmAsrProcessingInfo"])
884 audio_embeds = out_mm_data.get("audio_embeds")
885 if audio_embeds is not None:
886 embed = audio_embeds[item_idx]
887 + assert isinstance(embed, torch.Tensor)
888 num_features = embed.shape[0]
889 else:
890 raise ValueError(
@@ -1118,7 +1125,11 @@ class GlmAsrForConditionalGeneration(
1125 audio_token = cls._get_audio_token(model_config)
1126
1127 if task_type == "translate":
1121 - full_lang_name_to = cls.supported_languages.get(to_language, to_language)
1128 + full_lang_name_to = (
1129 + cls.supported_languages.get(to_language, to_language)
1130 + if to_language is not None
1131 + else ""
1132 + )
1133 user_content = f"{audio_token}translate the speech to {full_lang_name_to}"
1134 elif task_type == "transcribe":
1135 user_content = (
vllm/model_executor/models/gpt_j.py
+1
@@ -227,6 +227,7 @@ class GPTJModel(nn.Module):
227 else:
228 hidden_states = self.embed_input_ids(input_ids)
229 else:
230 + assert intermediate_tensors is not None
231 hidden_states = intermediate_tensors["hidden_states"]
232 for layer in islice(self.h, self.start_layer, self.end_layer):
233 hidden_states = layer(position_ids, hidden_states)
vllm/model_executor/models/gpt_neox.py
+1
@@ -244,6 +244,7 @@ class GPTNeoXModel(nn.Module):
244 else:
245 hidden_states = self.embed_input_ids(input_ids)
246 else:
247 + assert intermediate_tensors is not None
248 hidden_states = intermediate_tensors["hidden_states"]
249 for layer in islice(self.layers, self.start_layer, self.end_layer):
250 hidden_states = layer(position_ids, hidden_states)
vllm/model_executor/models/gpt_oss.py
+62 -21
@@ -79,6 +79,13 @@ _GPT_OSS_STREAMED_EXPERT_SUFFIX_TO_SHARD = {
79 }
80
81
82 +def _get_weight_loader(param: torch.Tensor) -> Callable[..., object]:
83 + weight_loader = getattr(param, "weight_loader", default_weight_loader)
84 + if not callable(weight_loader):
85 + raise TypeError("weight_loader must be callable")
86 + return weight_loader
87 +
88 +
89 class OAIAttention(nn.Module):
90 # Override to switch RoPE convention. gpt-oss uses NeoX (chunk halves);
91 # privacy-filter and similar derivatives use GPT-J (interleaved pairs).
@@ -273,6 +280,28 @@ class GptOssRoutedExperts(RoutedExperts):
280
281 self._copy_to_expert(expert_data, loaded_weight)
282
283 + @typing.overload
284 + def weight_loader(
285 + self,
286 + param: torch.nn.Parameter,
287 + loaded_weight: torch.Tensor,
288 + weight_name: str,
289 + shard_id: str,
290 + expert_id: int,
291 + return_success: typing.Literal[False],
292 + ) -> None: ...
293 +
294 + @typing.overload
295 + def weight_loader(
296 + self,
297 + param: torch.nn.Parameter,
298 + loaded_weight: torch.Tensor,
299 + weight_name: str,
300 + shard_id: str,
301 + expert_id: int,
302 + return_success: typing.Literal[True],
303 + ) -> bool: ...
304 +
305 def weight_loader(
306 self,
307 param: torch.nn.Parameter,
@@ -283,14 +312,24 @@ class GptOssRoutedExperts(RoutedExperts):
312 return_success: bool = False,
313 ) -> bool | None:
314 if shard_id not in ("gpt_oss_w13", "gpt_oss_w2"):
286 - return super().weight_loader(
315 + if return_success:
316 + return super().weight_loader(
317 + param,
318 + loaded_weight,
319 + weight_name,
320 + shard_id,
321 + expert_id,
322 + True,
323 + )
324 + super().weight_loader(
325 param,
326 loaded_weight,
327 weight_name,
328 shard_id,
329 expert_id,
292 - return_success,
330 + False,
331 )
332 + return None
333
334 expert_id = self._map_global_expert_id_to_local_expert_id(expert_id)
335 if expert_id == -1:
@@ -518,7 +557,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
557 heads_per_rank: int,
558 head_start: int,
559 weights: Iterable[tuple[str, torch.Tensor]],
521 - stacked_params_mapping: list[tuple[str, ...]],
560 + stacked_params_mapping: list[tuple[str, str, str]],
561 ) -> set[str]:
562 params_dict = dict(self.named_parameters())
563 loaded_params: set[str] = set()
@@ -564,7 +603,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
603 narrow_weight = weight[:, 2 * tp_rank_start : 2 * tp_rank_end, ...]
604
605 param = params_dict[name]
567 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
606 + weight_loader = _get_weight_loader(param)
607 weight_loader(
608 param,
609 narrow_weight,
@@ -586,7 +625,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
625 ]
626
627 param = params_dict[name]
589 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
628 + weight_loader = _get_weight_loader(param)
629 weight_loader(
630 param,
631 narrow_weight,
@@ -612,7 +651,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
651 narrow_weight = weight[:, 2 * tp_rank_start : 2 * tp_rank_end, ...]
652
653 param = params_dict[name]
615 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
654 + weight_loader = _get_weight_loader(param)
655 weight_loader(
656 param,
657 narrow_weight,
@@ -635,7 +674,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
674 narrow_weight = weight[..., tp_rank_start // 2 : tp_rank_end // 2]
675
676 param = params_dict[name]
638 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
677 + weight_loader = _get_weight_loader(param)
678 weight_loader(
679 param,
680 narrow_weight,
@@ -654,7 +693,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
693 narrow_weight = weight[:, 2 * tp_rank_start : 2 * tp_rank_end]
694
695 param = params_dict[name]
657 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
696 + weight_loader = _get_weight_loader(param)
697 weight_loader(
698 param,
699 narrow_weight,
@@ -667,7 +706,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
706 elif ".w2_bias" in name:
707 # Handle MLP down projection bias
708 param = params_dict[name]
670 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
709 + weight_loader = _get_weight_loader(param)
710 if use_ep:
711 weight = weight[ep_rank_start:ep_rank_end, ...]
712 else:
@@ -691,7 +730,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
730 continue
731 name = name.replace(weight_name, param_name)
732 param = params_dict[name]
694 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
733 + weight_loader = _get_weight_loader(param)
734 if weight_loader == default_weight_loader:
735 weight_loader(param, weight)
736 else:
@@ -702,7 +741,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
741 if name not in params_dict:
742 continue
743 param = params_dict[name]
705 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
744 + weight_loader = _get_weight_loader(param)
745 weight_loader(param, weight)
746 loaded_params.add(name)
747 return loaded_params
@@ -714,7 +753,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
753 heads_per_rank: int,
754 head_start: int,
755 weights: Iterable[tuple[str, torch.Tensor]],
717 - stacked_params_mapping: list[tuple[str, ...]],
756 + stacked_params_mapping: list[tuple[str, str, str]],
757 ) -> set[str]:
758 params_dict = dict(self.named_parameters())
759 loaded_params: set[str] = set()
@@ -820,6 +859,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
859 and expert_id is not None
860 ):
861 assert loaded_weight.numel() == 1
862 + assert fused_name is not None
863 expert_data = params_dict[fused_name].data[expert_id]
864 expert_data.copy_(loaded_weight)
865 loaded_params.add(fused_name)
@@ -901,6 +941,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
941 # fused gate_up_proj fused on disk, we cannot use the existing
942 # weight loaders without added complexity, so just do the
943 # direct load here.
944 + assert fused_name is not None
945 param = params_dict[fused_name]
946 expert_data = param.data[expert_id]
947 dim1 = sliced_weight.shape[0]
@@ -1062,9 +1103,10 @@ class GptOssModel(nn.Module, EagleModelMixin):
1103
1104 if name.endswith("scale"):
1105 # Remapping the name of FP8 kv-scale.
1065 - name = maybe_remap_kv_scale_name(name, params_dict)
1066 - if name is None:
1106 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
1107 + if remapped_name is None:
1108 continue
1109 + name = remapped_name
1110
1111 param = params_dict[name]
1112 weight_loader = param.weight_loader
@@ -1084,6 +1126,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
1126 if weight_name not in name:
1127 continue
1128
1129 + assert fused_name is not None
1130 param = params_dict[fused_name]
1131 # We should ask the weight loader to return success or not
1132 # here since otherwise we may skip experts with other
@@ -1112,9 +1155,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
1155 if name not in params_dict:
1156 continue
1157 param = params_dict[name]
1115 - weight_loader = getattr(
1116 - param, "weight_loader", default_weight_loader
1117 - )
1158 + weight_loader = _get_weight_loader(param)
1159 weight_loader(param, loaded_weight)
1160
1161 loaded_params.add(name)
@@ -1127,7 +1168,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
1168 heads_per_rank: int,
1169 head_start: int,
1170 weights: Iterable[tuple[str, torch.Tensor]],
1130 - stacked_params_mapping: list[tuple[str, ...]],
1171 + stacked_params_mapping: list[tuple[str, str, str]],
1172 ) -> set[str]:
1173 params_dict = dict(self.named_parameters())
1174 loaded_params: set[str] = set()
@@ -1224,7 +1265,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
1265 continue
1266 name = name.replace(weight_name, param_name)
1267 param = params_dict[name]
1227 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
1268 + weight_loader = _get_weight_loader(param)
1269 if weight_loader == default_weight_loader:
1270 weight_loader(param, weight)
1271 else:
@@ -1235,7 +1276,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
1276 if name not in params_dict:
1277 continue
1278 param = params_dict[name]
1238 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
1279 + weight_loader = _get_weight_loader(param)
1280 weight_loader(param, weight)
1281 loaded_params.add(name)
1282 return loaded_params
@@ -1367,7 +1408,7 @@ class GptOssModel(nn.Module, EagleModelMixin):
1408 class GptOssForCausalLM(
1409 nn.Module, SupportsPP, SupportsEagle, SupportsEagle3, SupportsLoRA
1410 ):
1370 - is_3d_moe_weight: bool = True
1411 + is_3d_moe_weight: typing.ClassVar[bool] = True
1412 packed_modules_mapping = {"qkv_proj": ["q_proj", "k_proj", "v_proj"]}
1413
1414 hf_to_vllm_mapper = WeightsMapper(
vllm/model_executor/models/granite4_vision.py
+2
@@ -130,6 +130,7 @@ class WindowQFormerDownsampler(nn.Module):
130
131 self.dropout = nn.Dropout(config.projector_dropout)
132
133 + self.downsampler: SpatialOffsetDownsampler | InterpolateDownsampler
134 if spatial_offset is not None:
135 self.downsampler = SpatialOffsetDownsampler(config, offset=spatial_offset)
136 else:
@@ -843,6 +844,7 @@ class Granite4VisionForConditionalGeneration(
844 # (possibly a chunk slice from the framework's encoder cache).
845 # Concatenate along token dim → (total_mm_tokens, lm_h * num_levels).
846 N, lm_h = inputs_embeds.shape
847 + assert multimodal_embeddings is not None
848 all_packed = torch.cat(
849 [t.to(dtype=inputs_embeds.dtype) for t in multimodal_embeddings],
850 dim=0,
vllm/model_executor/models/granite_speech.py
+13 -3
@@ -34,7 +34,7 @@ from torch import nn
34 from transformers import BatchFeature, PretrainedConfig
35
36 from vllm.config import CacheConfig, ModelConfig, SpeechToTextConfig, VllmConfig
37 -from vllm.config.multimodal import BaseDummyOptions
37 +from vllm.config.multimodal import AudioDummyOptions, BaseDummyOptions
38 from vllm.config.speech_to_text import SpeechToTextParams
39 from vllm.inputs import MultiModalDataDict, PromptType, TokensPrompt
40 from vllm.model_executor.layers.linear import ColumnParallelLinear, RowParallelLinear
@@ -166,7 +166,9 @@ class GraniteSpeechMultiModalProcessor(
166 def get_replacement(item_idx: int):
167 audios = mm_items.get_items("audio", AudioProcessorItems)
168 audio = audios.get(item_idx)
169 - audio_length = audio.shape[-1]
169 + if audio is None:
170 + raise ValueError(f"Missing audio item {item_idx}")
171 + audio_length = len(audio) if isinstance(audio, list) else audio.shape[-1]
172 num_projector_features = feature_extractor._get_num_audio_features(
173 [audio_length]
174 )[0]
@@ -225,6 +227,7 @@ class GraniteSpeechDummyInputsBuilder(
227 ) -> MultiModalDataDict:
228 num_audios = mm_counts.get("audio", 0)
229 audio_overrides = mm_options.get("audio")
230 + assert audio_overrides is None or isinstance(audio_overrides, AudioDummyOptions)
231
232 return {
233 "audio": self._get_dummy_audios(
@@ -659,6 +662,9 @@ class GraniteSpeechForConditionalGeneration(
662 if input_features is None:
663 return None
664
665 + if not isinstance(audio_embed_sizes, torch.Tensor):
666 + raise ValueError("audio_embed_sizes must be a tensor")
667 +
668 # If we have a batch of variable feature length audio clips, we need
669 # to mask the features; usually we would get an input_features_mask
670 # from the processor, but we handle rebuilding it here since
@@ -879,7 +885,11 @@ class GraniteSpeechForConditionalGeneration(
885 audio_tok = cls.get_placeholder_str("audio", 0)
886
887 if task_type == "translate":
882 - full_lang_name_to = cls.supported_languages.get(to_language, to_language)
888 + full_lang_name_to = (
889 + cls.supported_languages.get(to_language, to_language)
890 + if to_language is not None
891 + else ""
892 + )
893 user_prompt = f"{audio_tok}translate the speech to {full_lang_name_to}" # noqa: E501
894 elif task_type == "transcribe":
895 user_prompt = (
vllm/model_executor/models/granitemoehybrid.py
+7 -2
@@ -270,6 +270,7 @@ class GraniteMoeHybridAttention(nn.Module):
270 prefix=f"{prefix}.o_proj",
271 )
272
273 + self.rotary_emb: nn.Module | None
274 if config.position_embedding_type == "rope":
275 self.rotary_emb = get_rope(
276 self.head_dim,
@@ -454,7 +455,9 @@ class GraniteMoeHybridModel(nn.Module):
455 # Skip layers on other devices.
456 if not is_pp_missing_parameter(n, self):
457 param = params_dict[n]
457 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
458 + weight_loader = getattr(param, "weight_loader", None)
459 + if not callable(weight_loader):
460 + raise TypeError(f"{n} requires a shard-aware weight loader")
461 weight_loader(param, p, shard_id)
462 loaded_params.add(n)
463
@@ -462,7 +465,9 @@ class GraniteMoeHybridModel(nn.Module):
465 if n not in params_dict:
466 return
467 param = params_dict[n]
465 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
468 + weight_loader = getattr(param, "weight_loader", None)
469 + if not callable(weight_loader):
470 + raise TypeError(f"{n} requires an expert-aware weight loader")
471 weight_loader(param, p, name, shard_id=shard_id, expert_id=expert_id)
472 loaded_params.add(n)
473