[Mypy] Fix mypy typing for N/O models (#54142)

Signed-off-by: Taneem Ibrahim <taneem.ibrahim@gmail.com>

Taneem Ibrahim committed Sep 18, 2026 at 06:50 UTC 2b8a5495a88a9a44a9d92e8c4f4aae3b55b6b74b
19 files changed +321 -143
tools/pre_commit/mypy.py
-2
@@ -113,8 +113,6 @@ SEPARATE_GROUPS = [
113
114 EXCLUDE = [
115 r"vllm/model_executor/models/[kK]",
116 - r"vllm/model_executor/models/[nN]",
117 - r"vllm/model_executor/models/[oO]",
116 r"vllm/model_executor/models/[qQ]",
117 r"vllm/model_executor/models/[uU]",
118 r"vllm/model_executor/models/[vV]",
vllm/model_executor/layers/mamba/mamba_utils.py
+8 -2
@@ -222,12 +222,18 @@ class MambaStateShapeCalculator:
222 @classmethod
223 def append_replayssm_ring(
224 cls,
225 - base_shapes: tuple[tuple[int, ...], ...],
225 + base_shapes: tuple[tuple[int, int], tuple[int, int, int]],
226 n_groups: int,
227 tp_world_size: int,
228 logical_window: int,
229 backend: MambaBackendEnum,
230 - ) -> tuple[tuple[int, ...], ...]:
230 + ) -> tuple[
231 + tuple[int, int],
232 + tuple[int, int, int],
233 + tuple[int, int, int],
234 + tuple[int, int],
235 + tuple[int, int, int],
236 + ]:
237 """Append the physical ReplaySSM ring shapes.
238
239 ``base_shapes[1]`` is ``(nheads // tp, head_dim, state_size)``;
vllm/model_executor/models/aimv2.py
+14 -5
@@ -26,7 +26,10 @@ from vllm.transformers_utils.configs.ovis import AIMv2Config
26
27 class AIMv2SwiGLUFFN(nn.Module):
28 def __init__(
29 - self, config: AIMv2Config, quant_config: QuantizationConfig, prefix: str
29 + self,
30 + config: AIMv2Config,
31 + quant_config: QuantizationConfig | None,
32 + prefix: str,
33 ):
34 super().__init__()
35 hidden_features = config.intermediate_size
@@ -91,7 +94,10 @@ class AIMv2ViTPreprocessor(nn.Module):
94
95 class AIMv2Attention(nn.Module):
96 def __init__(
94 - self, config: AIMv2Config, quant_config: QuantizationConfig, prefix: str
97 + self,
98 + config: AIMv2Config,
99 + quant_config: QuantizationConfig | None,
100 + prefix: str,
101 ):
102 super().__init__()
103 self.config = config
@@ -144,7 +150,10 @@ class AIMv2Attention(nn.Module):
150
151 class AIMv2Block(nn.Module):
152 def __init__(
147 - self, config: AIMv2Config, quant_config: QuantizationConfig, prefix: str
153 + self,
154 + config: AIMv2Config,
155 + quant_config: QuantizationConfig | None,
156 + prefix: str,
157 ):
158 super().__init__()
159 self.attn = AIMv2Attention(
@@ -166,7 +175,7 @@ class AIMv2Transformer(nn.Module):
175 def __init__(
176 self,
177 config: AIMv2Config,
169 - quant_config: QuantizationConfig,
178 + quant_config: QuantizationConfig | None,
179 *,
180 require_post_norm: bool | None = None,
181 prefix: str = "",
@@ -206,7 +215,7 @@ class AIMv2Model(torch.nn.Module):
215 def __init__(
216 self,
217 config: AIMv2Config,
209 - quant_config: QuantizationConfig,
218 + quant_config: QuantizationConfig | None,
219 *,
220 require_post_norm: bool | None = None,
221 prefix: str = "",
vllm/model_executor/models/interfaces.py
+7
@@ -91,6 +91,13 @@ MambaStateShapes: TypeAlias = (
91 tuple[int, int, int],
92 tuple[int, int, int],
93 ]
94 + | tuple[
95 + tuple[int, int],
96 + tuple[int, int, int],
97 + tuple[int, int, int],
98 + tuple[int, int],
99 + tuple[int, int, int],
100 + ]
101 )
102
103
vllm/model_executor/models/nano_nemotron_vl.py
+85 -27
@@ -12,7 +12,7 @@ import warnings
12 from collections.abc import Iterable, Mapping, Sequence
13 from functools import cached_property
14 from io import BytesIO
15 -from typing import Annotated, Literal, TypeAlias
15 +from typing import Annotated, Literal, TypeAlias, TypedDict, cast
16
17 import torch
18 import torch.nn as nn
@@ -20,7 +20,11 @@ from transformers import BatchFeature, PretrainedConfig
20
21 from vllm import envs
22 from vllm.config import VllmConfig
23 -from vllm.config.multimodal import BaseDummyOptions, VideoDummyOptions
23 +from vllm.config.multimodal import (
24 + AudioDummyOptions,
25 + BaseDummyOptions,
26 + VideoDummyOptions,
27 +)
28 from vllm.inputs import MultiModalDataDict, MultiModalInput
29 from vllm.logger import init_logger
30 from vllm.lora.layers.base import BaseLayerWithLoRA
@@ -114,7 +118,7 @@ class NanoNemotronVLAudioFeatureInputs(TensorSchema):
118 type: Literal["audio_features"] = "audio_features"
119 input_audio_features: Annotated[torch.Tensor, TensorShape("c", "t", "f")]
120 feature_attention_mask: Annotated[torch.Tensor, TensorShape("c", "t")]
117 - audio_num_clips: list[int]
121 + audio_num_clips: list[int] | torch.Tensor
122
123
124 class NanoNemotronVLImagePixelInputs(TensorSchema):
@@ -195,6 +199,12 @@ NanoNemotronVLVideoInputs: TypeAlias = (
199 )
200
201
202 +class NanoNemotronVLMultiModalInputs(TypedDict, total=False):
203 + images: NanoNemotronVLImageInputs | None
204 + videos: NanoNemotronVLVideoInputs | None
205 + audios: NanoNemotronVLAudioFeatureInputs | None
206 +
207 +
208 class NanoNemotronVLProcessingInfo(BaseProcessingInfo):
209 def get_hf_processor(self, **kwargs: object) -> NanoNemotronVLProcessor:
210 # Not accepted by NanoNemotronVLProcessor.__init__
@@ -465,8 +475,11 @@ class NanoNemotronVLMultiModalProcessor(
475 if isinstance(images, ImageEmbeddingItems):
476 feature_size = images.get_feature_size(item_idx)
477 elif self.info.is_dynamic_tiler:
468 - feature_size = out_mm_data["num_tokens_per_image"][item_idx]
478 + num_tokens_per_image = out_mm_data["num_tokens_per_image"]
479 + assert isinstance(num_tokens_per_image, list)
480 + feature_size = num_tokens_per_image[item_idx]
481 else:
482 + assert isinstance(images, ImageProcessorItems)
483 image_size = images.get_image_size(item_idx)
484 max_num_tiles = hf_processor.max_num_tiles
485 feature_size = hf_processor.get_num_image_tokens(
@@ -530,10 +543,12 @@ class NanoNemotronVLMultiModalProcessor(
543 assert isinstance(num_patches, int)
544
545 T = hf_processor.video_temporal_patch_size
546 + num_tubelets: int | None
547 if T > 1 and num_patches is not None:
548 num_tubelets = math.ceil(num_patches / T)
549 else:
550 num_tubelets = num_patches
551 + assert num_tubelets is not None
552
553 video_pruning_rate = self.info.ctx.get_mm_config().video_pruning_rate
554 if video_pruning_rate is not None and video_pruning_rate > 0.0:
@@ -635,6 +650,7 @@ class NanoNemotronVLMultiModalProcessor(
650 audio_items: list[AudioItem] = []
651 has_audio: list[bool] = []
652 for idx, metadata in enumerate(metadata_list):
653 + assert metadata is not None
654 video_bytes = metadata.get("original_video_bytes")
655 if video_bytes is None or len(video_bytes) == 0:
656 raise ValueError(
@@ -661,21 +677,25 @@ class NanoNemotronVLMultiModalProcessor(
677
678 # Create a new VideoProcessorItems with metadata that does not contain
679 # the large video bytes, to avoid modifying the input `mm_items`.
664 - new_metadata_list = [
665 - {k: v for k, v in meta.items() if k != "original_video_bytes"}
666 - for meta in metadata_list
667 - ]
680 + new_metadata_list = metadata_list.copy()
681 + for idx, metadata in enumerate(metadata_list):
682 + assert metadata is not None
683 + new_metadata_list[idx] = {
684 + key: value
685 + for key, value in metadata.items()
686 + if key != "original_video_bytes"
687 + }
688 new_videos = VideoProcessorItems(data=videos.data, metadata=new_metadata_list)
689
670 - audio_parsed = {}
690 + audio_parsed = MultiModalDataItems()
691 if audio_items:
692 audio_parsed = self.data_parser.parse_mm_data({"audio": audio_items})
693
694 # Create a new MultiModalDataItems with the new video and audio items.
695 new_mm_items_dict = {**mm_items, **audio_parsed, "video": new_videos}
676 - mm_items = MultiModalDataItems(new_mm_items_dict)
696 + new_mm_items = MultiModalDataItems(new_mm_items_dict)
697
678 - return mm_items, audio_items, has_audio
698 + return new_mm_items, audio_items, has_audio
699
700 def apply(
701 self,
@@ -800,7 +820,7 @@ class NanoNemotronVLDummyInputsBuilder(
820 )
821 videos = [v.copy() for v in videos]
822
803 - video_items = []
823 + video_items: list[VideoItem] = []
824 for video in videos:
825 video_num_frames = video.shape[0]
826 video_metadata = {
@@ -869,6 +889,9 @@ class NanoNemotronVLDummyInputsBuilder(
889 target_num_frames = num_frames
890 num_videos = mm_counts.get("video", 0)
891 video_overrides = mm_options.get("video")
892 + assert video_overrides is None or isinstance(
893 + video_overrides, VideoDummyOptions
894 + )
895 dummy_video = {
896 "video": self._get_dummy_videos(
897 width=video_width,
@@ -884,6 +907,9 @@ class NanoNemotronVLDummyInputsBuilder(
907 if sound_config := self.info.sound_config:
908 num_audios = mm_counts.get("audio", 0)
909 audio_overrides = mm_options.get("audio") if mm_options else None
910 + assert audio_overrides is None or isinstance(
911 + audio_overrides, AudioDummyOptions
912 + )
913 tokens_per_audio = max(1, seq_len // max(num_audios, 1))
914 max_audio_num_samples = MAX_AUDIO_LEN_S * sound_config.sampling_rate
915 calculated_max_audio_num_samples = ParakeetExtractor.audio_length(
@@ -946,6 +972,7 @@ class NemotronH_Nano_VL_V2(
972 model_config = vllm_config.model_config
973 config = model_config.hf_config
974 multimodal_config = model_config.multimodal_config
975 + assert multimodal_config is not None
976 image_size = config.force_image_size
977 patch_size = config.patch_size
978 self.patch_size = patch_size
@@ -1138,17 +1165,26 @@ class NemotronH_Nano_VL_V2(
1165 return None
1166
1167 if self.dynamic_resolution:
1168 + assert isinstance(pixel_values_flat, (list, torch.Tensor))
1169 pixel_values_flat = DynamicResolutionImageTiler.stack(
1170 pixel_values_flat, self.patch_size
1171 )
1172 + imgs_sizes = kwargs.pop("imgs_sizes")
1173 + num_tokens_per_image = kwargs.pop("num_tokens_per_image")
1174 + assert isinstance(imgs_sizes, list)
1175 + assert isinstance(num_tokens_per_image, list)
1176 return NanoNemotronVLImagePixelInputsDynamic(
1145 - pixel_values_flat=pixel_values_flat, **kwargs
1177 + pixel_values_flat=pixel_values_flat,
1178 + imgs_sizes=imgs_sizes,
1179 + num_tokens_per_image=num_tokens_per_image,
1180 )
1181 else:
1182 + assert isinstance(pixel_values_flat, torch.Tensor)
1183 + image_num_patches = kwargs.pop("image_num_patches")
1184 + assert isinstance(image_num_patches, torch.Tensor)
1185 return NanoNemotronVLImagePixelInputs(
1186 pixel_values_flat=pixel_values_flat,
1150 - num_patches=kwargs.pop("image_num_patches"),
1151 - **kwargs,
1187 + num_patches=image_num_patches,
1188 )
1189
1190 def _process_image_input_dynamic(
@@ -1166,7 +1202,8 @@ class NemotronH_Nano_VL_V2(
1202 return image_embeds.split(num_tokens_per_image)
1203
1204 def _process_image_input(
1169 - self, image_input: NanoNemotronVLImagePixelInputs
1205 + self,
1206 + image_input: NanoNemotronVLImagePixelInputs | NanoNemotronVLVideoPixelInputs,
1207 ) -> tuple[torch.Tensor, ...]:
1208 image_embeds = self.extract_feature(image_input["pixel_values_flat"])
1209 num_patches = image_input["num_patches"]
@@ -1362,7 +1399,8 @@ class NemotronH_Nano_VL_V2(
1399 # of them than max_num_batched_tokens.
1400 # Embed them with the base weights -
1401 # the LoRA delta is undefined for tokens with no mapping entry.
1365 - embed_tokens = self.get_language_model().model.embed_tokens
1402 + language_model = cast(NemotronHForCausalLM, self.get_language_model())
1403 + embed_tokens = language_model.model.embed_tokens
1404 if isinstance(embed_tokens, BaseLayerWithLoRA):
1405 embed_tokens = embed_tokens.base_layer
1406 text_embeddings = embed_tokens(repl_token_ids)
@@ -1388,26 +1426,29 @@ class NemotronH_Nano_VL_V2(
1426 return None
1427
1428 if video_embeds is not None:
1429 + assert isinstance(video_embeds, (torch.Tensor, list))
1430 return NanoNemotronVLVideoEmbeddingInputs(
1431 type="video_embeds",
1432 data=video_embeds,
1433 )
1434
1435 if pixel_values_flat_video is not None:
1397 - if torch.is_tensor(frames_indices):
1436 + if isinstance(frames_indices, torch.Tensor):
1437 frames_indices = frames_indices.flatten()
1438 else:
1439 + assert isinstance(frames_indices, (list, tuple))
1440 frames_indices = torch.cat([f.flatten() for f in frames_indices], dim=0)
1441
1402 - if torch.is_tensor(frame_duration_ms):
1442 + if isinstance(frame_duration_ms, torch.Tensor):
1443 frame_duration_ms = frame_duration_ms.flatten()
1444 else:
1445 + assert isinstance(frame_duration_ms, (list, tuple))
1446 frame_duration_ms = torch.cat(
1447 [f.flatten() for f in frame_duration_ms], dim=0
1448 )
1449
1450 if (
1410 - torch.is_tensor(pixel_values_flat_video)
1451 + isinstance(pixel_values_flat_video, torch.Tensor)
1452 and pixel_values_flat_video.ndim == 5
1453 ):
1454 # batched._reduce_data stacked same-shape videos into
@@ -1415,9 +1456,11 @@ class NemotronH_Nano_VL_V2(
1456 # same-H,W cat path below handles it uniformly.
1457 pixel_values_flat_video = list(pixel_values_flat_video)
1458
1418 - if not torch.is_tensor(pixel_values_flat_video):
1459 + if not isinstance(pixel_values_flat_video, torch.Tensor):
1460 + assert isinstance(pixel_values_flat_video, (list, tuple))
1461 pixel_values_flat_video = torch.cat(pixel_values_flat_video, dim=0)
1462
1463 + assert isinstance(video_num_patches, torch.Tensor)
1464 expected_h = pixel_values_flat_video.shape[-2]
1465 expected_w = pixel_values_flat_video.shape[-1]
1466 num_frames = video_num_patches[0].item()
@@ -1434,8 +1477,10 @@ class NemotronH_Nano_VL_V2(
1477
1478 raise AssertionError("This line should be unreachable.")
1479
1437 - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
1438 - modalities = {}
1480 + def _parse_and_validate_multimodal_inputs(
1481 + self, **kwargs: object
1482 + ) -> NanoNemotronVLMultiModalInputs:
1483 + modalities: NanoNemotronVLMultiModalInputs = {}
1484 # Preserve the order of modalities if there are multiple of them
1485 # from the order of kwargs.
1486 for input_key in kwargs:
@@ -1458,8 +1503,17 @@ class NemotronH_Nano_VL_V2(
1503 )
1504 and "audios" not in modalities
1505 ):
1506 + input_audio_features = kwargs.get("input_audio_features")
1507 + feature_attention_mask = kwargs.get("feature_attention_mask")
1508 + audio_num_clips = kwargs.get("audio_num_clips")
1509 + assert isinstance(input_audio_features, torch.Tensor)
1510 + assert isinstance(feature_attention_mask, torch.Tensor)
1511 + assert isinstance(audio_num_clips, (list, torch.Tensor))
1512 modalities["audios"] = NanoNemotronVLAudioFeatureInputs(
1462 - **kwargs, validate=False
1513 + input_audio_features=input_audio_features,
1514 + feature_attention_mask=feature_attention_mask,
1515 + audio_num_clips=audio_num_clips,
1516 + validate=False,
1517 )
1518
1519 return modalities
@@ -1479,23 +1533,26 @@ class NemotronH_Nano_VL_V2(
1533 for modality in modalities:
1534 if modality == "images":
1535 image_input = modalities["images"]
1482 - if image_input["type"] == "image_embeds":
1536 + assert image_input is not None
1537 + if isinstance(image_input, NanoNemotronVLImageEmbeddingInputs):
1538 image_embeddings = image_input["data"]
1484 - elif self.dynamic_resolution:
1485 - assert image_input["type"] == "pixel_values_dynamic"
1539 + elif isinstance(image_input, NanoNemotronVLImagePixelInputsDynamic):
1540 image_embeddings = self._process_image_input_dynamic(image_input)
1541 else:
1542 image_embeddings = self._process_image_input(image_input)
1543 multimodal_embeddings += tuple(image_embeddings)
1544 if modality == "videos":
1545 video_input = modalities["videos"]
1546 + assert video_input is not None
1547 if video_input["type"] == "video_embeds":
1548 video_embeddings = video_input["data"]
1549 else:
1550 + assert isinstance(video_input, NanoNemotronVLVideoPixelInputs)
1551 video_embeddings = self._process_video_input(video_input)
1552 multimodal_embeddings += tuple(video_embeddings)
1553 if modality == "audios":
1554 audio_input = modalities["audios"]
1555 + assert audio_input is not None
1556 audio_embeddings = self._process_audio_input(audio_input)
1557 multimodal_embeddings += tuple(audio_embeddings)
1558
@@ -1538,6 +1595,7 @@ class NemotronH_Nano_VL_V2(
1595
1596 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
1597 mm_config = self.model_config.multimodal_config
1598 + assert mm_config is not None
1599 load_multimodal_weights = not all(
1600 mm_config.get_limit_per_prompt(modality) == 0
1601 for modality in ("image", "video", "audio")
vllm/model_executor/models/nemotron_h.py
+7 -2
@@ -68,6 +68,7 @@ from vllm.model_executor.models.interfaces import (
68 EagleModelMixin,
69 HasInnerState,
70 IsHybrid,
71 + MambaStateShapes,
72 MixtureOfExperts,
73 SupportsEagle,
74 SupportsEagle3,
@@ -138,6 +139,7 @@ class NemotronHMoE(nn.Module):
139 prefix: str = "",
140 ):
141 super().__init__()
142 + assert parallel_config is not None
143 self.tp_size = get_tensor_model_parallel_world_size()
144 self.routed_scaling_factor = config.routed_scaling_factor
145
@@ -188,6 +190,8 @@ class NemotronHMoE(nn.Module):
190 prefix=f"{prefix}.shared_experts",
191 )
192
193 + self.fc1_latent_proj: ReplicatedLinear | None
194 + self.fc2_latent_proj: ReplicatedLinear | None
195 if self.use_latent_moe:
196 self.fc1_latent_proj = ReplicatedLinear(
197 input_size=config.hidden_size,
@@ -722,7 +726,7 @@ class NemotronHForCausalLM(
726 SupportsReplaySSM,
727 ):
728 # Relevant only if self.has_moe is True
725 - is_non_gated_moe: bool = True
729 + is_non_gated_moe = True
730
731 hf_to_vllm_mapper = WeightsMapper(
732 orig_to_new_prefix={"backbone": "model", "mtp": None},
@@ -773,7 +777,7 @@ class NemotronHForCausalLM(
777 def get_mamba_state_shape_from_config(
778 cls,
779 vllm_config: "VllmConfig",
776 - ) -> tuple[tuple[int, ...], ...]:
780 + ) -> MambaStateShapes:
781 """Calculate shapes for Mamba's convolutional and state caches.
782
783 Args:
@@ -858,6 +862,7 @@ class NemotronHForCausalLM(
862 self.moe_layers.append(layer.mixer.experts)
863
864 self.num_moe_layers = len(self.moe_layers)
865 + assert example_moe is not None
866 self.num_logical_experts = example_moe.n_logical_experts
867 self.num_physical_experts = example_moe.n_physical_experts
868 self.num_local_physical_experts = example_moe.n_local_physical_experts # noqa: E501
vllm/model_executor/models/nemotron_h_mtp.py
+16 -8
@@ -92,10 +92,12 @@ class NemotronHMTPAttentionDecoderLayer(NemotronHAttentionDecoderLayer):
92
93 def forward(
94 self,
95 - inputs_embeds: torch.Tensor,
95 positions: torch.Tensor,
96 hidden_states: torch.Tensor,
97 residual: torch.Tensor | None = None,
98 + *,
99 + inputs_embeds: torch.Tensor | None = None,
100 + **kwargs: object,
101 ) -> tuple[torch.Tensor, torch.Tensor | None]:
102 # Start projections (Fusion)
103 if self.has_start_projections:
@@ -179,10 +181,11 @@ class NemotronHMTPMoEDecoderLayer(NemotronHMoEDecoderLayer):
181
182 def forward(
183 self,
182 - inputs_embeds: torch.Tensor,
183 - positions: torch.Tensor,
184 hidden_states: torch.Tensor,
185 residual: torch.Tensor | None = None,
186 + *,
187 + inputs_embeds: torch.Tensor | None = None,
188 + **kwargs: object,
189 ) -> tuple[torch.Tensor, torch.Tensor | None]:
190 # Start projections (Fusion)
191 if self.has_start_projections:
@@ -305,13 +308,14 @@ class NemotronHMultiTokenPredictor(nn.Module):
308
309 def forward(
310 self,
308 - input_ids: torch.Tensor,
311 + input_ids: torch.Tensor | None,
312 positions: torch.Tensor,
313 hidden_states: torch.Tensor,
314 intermediate_tensors: IntermediateTensors | None = None,
315 inputs_embeds: torch.Tensor | None = None,
316 ) -> torch.Tensor | IntermediateTensors:
317 if inputs_embeds is None:
318 + assert input_ids is not None
319 inputs_embeds = self.get_input_embeddings(input_ids)
320
321 residual = None
@@ -386,6 +390,7 @@ class NemotronHMTP(nn.Module, SupportsPP, SupportsQuant):
390 @staticmethod
391 def _find_quant_config(*args, **kwargs) -> QuantizationConfig | None:
392 vllm_config = kwargs.get("vllm_config")
393 + assert isinstance(vllm_config, VllmConfig)
394 return get_draft_quant_config(vllm_config)
395
396 def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
@@ -393,14 +398,15 @@ class NemotronHMTP(nn.Module, SupportsPP, SupportsQuant):
398
399 def forward(
400 self,
396 - input_ids: torch.Tensor,
401 + input_ids: torch.Tensor | None,
402 positions: torch.Tensor,
398 - hidden_states: torch.Tensor,
403 + hidden_states: torch.Tensor | None = None,
404 intermediate_tensors: IntermediateTensors | None = None,
405 inputs_embeds: torch.Tensor | None = None,
406 **kwargs: object,
407 ) -> torch.Tensor:
408 """Forward - applies attention-based MTP."""
409 + assert hidden_states is not None
410 hidden_states = self.model(
411 input_ids,
412 positions,
@@ -473,9 +479,10 @@ class NemotronHMTP(nn.Module, SupportsPP, SupportsQuant):
479 name = name.replace("backbone.", "model.")
480
481 if "scale" in name or "zero_point" in name:
476 - name = maybe_remap_kv_scale_name(name, params_dict)
477 - if name is None:
482 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
483 + if remapped_name is None:
484 continue
485 + name = remapped_name
486
487 # Handle stacked parameters (qkv_proj) for attention layers
488 is_stacked = False
@@ -547,6 +554,7 @@ class NemotronHMTP(nn.Module, SupportsPP, SupportsQuant):
554
555 param = params_dict[name]
556 weight_loader = getattr(param, "weight_loader", default_weight_loader)
557 + assert weight_loader is not None
558 weight_loader(param, loaded_weight)
559 loaded_params.add(name)
560
vllm/model_executor/models/nemotron_vl.py
+14 -5
@@ -138,7 +138,7 @@ class LlamaNemotronVLChatModel(nn.Module, SupportsMultiModal, SupportsPP, Suppor
138 prefix=maybe_prefix(prefix, "language_model"),
139 )
140
141 - self.img_context_token_id = None
141 + self.img_context_token_id: int | None = None
142
143 self.visual_token_mask = None
144 self.make_empty_intermediate_tensors = (
@@ -146,7 +146,7 @@ class LlamaNemotronVLChatModel(nn.Module, SupportsMultiModal, SupportsPP, Suppor
146 )
147
148 def _patch_quant_config(
149 - self, config: PretrainedConfig, quant_config: QuantizationConfig
149 + self, config: PretrainedConfig, quant_config: QuantizationConfig | None
150 ):
151 # the awq models from OpenGVLab missing `modules_to_not_convert`
152 # patch the quant_config to add `modules_to_not_convert` back
@@ -518,12 +518,21 @@ class LlamaNemotronVLForEmbedding(LlamaNemotronVLChatModel, VllmModelForPooling)
518 use_head=False,
519 )
520
521 - def _init_mlp1(self, config: PretrainedConfig) -> nn.Module:
521 + def _init_mlp1(
522 + self,
523 + config: PretrainedConfig,
524 + vit_hidden_size: int | None = None,
525 + vision_projection_hidden_size: int | None = None,
526 + ) -> nn.Module:
527 """Override to use different MLP structure for embedding model."""
528 + if vit_hidden_size is None:
529 + vit_hidden_size = config.vision_config.hidden_size
530 + if vision_projection_hidden_size is None:
531 + vision_projection_hidden_size = config.get_text_config().hidden_size
532 return super()._init_mlp1(
533 config,
525 - vit_hidden_size=config.vision_config.hidden_size,
526 - vision_projection_hidden_size=config.get_text_config().hidden_size,
534 + vit_hidden_size=vit_hidden_size,
535 + vision_projection_hidden_size=vision_projection_hidden_size,
536 )
537
538 def _call_vision_model(self, pixel_values: torch.Tensor) -> torch.Tensor:
vllm/model_executor/models/nvlm_d.py
+1
@@ -128,6 +128,7 @@ class NVLMMultiModalProcessor(BaseInternVLMultiModalProcessor[NVLMProcessingInfo
128 if isinstance(images, ImageEmbeddingItems):
129 feature_size = images.get_feature_size(item_idx)
130 else:
131 + assert isinstance(images, ImageProcessorItems)
132 image_size = images.get_image_size(item_idx)
133 feature_size = self.info.get_num_image_tokens(
134 image_width=image_size.width,
vllm/model_executor/models/olmo_hybrid.py
+2
@@ -143,6 +143,7 @@ class OlmoHybridAttention(nn.Module):
143 rope_parameters["rope_theta"] is not None
144 )
145
146 + self.rotary_emb: nn.Module | None
147 if self._use_rope:
148 self.rotary_emb = get_rope(
149 self.head_dim,
@@ -183,6 +184,7 @@ class OlmoHybridAttention(nn.Module):
184 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
185 q, k = self._apply_qk_norm(q, k)
186 if self._use_rope:
187 + assert self.rotary_emb is not None
188 q, k = self.rotary_emb(positions, q, k)
189 attn_output = self.attn(q, k, v)
190 output, _ = self.o_proj(attn_output)
vllm/model_executor/models/opencua.py
+10 -2
@@ -7,6 +7,7 @@
7
8 """Inference-only OpenCUA-7B model compatible with HuggingFace weights."""
9
10 +import typing
11 from collections.abc import Mapping, Sequence
12 from typing import Any
13
@@ -27,6 +28,7 @@ from vllm.multimodal.inputs import (
28 )
29 from vllm.multimodal.parse import MultiModalDataItems
30 from vllm.multimodal.processing import (
31 + BaseDummyInputsBuilder,
32 BaseMultiModalProcessor,
33 PromptReplacement,
34 PromptUpdate,
@@ -178,7 +180,13 @@ class OpenCUAMultiModalProcessor(BaseMultiModalProcessor[OpenCUAProcessingInfo])
180 ]
181
182
181 -class OpenCUADummyInputsBuilder(Qwen2VLDummyInputsBuilder):
183 +if typing.TYPE_CHECKING:
184 + _OpenCUADummyInputsBuilderBase = BaseDummyInputsBuilder[OpenCUAProcessingInfo]
185 +else:
186 + _OpenCUADummyInputsBuilderBase = Qwen2VLDummyInputsBuilder
187 +
188 +
189 +class OpenCUADummyInputsBuilder(_OpenCUADummyInputsBuilderBase):
190 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
191 num_images = mm_counts.get("image", 0)
192
@@ -220,7 +228,7 @@ class OpenCUAForConditionalGeneration(Qwen2_5_VLForConditionalGeneration):
228 nn.Module.__init__(self)
229 config = vllm_config.model_config.hf_config
230 quant_config = vllm_config.quant_config
223 - multimodal_config = vllm_config.model_config.multimodal_config
231 + multimodal_config = vllm_config.model_config.get_multimodal_config()
232
233 self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
234 self.config = config
vllm/model_executor/models/openpangu.py
+5 -1
@@ -175,6 +175,7 @@ class OpenPanguMoE(nn.Module):
175 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
176 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
177
178 + self.shared_experts: OpenPanguMLP | None
179 if config.n_shared_experts is not None:
180 intermediate_size = config.moe_intermediate_size * config.n_shared_experts
181 self.shared_experts = OpenPanguMLP(
@@ -283,7 +284,7 @@ class OpenPanguMLAAttention(nn.Module):
284 )
285 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
286 self.q_b_proj = ColumnParallelLinear(
286 - q_lora_rank,
287 + self.q_lora_rank,
288 self.num_heads * self.qk_head_dim,
289 bias=False,
290 quant_config=quant_config,
@@ -568,6 +569,9 @@ class OpenPanguSinkAttention(nn.Module):
569 self.qk_nope_dim = getattr(config, "qk_nope_dim", None)
570 self.qk_rope_dim = getattr(config, "qk_rope_dim", None)
571 self.v_channels = getattr(config, "v_channels", None)
572 + assert self.qk_nope_dim is not None
573 + assert self.qk_rope_dim is not None
574 + assert self.v_channels is not None
575 self.head_dim = self.qk_rope_dim + self.qk_nope_dim
576 self.q_size = self.num_heads * self.head_dim
577 self.k_size = self.num_kv_heads * self.head_dim
vllm/model_executor/models/openpangu_mtp.py
+12 -5
@@ -52,7 +52,9 @@ class OpenPanguMultiTokenPredictorLayer(DeepSeekMultiTokenPredictorLayer):
52 def __init__(self, vllm_config: VllmConfig, prefix: str) -> None:
53 nn.Module.__init__(self)
54
55 - config = vllm_config.speculative_config.draft_model_config.hf_config
55 + speculative_config = vllm_config.speculative_config
56 + assert speculative_config is not None
57 + config = speculative_config.draft_model_config.hf_config
58 self.config = config
59 quant_config = vllm_config.quant_config
60
@@ -200,10 +202,15 @@ class OpenPanguMTP(nn.Module):
202 break
203 else:
204 for mapping in expert_params_mapping:
203 - param_name, weight_name, expert_id, shard_id = mapping
204 - if weight_name not in name:
205 + (
206 + expert_param_name,
207 + expert_weight_name,
208 + expert_id,
209 + expert_shard_id,
210 + ) = mapping
211 + if expert_weight_name not in name:
212 continue
206 - name = name.replace(weight_name, param_name)
213 + name = name.replace(expert_weight_name, expert_param_name)
214
215 param = params_dict[name]
216 weight_loader = param.weight_loader
@@ -211,7 +218,7 @@ class OpenPanguMTP(nn.Module):
218 param,
219 loaded_weight,
220 name,
214 - shard_id=shard_id,
221 + shard_id=expert_shard_id,
222 expert_id=expert_id,
223 )
224 break
vllm/model_executor/models/openpangu_vl.py
+101 -74
@@ -22,7 +22,7 @@
22
23 from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
24 from functools import lru_cache, partial
25 -from typing import Annotated, Literal, Optional
25 +from typing import Annotated, Literal, Optional, TypeAlias
26
27 import torch
28 import torch.nn as nn
@@ -627,7 +627,7 @@ class OpenPanguVisionTransformer(nn.Module):
627 pad[-(dim + 1) * 2 + 1] = pad_len
628 return F.pad(w, pad, mode="constant", value=0)
629
630 - stacked_params_mapping = [
630 + stacked_params_mapping: list[tuple[str, str, str | int]] = [
631 ("attn.qkv.", "attn.q.", "q"),
632 ("attn.qkv.", "attn.k.", "k"),
633 ("attn.qkv.", "attn.v.", "v"),
@@ -677,15 +677,11 @@ class OpenPanguVLProcessingInfo(Qwen2_5_VLProcessingInfo):
677 def get_hf_config(self):
678 return self.ctx.model_config.hf_config
679
680 - def get_hf_processor(
681 - self,
682 - *,
683 - min_pixels: int | None = None,
684 - max_pixels: int | None = None,
685 - size: dict[str, int] | None = None,
686 - fps: float | list[float] | None = None,
687 - **kwargs: object,
688 - ):
680 + def get_hf_processor(self, **kwargs: object):
681 + kwargs.pop("min_pixels", None)
682 + kwargs.pop("max_pixels", None)
683 + kwargs.pop("size", None)
684 + fps = kwargs.pop("fps", None)
685 if fps is not None:
686 kwargs["fps"] = fps
687
@@ -747,11 +743,19 @@ class OpenPanguVLVideoEmbeddingInputs(TensorSchema):
743 ]
744
745
746 +OpenPanguVLImageInputs: TypeAlias = (
747 + OpenPanguVLImagePixelInputs | OpenPanguVLImageEmbeddingInputs
748 +)
749 +OpenPanguVLVideoInputs: TypeAlias = (
750 + OpenPanguVLVideoPixelInputs | OpenPanguVLVideoEmbeddingInputs
751 +)
752 +
753 +
754 class OpenPanguVLMultiModalProcessor(Qwen2_5_VLMultiModalProcessor):
755 def _get_prompt_updates(
756 self,
757 mm_items: MultiModalDataItems,
754 - hf_processor_mm_kwargs: Mapping[str, any],
758 + hf_processor_mm_kwargs: Mapping[str, object],
759 out_mm_kwargs: MultiModalKwargsItems,
760 ) -> Sequence[PromptUpdate]:
761 hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
@@ -878,10 +882,12 @@ class OpenPanguVLForConditionalGeneration(
882 self.do_rescale = image_processor.do_rescale
883 self.rescale_factor = image_processor.rescale_factor
884 self.do_normalize = image_processor.do_normalize
881 - self.image_mean = tuple(image_processor.image_mean)
882 - self.image_std = tuple(image_processor.image_std)
885 + self.image_mean: tuple[float, ...] = tuple(image_processor.image_mean)
886 + self.image_std: tuple[float, ...] = tuple(image_processor.image_std)
887
884 - def _maybe_ignore_quant_config(self, quant_config: QuantizationConfig):
888 + def _maybe_ignore_quant_config(
889 + self, quant_config: QuantizationConfig | None
890 + ) -> QuantizationConfig | None:
891 if isinstance(quant_config, AutoGPTQConfig):
892 return None
893 return quant_config
@@ -904,7 +910,9 @@ class OpenPanguVLForConditionalGeneration(
910 else:
911 return torch.concat(mm_input)
912
907 - def _parse_and_validate_image_input(self, **kwargs: object):
913 + def _parse_and_validate_image_input(
914 + self, **kwargs: object
915 + ) -> OpenPanguVLImageInputs | None:
916 pixel_values = kwargs.pop("pixel_values", None)
917 image_embeds = kwargs.pop("image_embeds", None)
918 image_grid_thw = kwargs.pop("image_grid_thw", None)
@@ -932,26 +940,27 @@ class OpenPanguVLForConditionalGeneration(
940 image_grid_thw=image_grid_thw,
941 )
942
935 - if image_embeds is not None:
936 - image_embeds = self._validate_and_reshape_mm_tensor(
937 - image_embeds, "image embeds"
938 - )
939 - image_grid_thw = self._validate_and_reshape_mm_tensor(
940 - image_grid_thw, "image grid_thw"
941 - )
943 + assert image_embeds is not None
944 + image_embeds = self._validate_and_reshape_mm_tensor(
945 + image_embeds, "image embeds"
946 + )
947 + image_grid_thw = self._validate_and_reshape_mm_tensor(
948 + image_grid_thw, "image grid_thw"
949 + )
950
943 - if not isinstance(image_embeds, torch.Tensor):
944 - raise ValueError(
945 - "Incorrect type of image embeddings. "
946 - f"Got type: {type(image_embeds)}"
947 - )
948 - return OpenPanguVLImageEmbeddingInputs(
949 - type="image_embeds",
950 - image_embeds=image_embeds,
951 - image_grid_thw=image_grid_thw,
951 + if not isinstance(image_embeds, torch.Tensor):
952 + raise ValueError(
953 + f"Incorrect type of image embeddings. Got type: {type(image_embeds)}"
954 )
955 + return OpenPanguVLImageEmbeddingInputs(
956 + type="image_embeds",
957 + image_embeds=image_embeds,
958 + image_grid_thw=image_grid_thw,
959 + )
960
954 - def _parse_and_validate_video_input(self, **kwargs: object):
961 + def _parse_and_validate_video_input(
962 + self, **kwargs: object
963 + ) -> OpenPanguVLVideoInputs | None:
964 pixel_values_videos = kwargs.pop("pixel_values_videos", None)
965 video_embeds = kwargs.pop("video_embeds", None)
966 video_grid_thw = kwargs.pop("video_grid_thw", None)
@@ -973,27 +982,30 @@ class OpenPanguVLForConditionalGeneration(
982 video_grid_thw=video_grid_thw,
983 )
984
976 - if video_embeds is not None:
977 - video_embeds = self._validate_and_reshape_mm_tensor(
978 - video_embeds, "video embeds"
979 - )
980 - video_grid_thw = self._validate_and_reshape_mm_tensor(
981 - video_grid_thw, "video grid_thw"
982 - )
985 + assert video_embeds is not None
986 + video_embeds = self._validate_and_reshape_mm_tensor(
987 + video_embeds, "video embeds"
988 + )
989 + video_grid_thw = self._validate_and_reshape_mm_tensor(
990 + video_grid_thw, "video grid_thw"
991 + )
992
984 - if not isinstance(video_embeds, torch.Tensor):
985 - raise ValueError(
986 - "Incorrect type of video embeddings. "
987 - f"Got type: {type(video_embeds)}"
988 - )
989 - return OpenPanguVLVideoEmbeddingInputs(
990 - type="video_embeds",
991 - video_embeds=video_embeds,
992 - video_grid_thw=video_grid_thw,
993 + if not isinstance(video_embeds, torch.Tensor):
994 + raise ValueError(
995 + f"Incorrect type of video embeddings. Got type: {type(video_embeds)}"
996 )
997 + return OpenPanguVLVideoEmbeddingInputs(
998 + type="video_embeds",
999 + video_embeds=video_embeds,
1000 + video_grid_thw=video_grid_thw,
1001 + )
1002
995 - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
996 - mm_input_by_modality = {}
1003 + def _parse_and_validate_multimodal_inputs(
1004 + self, **kwargs: object
1005 + ) -> dict[str, OpenPanguVLImageInputs | OpenPanguVLVideoInputs | None]:
1006 + mm_input_by_modality: dict[
1007 + str, OpenPanguVLImageInputs | OpenPanguVLVideoInputs | None
1008 + ] = {}
1009 for input_key in kwargs:
1010 if (
1011 input_key in ("pixel_values", "image_embeds")
@@ -1019,16 +1031,19 @@ class OpenPanguVLForConditionalGeneration(
1031 multimodal_embeddings: tuple[torch.Tensor, ...] = ()
1032
1033 for modality in mm_input_by_modality:
1022 - multimodal_input = mm_input_by_modality[modality]
1034 if modality == "image":
1024 - vision_embeddings = self._process_image_input(multimodal_input)
1035 + image_input = mm_input_by_modality["image"]
1036 + assert isinstance(image_input, OpenPanguVLImageInputs)
1037 + vision_embeddings = self._process_image_input(image_input)
1038 multimodal_embeddings = (
1039 multimodal_embeddings
1040 if not vision_embeddings
1041 else (multimodal_embeddings + vision_embeddings)
1042 )
1043 if modality == "video":
1031 - video_embeddings = self._process_video_input(multimodal_input)
1044 + video_input = mm_input_by_modality["video"]
1045 + assert isinstance(video_input, OpenPanguVLVideoInputs)
1046 + video_embeddings = self._process_video_input(video_input)
1047 multimodal_embeddings = (
1048 multimodal_embeddings
1049 if not video_embeddings
@@ -1041,20 +1056,19 @@ class OpenPanguVLForConditionalGeneration(
1056 input_ids: torch.Tensor,
1057 multimodal_embeddings: MultiModalEmbeddings | None = None,
1058 ) -> torch.Tensor:
1044 - if multimodal_embeddings is None:
1045 - return self.language_model.embed_input_ids(input_ids)
1046 -
1047 - mm_token_ids = input_ids.new_tensor(
1048 - [self.config.image_token_id, self.config.video_token_id]
1049 - )
1050 - is_multimodal = torch.isin(input_ids, mm_token_ids)
1051 - return self.embed_input_ids(
1052 - input_ids,
1053 - multimodal_embeddings,
1054 - is_multimodal=is_multimodal,
1055 - )
1059 + inputs_embeds = self.language_model.embed_input_ids(input_ids)
1060 + if multimodal_embeddings is not None:
1061 + inputs_embeds = self.embed_input_ids( # type: ignore[call-overload]
1062 + input_ids,
1063 + inputs_embeds,
1064 + multimodal_embeddings,
1065 + [self.config.image_token_id, self.config.video_token_id],
1066 + )
1067 + return inputs_embeds
1068
1057 - def _process_image_input(self, image_input) -> tuple[torch.Tensor, ...]:
1069 + def _process_image_input(
1070 + self, image_input: OpenPanguVLImageInputs
1071 + ) -> tuple[torch.Tensor, ...]:
1072 grid_thw = image_input["image_grid_thw"]
1073 if grid_thw.ndim != 2:
1074 raise ValueError(f"grid_thw.ndim must be 2, but it is {grid_thw.ndim}")
@@ -1085,7 +1099,9 @@ class OpenPanguVLForConditionalGeneration(
1099 sizes = grid_thw.prod(-1) // merge_size // merge_size
1100 return image_embeds.split(sizes.tolist())
1101
1088 - def _process_video_input(self, video_input) -> torch.Tensor:
1102 + def _process_video_input(
1103 + self, video_input: OpenPanguVLVideoInputs
1104 + ) -> tuple[torch.Tensor, ...]:
1105 grid_thw = video_input["video_grid_thw"]
1106 if grid_thw.ndim != 2:
1107 raise ValueError(f"grid_thw.ndim must be 2, but it is {grid_thw.ndim}")
@@ -1158,8 +1174,13 @@ class OpenPanguVLForConditionalGeneration(
1174 for mm_feature in sorted(mm_features, key=lambda f: f.mm_position.offset):
1175 offset = mm_feature.mm_position.offset
1176 modality = mm_feature.modality
1177 + feature_data = mm_feature.data
1178 + assert feature_data is not None
1179 if modality == "image":
1162 - t, h, w = mm_feature.data["image_grid_thw"].data.tolist()
1180 + grid_thw_item = feature_data["image_grid_thw"]
1181 + grid_thw = grid_thw_item.data
1182 + assert isinstance(grid_thw, torch.Tensor)
1183 + t, h, w = grid_thw.tolist()
1184 assert t == 1, f"Image must have 1 frame, got {t}"
1185 yield (
1186 modality,
@@ -1169,7 +1190,10 @@ class OpenPanguVLForConditionalGeneration(
1190 w // spatial_merge_size,
1191 )
1192 elif modality == "video":
1172 - t, h, w = mm_feature.data["video_grid_thw"].data.tolist()
1193 + grid_thw_item = feature_data["video_grid_thw"]
1194 + grid_thw = grid_thw_item.data
1195 + assert isinstance(grid_thw, torch.Tensor)
1196 + t, h, w = grid_thw.tolist()
1197 yield (
1198 modality,
1199 offset,
@@ -1281,14 +1305,17 @@ def normalize(image, mean, std):
1305 @lru_cache(maxsize=10)
1306 def _fuse_mean_std_and_rescale_factor(
1307 do_normalize: bool | None = None,
1284 - image_mean: float | list[float] | None = None,
1285 - image_std: float | list[float] | None = None,
1308 + image_mean: float | tuple[float, ...] | None = None,
1309 + image_std: float | tuple[float, ...] | None = None,
1310 do_rescale: bool | None = None,
1311 rescale_factor: float | None = None,
1312 device: Optional["torch.device"] = None,
1313 ) -> tuple:
1314 if do_rescale and do_normalize:
1315 # Fused rescale and normalize
1316 + assert rescale_factor is not None
1317 + assert image_mean is not None
1318 + assert image_std is not None
1319 image_mean = torch.tensor(image_mean, device=device) * (1.0 / rescale_factor)
1320 image_std = torch.tensor(image_std, device=device) * (1.0 / rescale_factor)
1321 do_rescale = False
@@ -1300,8 +1327,8 @@ def rescale_and_normalize(
1327 do_rescale: bool,
1328 rescale_factor: float,
1329 do_normalize: bool,
1303 - image_mean: float | list[float],
1304 - image_std: float | list[float],
1330 + image_mean: float | tuple[float, ...],
1331 + image_std: float | tuple[float, ...],
1332 dtype: torch.dtype = torch.bfloat16,
1333 ) -> "torch.Tensor":
1334 """Rescale and normalize images."""
vllm/model_executor/models/openvla.py
+1
@@ -349,6 +349,7 @@ class OpenVLAMultiModalProcessor(BaseMultiModalProcessor[OpenVLAProcessingInfo])
349 if isinstance(images, ImageEmbeddingItems):
350 num_image_tokens = images.get_feature_size(item_idx)
351 else:
352 + assert isinstance(images, ImageProcessorItems)
353 image_size = images.get_image_size(item_idx)
354 num_image_tokens = self.info.get_num_image_tokens(
355 image_width=image_size.width,
vllm/model_executor/models/opt.py
+2
@@ -218,6 +218,7 @@ class OPTDecoder(nn.Module):
218 )
219
220 # Project out & in will be replicated if they exist.
221 + self.project_out: ReplicatedLinear | None
222 if config.word_embed_proj_dim != config.hidden_size:
223 self.project_out = ReplicatedLinear(
224 config.hidden_size,
@@ -229,6 +230,7 @@ class OPTDecoder(nn.Module):
230 else:
231 self.project_out = None
232
233 + self.project_in: ReplicatedLinear | None
234 if config.word_embed_proj_dim != config.hidden_size:
235 self.project_in = ReplicatedLinear(
236 config.word_embed_proj_dim,
vllm/model_executor/models/ovis.py
+10 -3
@@ -59,7 +59,12 @@ from vllm.sequence import IntermediateTensors
59 from vllm.transformers_utils.processors.ovis import OvisProcessor
60 from vllm.utils.tensor_schema import TensorSchema, TensorShape
61
62 -from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
62 +from .interfaces import (
63 + MultiModalEmbeddings,
64 + SupportsMultiModal,
65 + SupportsPP,
66 + supports_pp,
67 +)
68
69 # Cannot find the following number from hf config.
70 IMAGE_TOKEN = "<image>"
@@ -281,7 +286,7 @@ class OvisProcessingInfo(BaseProcessingInfo):
286 # minus 1 for presented image token
287 return (patch_grid_length // hidden_stride) ** 2 - 1
288
284 - def get_image_pad_token(self) -> str:
289 + def get_image_pad_token(self) -> str | None:
290 hf_text_config = self.get_hf_config().get_text_config()
291 text_model_type = hf_text_config.model_type
292 return IMAGE_PAD_TOKEN_MAP.get(text_model_type)
@@ -439,8 +444,10 @@ class Ovis(nn.Module, SupportsMultiModal, SupportsPP):
444 text_model_type = self.config.get_text_config().model_type
445 self.image_pad_token_id = IMAGE_PAD_TOKEN_ID_MAP[text_model_type]
446
447 + language_model = self.get_language_model()
448 + assert supports_pp(language_model)
449 self.make_empty_intermediate_tensors = (
443 - self.get_language_model().make_empty_intermediate_tensors
450 + language_model.make_empty_intermediate_tensors
451 )
452
453 def _parse_and_validate_image_input(
vllm/model_executor/models/ovis2_5.py
+23 -6
@@ -4,14 +4,14 @@
4
5 from collections.abc import Iterable, Mapping
6 from functools import partial
7 -from typing import Annotated, Literal
7 +from typing import Annotated, Literal, TypedDict
8
9 import torch
10 import torch.nn as nn
11 from transformers import BaseImageProcessor, BatchFeature, PretrainedConfig
12
13 from vllm.config import VllmConfig
14 -from vllm.config.multimodal import BaseDummyOptions
14 +from vllm.config.multimodal import BaseDummyOptions, VideoDummyOptions
15 from vllm.inputs import MultiModalDataDict
16 from vllm.model_executor.layers.linear import ReplicatedLinear
17 from vllm.model_executor.layers.quantization import QuantizationConfig
@@ -40,7 +40,12 @@ from vllm.sequence import IntermediateTensors
40 from vllm.transformers_utils.processors.ovis2_5 import Ovis2_5Processor
41 from vllm.utils.tensor_schema import TensorSchema, TensorShape
42
43 -from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
43 +from .interfaces import (
44 + MultiModalEmbeddings,
45 + SupportsMultiModal,
46 + SupportsPP,
47 + supports_pp,
48 +)
49
50 IMAGE_TOKEN = "<image>"
51 VIDEO_TOKEN = "<video>"
@@ -80,6 +85,11 @@ class Ovis2_5VideoPatchInputs(TensorSchema):
85 # This is used to restore the first two dimensions of `flat_data`.
86
87
88 +class Ovis2_5MultiModalInputs(TypedDict, total=False):
89 + images: Ovis2_5ImagePatchInputs | None
90 + videos: Ovis2_5VideoPatchInputs | None
91 +
92 +
93 class VisualTokenizer(torch.nn.Module):
94 """VIT."""
95
@@ -292,6 +302,7 @@ class Ovis2_5DummyInputsBuilder(BaseDummyInputsBuilder[Ovis2_5ProcessingInfo]):
302
303 image_overrides = mm_options.get("image")
304 video_overrides = mm_options.get("video")
305 + assert video_overrides is None or isinstance(video_overrides, VideoDummyOptions)
306
307 mm_data = {
308 "image": self._get_dummy_images(
@@ -453,8 +464,10 @@ class Ovis2_5(nn.Module, SupportsMultiModal, SupportsPP):
464
465 self.image_pad_token_id: int = IMAGE_PAD_TOKEN_ID
466
467 + language_model = self.get_language_model()
468 + assert supports_pp(language_model)
469 self.make_empty_intermediate_tensors = (
457 - self.get_language_model().make_empty_intermediate_tensors
470 + language_model.make_empty_intermediate_tensors
471 )
472
473 def _parse_and_validate_image_input(
@@ -562,8 +575,10 @@ class Ovis2_5(nn.Module, SupportsMultiModal, SupportsPP):
575 vision_embeddings.append(torch.cat(vision_embeddings_per_image, dim=0))
576 return tuple(vision_embeddings)
577
565 - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
566 - modalities = {}
578 + def _parse_and_validate_multimodal_inputs(
579 + self, **kwargs: object
580 + ) -> Ovis2_5MultiModalInputs:
581 + modalities: Ovis2_5MultiModalInputs = {}
582
583 # Preserve the order of modalities if there are multiple of them
584 # from the order of kwargs.
@@ -593,10 +608,12 @@ class Ovis2_5(nn.Module, SupportsMultiModal, SupportsPP):
608 for modality in modalities:
609 if modality == "images":
610 image_input = modalities["images"]
611 + assert image_input is not None
612 image_embeddings = self._process_visual_input(image_input)
613 multimodal_embeddings += tuple(image_embeddings)
614 if modality == "videos":
615 video_input = modalities["videos"]
616 + assert video_input is not None
617 video_embeddings = self._process_visual_input(video_input)
618 multimodal_embeddings += tuple(video_embeddings)
619
vllm/transformers_utils/processors/nano_nemotron_vl.py
+3 -1
@@ -551,7 +551,9 @@ class DynamicResolutionImageTiler:
551 )
552
553 @staticmethod
554 - def stack(images: list[torch.Tensor], patch_size: int) -> torch.Tensor:
554 + def stack(
555 + images: list[torch.Tensor] | torch.Tensor, patch_size: int
556 + ) -> torch.Tensor:
557 assert len(images) > 0, "No images to stack"
558
559 def rearrange_img(x):