@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / d55244df31
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 15, 14:45 UTC

[Model] Add `SupportsMultiModal.get_language_model` interface (#16007)

Signed-off-by: NickLucche <nlucches@redhat.com>

Nicolò Lucchesi committed Apr 9, 2025 at 13:12 UTC d55244df31969e7df435603b5d7014939e60881b
33 files changed +116
docs/source/contributing/model/multimodal.md
+11
@@ -79,6 +79,17 @@ Further update the model as follows:
79 return inputs_embeds
80 ```
81
82 +- Implement {meth}`~vllm.model_executor.models.interfaces.SupportsMultiModal.get_language_model` getter to provide stable access to the underlying language model.
83 +
84 + ```python
85 + class YourModelForImage2Seq(nn.Module):
86 + ...
87 +
88 + def get_language_model(self) -> torch.nn.Module:
89 + # Change `language_model` according to your implementation.
90 + return self.language_model
91 + ```
92 +
93 - Once the above steps are done, update the model class with the {class}`~vllm.model_executor.models.interfaces.SupportsMultiModal` interface.
94
95 ```diff
vllm/model_executor/models/aria.py
+3
@@ -605,6 +605,9 @@ class AriaForConditionalGeneration(nn.Module, SupportsMultiModal):
605
606 return self.multi_modal_projector(image_outputs, image_attn_mask)
607
608 + def get_language_model(self) -> torch.nn.Module:
609 + return self.language_model
610 +
611 def get_multimodal_embeddings(
612 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
613 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/aya_vision.py
+3
@@ -424,6 +424,9 @@ class AyaVisionForConditionalGeneration(nn.Module, SupportsMultiModal,
424 num_patches=num_patches,
425 )
426
427 + def get_language_model(self) -> torch.nn.Module:
428 + return self.language_model
429 +
430 def get_multimodal_embeddings(
431 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
432 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/blip2.py
+3
@@ -627,6 +627,9 @@ class Blip2ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
627
628 return self.language_projection(query_output)
629
630 + def get_language_model(self) -> torch.nn.Module:
631 + return self.language_model
632 +
633 def get_multimodal_embeddings(
634 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
635 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/chameleon.py
+3
@@ -988,6 +988,9 @@ class ChameleonForConditionalGeneration(nn.Module, SupportsMultiModal,
988 data=self._validate_pixel_values(pixel_values),
989 )
990
991 + def get_language_model(self) -> torch.nn.Module:
992 + return self.model
993 +
994 def get_multimodal_embeddings(
995 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
996 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/deepseek_vl2.py
+3
@@ -604,6 +604,9 @@ class DeepseekVLV2ForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
604 return self._pixel_values_to_embedding(
605 pixel_values=pixel_values, images_spatial_crop=images_spatial_crop)
606
607 + def get_language_model(self) -> torch.nn.Module:
608 + return self.language_model
609 +
610 def get_multimodal_embeddings(
611 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
612 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/florence2.py
+3
@@ -1050,6 +1050,9 @@ class Florence2ForConditionalGeneration(nn.Module, SupportsMultiModal,
1050 pixel_values = image_input["data"]
1051 return self._encode_image(pixel_values)
1052
1053 + def get_language_model(self) -> torch.nn.Module:
1054 + return self.language_model
1055 +
1056 def get_multimodal_embeddings(
1057 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
1058 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/fuyu.py
+3
@@ -341,6 +341,9 @@ class FuyuForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
341
342 return vision_embeddings_flat.split(patches_per_image, dim=0)
343
344 + def get_language_model(self) -> torch.nn.Module:
345 + return self.language_model
346 +
347 def get_multimodal_embeddings(
348 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
349 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/gemma3_mm.py
+3
@@ -591,6 +591,9 @@ class Gemma3ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
591 e.flatten(0, 1) for e in image_embeds.split(num_patches.tolist())
592 ]
593
594 + def get_language_model(self) -> torch.nn.Module:
595 + return self.language_model
596 +
597 def get_multimodal_embeddings(
598 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
599 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/glm4v.py
+3
@@ -596,6 +596,9 @@ class GLM4VForCausalLM(ChatGLMBaseModel, SupportsLoRA, SupportsPP,
596
597 return self.transformer.vision(pixel_values)
598
599 + def get_language_model(self) -> torch.nn.Module:
600 + return self.transformer
601 +
602 def get_multimodal_embeddings(
603 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
604 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/idefics3.py
+3
@@ -710,6 +710,9 @@ class Idefics3ForConditionalGeneration(nn.Module, SupportsMultiModal,
710 e.flatten(0, 1) for e in image_features.split(num_patches.tolist())
711 ]
712
713 + def get_language_model(self) -> torch.nn.Module:
714 + return self.model
715 +
716 def get_multimodal_embeddings(
717 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
718 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/interfaces.py
+12
@@ -56,6 +56,18 @@ class SupportsMultiModal(Protocol):
56 """
57 ...
58
59 + def get_language_model(self) -> torch.nn.Module:
60 + """
61 + Returns the underlying language model used for text generation.
62 +
63 + This is typically the `torch.nn.Module` instance responsible for
64 + processing the merged multimodal embeddings and producing hidden states
65 +
66 + Returns:
67 + torch.nn.Module: The core language model component.
68 + """
69 + ...
70 +
71 # Only for models that support v0 chunked prefill
72 # TODO(ywang96): Remove this overload once v0 is deprecated
73 @overload
vllm/model_executor/models/internvl.py
+3
@@ -884,6 +884,9 @@ class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP):
884 else:
885 self.visual_token_mask = None
886
887 + def get_language_model(self) -> torch.nn.Module:
888 + return self.language_model
889 +
890 def get_multimodal_embeddings(
891 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
892 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/llava.py
+3
@@ -674,6 +674,9 @@ class LlavaForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP):
674 image_embeds = torch.split(image_embeds, feature_sizes)
675 return image_embeds
676
677 + def get_language_model(self) -> torch.nn.Module:
678 + return self.language_model
679 +
680 def get_multimodal_embeddings(
681 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
682 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/llava_next.py
+3
@@ -480,6 +480,9 @@ class LlavaNextForConditionalGeneration(nn.Module, SupportsMultiModal,
480 for i, patch_features_batch in enumerate(patch_embeddings)
481 ]
482
483 + def get_language_model(self) -> torch.nn.Module:
484 + return self.language_model
485 +
486 def get_multimodal_embeddings(
487 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
488 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/llava_next_video.py
+3
@@ -421,6 +421,9 @@ class LlavaNextVideoForConditionalGeneration(nn.Module, SupportsMultiModal,
421
422 return [e.flatten(0, 1) for e in embeds]
423
424 + def get_language_model(self) -> torch.nn.Module:
425 + return self.language_model
426 +
427 def get_multimodal_embeddings(
428 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
429 video_input = self._parse_and_validate_video_input(**kwargs)
vllm/model_executor/models/llava_onevision.py
+3
@@ -852,6 +852,9 @@ class LlavaOnevisionForConditionalGeneration(nn.Module, SupportsMultiModal,
852 image_feature = image_feature.view(batch_frames, -1, dim)
853 return image_feature
854
855 + def get_language_model(self) -> torch.nn.Module:
856 + return self.language_model
857 +
858 def get_multimodal_embeddings(
859 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
860 modalities = self._parse_and_validate_multimodal_inputs(**kwargs)
vllm/model_executor/models/minicpmv.py
+3
@@ -892,6 +892,9 @@ class MiniCPMVBaseModel(nn.Module, SupportsMultiModal, SupportsPP):
892
893 return multimodal_embeddings
894
895 + def get_language_model(self) -> torch.nn.Module:
896 + return self.llm
897 +
898 def get_multimodal_embeddings(
899 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
900 modalities = self._parse_and_validate_multimodal_inputs(**kwargs)
vllm/model_executor/models/mistral3.py
+3
@@ -514,6 +514,9 @@ class Mistral3ForConditionalGeneration(nn.Module, SupportsMultiModal,
514 image_embeds = (image_embeds, )
515 return image_embeds
516
517 + def get_language_model(self) -> torch.nn.Module:
518 + return self.language_model
519 +
520 def get_multimodal_embeddings(
521 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
522 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/mllama.py
+3
@@ -1325,6 +1325,9 @@ class MllamaForConditionalGeneration(nn.Module, SupportsMultiModal,
1325 cross_attention_states = cross_attention_states_flat
1326 return cross_attention_states
1327
1328 + def get_language_model(self) -> torch.nn.Module:
1329 + return self.language_model
1330 +
1331 def get_cross_attention_states(
1332 self,
1333 image_inputs: MllamaImagePixelInputs,
vllm/model_executor/models/mllama4.py
+3
@@ -742,6 +742,9 @@ class Llama4ForConditionalGeneration(nn.Module, SupportsMultiModal,
742 for img in vision_embeddings_flat.split(patches_per_image, dim=0)
743 ]
744
745 + def get_language_model(self) -> torch.nn.Module:
746 + return self.language_model
747 +
748 def get_multimodal_embeddings(self,
749 **kwargs) -> Optional[MultiModalEmbeddings]:
750 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/molmo.py
+3
@@ -1488,6 +1488,9 @@ class MolmoForCausalLM(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA,
1488 )
1489 ]
1490
1491 + def get_language_model(self) -> torch.nn.Module:
1492 + return self.model
1493 +
1494 def get_multimodal_embeddings(
1495 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
1496 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/paligemma.py
+3
@@ -323,6 +323,9 @@ class PaliGemmaForConditionalGeneration(nn.Module, SupportsMultiModal,
323
324 return self.multi_modal_projector(image_features)
325
326 + def get_language_model(self) -> torch.nn.Module:
327 + return self.language_model
328 +
329 def get_multimodal_embeddings(
330 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
331 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/phi3v.py
+3
@@ -674,6 +674,9 @@ class Phi3VForCausalLM(nn.Module, SupportsMultiModal, SupportsPP,
674
675 return image_embeds
676
677 + def get_language_model(self) -> torch.nn.Module:
678 + return self.language_model
679 +
680 def get_multimodal_embeddings(
681 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
682 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/phi4mm.py
+3
@@ -1802,3 +1802,6 @@ class Phi4MMForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal,
1802 connector=["audio_projection_for_vision", "audio_projection"],
1803 tower_model=["vision_encoder", "embed_tokens_extend"],
1804 )
1805 +
1806 + def get_language_model(self) -> torch.nn.Module:
1807 + return self.model
vllm/model_executor/models/pixtral.py
+3
@@ -396,6 +396,9 @@ class PixtralForConditionalGeneration(nn.Module, SupportsMultiModal,
396 image_embeds = torch.split(image_embeds, feature_sizes)
397 return image_embeds
398
399 + def get_language_model(self) -> torch.nn.Module:
400 + return self.language_model
401 +
402 def get_multimodal_embeddings(
403 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
404 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/qwen2_5_vl.py
+3
@@ -967,6 +967,9 @@ class Qwen2_5_VLForConditionalGeneration(nn.Module, SupportsMultiModal,
967 **kwargs)
968 return modalities
969
970 + def get_language_model(self) -> torch.nn.Module:
971 + return self.language_model
972 +
973 def get_multimodal_embeddings(
974 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
975
vllm/model_executor/models/qwen2_audio.py
+3
@@ -355,6 +355,9 @@ class Qwen2AudioForConditionalGeneration(nn.Module, SupportsMultiModal,
355 return torch.split(masked_audio_features,
356 audio_output_lengths.flatten().tolist())
357
358 + def get_language_model(self) -> torch.nn.Module:
359 + return self.language_model
360 +
361 def get_multimodal_embeddings(
362 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
363 audio_input = self._parse_and_validate_audio_input(**kwargs)
vllm/model_executor/models/qwen2_vl.py
+3
@@ -1276,6 +1276,9 @@ class Qwen2VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1276
1277 return modalities
1278
1279 + def get_language_model(self) -> torch.nn.Module:
1280 + return self.language_model
1281 +
1282 def get_multimodal_embeddings(
1283 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
1284
vllm/model_executor/models/qwen_vl.py
+3
@@ -740,6 +740,9 @@ class QwenVLForConditionalGeneration(QWenBaseModel, SupportsPP, SupportsLoRA,
740
741 return self.transformer.visual(image_input["data"])
742
743 + def get_language_model(self) -> torch.nn.Module:
744 + return self.transformer
745 +
746 def get_multimodal_embeddings(
747 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
748 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/skyworkr1v.py
+3
@@ -889,6 +889,9 @@ class SkyworkR1VChatModel(nn.Module, SupportsMultiModal, SupportsPP):
889 else:
890 self.visual_token_mask = None
891
892 + def get_language_model(self) -> torch.nn.Module:
893 + return self.language_model
894 +
895 def get_multimodal_embeddings(
896 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
897 image_input = self._parse_and_validate_image_input(**kwargs)
vllm/model_executor/models/ultravox.py
+3
@@ -563,6 +563,9 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
563 ]
564 return flattened_embeddings.split(embed_lens)
565
566 + def get_language_model(self) -> torch.nn.Module:
567 + return self.language_model
568 +
569 def get_multimodal_embeddings(
570 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
571 audio_input = self._parse_and_validate_audio_input(**kwargs)
vllm/model_executor/models/whisper.py
+3
@@ -692,6 +692,9 @@ class WhisperForConditionalGeneration(nn.Module, SupportsTranscription,
692 )
693 return decoder_outputs
694
695 + def get_language_model(self) -> torch.nn.Module:
696 + return self.model.decoder
697 +
698 def get_multimodal_embeddings(
699 self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
700 # TODO: This method does not obey the interface for SupportsMultiModal.