[Mypy] Fix mypy typing for model interfaces and H/I models (#54079)

Signed-off-by: Taneem Ibrahim <taneem.ibrahim@gmail.com> Co-authored-by: Wentao Ye <44945378+yewentao256@users.noreply.github.com>

Taneem Ibrahim committed Aug 28, 2026 at 15:49 UTC 21fa2c5a2a471b9c978a5a3b7208a4c7fe88cefa
23 files changed +222 -137
tools/pre_commit/mypy.py
-2
@@ -112,8 +112,6 @@ SEPARATE_GROUPS = [
112 ]
113
114 EXCLUDE = [
115 - r"vllm/model_executor/models/[hH]",
116 - r"vllm/model_executor/models/[iI]",
115 r"vllm/model_executor/models/[jJ]",
116 r"vllm/model_executor/models/[kK]",
117 r"vllm/model_executor/models/[lL]",
vllm/model_executor/models/chatglm.py
+1 -1
@@ -430,7 +430,7 @@ class ChatGLMBaseModel(nn.Module):
430
431
432 class ChatGLMForCausalLM(ChatGLMBaseModel, SupportsLoRA, SupportsPP, SupportsQuant):
433 - packed_modules_mapping = {
433 + packed_modules_mapping: dict[str, list[str]] = {
434 "query_key_value": ["query_key_value"],
435 "dense_h_to_4h": ["dense_h_to_4h"],
436 }
vllm/model_executor/models/commandr.py
+1 -1
@@ -417,7 +417,7 @@ class CohereForCausalLM(nn.Module, SupportsLoRA, SupportsPP, SupportsQuant):
417 orig_to_new_substr={"_quantizer.": None},
418 orig_to_new_prefix={"lm_head": None},
419 )
420 - packed_modules_mapping = {
420 + packed_modules_mapping: dict[str, list[str]] = {
421 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
422 "gate_up_proj": ["gate_proj", "up_proj"],
423 }
vllm/model_executor/models/gemma.py
+1 -1
@@ -339,7 +339,7 @@ class GemmaForCausalLM(nn.Module, SupportsLoRA, SupportsPP, SupportsQuant):
339 ".up_proj": (".gate_up_proj", 1),
340 }
341 )
342 - packed_modules_mapping = {
342 + packed_modules_mapping: dict[str, list[str]] = {
343 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
344 "gate_up_proj": ["gate_proj", "up_proj"],
345 }
vllm/model_executor/models/gemma4_mm.py
+1 -1
@@ -1019,7 +1019,7 @@ class Gemma4ForConditionalGeneration(
1019 mm_prefix_clamp_sliding_window: bool = True
1020 supports_tower_connector_lora = True
1021
1022 - packed_modules_mapping = {
1022 + packed_modules_mapping: dict[str, list[str]] = {
1023 "qkv_proj": [
1024 "q_proj",
1025 "k_proj",
vllm/model_executor/models/granite.py
+1 -1
@@ -395,7 +395,7 @@ class GraniteModel(nn.Module):
395 class GraniteForCausalLM(nn.Module, SupportsLoRA, SupportsPP, SupportsQuant):
396 hf_to_vllm_mapper = GraniteModel.hf_to_vllm_mapper
397 # LoRA specific attributes
398 - packed_modules_mapping = {
398 + packed_modules_mapping: dict[str, list[str]] = {
399 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
400 "gate_up_proj": ["gate_proj", "up_proj"],
401 }
vllm/model_executor/models/granitemoehybrid.py
+1 -1
@@ -587,7 +587,7 @@ class GraniteMoeHybridForCausalLM(
587 SupportsQuant,
588 SupportsMambaPrefixCaching,
589 ):
590 - packed_modules_mapping = {
590 + packed_modules_mapping: dict[str, list[str]] = {
591 "qkv_proj": [
592 "q_proj",
593 "k_proj",
vllm/model_executor/models/h2ovl.py
+1
@@ -114,6 +114,7 @@ class H2OVLMultiModalProcessor(BaseInternVLMultiModalProcessor[H2OVLProcessingIn
114 if isinstance(images, ImageEmbeddingItems):
115 feature_size = images.get_feature_size(item_idx)
116 else:
117 + assert isinstance(images, ImageProcessorItems)
118 image_size = images.get_image_size(item_idx)
119 feature_size = self.info.get_num_image_tokens(
120 image_width=image_size.width,
vllm/model_executor/models/hrm_text.py
+2 -2
@@ -248,7 +248,7 @@ class HrmTextDecoderLayer(nn.Module):
248 self,
249 config: PretrainedConfig,
250 layer_idx_in_stack: int,
251 - stack_kind: str,
251 + stack_kind: Literal["L", "H"],
252 cache_config: CacheConfig | None = None,
253 quant_config: QuantizationConfig | None = None,
254 prefix: str = "",
@@ -312,7 +312,7 @@ class HrmTextStack(nn.Module):
312 def __init__(
313 self,
314 config: PretrainedConfig,
315 - stack_kind: str,
315 + stack_kind: Literal["L", "H"],
316 cache_config: CacheConfig | None = None,
317 quant_config: QuantizationConfig | None = None,
318 prefix: str = "",
vllm/model_executor/models/hy_v3.py
+12 -11
@@ -161,6 +161,7 @@ class HYV3MoEFused(nn.Module):
161 prefix=f"{prefix}.gate",
162 )
163
164 + self.shared_mlp: HYV3FeedForward | None
165 if config.num_shared_experts > 0:
166 self.shared_mlp = HYV3FeedForward(
167 hidden_size=config.hidden_size,
@@ -467,28 +468,27 @@ class HYV3Model(nn.Module, MixtureOfExperts):
468 # Set MoE hyperparameters
469 self.num_expert_groups = 1
470 self.moe_layers = []
470 - example_layer = None
471 + example_layer: HYV3MoEFused | None = None
472 for layer in self.layers:
473 if isinstance(layer, PPMissingLayer):
474 continue
475
476 assert isinstance(layer, HYV3DecoderLayer)
477 if layer.block_type == "moe":
478 + assert isinstance(layer.mlp, HYV3MoEFused)
479 example_layer = layer.mlp
478 - self.moe_layers.append(layer.mlp.experts)
480 + self.moe_layers.append(example_layer.experts)
481
482 if example_layer is None:
483 self.num_moe_layers = 0
484 raise RuntimeError("No MoE layer found in model.layers.")
485
486 self.num_moe_layers = len(self.moe_layers)
485 - self.num_logical_experts = getattr(example_layer, "n_logical_experts", None)
486 - self.num_physical_experts = getattr(example_layer, "n_physical_experts", None)
487 - self.num_local_physical_experts = getattr(
488 - example_layer, "n_local_physical_experts", None
489 - )
490 - self.num_routed_experts = getattr(example_layer, "n_routed_experts", None)
491 - self.num_redundant_experts = getattr(example_layer, "n_redundant_experts", None)
487 + self.num_logical_experts = example_layer.n_logical_experts
488 + self.num_physical_experts = example_layer.n_physical_experts
489 + self.num_local_physical_experts = example_layer.n_local_physical_experts
490 + self.num_routed_experts = example_layer.n_routed_experts
491 + self.num_redundant_experts = example_layer.n_redundant_experts
492
493 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
494 return self.embed_tokens(input_ids)
@@ -572,9 +572,10 @@ class HYV3Model(nn.Module, MixtureOfExperts):
572 continue
573 if "scale" in name:
574 # Remapping the name of FP8 kv-scale.
575 - name = maybe_remap_kv_scale_name(name, params_dict)
576 - if name is None:
575 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
576 + if remapped_name is None:
577 continue
578 + name = remapped_name
579 is_found = False
580 for param_name, weight_name, shard_id in stacked_params_mapping:
581 if weight_name not in name:
vllm/model_executor/models/hy_v3_mtp.py
+14 -5
@@ -24,7 +24,7 @@
24 # limitations under the License.
25 """Inference-only HY V3 MTP model compatible with HuggingFace weights."""
26
27 -from collections.abc import Iterable
27 +from collections.abc import Callable, Iterable
28
29 import regex as re
30 import torch
@@ -288,7 +288,15 @@ class HYV3MTP(nn.Module):
288 num_kv_heads = getattr(
289 self.config, "num_key_value_heads", self.config.num_attention_heads
290 )
291 - split_params_mapping = [
291 + split_params_mapping: list[
292 + tuple[
293 + str,
294 + str,
295 + int,
296 + list[tuple[str | int, int]],
297 + Callable[[torch.Tensor], torch.Tensor] | None,
298 + ]
299 + ] = [
300 (".gate_up_proj", ".gate_and_up_proj", 2, [(1, 1), (0, 1)], None),
301 (
302 ".qkv_proj",
@@ -308,7 +316,7 @@ class HYV3MTP(nn.Module):
316 num_experts=self.config.num_experts,
317 )
318 else:
311 - expert_params_mapping = {}
319 + expert_params_mapping = []
320
321 params_dict = dict(self.named_parameters())
322
@@ -354,9 +362,10 @@ class HYV3MTP(nn.Module):
362 if name == "__skip__":
363 continue
364 if "scale" in name:
357 - name = maybe_remap_kv_scale_name(name, params_dict)
358 - if name is None:
365 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
366 + if remapped_name is None:
367 continue
368 + name = remapped_name
369 is_found = False
370
371 for param_name, weight_name, shard_id in stacked_params_mapping:
vllm/model_executor/models/hyperclovax_vision_v2.py
+12 -3
@@ -12,7 +12,7 @@ Supports:
12
13 from collections.abc import Iterable, Mapping, Sequence
14 from functools import partial
15 -from typing import Annotated, Literal
15 +from typing import Annotated, Literal, TypedDict
16
17 import torch
18 import torch.nn as nn
@@ -122,6 +122,11 @@ class HCXVisionV2VideoEmbeddingInputs(TensorSchema):
122 HCXVisionV2VideoInputs = HCXVisionV2VideoPixelInputs | HCXVisionV2VideoEmbeddingInputs
123
124
125 +class HCXVisionV2MultiModalInputs(TypedDict, total=False):
126 + image: HCXVisionV2ImageInputs | None
127 + video: HCXVisionV2VideoInputs | None
128 +
129 +
130 class HCXVisionV2ProcessingInfo(BaseProcessingInfo):
131 """Processing info for HyperCLOVAX V2 (32B Think model)."""
132
@@ -329,6 +334,7 @@ class HCXVisionV2MultiModalProcessor(
334 if grid_thw_elem is not None:
335 # Access .data to get the actual tensor from MultiModalFieldElem
336 grid_thw = grid_thw_elem.data
337 + assert isinstance(grid_thw, torch.Tensor)
338 # Qwen2.5-VL style calculation
339 h, w = grid_thw[1].item(), grid_thw[2].item()
340 num_tokens = (h * w) // (merge_size**2)
@@ -340,6 +346,7 @@ class HCXVisionV2MultiModalProcessor(
346 if grid_thw_elem is not None:
347 # Access .data to get the actual tensor from MultiModalFieldElem
348 grid_thw = grid_thw_elem.data
349 + assert isinstance(grid_thw, torch.Tensor)
350 t, h, w = grid_thw[0].item(), grid_thw[1].item(), grid_thw[2].item()
351 num_tokens = (t * h * w) // (merge_size**2)
352 else:
@@ -606,8 +613,10 @@ class HCXVisionV2ForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
613 sizes = (grid_thw.prod(-1) // merge_size // merge_size).tolist()
614 return video_embeds.split(sizes)
615
609 - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
610 - modalities = {}
616 + def _parse_and_validate_multimodal_inputs(
617 + self, **kwargs: object
618 + ) -> HCXVisionV2MultiModalInputs:
619 + modalities: HCXVisionV2MultiModalInputs = {}
620
621 for input_key in kwargs:
622 if (
vllm/model_executor/models/idefics3.py
+2 -1
@@ -625,6 +625,7 @@ class Idefics3ForConditionalGeneration(
625 if image_input["type"] == "image_embeds":
626 return image_input["data"]
627
628 + assert isinstance(image_input, Idefics3ImagePixelInputs)
629 image_features = self._process_image_pixels(image_input)
630 image_features = self.model.connector(image_features)
631
@@ -788,7 +789,7 @@ class Idefics3ForConditionalGeneration(
789 path: str = "default",
790 ) -> torch.Tensor:
791 image_input = self._parse_and_validate_image_input(**mm_kwargs)
791 - assert image_input is not None and image_input["type"] == "pixel_values"
792 + assert isinstance(image_input, Idefics3ImagePixelInputs)
793 image_features = self._process_image_pixels(image_input)
794 image_features = self.model.connector(image_features)
795 return image_features.flatten(0, 1)
vllm/model_executor/models/interfaces.py
+70 -44
@@ -32,6 +32,7 @@ from typing_extensions import Self, TypeIs
32
33 from vllm.logger import init_logger
34 from vllm.model_executor.layers.quantization import QuantizationConfig
35 +from vllm.model_executor.models.interfaces_base import VllmModel
36 from vllm.utils.collection_utils import common_prefix
37 from vllm.utils.func_utils import supports_kw
38
@@ -46,8 +47,9 @@ if TYPE_CHECKING:
47 from vllm.inputs import PromptType, TokensPrompt
48 from vllm.lora.model_manager import LoRAModelManager
49 from vllm.model_executor.layers.fused_moe import MoERunner
50 + from vllm.model_executor.layers.logits_processor import LogitsProcessor
51 from vllm.model_executor.layers.mamba.mamba_utils import MambaStateCopyFunc
50 - from vllm.model_executor.models.interfaces_base import VllmModel
52 + from vllm.model_executor.layers.vocab_parallel_embedding import ParallelLMHead
53 from vllm.model_executor.models.utils import WeightsMapper
54 from vllm.multimodal.inputs import MultiModalFeatureSpec, MultiModalKwargsItem
55 from vllm.multimodal.registry import _ProcessorFactories
@@ -128,14 +130,24 @@ class SupportsMultiModalEmbeddings(Protocol):
130
131 supports_multimodal_embeddings: ClassVar[Literal[True]] = True
132
133 + @overload
134 def embed_input_ids(
135 self,
136 input_ids: Tensor,
134 - multimodal_embeddings: MultiModalEmbeddings | None = None,
137 + multimodal_embeddings: None = None,
138 *,
139 is_multimodal: Tensor | None = None,
140 ) -> Tensor: ...
141
142 + @overload
143 + def embed_input_ids(
144 + self,
145 + input_ids: Tensor,
146 + multimodal_embeddings: MultiModalEmbeddings,
147 + *,
148 + is_multimodal: Tensor,
149 + ) -> Tensor: ...
150 +
151
152 @runtime_checkable
153 class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
@@ -243,26 +255,30 @@ class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
255 torch.nn.Module: The core language model component.
256 """
257 # Cached
246 - if self in _language_model_by_module:
247 - return _language_model_by_module[self]
258 + assert isinstance(self, nn.Module)
259 + self_module = self
260 + if self_module in _language_model_by_module:
261 + return _language_model_by_module[self_module]
262
263 if self._language_model_names:
250 - mod = self
264 + mod = self_module
265 for attr in common_prefix(
266 [name.split(".") for name in self._language_model_names]
267 ):
268 if attr:
269 mod = getattr(mod, attr)
270
257 - if mod is not self and hasattr(mod, "embed_input_ids"):
258 - _language_model_by_module[self] = mod
259 - return mod
271 + candidate: object = mod
272 + if candidate is not self_module and isinstance(candidate, VllmModel):
273 + _language_model_by_module[self_module] = candidate
274 + return candidate
275
276 # Fallback
262 - for mod in self.children():
263 - if hasattr(mod, "embed_input_ids"):
264 - _language_model_by_module[self] = mod
265 - return mod
277 + for mod in self_module.children():
278 + candidate = mod
279 + if isinstance(candidate, VllmModel):
280 + _language_model_by_module[self_module] = candidate
281 + return candidate
282
283 raise NotImplementedError(
284 f"No language model found in {type(self).__name__}! "
@@ -289,7 +305,7 @@ class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
305 """
306 from .utils import StageMissingLayer, collect_children, no_init_weights
307
292 - mm_config = vllm_config.model_config.multimodal_config
308 + mm_config = vllm_config.model_config.get_multimodal_config()
309
310 with collect_children(self, targets=targets) as children_names: # noqa: SIM117
311 with (
@@ -339,7 +355,7 @@ class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
355 else:
356 stage_name = "_".join([*modalities, "tower"])
357
342 - mm_config = vllm_config.model_config.multimodal_config
358 + mm_config = vllm_config.model_config.get_multimodal_config()
359
360 with collect_children(self, targets=targets) as children_names: # noqa: SIM117
361 with (
@@ -361,7 +377,8 @@ class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
377 offloader = get_offloader()
378 if offloader.supports_tower_offload:
379 for name in children_names:
364 - offloader.wrap_modules(iter([attrgetter(name)(self)]), prefix=name)
380 + modules = (module for module in [attrgetter(name)(self)])
381 + offloader.wrap_modules(modules, prefix=name)
382
383 @contextmanager
384 def _mark_composite_model(
@@ -434,18 +451,6 @@ class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
451 num_connector_tokens if isinstance(num_connector_tokens, int) else None,
452 )
453
437 - @overload
438 - def embed_input_ids(self, input_ids: Tensor) -> Tensor: ...
439 -
440 - @overload
441 - def embed_input_ids(
442 - self,
443 - input_ids: Tensor,
444 - multimodal_embeddings: MultiModalEmbeddings,
445 - *,
446 - is_multimodal: torch.Tensor,
447 - ) -> Tensor: ...
448 -
454 def _embed_text_input_ids(
455 self,
456 input_ids: Tensor,
@@ -465,6 +470,24 @@ class SupportsMultiModal(SupportsMultiModalEmbeddings, Protocol):
470
471 return embed_input_ids(input_ids)
472
473 + @overload
474 + def embed_input_ids(
475 + self,
476 + input_ids: Tensor,
477 + multimodal_embeddings: None = None,
478 + *,
479 + is_multimodal: Tensor | None = None,
480 + ) -> Tensor: ...
481 +
482 + @overload
483 + def embed_input_ids(
484 + self,
485 + input_ids: Tensor,
486 + multimodal_embeddings: MultiModalEmbeddings,
487 + *,
488 + is_multimodal: Tensor,
489 + ) -> Tensor: ...
490 +
491 def embed_input_ids(
492 self,
493 input_ids: Tensor,
@@ -671,7 +694,7 @@ class SupportsLoRA(Protocol):
694 # The `embedding_module` and `embedding_padding_modules`
695 # are empty by default.
696 embedding_modules: ClassVar[dict[str, str]] = {}
674 - packed_modules_mapping: ClassVar[dict[str, list[str]]] = {}
697 + packed_modules_mapping: dict[str, list[str]] = {}
698 # Module prefixes to skip during LoRA loading (e.g., ["mtp."] for MTP layers)
699 lora_skip_prefixes: ClassVar[list[str]] = []
700 lora_manager: "LoRAModelManager | None"
@@ -864,11 +887,11 @@ class HasInnerState(Protocol):
887
888
889 @overload
867 -def has_inner_state(model: object) -> TypeIs[HasInnerState]: ...
890 +def has_inner_state(model: type[object]) -> TypeIs[type[HasInnerState]]: ...
891
892
893 @overload
871 -def has_inner_state(model: type[object]) -> TypeIs[type[HasInnerState]]: ...
894 +def has_inner_state(model: object) -> TypeIs[HasInnerState]: ...
895
896
897 def has_inner_state(
@@ -891,11 +914,11 @@ class IsAttentionFree(Protocol):
914
915
916 @overload
894 -def is_attention_free(model: object) -> TypeIs[IsAttentionFree]: ...
917 +def is_attention_free(model: type[object]) -> TypeIs[type[IsAttentionFree]]: ...
918
919
920 @overload
898 -def is_attention_free(model: type[object]) -> TypeIs[type[IsAttentionFree]]: ...
921 +def is_attention_free(model: object) -> TypeIs[IsAttentionFree]: ...
922
923
924 def is_attention_free(
@@ -946,11 +969,11 @@ class IsHybrid(Protocol):
969
970
971 @overload
949 -def is_hybrid(model: object) -> TypeIs[IsHybrid]: ...
972 +def is_hybrid(model: type[object]) -> TypeIs[type[IsHybrid]]: ...
973
974
975 @overload
953 -def is_hybrid(model: type[object]) -> TypeIs[type[IsHybrid]]: ...
976 +def is_hybrid(model: object) -> TypeIs[IsHybrid]: ...
977
978
979 def is_hybrid(
@@ -1025,7 +1048,7 @@ class MixtureOfExperts(Protocol):
1048 self.expert_weights = []
1049 for layer_idx, layer in enumerate(self.moe_layers):
1050 # Register the expert weights.
1028 - self.expert_weights.append(layer.get_expert_weights())
1051 + self.expert_weights.append(list(layer.get_expert_weights()))
1052 layer.set_eplb_state(
1053 moe_layer_idx=layer_idx,
1054 expert_load_view=expert_load_view,
@@ -1083,11 +1106,11 @@ class HasNoOps(Protocol):
1106
1107
1108 @overload
1086 -def has_noops(model: object) -> TypeIs[HasNoOps]: ...
1109 +def has_noops(model: type[object]) -> TypeIs[type[HasNoOps]]: ...
1110
1111
1112 @overload
1090 -def has_noops(model: type[object]) -> TypeIs[type[HasNoOps]]: ...
1113 +def has_noops(model: object) -> TypeIs[HasNoOps]: ...
1114
1115
1116 def has_noops(
@@ -1108,14 +1131,14 @@ class SupportsMambaPrefixCaching(Protocol):
1131
1132 @overload
1133 def supports_mamba_prefix_caching(
1111 - model: object,
1112 -) -> TypeIs[SupportsMambaPrefixCaching]: ...
1134 + model: type[object],
1135 +) -> TypeIs[type[SupportsMambaPrefixCaching]]: ...
1136
1137
1138 @overload
1139 def supports_mamba_prefix_caching(
1117 - model: type[object],
1118 -) -> TypeIs[type[SupportsMambaPrefixCaching]]: ...
1140 + model: object,
1141 +) -> TypeIs[SupportsMambaPrefixCaching]: ...
1142
1143
1144 def supports_mamba_prefix_caching(
@@ -1136,11 +1159,11 @@ class SupportsReplaySSM(Protocol):
1159
1160
1161 @overload
1139 -def supports_replayssm(model: object) -> TypeIs[SupportsReplaySSM]: ...
1162 +def supports_replayssm(model: type[object]) -> TypeIs[type[SupportsReplaySSM]]: ...
1163
1164
1165 @overload
1143 -def supports_replayssm(model: type[object]) -> TypeIs[type[SupportsReplaySSM]]: ...
1166 +def supports_replayssm(model: object) -> TypeIs[SupportsReplaySSM]: ...
1167
1168
1169 def supports_replayssm(
@@ -1172,7 +1195,7 @@ class SupportsQuant:
1195 """The interface required for all models that support quantization."""
1196
1197 hf_to_vllm_mapper: "WeightsMapper | None" = None
1175 - packed_modules_mapping: ClassVar[dict[str, list[str]]]
1198 + packed_modules_mapping: dict[str, list[str]]
1199 quant_config: QuantizationConfig | None = None
1200
1201 def __new__(cls, *args, **kwargs) -> Self:
@@ -1504,6 +1527,9 @@ class LocalArgmaxMixin:
1527 ``self.draft_id_to_target_id`` (optional): nn.Parameter
1528 """
1529
1530 + logits_processor: "LogitsProcessor"
1531 + lm_head: "ParallelLMHead"
1532 +
1533 def get_top_tokens(self, hidden_states: torch.Tensor) -> torch.Tensor:
1534 """Vocab-parallel argmax with optional D2T remapping."""
1535 top = self.logits_processor.get_top_tokens(
vllm/model_executor/models/interfaces_base.py
+6 -5
@@ -57,7 +57,7 @@ class VllmModel(Protocol[T_co]):
57
58
59 def _check_vllm_model_init(model: type[object] | object) -> bool:
60 - model_init = model.__init__
60 + model_init = getattr(model, "__init__") # noqa: B009
61 return supports_kw(model_init, "vllm_config")
62
63
@@ -274,10 +274,11 @@ def get_attn_type(model: type[object] | object) -> AttnTypeStr:
274
275
276 def get_score_type(model: type[object] | object) -> ScoreType:
277 - score_types = set()
278 - for m in model.__mro__:
279 - score_type = getattr(m, "score_type", "bi-encoder")
277 + score_types: set[ScoreType] = set()
278 + model_cls = model if isinstance(model, type) else type(model)
279 + for m in model_cls.__mro__:
280 + score_type: ScoreType = getattr(m, "score_type", "bi-encoder")
281 if score_type != "bi-encoder":
282 score_types.add(score_type)
283 assert len(score_types) < 2
283 - return "bi-encoder" if not score_types else list(score_types)[0]
284 + return "bi-encoder" if not score_types else next(iter(score_types))
vllm/model_executor/models/internlm2.py
+1 -1
@@ -323,7 +323,7 @@ class InternLM2Model(nn.Module):
323
324 class InternLM2ForCausalLM(nn.Module, SupportsPP, SupportsLoRA, SupportsQuant):
325 hf_to_vllm_mapper = InternLM2Model.hf_to_vllm_mapper
326 - packed_modules_mapping = {
326 + packed_modules_mapping: dict[str, list[str]] = {
327 "wqkv": ["wqkv"],
328 "gate_up_proj": ["w1", "w3"],
329 }
vllm/model_executor/models/interns1.py
+31 -19
@@ -7,7 +7,7 @@
7 # Licensed under The MIT License [see LICENSE for details]
8 # --------------------------------------------------------
9 from collections.abc import Iterable, Mapping, Sequence
10 -from typing import Annotated, Literal, TypeAlias
10 +from typing import Annotated, Literal, TypeAlias, TypedDict
11
12 import regex as re
13 import torch
@@ -22,7 +22,7 @@ from transformers.models.internvl.video_processing_internvl import (
22 )
23
24 from vllm.config import VllmConfig
25 -from vllm.config.multimodal import BaseDummyOptions
25 +from vllm.config.multimodal import BaseDummyOptions, VideoDummyOptions
26 from vllm.inputs import MultiModalDataDict
27 from vllm.model_executor.layers.quantization import QuantizationConfig
28 from vllm.model_executor.models.interns1_vit import InternS1VisionModel
@@ -148,6 +148,11 @@ class InternS1VideoEmbeddingInputs(TensorSchema):
148 InternS1VideoInputs: TypeAlias = InternS1VideoPixelInputs | InternS1VideoEmbeddingInputs
149
150
151 +class InternS1MultiModalInputs(TypedDict, total=False):
152 + images: InternS1ImageInputs | None
153 + videos: InternS1VideoInputs | None
154 +
155 +
156 def resolve_interns1_min_max_num(
157 min_dynamic_patch: int,
158 max_dynamic_patch: int,
@@ -312,6 +317,7 @@ class InternS1DummyInputsBuilder(BaseDummyInputsBuilder[InternS1ProcessingInfo])
317
318 image_overrides = mm_options.get("image")
319 video_overrides = mm_options.get("video")
320 + assert video_overrides is None or isinstance(video_overrides, VideoDummyOptions)
321
322 return {
323 "image": self._get_dummy_images(
@@ -474,22 +480,24 @@ class InternS1MultiModalProcessor(BaseMultiModalProcessor[InternS1ProcessingInfo
480 video_token = hf_processor.video_token
481
482 tokenizer = self.info.get_tokenizer()
477 - video_token_id = cached_encode(tokenizer, video_token, add_special_tokens=False)
478 - assert len(video_token_id) == 1
479 - video_token_id = video_token_id[0]
483 + video_token_ids = cached_encode(
484 + tokenizer, video_token, add_special_tokens=False
485 + )
486 + assert len(video_token_ids) == 1
487 + video_token_id = video_token_ids[0]
488
489 out_mm_data = out_mm_kwargs.get_data()
490 if "video_num_patches" in out_mm_data:
483 - video_num_patches = out_mm_data["video_num_patches"]
484 - assert isinstance(video_num_patches, torch.Tensor)
485 - video_num_patches = video_num_patches.tolist()
491 + video_num_patches_tensor = out_mm_data["video_num_patches"]
492 + assert isinstance(video_num_patches_tensor, torch.Tensor)
493 + video_num_patches: list[int] = video_num_patches_tensor.tolist()
494 else:
495 video_num_patches = []
496
497 if "image_num_patches" in out_mm_data:
490 - image_num_patches = out_mm_data["image_num_patches"]
491 - assert isinstance(image_num_patches, torch.Tensor)
492 - image_num_patches = image_num_patches.tolist()
498 + image_num_patches_tensor = out_mm_data["image_num_patches"]
499 + assert isinstance(image_num_patches_tensor, torch.Tensor)
500 + image_num_patches: list[int] = image_num_patches_tensor.tolist()
501 else:
502 image_num_patches = []
503
@@ -602,8 +610,8 @@ class InternS1ForConditionalGeneration(
610 prefix=maybe_prefix(prefix, "language_model"),
611 )
612
605 - self.img_context_token_id = None
606 - self.video_context_token_id = None
613 + self.img_context_token_id: int | None = None
614 + self.video_context_token_id: int | None = None
615
616 self.visual_token_mask = None
617 self.make_empty_intermediate_tensors = (
@@ -756,8 +764,10 @@ class InternS1ForConditionalGeneration(
764 ]
765 return image_embeds.split(image_feature_sizes)
766
759 - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
760 - modalities = {}
767 + def _parse_and_validate_multimodal_inputs(
768 + self, **kwargs: object
769 + ) -> InternS1MultiModalInputs:
770 + modalities: InternS1MultiModalInputs = {}
771
772 # Preserve the order of modalities if there are multiple of them
773 # from the order of kwargs.
@@ -789,12 +799,14 @@ class InternS1ForConditionalGeneration(
799 for modality in modalities:
800 if modality == "images":
801 image_input = modalities["images"]
792 - image_embeddings = self._process_vision_input(image_input)
793 - multimodal_embeddings += tuple(image_embeddings)
802 + if image_input is not None:
803 + image_embeddings = self._process_vision_input(image_input)
804 + multimodal_embeddings += tuple(image_embeddings)
805 if modality == "videos":
806 video_input = modalities["videos"]
796 - video_embeddings = self._process_vision_input(video_input)
797 - multimodal_embeddings += tuple(video_embeddings)
807 + if video_input is not None:
808 + video_embeddings = self._process_vision_input(video_input)
809 + multimodal_embeddings += tuple(video_embeddings)
810
811 return multimodal_embeddings
812
vllm/model_executor/models/interns1_pro.py
+18 -12
@@ -26,7 +26,7 @@
26
27 import functools
28 from collections.abc import Iterable
29 -from typing import Any
29 +from typing import Any, ClassVar
30
31 import torch
32 from torch import nn
@@ -321,6 +321,14 @@ class InternS1ProMoeAttention(nn.Module):
321 dual_chunk_attention_config=dual_chunk_attention_config,
322 )
323
324 + attn_kwargs: dict[str, Any] = (
325 + {
326 + "layer_idx": extract_layer_index(prefix),
327 + "dual_chunk_attention_config": dual_chunk_attention_config,
328 + }
329 + if dual_chunk_attention_config
330 + else {}
331 + )
332 self.attn = Attention(
333 self.num_heads,
334 self.head_dim,
@@ -329,12 +337,7 @@ class InternS1ProMoeAttention(nn.Module):
337 cache_config=cache_config,
338 quant_config=quant_config,
339 prefix=f"{prefix}.attn",
332 - **{
333 - "layer_idx": extract_layer_index(prefix),
334 - "dual_chunk_attention_config": dual_chunk_attention_config,
335 - }
336 - if dual_chunk_attention_config
337 - else {},
340 + **attn_kwargs,
341 )
342
343 self.q_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
@@ -489,6 +492,9 @@ class InternS1ProMoeLLMForCausalLM(Qwen3MoeForCausalLM):
492
493
494 class InternS1ProMoeMixtureOfExperts(MixtureOfExperts):
495 + language_model: InternS1ProMoeLLMForCausalLM
496 + num_local_physical_experts: int
497 +
498 def update_physical_experts_metadata(
499 self,
500 num_physical_experts: int,
@@ -538,8 +544,8 @@ class InternS1ProMoeMixtureOfExperts(MixtureOfExperts):
544 class InternS1ProForConditionalGeneration(
545 Qwen3VLForConditionalGeneration, InternS1ProMoeMixtureOfExperts
546 ):
541 - is_3d_moe_weight: bool = True
542 - packed_modules_mapping = {
547 + is_3d_moe_weight: ClassVar[bool] = True
548 + packed_modules_mapping: dict[str, list[str]] = {
549 "qkv_proj": [
550 "q_proj",
551 "k_proj",
@@ -559,7 +565,7 @@ class InternS1ProForConditionalGeneration(
565 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
566 super(Qwen3VLForConditionalGeneration, self).__init__()
567 config: PretrainedConfig = vllm_config.model_config.hf_config
562 - multimodal_config = vllm_config.model_config.multimodal_config
568 + multimodal_config = vllm_config.model_config.get_multimodal_config()
569
570 self.config = config
571 self.multimodal_config = multimodal_config
@@ -601,8 +607,8 @@ class InternS1ProForConditionalGeneration(
607 # Set MoE hyperparameters
608 self.set_moe_parameters()
609
604 - def get_frope_params_map(self) -> str:
605 - mapper = {}
610 + def get_frope_params_map(self) -> dict[str, str]:
611 + mapper: dict[str, str] = {}
612 for name, params in self.language_model.model.named_parameters():
613 if "rotary_emb.sin_coef" in name:
614 mapper["language_model.model.rotary_emb.sin_coef"] = (
vllm/model_executor/models/interns2_mobius.py
+1 -1
@@ -476,7 +476,7 @@ class InternS2MobiusForConditionalGeneration(InternS2PreviewForConditionalGenera
476 if getattr(config, "image_token_index", None) is None:
477 config.image_token_index = config.image_token_id
478 quant_config = vllm_config.quant_config
479 - multimodal_config = vllm_config.model_config.multimodal_config
479 + multimodal_config = vllm_config.model_config.get_multimodal_config()
480
481 self.config = config
482 self.model_config = vllm_config.model_config
vllm/model_executor/models/internvl.py
+29 -16
@@ -10,14 +10,14 @@
10 from abc import abstractmethod
11 from collections.abc import Iterable, Mapping, Sequence
12 from functools import cached_property
13 -from typing import Annotated, Any, Literal, TypeAlias, TypeVar
13 +from typing import Annotated, Any, Literal, TypeAlias, TypedDict, TypeVar
14
15 import torch
16 import torch.nn as nn
17 from transformers import BatchFeature, PretrainedConfig
18
19 from vllm.config import VllmConfig
20 -from vllm.config.multimodal import BaseDummyOptions
20 +from vllm.config.multimodal import BaseDummyOptions, VideoDummyOptions
21 from vllm.inputs import MultiModalDataDict
22 from vllm.model_executor.layers.quantization import QuantizationConfig
23 from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig
@@ -128,6 +128,11 @@ class InternVLVideoEmbeddingInputs(TensorSchema):
128 InternVLVideoInputs: TypeAlias = InternVLVideoPixelInputs | InternVLVideoEmbeddingInputs
129
130
131 +class InternVLMultiModalInputs(TypedDict, total=False):
132 + images: InternVLImageInputs | None
133 + videos: InternVLVideoInputs | None
134 +
135 +
136 class BaseInternVLProcessingInfo(BaseProcessingInfo):
137 """Basic image-only ProcessingInfo for InternVL-style models."""
138
@@ -286,6 +291,7 @@ class BaseInternVLMultiModalProcessor(BaseMultiModalProcessor[_I]):
291 if isinstance(images, ImageEmbeddingItems):
292 feature_size = images.get_feature_size(item_idx)
293 else:
294 + assert isinstance(images, ImageProcessorItems)
295 image_size = images.get_image_size(item_idx)
296 feature_size = self.info.get_num_image_tokens(
297 image_width=image_size.width,
@@ -433,6 +439,9 @@ class InternVLDummyInputsBuilder(
439 )
440 num_videos = mm_counts.get("video", 0)
441 video_overrides = mm_options.get("video")
442 + assert video_overrides is None or isinstance(
443 + video_overrides, VideoDummyOptions
444 + )
445 dummy_video = {
446 "video": self._get_dummy_videos(
447 width=image_size,
@@ -504,9 +513,9 @@ class InternVLMultiModalProcessor(
513 tokenizer = self.info.get_tokenizer()
514
515 if "video_num_patches" in out_mm_data:
507 - video_num_patches = out_mm_data["video_num_patches"]
508 - assert isinstance(video_num_patches, torch.Tensor)
509 - video_num_patches = video_num_patches.tolist()
516 + video_num_patches_tensor = out_mm_data["video_num_patches"]
517 + assert isinstance(video_num_patches_tensor, torch.Tensor)
518 + video_num_patches: list[int] = video_num_patches_tensor.tolist()
519 else:
520 video_num_patches = []
521
@@ -591,7 +600,7 @@ class InternVLChatModel(
600
601 config = vllm_config.model_config.hf_config
602 quant_config = vllm_config.quant_config
594 - multimodal_config = vllm_config.model_config.multimodal_config
603 + multimodal_config = vllm_config.model_config.get_multimodal_config()
604
605 self.config = config
606 self.multimodal_config = multimodal_config
@@ -621,15 +630,15 @@ class InternVLChatModel(
630 prefix=maybe_prefix(prefix, "language_model"),
631 )
632
624 - self.img_context_token_id = None
625 - self.video_context_token_id = None
633 + self.img_context_token_id: int | None = None
634 + self.video_context_token_id: int | None = None
635
636 self.make_empty_intermediate_tensors = (
637 self.language_model.make_empty_intermediate_tensors
638 )
639
640 def _patch_quant_config(
632 - self, config: PretrainedConfig, quant_config: QuantizationConfig
641 + self, config: PretrainedConfig, quant_config: QuantizationConfig | None
642 ):
643 # the awq models from OpenGVLab missing `modules_to_not_convert`
644 # patch the quant_config to add `modules_to_not_convert` back
@@ -743,7 +752,7 @@ class InternVLChatModel(
752
753 def _parse_and_validate_video_input(
754 self, **kwargs: object
746 - ) -> InternVLVideoPixelInputs | None:
755 + ) -> InternVLVideoInputs | None:
756 pixel_values_flat_video = kwargs.pop("pixel_values_flat_video", None)
757 video_num_patches = kwargs.pop("video_num_patches", None)
758 video_embeds = kwargs.pop("image_embeds", None)
@@ -804,8 +813,10 @@ class InternVLChatModel(
813 ]
814 return image_embeds.split(image_feature_sizes)
815
807 - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
808 - modalities = {}
816 + def _parse_and_validate_multimodal_inputs(
817 + self, **kwargs: object
818 + ) -> InternVLMultiModalInputs:
819 + modalities: InternVLMultiModalInputs = {}
820
821 # Preserve the order of modalities if there are multiple of them
822 # from the order of kwargs.
@@ -834,12 +845,14 @@ class InternVLChatModel(
845 for modality in modalities:
846 if modality == "images":
847 image_input = modalities["images"]
837 - image_embeddings = self._process_vision_input(image_input)
838 - multimodal_embeddings += tuple(image_embeddings)
848 + if image_input is not None:
849 + image_embeddings = self._process_vision_input(image_input)
850 + multimodal_embeddings += tuple(image_embeddings)
851 if modality == "videos":
852 video_input = modalities["videos"]
841 - video_embeddings = self._process_vision_input(video_input)
842 - multimodal_embeddings += tuple(video_embeddings)
853 + if video_input is not None:
854 + video_embeddings = self._process_vision_input(video_input)
855 + multimodal_embeddings += tuple(video_embeddings)
856
857 return multimodal_embeddings
858
vllm/model_executor/models/iquest_loopcoder.py
+10 -6
@@ -16,7 +16,7 @@
16
17 from __future__ import annotations
18
19 -from collections.abc import Iterable
19 +from collections.abc import Callable, Iterable
20 from dataclasses import replace
21 from typing import Any
22
@@ -487,11 +487,14 @@ class IQuestLoopCoderModel(nn.Module):
487 continue
488 if name.endswith("scale"):
489 # Remapping the name of FP8 kv-scale.
490 - name = maybe_remap_kv_scale_name(name, params_dict)
491 - if name is None:
490 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
491 + if remapped_name is None:
492 continue
493 + name = remapped_name
494 param = params_dict[name]
494 - weight_loader = getattr(param, "weight_loader", default_weight_loader)
495 + weight_loader: Callable[..., None] = getattr(
496 + param, "weight_loader", default_weight_loader
497 + )
498 if weight_loader == default_weight_loader:
499 weight_loader(param, loaded_weight)
500 else:
@@ -518,9 +521,10 @@ class IQuestLoopCoderModel(nn.Module):
521 if name.endswith(".bias") and name not in params_dict:
522 continue
523 # Remapping the name of FP8 kv-scale.
521 - name = maybe_remap_kv_scale_name(name, params_dict)
522 - if name is None:
524 + remapped_name = maybe_remap_kv_scale_name(name, params_dict)
525 + if remapped_name is None:
526 continue
527 + name = remapped_name
528 param = params_dict[name]
529 weight_loader = getattr(param, "weight_loader", default_weight_loader)
530 weight_loader(param, loaded_weight)
vllm/model_executor/models/isaac.py
+6 -2
@@ -901,7 +901,11 @@ class IsaacForConditionalGeneration(
901 for mm_feature in sorted(mm_features, key=lambda f: f.mm_position.offset):
902 offset = mm_feature.mm_position.offset
903 if mm_feature.modality == "image":
904 - t, h, w = mm_feature.data["image_grid_thw"].data.tolist()
904 + mm_data = mm_feature.data
905 + assert mm_data is not None
906 + grid_thw = mm_data["image_grid_thw"].data
907 + assert isinstance(grid_thw, torch.Tensor)
908 + t, h, w = grid_thw.tolist()
909 assert t == 1, f"Image must have 1 frame, got {t}"
910 yield offset, h // spatial_merge_size, w // spatial_merge_size
911 else:
@@ -912,7 +916,7 @@ class IsaacForConditionalGeneration(
916 input_tokens: list[int],
917 mm_features: list[MultiModalFeatureSpec],
918 ) -> tuple[torch.Tensor, int]:
915 - llm_pos_ids_list = []
919 + llm_pos_ids_list: list[np.ndarray] = []
920 st = 0
921 for offset, llm_grid_h, llm_grid_w in self.iter_mm_grid_hw(
922 input_tokens, mm_features
vllm/model_executor/models/qwen3_vl.py
+1 -1
@@ -1753,7 +1753,7 @@ class Qwen3VLForConditionalGeneration(
1753 SupportsEagle3,
1754 SupportsMultiModalPruning,
1755 ):
1756 - packed_modules_mapping = {
1756 + packed_modules_mapping: dict[str, list[str]] = {
1757 "qkv_proj": [
1758 "q_proj",
1759 "k_proj",