@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 27d7638b94
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 14, 05:30 UTC

[Bugfix] Merge MM embeddings by index instead of token IDs (#16229)

Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk> Signed-off-by: NickLucche <nlucches@redhat.com> Signed-off-by: Roger Wang <hey@rogerw.io> Co-authored-by: NickLucche <nlucches@redhat.com> Co-authored-by: Roger Wang <hey@rogerw.io>

Cyrus Leung committed Sep 27, 2025 at 16:15 UTC 27d7638b9476062931a6770ed90714792e77cc83
80 files changed +965 -1138
docs/contributing/model/multimodal.md
+5 -28
@@ -66,35 +66,12 @@ Further update the model as follows:
66 !!! important
67 The returned `multimodal_embeddings` must be either a **3D [torch.Tensor][]** of shape `(num_items, feature_size, hidden_size)`, or a **list / tuple of 2D [torch.Tensor][]'s** of shape `(feature_size, hidden_size)`, so that `multimodal_embeddings[i]` retrieves the embeddings generated from the `i`-th multimodal data item (e.g, image) of the request.
68
69 -- Implement [get_input_embeddings][vllm.model_executor.models.interfaces.SupportsMultiModal.get_input_embeddings] to merge `multimodal_embeddings` with text embeddings from the `input_ids`. If input processing for the model is implemented correctly (see sections below), then you can leverage the utility function we provide to easily merge the embeddings.
70 -
71 - ??? code
72 -
73 - ```python
74 - from .utils import merge_multimodal_embeddings
75 -
76 - class YourModelForImage2Seq(nn.Module):
77 - ...
78 -
79 - def get_input_embeddings(
80 - self,
81 - input_ids: torch.Tensor,
82 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
83 - ) -> torch.Tensor:
84 -
85 - # `get_input_embeddings` should already be implemented for the language
86 - # model as one of the requirements of basic vLLM model implementation.
87 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
88 -
89 - if multimodal_embeddings is not None:
90 - inputs_embeds = merge_multimodal_embeddings(
91 - input_ids=input_ids,
92 - inputs_embeds=inputs_embeds,
93 - multimodal_embeddings=multimodal_embeddings,
94 - placeholder_token_id=self.config.image_token_index)
69 +!!! note
70 + By default, vLLM merges the multimodal embeddings into text embeddings depending on the information of their locations defined in
71 + [PlaceholderRange][vllm.multimodal.inputs.PlaceholderRange] from input processing.
72 + This logic can be found at [get_input_embeddings][vllm.model_executor.models.interfaces.SupportsMultiModal.get_input_embeddings].
73
96 - return inputs_embeds
97 - ```
74 + You may override this method if additional logic is required for your model when merging embeddings.
75
76 - Implement [get_language_model][vllm.model_executor.models.interfaces.SupportsMultiModal.get_language_model] getter to provide stable access to the underlying language model.
77
vllm/config/model.py
+6 -1
@@ -509,9 +509,14 @@ class ModelConfig:
509 else: # task == "auto"
510 pass
511 else:
512 + debug_info = {
513 + "architectures": architectures,
514 + "is_generative_model": is_generative_model,
515 + "is_pooling_model": is_pooling_model,
516 + }
517 raise AssertionError("The model should be a generative or "
518 "pooling model when task is set to "
514 - f"{self.task!r}.")
519 + f"{self.task!r}. Found: {debug_info}")
520
521 self.runner = runner
522 self.convert = convert
vllm/model_executor/models/aria.py
+6 -19
@@ -38,8 +38,7 @@ from .idefics2_vision_model import (
38 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsQuant
39 from .llama import LlamaDecoderLayer, LlamaMLP, LlamaModel
40 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
41 - is_pp_missing_parameter, maybe_prefix,
42 - merge_multimodal_embeddings)
41 + is_pp_missing_parameter, maybe_prefix)
42
43
44 class AriaImagePixelInputs(TensorSchema):
@@ -605,19 +604,6 @@ class AriaForConditionalGeneration(nn.Module, SupportsMultiModal):
604 multimodal_embeddings = self._process_image_input(image_input)
605 return multimodal_embeddings
606
608 - def get_input_embeddings(
609 - self,
610 - input_ids: torch.Tensor,
611 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
612 - ) -> torch.Tensor:
613 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
614 - if multimodal_embeddings is not None \
615 - and len(multimodal_embeddings) != 0:
616 - inputs_embeds = merge_multimodal_embeddings(
617 - input_ids, inputs_embeds, multimodal_embeddings,
618 - self.config.image_token_index)
619 - return inputs_embeds
620 -
607 def forward(
608 self,
609 input_ids: torch.Tensor,
@@ -628,10 +614,11 @@ class AriaForConditionalGeneration(nn.Module, SupportsMultiModal):
614 ) -> Union[torch.Tensor, IntermediateTensors]:
615 if inputs_embeds is None:
616 multimodal_embeddings = self.get_multimodal_embeddings(**kwargs)
631 - # always pass the input via `inputs_embeds`
632 - # to make sure the computation graph is consistent
633 - inputs_embeds = self.get_input_embeddings(input_ids,
634 - multimodal_embeddings)
617 + inputs_embeds = self.get_input_embeddings(
618 + input_ids,
619 + multimodal_embeddings,
620 + is_multimodal=input_ids == self.config.image_token_index,
621 + )
622 input_ids = None
623
624 hidden_states = self.language_model(
vllm/model_executor/models/aya_vision.py
+6 -21
@@ -33,8 +33,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
33 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
34 from .siglip import SiglipVisionModel
35 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
36 - init_vllm_registered_model, maybe_prefix,
37 - merge_multimodal_embeddings)
36 + init_vllm_registered_model, maybe_prefix)
37
38
39 class AyaVisionImagePixelInputs(TensorSchema):
@@ -417,23 +416,6 @@ class AyaVisionForConditionalGeneration(nn.Module, SupportsMultiModal,
416
417 return self._process_image_input(image_input, **kwargs)
418
420 - def get_input_embeddings(
421 - self,
422 - input_ids: torch.Tensor,
423 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
424 - ) -> torch.Tensor:
425 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
426 - if multimodal_embeddings is not None \
427 - and len(multimodal_embeddings) != 0:
428 - inputs_embeds = merge_multimodal_embeddings(
429 - input_ids=input_ids,
430 - inputs_embeds=inputs_embeds,
431 - multimodal_embeddings=multimodal_embeddings,
432 - placeholder_token_id=self.config.image_token_index,
433 - )
434 -
435 - return inputs_embeds
436 -
419 def forward(
420 self,
421 input_ids: torch.Tensor,
@@ -449,8 +431,11 @@ class AyaVisionForConditionalGeneration(nn.Module, SupportsMultiModal,
431 # condition is for v0 compatibility.
432 elif inputs_embeds is None:
433 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
452 - inputs_embeds = self.get_input_embeddings(input_ids,
453 - vision_embeddings)
434 + inputs_embeds = self.get_input_embeddings(
435 + input_ids,
436 + vision_embeddings,
437 + is_multimodal=input_ids == self.config.image_token_index,
438 + )
439 input_ids = None
440
441 hidden_states = self.language_model.model(
vllm/model_executor/models/bert.py
+12
@@ -348,6 +348,9 @@ class BertModel(nn.Module, SupportsQuant):
348 self.encoder = BertEncoder(vllm_config=vllm_config,
349 prefix=f"{prefix}.encoder")
350
351 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
352 + return self.embeddings(input_ids)
353 +
354 def forward(
355 self,
356 input_ids: torch.Tensor,
@@ -457,6 +460,9 @@ class BertEmbeddingModel(nn.Module, SupportsQuant):
460 prefix=maybe_prefix(prefix, "model"))
461 self.pooler = self._build_pooler(pooler_config)
462
463 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
464 + return self.model.get_input_embeddings(input_ids)
465 +
466 def forward(
467 self,
468 input_ids: torch.Tensor,
@@ -588,6 +594,9 @@ class BertForSequenceClassification(nn.Module, SupportsCrossEncoding,
594 ),
595 })
596
597 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
598 + return self.bert.get_input_embeddings(input_ids)
599 +
600 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
601 loader = AutoWeightsLoader(self)
602 loaded_params = loader.load_weights(weights)
@@ -637,6 +646,9 @@ class BertForTokenClassification(nn.Module):
646 Pooler.for_encode(pooler_config),
647 })
648
649 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
650 + return self.bert.get_input_embeddings(input_ids)
651 +
652 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
653 loader = AutoWeightsLoader(self)
654 loaded_params = loader.load_weights(weights)
vllm/model_executor/models/bert_with_rope.py
+6
@@ -426,6 +426,9 @@ class BertWithRope(nn.Module, SupportsQuant):
426 prefix=f"{prefix}.encoder")
427 self.pooler = BertPooler(self.config) if add_pooling_layer else None
428
429 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
430 + return self.embeddings(input_ids)
431 +
432 def forward(
433 self,
434 input_ids: torch.Tensor,
@@ -673,6 +676,9 @@ class GteNewForSequenceClassification(nn.Module, SupportsCrossEncoding):
676 loaded_params = loader.load_weights(weights)
677 return loaded_params
678
679 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
680 + return self.new.get_input_embeddings(input_ids)
681 +
682 def forward(
683 self,
684 input_ids: Optional[torch.Tensor],
vllm/model_executor/models/blip2.py
+6 -16
@@ -27,7 +27,7 @@ from .blip import BlipVisionModel
27 from .interfaces import (MultiModalEmbeddings, SupportsMultiModal, SupportsPP,
28 SupportsQuant)
29 from .utils import (AutoWeightsLoader, flatten_bn, init_vllm_registered_model,
30 - maybe_prefix, merge_multimodal_embeddings)
30 + maybe_prefix)
31
32 # We use this internally as placeholders since there is no image token
33 # defined on the HuggingFace repo
@@ -631,19 +631,6 @@ class Blip2ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
631 vision_embeddings = self._process_image_input(image_input)
632 return vision_embeddings
633
634 - def get_input_embeddings(
635 - self,
636 - input_ids: torch.Tensor,
637 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
638 - ) -> torch.Tensor:
639 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
640 - if multimodal_embeddings is not None \
641 - and len(multimodal_embeddings) != 0:
642 - inputs_embeds = merge_multimodal_embeddings(
643 - input_ids, inputs_embeds, multimodal_embeddings,
644 - _IMAGE_TOKEN_ID)
645 - return inputs_embeds
646 -
634 def forward(
635 self,
636 input_ids: torch.Tensor,
@@ -689,8 +676,11 @@ class Blip2ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
676 # condition is for v0 compatibility.
677 elif inputs_embeds is None:
678 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
692 - inputs_embeds = self.get_input_embeddings(input_ids,
693 - vision_embeddings)
679 + inputs_embeds = self.get_input_embeddings(
680 + input_ids,
681 + vision_embeddings,
682 + is_multimodal=input_ids == _IMAGE_TOKEN_ID,
683 + )
684 input_ids = None
685
686 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/chameleon.py
+7 -17
@@ -44,7 +44,7 @@ from .interfaces import (MultiModalEmbeddings, SupportsMultiModal, SupportsPP,
44 SupportsQuant)
45 from .utils import (flatten_bn, is_pp_missing_parameter,
46 make_empty_intermediate_tensors_factory, make_layers,
47 - maybe_prefix, merge_multimodal_embeddings)
47 + maybe_prefix)
48
49 logger = init_logger(__name__)
50
@@ -1002,20 +1002,6 @@ class ChameleonForConditionalGeneration(nn.Module, SupportsMultiModal,
1002 vision_embeddings = self.model.get_input_embeddings(image_tokens)
1003 return vision_embeddings
1004
1005 - def get_input_embeddings(
1006 - self,
1007 - input_ids: torch.Tensor,
1008 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1009 - ) -> torch.Tensor:
1010 -
1011 - inputs_embeds = self.model.get_input_embeddings(input_ids)
1012 - if multimodal_embeddings is not None \
1013 - and len(multimodal_embeddings) != 0:
1014 - inputs_embeds = merge_multimodal_embeddings(
1015 - input_ids, inputs_embeds, multimodal_embeddings,
1016 - self.model.vocabulary_mapping.image_token_id)
1017 - return inputs_embeds
1018 -
1005 def forward(
1006 self,
1007 input_ids: torch.Tensor,
@@ -1032,8 +1018,12 @@ class ChameleonForConditionalGeneration(nn.Module, SupportsMultiModal,
1018 # condition is for v0 compatibility.
1019 elif inputs_embeds is None:
1020 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
1035 - inputs_embeds = self.get_input_embeddings(input_ids,
1036 - vision_embeddings)
1021 + image_token_id = self.model.vocabulary_mapping.image_token_id
1022 + inputs_embeds = self.get_input_embeddings(
1023 + input_ids,
1024 + vision_embeddings,
1025 + is_multimodal=input_ids == image_token_id,
1026 + )
1027 input_ids = None
1028
1029 hidden_states = self.model(input_ids,
vllm/model_executor/models/chatglm.py
+3
@@ -433,6 +433,9 @@ class ChatGLMBaseModel(nn.Module):
433 self.make_empty_intermediate_tensors = (
434 self.transformer.make_empty_intermediate_tensors)
435
436 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
437 + return self.transformer.get_input_embeddings(input_ids)
438 +
439 def compute_logits(
440 self,
441 hidden_states: torch.Tensor,
vllm/model_executor/models/cohere2_vision.py
+6 -21
@@ -37,8 +37,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
37 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
38 from .siglip import SiglipVisionModel
39 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
40 - init_vllm_registered_model, maybe_prefix,
41 - merge_multimodal_embeddings)
40 + init_vllm_registered_model, maybe_prefix)
41
42
43 class Cohere2VisionImagePixelInputs(TensorSchema):
@@ -430,23 +429,6 @@ class Cohere2VisionForConditionalGeneration(nn.Module, SupportsMultiModal,
429
430 return self._process_image_input(image_input, **kwargs)
431
433 - def get_input_embeddings(
434 - self,
435 - input_ids: torch.Tensor,
436 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
437 - ) -> torch.Tensor:
438 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
439 - if multimodal_embeddings is not None \
440 - and len(multimodal_embeddings) != 0:
441 - inputs_embeds = merge_multimodal_embeddings(
442 - input_ids=input_ids,
443 - inputs_embeds=inputs_embeds,
444 - multimodal_embeddings=multimodal_embeddings,
445 - placeholder_token_id=self.config.image_token_id,
446 - )
447 -
448 - return inputs_embeds
449 -
432 def forward(
433 self,
434 input_ids: torch.Tensor,
@@ -462,8 +444,11 @@ class Cohere2VisionForConditionalGeneration(nn.Module, SupportsMultiModal,
444 # condition is for v0 compatibility.
445 elif inputs_embeds is None:
446 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
465 - inputs_embeds = self.get_input_embeddings(input_ids,
466 - vision_embeddings)
447 + inputs_embeds = self.get_input_embeddings(
448 + input_ids,
449 + vision_embeddings,
450 + is_multimodal=input_ids == self.config.image_token_id,
451 + )
452 input_ids = None
453
454 hidden_states = self.language_model.model(
vllm/model_executor/models/deepseek_eagle.py
+6
@@ -66,6 +66,9 @@ class DeepseekV2Model(nn.Module):
66 self.norm = RMSNorm(self.config.hidden_size,
67 eps=self.config.rms_norm_eps)
68
69 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
70 + return self.embed_tokens(input_ids)
71 +
72 def forward(
73 self,
74 input_ids: torch.Tensor,
@@ -205,6 +208,9 @@ class EagleDeepseekV3ForCausalLM(DeepseekV3ForCausalLM):
208 self.logits_processor = LogitsProcessor(self.config.vocab_size,
209 scale=logit_scale)
210
211 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
212 + return self.model.get_input_embeddings(input_ids)
213 +
214 def forward(
215 self,
216 input_ids: torch.Tensor,
vllm/model_executor/models/deepseek_mtp.py
+6
@@ -101,6 +101,9 @@ class DeepSeekMultiTokenPredictor(nn.Module):
101 )
102 self.logits_processor = LogitsProcessor(config.vocab_size)
103
104 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
105 + return self.embed_tokens(input_ids)
106 +
107 def forward(
108 self,
109 input_ids: torch.Tensor,
@@ -142,6 +145,9 @@ class DeepSeekMTP(nn.Module, SupportsPP):
145 prefix=maybe_prefix(
146 prefix, "model"))
147
148 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
149 + return self.model.get_input_embeddings(input_ids)
150 +
151 def forward(
152 self,
153 input_ids: torch.Tensor,
vllm/model_executor/models/deepseek_vl2.py
+7 -18
@@ -41,8 +41,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
41
42 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
43 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
44 - init_vllm_registered_model, maybe_prefix,
45 - merge_multimodal_embeddings)
44 + init_vllm_registered_model, maybe_prefix)
45
46 # The image token id may be various
47 _IMAGE_TOKEN = "<image>"
@@ -346,7 +345,7 @@ class DeepseekVLV2ForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
345
346 model_config = vllm_config.model_config
347 tokenizer = cached_tokenizer_from_config(model_config)
349 - self.image_token_id = tokenizer.vocab[_IMAGE_TOKEN]
348 + self.image_token_id: int = tokenizer.vocab[_IMAGE_TOKEN]
349
350 self.vision = self._init_vision_module(self.vision_config,
351 quant_config,
@@ -605,19 +604,6 @@ class DeepseekVLV2ForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
604 vision_embeddings = self._process_image_input(image_input)
605 return vision_embeddings
606
608 - def get_input_embeddings(
609 - self,
610 - input_ids: torch.Tensor,
611 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
612 - ) -> torch.Tensor:
613 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
614 - if multimodal_embeddings is not None \
615 - and len(multimodal_embeddings) != 0:
616 - inputs_embeds = merge_multimodal_embeddings(
617 - input_ids, inputs_embeds, multimodal_embeddings,
618 - self.image_token_id)
619 - return inputs_embeds
620 -
607 def forward(self,
608 input_ids: torch.Tensor,
609 positions: torch.Tensor,
@@ -632,8 +618,11 @@ class DeepseekVLV2ForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
618 # condition is for v0 compatibility
619 elif inputs_embeds is None:
620 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
635 - inputs_embeds = self.get_input_embeddings(input_ids,
636 - vision_embeddings)
621 + inputs_embeds = self.get_input_embeddings(
622 + input_ids,
623 + vision_embeddings,
624 + is_multimodal=input_ids == self.image_token_id,
625 + )
626 input_ids = None
627
628 hidden_states = self.language_model(input_ids,
vllm/model_executor/models/dots_ocr.py
+12 -32
@@ -34,8 +34,7 @@ from vllm.model_executor.models.qwen2_vl import (Qwen2VLDummyInputsBuilder,
34 Qwen2VLProcessingInfo)
35 from vllm.model_executor.models.utils import (AutoWeightsLoader, WeightsMapper,
36 init_vllm_registered_model,
37 - maybe_prefix,
38 - merge_multimodal_embeddings)
37 + maybe_prefix)
38 from vllm.model_executor.models.vision import get_vit_attn_backend
39 from vllm.multimodal import MULTIMODAL_REGISTRY
40 from vllm.multimodal.inputs import MultiModalDataDict
@@ -796,33 +795,17 @@ class DotsOCRForCausalLM(nn.Module, SupportsMultiModal, SupportsPP,
795 def get_language_model(self) -> torch.nn.Module:
796 return self.language_model
797
799 - def get_multimodal_embeddings(
800 - self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
798 + def get_multimodal_embeddings(self,
799 + **kwargs: object) -> MultiModalEmbeddings:
800 image_input = self._parse_and_validate_image_input(**kwargs)
801 if image_input is None:
802 return []
803 vision_embeddings = self._process_image_input(image_input)
804 return vision_embeddings
805
807 - def get_input_embeddings(
808 - self,
809 - input_ids: torch.Tensor,
810 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
811 - ) -> torch.Tensor:
812 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
813 - if multimodal_embeddings is not None:
814 - inputs_embeds = merge_multimodal_embeddings(
815 - input_ids,
816 - inputs_embeds,
817 - multimodal_embeddings,
818 - self.config.image_token_id,
819 - )
820 -
821 - return inputs_embeds
822 -
806 def forward(
807 self,
825 - input_ids: Optional[torch.Tensor],
808 + input_ids: torch.Tensor,
809 positions: torch.Tensor,
810 intermediate_tensors: Optional[IntermediateTensors] = None,
811 inputs_embeds: Optional[torch.Tensor] = None,
@@ -830,17 +813,14 @@ class DotsOCRForCausalLM(nn.Module, SupportsMultiModal, SupportsPP,
813 ) -> Union[torch.Tensor, IntermediateTensors]:
814 if intermediate_tensors is not None:
815 inputs_embeds = None
833 - elif inputs_embeds is None and kwargs.get("pixel_values") is not None:
834 - image_input = self._parse_and_validate_image_input(**kwargs)
835 - if image_input is None:
836 - inputs_embeds = None
837 - else:
838 - assert input_ids is not None
839 - inputs_embeds = self.get_multimodal_embeddings(
840 - input_ids,
841 - image_input=image_input,
842 - )
843 - input_ids = None
816 + elif inputs_embeds is None:
817 + vision_embeddings = self.get_multimodal_embeddings(**kwargs)
818 + inputs_embeds = self.get_input_embeddings(
819 + input_ids,
820 + vision_embeddings,
821 + is_multimodal=input_ids == self.config.image_token_id,
822 + )
823 + input_ids = None
824
825 hidden_states = self.language_model(
826 input_ids=input_ids,
vllm/model_executor/models/ernie45_vl.py
+18 -13
@@ -60,8 +60,7 @@ from vllm.sequence import IntermediateTensors
60 from .ernie45_vl_moe import Ernie4_5_VLMoeForCausalLM
61 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
62 SupportsMultiModal, SupportsPP)
63 -from .utils import (AutoWeightsLoader, WeightsMapper, maybe_prefix,
64 - merge_multimodal_embeddings)
63 +from .utils import AutoWeightsLoader, WeightsMapper, maybe_prefix
64 from .vision import get_vit_attn_backend
65
66 logger = init_logger(__name__)
@@ -1467,18 +1466,24 @@ class Ernie4_5_VLMoeForConditionalGeneration(nn.Module, SupportsMultiModal,
1466 self,
1467 input_ids: torch.Tensor,
1468 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1469 + *,
1470 + is_multimodal: Optional[torch.Tensor] = None,
1471 + handle_oov_mm_token: bool = False,
1472 ) -> torch.Tensor:
1471 -
1472 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1473 -
1474 - if multimodal_embeddings is None:
1475 - return inputs_embeds
1476 -
1477 - self._set_visual_token_mask(input_ids)
1478 - inputs_embeds = merge_multimodal_embeddings(input_ids, inputs_embeds,
1479 - multimodal_embeddings,
1480 - [self.config.im_patch_id])
1481 - return inputs_embeds
1473 + if multimodal_embeddings is not None and len(
1474 + multimodal_embeddings) > 0:
1475 + self._set_visual_token_mask(input_ids)
1476 +
1477 + # This is to satisfy the type checker for each overload
1478 + if multimodal_embeddings is None or is_multimodal is None:
1479 + return super().get_input_embeddings(input_ids)
1480 +
1481 + return super().get_input_embeddings(
1482 + input_ids,
1483 + multimodal_embeddings=multimodal_embeddings,
1484 + is_multimodal=is_multimodal,
1485 + handle_oov_mm_token=handle_oov_mm_token,
1486 + )
1487
1488 def forward(
1489 self,
vllm/model_executor/models/ernie_mtp.py
+6
@@ -116,6 +116,9 @@ class ErnieMultiTokenPredictor(nn.Module):
116 )
117 self.logits_processor = LogitsProcessor(config.vocab_size)
118
119 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
120 + return self.embed_tokens(input_ids)
121 +
122 def forward(
123 self,
124 input_ids: torch.Tensor,
@@ -160,6 +163,9 @@ class ErnieMTP(nn.Module, SupportsPP):
163 if self.config.tie_word_embeddings:
164 self.lm_head.weight = self.model.embed_tokens.weight
165
166 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
167 + return self.model.get_input_embeddings(input_ids)
168 +
169 def forward(
170 self,
171 input_ids: torch.Tensor,
vllm/model_executor/models/fuyu.py
+6 -20
@@ -42,8 +42,7 @@ from vllm.sequence import IntermediateTensors
42 from vllm.utils.tensor_schema import TensorSchema, TensorShape
43
44 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
45 -from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn, maybe_prefix,
46 - merge_multimodal_embeddings)
45 +from .utils import AutoWeightsLoader, WeightsMapper, flatten_bn, maybe_prefix
46
47 # Cannot find the following 2 numbers from hf config.
48 _IMAGE_TOKEN_ID = 71011
@@ -342,22 +341,6 @@ class FuyuForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
341
342 return self._process_image_input(image_input)
343
345 - def get_input_embeddings(
346 - self,
347 - input_ids: torch.Tensor,
348 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
349 - ) -> torch.Tensor:
350 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
351 - if multimodal_embeddings is not None \
352 - and len(multimodal_embeddings) != 0:
353 - inputs_embeds = merge_multimodal_embeddings(
354 - input_ids,
355 - inputs_embeds,
356 - multimodal_embeddings,
357 - _IMAGE_TOKEN_ID,
358 - )
359 - return inputs_embeds
360 -
344 def forward(
345 self,
346 input_ids: torch.Tensor,
@@ -373,8 +356,11 @@ class FuyuForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
356 # condition is for v0 compatibility.
357 elif inputs_embeds is None:
358 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
376 - inputs_embeds = self.get_input_embeddings(input_ids,
377 - vision_embeddings)
359 + inputs_embeds = self.get_input_embeddings(
360 + input_ids,
361 + vision_embeddings,
362 + is_multimodal=input_ids == _IMAGE_TOKEN_ID,
363 + )
364 input_ids = None
365
366 hidden_states = self.language_model(
vllm/model_executor/models/gemma3_mm.py
+6 -20
@@ -37,8 +37,7 @@ from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
37 SupportsMultiModal, SupportsPP)
38 from .siglip import SiglipVisionModel
39 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
40 - init_vllm_registered_model, maybe_prefix,
41 - merge_multimodal_embeddings)
40 + init_vllm_registered_model, maybe_prefix)
41
42 logger = init_logger(__name__)
43
@@ -588,22 +587,6 @@ class Gemma3ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
587
588 return self._process_image_input(image_input)
589
591 - def get_input_embeddings(
592 - self,
593 - input_ids: torch.Tensor,
594 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
595 - ) -> torch.Tensor:
596 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
597 - if multimodal_embeddings is not None \
598 - and len(multimodal_embeddings) != 0:
599 - inputs_embeds = merge_multimodal_embeddings(
600 - input_ids,
601 - inputs_embeds,
602 - multimodal_embeddings,
603 - self.config.image_token_index,
604 - )
605 - return inputs_embeds
606 -
590 def forward(self,
591 input_ids: torch.Tensor,
592 positions: torch.Tensor,
@@ -618,8 +601,11 @@ class Gemma3ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
601 elif inputs_embeds is None:
602 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
603
621 - inputs_embeds = self.get_input_embeddings(input_ids,
622 - vision_embeddings)
604 + inputs_embeds = self.get_input_embeddings(
605 + input_ids,
606 + vision_embeddings,
607 + is_multimodal=input_ids == self.config.image_token_index,
608 + )
609 if (vision_embeddings is not None) and len(vision_embeddings) != 0:
610 kwargs = self.prepare_attn_masks(
611 input_ids,
vllm/model_executor/models/gemma3n_mm.py
+13 -10
@@ -632,8 +632,10 @@ class Gemma3nForConditionalGeneration(nn.Module, SupportsMultiModal,
632 self,
633 input_ids: torch.Tensor,
634 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
635 + *,
636 + is_multimodal: Optional[torch.Tensor] = None,
637 + handle_oov_mm_token: bool = False,
638 ) -> torch.Tensor:
636 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
639 # NOTE (NickLucche) Each pass needs tokens to compute PLE so we cache
640 # them here, as the model forward has only access to the input_embeds.
641 if input_ids is not None:
@@ -645,15 +647,16 @@ class Gemma3nForConditionalGeneration(nn.Module, SupportsMultiModal,
647 self.per_layer_embeddings[:per_layer_inputs.shape[0]].copy_(
648 per_layer_inputs)
649
648 - if multimodal_embeddings is not None \
649 - and len(multimodal_embeddings) != 0:
650 - inputs_embeds = merge_multimodal_embeddings(
651 - input_ids,
652 - inputs_embeds,
653 - multimodal_embeddings,
654 - # NOTE: this order of processing mm items is important
655 - [self.config.image_token_id, self.config.audio_token_id])
656 - return inputs_embeds
650 + # This is to satisfy the type checker for each overload
651 + if multimodal_embeddings is None or is_multimodal is None:
652 + return super().get_input_embeddings(input_ids)
653 +
654 + return super().get_input_embeddings(
655 + input_ids,
656 + multimodal_embeddings=multimodal_embeddings,
657 + is_multimodal=is_multimodal,
658 + handle_oov_mm_token=handle_oov_mm_token,
659 + )
660
661 def forward(self,
662 input_ids: torch.Tensor,
vllm/model_executor/models/glm4_1v.py
-17
@@ -1552,23 +1552,6 @@ class Glm4vForConditionalGeneration(nn.Module, SupportsMultiModal,
1552 multimodal_embeddings += video_embeddings
1553 return multimodal_embeddings
1554
1555 - def get_input_embeddings(
1556 - self,
1557 - input_ids: torch.Tensor,
1558 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1559 - ) -> torch.Tensor:
1560 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1561 - if (multimodal_embeddings is not None
1562 - and len(multimodal_embeddings) != 0
1563 - and all(embed.numel() > 0 for embed in multimodal_embeddings)):
1564 - inputs_embeds = merge_multimodal_embeddings(
1565 - input_ids,
1566 - inputs_embeds,
1567 - multimodal_embeddings,
1568 - [self.config.image_token_id, self.config.video_token_id],
1569 - )
1570 - return inputs_embeds
1571 -
1555 def get_input_embeddings_v0(
1556 self,
1557 input_ids: torch.Tensor,
vllm/model_executor/models/glm4_moe_mtp.py
+6
@@ -132,6 +132,9 @@ class Glm4MoeMultiTokenPredictor(nn.Module):
132 )
133 self.logits_processor = LogitsProcessor(config.vocab_size)
134
135 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
136 + return self.embed_tokens(input_ids)
137 +
138 def forward(
139 self,
140 input_ids: torch.Tensor,
@@ -173,6 +176,9 @@ class Glm4MoeMTP(nn.Module, SupportsPP):
176 prefix=maybe_prefix(
177 prefix, "model"))
178
179 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
180 + return self.model.get_input_embeddings(input_ids)
181 +
182 def forward(
183 self,
184 input_ids: torch.Tensor,
vllm/model_executor/models/glm4v.py
+10 -25
@@ -43,7 +43,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
43 from .chatglm import ChatGLMBaseModel, ChatGLMModel
44 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
45 SupportsMultiModal, SupportsPP)
46 -from .utils import flatten_bn, merge_multimodal_embeddings
46 +from .utils import flatten_bn, isin_list
47
48
49 class GLMVImagePixelInputs(TensorSchema):
@@ -607,28 +607,6 @@ class GLM4VForCausalLM(ChatGLMBaseModel, SupportsLoRA, SupportsPP,
607 vision_embeddings = self._process_image_input(image_input)
608 return vision_embeddings
609
610 - def get_input_embeddings(
611 - self,
612 - input_ids: torch.Tensor,
613 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
614 - ) -> torch.Tensor:
615 - inputs_embeds = self.transformer.get_input_embeddings(input_ids)
616 -
617 - if multimodal_embeddings is not None \
618 - and len(multimodal_embeddings) != 0:
619 - inputs_embeds = merge_multimodal_embeddings(
620 - input_ids=input_ids,
621 - inputs_embeds=inputs_embeds,
622 - multimodal_embeddings=multimodal_embeddings,
623 - placeholder_token_id=[
624 - self.config.boi_token_id,
625 - self.config.pad_token_id,
626 - self.config.eoi_token_id,
627 - ],
628 - )
629 -
630 - return inputs_embeds
631 -
610 def forward(
611 self,
612 input_ids: torch.Tensor,
@@ -644,8 +622,15 @@ class GLM4VForCausalLM(ChatGLMBaseModel, SupportsLoRA, SupportsPP,
622 # condition is for v0 compatibility.
623 elif inputs_embeds is None:
624 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
647 - inputs_embeds = self.get_input_embeddings(input_ids,
648 - vision_embeddings)
625 + inputs_embeds = self.get_input_embeddings(
626 + input_ids,
627 + vision_embeddings,
628 + is_multimodal=isin_list(input_ids, [
629 + self.config.boi_token_id,
630 + self.config.pad_token_id,
631 + self.config.eoi_token_id,
632 + ]),
633 + )
634 input_ids = None
635
636 hidden_states = self.transformer(input_ids, positions,
vllm/model_executor/models/granite_speech.py
+21 -13
@@ -52,8 +52,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
52 from .blip2 import Blip2QFormerModel
53 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
54 SupportsMultiModal, SupportsPP)
55 -from .utils import (AutoWeightsLoader, embed_multimodal,
56 - init_vllm_registered_model, maybe_prefix)
55 +from .utils import AutoWeightsLoader, init_vllm_registered_model, maybe_prefix
56
57
58 ### Audio Input
@@ -720,6 +719,9 @@ class GraniteSpeechForConditionalGeneration(
719 # Split variable length features into a tuple
720 return torch.split(masked_embeds, audio_input["audio_embed_sizes"])
721
722 + def get_language_model(self) -> torch.nn.Module:
723 + return self.language_model
724 +
725 def get_multimodal_embeddings(
726 self,
727 **kwargs: object,
@@ -728,7 +730,7 @@ class GraniteSpeechForConditionalGeneration(
730 audio_input = self._parse_and_validate_audio_input(**kwargs)
731 if audio_input is None:
732 return []
731 - return None
733 +
734 audio_features = self._process_audio_input(audio_input)
735 return audio_features
736
@@ -736,19 +738,21 @@ class GraniteSpeechForConditionalGeneration(
738 self,
739 input_ids: torch.Tensor,
740 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
741 + *,
742 + is_multimodal: Optional[torch.Tensor] = None,
743 + # Multi-modal token ID may exceed vocab size
744 + handle_oov_mm_token: bool = True,
745 ) -> torch.Tensor:
740 - """Compute the merged LLM / audio embeddings."""
741 - if multimodal_embeddings is None \
742 - or len(multimodal_embeddings) == 0:
743 - return self.language_model.get_input_embeddings(input_ids)
746 + # This is to satisfy the type checker for each overload
747 + if multimodal_embeddings is None or is_multimodal is None:
748 + return super().get_input_embeddings(input_ids)
749
745 - inputs_embeds = embed_multimodal(
750 + return super().get_input_embeddings(
751 input_ids,
747 - self.config.audio_token_index,
748 - self.language_model.model.get_input_embeddings,
749 - multimodal_embeddings,
752 + multimodal_embeddings=multimodal_embeddings,
753 + is_multimodal=is_multimodal,
754 + handle_oov_mm_token=handle_oov_mm_token,
755 )
751 - return inputs_embeds
756
757 def forward(
758 self,
@@ -765,7 +769,11 @@ class GraniteSpeechForConditionalGeneration(
769 # condition is for v0 compatibility.
770 elif inputs_embeds is None:
771 audio_embeds = self.get_multimodal_embeddings(**kwargs)
768 - inputs_embeds = self.get_input_embeddings(input_ids, audio_embeds)
772 + inputs_embeds = self.get_input_embeddings(
773 + input_ids,
774 + audio_embeds,
775 + is_multimodal=input_ids == self.config.audio_token_index,
776 + )
777 input_ids = None
778
779 model_output = self.language_model(input_ids, positions,
vllm/model_executor/models/hunyuan_v1.py
+3
@@ -989,6 +989,9 @@ class HunYuanV1Base(nn.Module, SupportsLoRA, SupportsPP, MixtureOfExperts):
989 moe.n_redundant_experts = self.num_redundant_experts
990 moe.experts.update_expert_map()
991
992 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
993 + return self.model.get_input_embeddings(input_ids)
994 +
995 def forward(
996 self,
997 input_ids: torch.Tensor,
vllm/model_executor/models/hyperclovax_vision.py
+10 -25
@@ -45,8 +45,8 @@ from vllm.sequence import IntermediateTensors
45 from .clip import CLIPVisionModel
46 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
47 from .siglip import SiglipVisionModel
48 -from .utils import (AutoWeightsLoader, init_vllm_registered_model,
49 - maybe_prefix, merge_multimodal_embeddings)
48 +from .utils import (AutoWeightsLoader, init_vllm_registered_model, isin_list,
49 + maybe_prefix)
50 from .vision import get_vision_encoder_info
51
52 EOT = "<|endofturn|>"
@@ -691,7 +691,7 @@ class HCXVisionForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
691 def get_multimodal_embeddings(
692 self,
693 **kwargs: Unpack[HCXVisionMultimodalInputs],
694 - ) -> Optional[MultiModalEmbeddings]:
694 + ) -> MultiModalEmbeddings:
695
696 multimodal_embeddings = list()
697 if kwargs.get("pixel_values_images") is not None:
@@ -736,26 +736,6 @@ class HCXVisionForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
736 multimodal_embeddings.append(_multimodal_embeddings_videos)
737 return multimodal_embeddings
738
739 - def get_input_embeddings(
740 - self,
741 - input_ids: torch.Tensor,
742 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
743 - ) -> torch.Tensor:
744 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
745 - if multimodal_embeddings is not None \
746 - and len(multimodal_embeddings) != 0:
747 - inputs_embeds = merge_multimodal_embeddings(
748 - input_ids,
749 - inputs_embeds,
750 - multimodal_embeddings,
751 - placeholder_token_id=[
752 - self.config.image_token_id,
753 - self.config.video_token_id,
754 - ],
755 - )
756 -
757 - return inputs_embeds
758 -
739 def forward(
740 self,
741 input_ids: torch.Tensor,
@@ -771,8 +751,13 @@ class HCXVisionForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
751 # condition is for v0 compatibility.
752 elif inputs_embeds is None:
753 multimodal_embeddings = self.get_multimodal_embeddings(**kwargs)
774 - inputs_embeds = self.get_input_embeddings(input_ids,
775 - multimodal_embeddings)
754 + inputs_embeds = self.get_input_embeddings(
755 + input_ids,
756 + multimodal_embeddings,
757 + is_multimodal=isin_list(
758 + input_ids,
759 + [self.config.image_token_id, self.config.video_token_id]),
760 + )
761 input_ids = None
762 hidden_states = self.language_model.model(input_ids,
763 positions,
vllm/model_executor/models/idefics3.py
+7 -24
@@ -52,8 +52,7 @@ from .idefics2_vision_model import (
52 # yapf: enable
53 from .interfaces import MultiModalEmbeddings, SupportsLoRA, SupportsMultiModal
54 from .llama import LlamaModel
55 -from .utils import (AutoWeightsLoader, flatten_bn, maybe_prefix,
56 - merge_multimodal_embeddings)
55 +from .utils import AutoWeightsLoader, flatten_bn, maybe_prefix
56
57
58 class Idefics3ImagePixelInputs(TensorSchema):
@@ -539,10 +538,7 @@ class Idefics3Model(nn.Module):
538
539 return image_hidden_states
540
542 - def get_input_embeddings(
543 - self,
544 - input_ids: torch.Tensor,
545 - ) -> torch.Tensor:
541 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
542 return self.text_model.get_input_embeddings(input_ids)
543
544 def forward(
@@ -695,22 +691,6 @@ class Idefics3ForConditionalGeneration(nn.Module, SupportsMultiModal,
691
692 return self._process_image_input(image_input)
693
698 - def get_input_embeddings(
699 - self,
700 - input_ids: torch.Tensor,
701 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
702 - ) -> torch.Tensor:
703 - inputs_embeds = self.model.get_input_embeddings(input_ids)
704 - if multimodal_embeddings is not None \
705 - and len(multimodal_embeddings) != 0:
706 - inputs_embeds = merge_multimodal_embeddings(
707 - input_ids,
708 - inputs_embeds,
709 - multimodal_embeddings,
710 - self.config.image_token_id,
711 - )
712 - return inputs_embeds
713 -
694 def forward(
695 self,
696 input_ids: torch.Tensor,
@@ -726,8 +706,11 @@ class Idefics3ForConditionalGeneration(nn.Module, SupportsMultiModal,
706 # condition is for v0 compatibility.
707 elif inputs_embeds is None:
708 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
729 - inputs_embeds = self.get_input_embeddings(input_ids,
730 - vision_embeddings)
709 + inputs_embeds = self.get_input_embeddings(
710 + input_ids,
711 + vision_embeddings,
712 + is_multimodal=input_ids == self.config.image_token_id,
713 + )
714 input_ids = None
715
716 hidden_states = self.model.text_model(input_ids,
vllm/model_executor/models/interfaces.py
+75 -8
@@ -2,8 +2,8 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable, Mapping, MutableSequence
5 -from typing import (TYPE_CHECKING, ClassVar, Literal, Optional, Protocol,
6 - Union, overload, runtime_checkable)
5 +from typing import (TYPE_CHECKING, Callable, ClassVar, Literal, Optional,
6 + Protocol, Union, overload, runtime_checkable)
7
8 import numpy as np
9 import torch
@@ -20,7 +20,7 @@ from vllm.model_executor.layers.quantization.base_config import (
20 QuantizationConfig)
21 from vllm.utils import supports_kw
22
23 -from .interfaces_base import is_pooling_model
23 +from .interfaces_base import VllmModel, is_pooling_model
24
25 if TYPE_CHECKING:
26 from vllm.config import VllmConfig
@@ -90,7 +90,7 @@ class SupportsMultiModal(Protocol):
90 """
91 ...
92
93 - def get_language_model(self) -> torch.nn.Module:
93 + def get_language_model(self) -> VllmModel:
94 """
95 Returns the underlying language model used for text generation.
96
@@ -102,17 +102,84 @@ class SupportsMultiModal(Protocol):
102 """
103 ...
104
105 + @overload
106 + def get_input_embeddings(self, input_ids: Tensor) -> Tensor:
107 + ...
108 +
109 + @overload
110 + def get_input_embeddings(
111 + self,
112 + input_ids: Tensor,
113 + multimodal_embeddings: MultiModalEmbeddings,
114 + *,
115 + is_multimodal: torch.Tensor,
116 + handle_oov_mm_token: bool = False,
117 + ) -> Tensor:
118 + ...
119 +
120 + def _get_text_embeddings(
121 + self,
122 + input_ids: Tensor,
123 + get_input_embeddings: Callable[[Tensor], Tensor],
124 + *,
125 + is_multimodal: Optional[Tensor],
126 + handle_oov_mm_token: bool,
127 + ) -> Tensor:
128 + if handle_oov_mm_token and is_multimodal is not None:
129 + is_text = ~is_multimodal
130 + text_embeds = get_input_embeddings(input_ids[is_text])
131 +
132 + return torch.empty(
133 + (input_ids.shape[0], text_embeds.shape[1]),
134 + dtype=text_embeds.dtype,
135 + device=text_embeds.device,
136 + ).masked_scatter_(is_text.unsqueeze_(-1), text_embeds)
137 +
138 + return get_input_embeddings(input_ids)
139 +
140 def get_input_embeddings(
141 self,
142 input_ids: Tensor,
143 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
144 + *,
145 + is_multimodal: Optional[Tensor] = None,
146 + handle_oov_mm_token: bool = False,
147 ) -> Tensor:
148 """
111 - Returns the input embeddings merged from the text embeddings from
112 - input_ids and the multimodal embeddings generated from multimodal
113 - kwargs.
149 + Apply token embeddings to `input_ids`.
150 +
151 + If `multimodal_embeddings` is passed, scatter them into
152 + `input_ids` according to the mask `is_multimodal`.
153 +
154 + In case the multi-modal token IDs exceed the vocabulary size of
155 + the language model, you can set `handle_oov_mm_token=False`
156 + to avoid calling the language model's `get_input_embeddings` method
157 + on those tokens. Note however that doing so increases memory usage
158 + as an additional buffer is needed to hold the input embeddings.
159 """
115 - ...
160 + from .utils import _merge_multimodal_embeddings
161 +
162 + inputs_embeds = self._get_text_embeddings(
163 + input_ids,
164 + self.get_language_model().get_input_embeddings,
165 + is_multimodal=is_multimodal,
166 + handle_oov_mm_token=handle_oov_mm_token,
167 + )
168 +
169 + if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
170 + return inputs_embeds
171 +
172 + if is_multimodal is None:
173 + raise ValueError(
174 + "`get_input_embeddings` now requires `is_multimodal` arg, "
175 + "please update your model runner according to "
176 + "https://github.com/vllm-project/vllm/pull/16229.")
177 +
178 + return _merge_multimodal_embeddings(
179 + inputs_embeds=inputs_embeds,
180 + multimodal_embeddings=multimodal_embeddings,
181 + is_multimodal=is_multimodal,
182 + )
183
184
185 @runtime_checkable
vllm/model_executor/models/interfaces_base.py
+23 -1
@@ -41,6 +41,13 @@ class VllmModel(Protocol[T_co]):
41 ) -> None:
42 ...
43
44 + def get_input_embeddings(
45 + self,
46 + input_ids: torch.Tensor,
47 + ) -> torch.Tensor:
48 + """Apply token embeddings to `input_ids`."""
49 + ...
50 +
51 def forward(
52 self,
53 input_ids: torch.Tensor,
@@ -54,6 +61,19 @@ def _check_vllm_model_init(model: Union[type[object], object]) -> bool:
61 return supports_kw(model_init, "vllm_config")
62
63
64 +def _check_vllm_model_get_input_embeddings(
65 + model: Union[type[object], object]) -> bool:
66 + model_get_input_embeddings = getattr(model, "get_input_embeddings", None)
67 + if not callable(model_get_input_embeddings):
68 + logger.warning(
69 + "The model (%s) is missing the `get_input_embeddings` method.",
70 + model,
71 + )
72 + return False
73 +
74 + return True
75 +
76 +
77 def _check_vllm_model_forward(model: Union[type[object], object]) -> bool:
78 model_forward = getattr(model, "forward", None)
79 if not callable(model_forward):
@@ -88,7 +108,9 @@ def is_vllm_model(model: object) -> TypeIs[VllmModel]:
108 def is_vllm_model(
109 model: Union[type[object], object],
110 ) -> Union[TypeIs[type[VllmModel]], TypeIs[VllmModel]]:
91 - return _check_vllm_model_init(model) and _check_vllm_model_forward(model)
111 + return (_check_vllm_model_init(model)
112 + and _check_vllm_model_get_input_embeddings(model)
113 + and _check_vllm_model_forward(model))
114
115
116 @runtime_checkable
vllm/model_executor/models/interns1.py
+27 -20
@@ -40,8 +40,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
40 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
41 SupportsMultiModal, SupportsPP)
42 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
43 - init_vllm_registered_model, maybe_prefix,
44 - merge_multimodal_embeddings)
43 + init_vllm_registered_model, isin_list, maybe_prefix)
44
45
46 class InternS1MultiModalProjector(nn.Module):
@@ -767,24 +766,24 @@ class InternS1ForConditionalGeneration(nn.Module, SupportsMultiModal,
766 self,
767 input_ids: torch.Tensor,
768 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
769 + *,
770 + is_multimodal: Optional[torch.Tensor] = None,
771 + handle_oov_mm_token: bool = False,
772 ) -> torch.Tensor:
771 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
772 - if multimodal_embeddings is not None \
773 - and len(multimodal_embeddings) != 0:
774 - context_token_ids = [
775 - token_id for token_id in (self.img_context_token_id,
776 - self.video_context_token_id)
777 - if token_id is not None
778 - ]
779 - assert len(context_token_ids) >= 1
773 + if multimodal_embeddings is not None and len(
774 + multimodal_embeddings) > 0:
775 self._set_visual_token_mask(input_ids)
781 - inputs_embeds = merge_multimodal_embeddings(
782 - input_ids,
783 - inputs_embeds,
784 - multimodal_embeddings,
785 - context_token_ids,
786 - )
787 - return inputs_embeds
776 +
777 + # This is to satisfy the type checker for each overload
778 + if multimodal_embeddings is None or is_multimodal is None:
779 + return super().get_input_embeddings(input_ids)
780 +
781 + return super().get_input_embeddings(
782 + input_ids,
783 + multimodal_embeddings=multimodal_embeddings,
784 + is_multimodal=is_multimodal,
785 + handle_oov_mm_token=handle_oov_mm_token,
786 + )
787
788 def forward(
789 self,
@@ -802,9 +801,17 @@ class InternS1ForConditionalGeneration(nn.Module, SupportsMultiModal,
801 # NOTE: In v1, inputs_embeds is always generated at model runner, this
802 # condition is for v0 compatibility.
803 elif inputs_embeds is None:
804 + context_token_ids = [
805 + token_id for token_id in (self.img_context_token_id,
806 + self.video_context_token_id)
807 + if token_id is not None
808 + ]
809 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
806 - inputs_embeds = self.get_input_embeddings(input_ids,
807 - vision_embeddings)
810 + inputs_embeds = self.get_input_embeddings(
811 + input_ids,
812 + vision_embeddings,
813 + is_multimodal=isin_list(input_ids, context_token_ids),
814 + )
815 input_ids = None
816
817 forward_kwargs = {
vllm/model_executor/models/internvl.py
+27 -19
@@ -43,7 +43,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
43 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
44 SupportsMultiModal, SupportsPP)
45 from .utils import (AutoWeightsLoader, flatten_bn, init_vllm_registered_model,
46 - maybe_prefix, merge_multimodal_embeddings)
46 + isin_list, maybe_prefix)
47
48 IMG_START = '<img>'
49 IMG_END = '</img>'
@@ -1339,24 +1339,24 @@ class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
1339 self,
1340 input_ids: torch.Tensor,
1341 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1342 + *,
1343 + is_multimodal: Optional[torch.Tensor] = None,
1344 + handle_oov_mm_token: bool = False,
1345 ) -> torch.Tensor:
1343 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1344 - if multimodal_embeddings is not None \
1345 - and len(multimodal_embeddings) != 0:
1346 - context_token_ids = [
1347 - token_id for token_id in (self.img_context_token_id,
1348 - self.video_context_token_id)
1349 - if token_id is not None
1350 - ]
1351 - assert len(context_token_ids) >= 1
1346 + if multimodal_embeddings is not None and len(
1347 + multimodal_embeddings) > 0:
1348 self._set_visual_token_mask(input_ids)
1353 - inputs_embeds = merge_multimodal_embeddings(
1354 - input_ids,
1355 - inputs_embeds,
1356 - multimodal_embeddings,
1357 - context_token_ids,
1358 - )
1359 - return inputs_embeds
1349 +
1350 + # This is to satisfy the type checker for each overload
1351 + if multimodal_embeddings is None or is_multimodal is None:
1352 + return super().get_input_embeddings(input_ids)
1353 +
1354 + return super().get_input_embeddings(
1355 + input_ids,
1356 + multimodal_embeddings=multimodal_embeddings,
1357 + is_multimodal=is_multimodal,
1358 + handle_oov_mm_token=handle_oov_mm_token,
1359 + )
1360
1361 def forward(
1362 self,
@@ -1374,9 +1374,17 @@ class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
1374 # NOTE: In v1, inputs_embeds is always generated at model runner, this
1375 # condition is for v0 compatibility.
1376 elif inputs_embeds is None:
1377 + context_token_ids = [
1378 + token_id for token_id in (self.img_context_token_id,
1379 + self.video_context_token_id)
1380 + if token_id is not None
1381 + ]
1382 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
1378 - inputs_embeds = self.get_input_embeddings(input_ids,
1379 - vision_embeddings)
1383 + inputs_embeds = self.get_input_embeddings(
1384 + input_ids,
1385 + vision_embeddings,
1386 + is_multimodal=isin_list(input_ids, context_token_ids),
1387 + )
1388 input_ids = None
1389
1390 forward_kwargs = {
vllm/model_executor/models/keye.py
-18
@@ -1450,24 +1450,6 @@ class BaseKeyeModule(nn.Module):
1450 multimodal_embeddings += video_embeddings
1451 return multimodal_embeddings
1452
1453 - def get_input_embeddings(
1454 - self,
1455 - input_ids: torch.Tensor,
1456 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1457 - ) -> torch.Tensor:
1458 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1459 - if multimodal_embeddings is not None:
1460 - inputs_embeds = merge_multimodal_embeddings(
1461 - input_ids,
1462 - inputs_embeds,
1463 - multimodal_embeddings,
1464 - [
1465 - self.config.image_token_id,
1466 - self.config.video_token_id,
1467 - ],
1468 - )
1469 - return inputs_embeds
1470 -
1453 def get_input_embeddings_v0(
1454 self,
1455 input_ids: torch.Tensor,
vllm/model_executor/models/kimi_vl.py
+3 -26
@@ -66,7 +66,6 @@ from vllm.model_executor.models.deepseek_v2 import DeepseekV2Model
66 from vllm.model_executor.models.interfaces import (SupportsMultiModal,
67 SupportsPP)
68 from vllm.model_executor.models.moonvit import MoonVitPretrainedModel
69 -from vllm.model_executor.models.utils import merge_multimodal_embeddings
69 from vllm.multimodal import MULTIMODAL_REGISTRY
70 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
71 MultiModalKwargsItems, NestedTensors)
@@ -424,26 +423,6 @@ class KimiVLForConditionalGeneration(nn.Module, SupportsMultiModal,
423 vision_embeddings = self._process_image_input(image_input)
424 return vision_embeddings
425
427 - def get_input_embeddings(
428 - self,
429 - input_ids: torch.Tensor,
430 - multimodal_embeddings: Optional[NestedTensors] = None,
431 - ) -> torch.Tensor:
432 -
433 - # `get_input_embeddings` should already be implemented for the language
434 - # model as one of the requirements of basic vLLM model implementation.
435 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
436 -
437 - if multimodal_embeddings is not None and len(
438 - multimodal_embeddings) != 0:
439 - inputs_embeds = merge_multimodal_embeddings(
440 - input_ids=input_ids,
441 - inputs_embeds=inputs_embeds,
442 - multimodal_embeddings=multimodal_embeddings,
443 - placeholder_token_id=self.config.media_placeholder_token_id)
444 -
445 - return inputs_embeds
446 -
426 def forward(
427 self,
428 input_ids: torch.Tensor,
@@ -462,14 +441,12 @@ class KimiVLForConditionalGeneration(nn.Module, SupportsMultiModal,
441 if image_input is None:
442 inputs_embeds = None
443 else:
465 - inputs_embeds = self.get_input_embeddings(input_ids)
444 image_embeds = self._process_image_input(image_input)
467 - inputs_embeds = merge_multimodal_embeddings(
445 + inputs_embeds = self.get_input_embeddings(
446 input_ids,
469 - inputs_embeds,
447 image_embeds,
471 - placeholder_token_id=self.config.
472 - media_placeholder_token_id,
448 + is_multimodal=input_ids ==
449 + self.config.media_placeholder_token_id,
450 )
451 input_ids = None
452
vllm/model_executor/models/lfm2.py
+3
@@ -522,6 +522,9 @@ class Lfm2ForCausalLM(nn.Module, HasInnerState, SupportsLoRA, SupportsPP,
522 self.make_empty_intermediate_tensors = (
523 self.model.make_empty_intermediate_tensors)
524
525 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
526 + return self.model.get_input_embeddings(input_ids)
527 +
528 def forward(
529 self,
530 input_ids: torch.Tensor,
vllm/model_executor/models/llama4_eagle.py
+8 -23
@@ -37,9 +37,9 @@ from vllm.model_executor.model_loader.weight_utils import default_weight_loader
37 from vllm.model_executor.models.llama4 import (Llama4DecoderLayer,
38 Llama4ForCausalLM)
39 from vllm.model_executor.models.utils import extract_layer_index
40 -from vllm.multimodal.inputs import NestedTensors
40
42 -from .utils import AutoWeightsLoader, maybe_prefix, merge_multimodal_embeddings
41 +from .interfaces import SupportsMultiModal
42 +from .utils import AutoWeightsLoader, maybe_prefix
43
44 logger = init_logger(__name__)
45
@@ -79,10 +79,7 @@ class LlamaModel(nn.Module):
79 self.norm = RMSNorm(self.config.hidden_size,
80 eps=self.config.rms_norm_eps)
81
82 - def get_input_embeddings(
83 - self,
84 - input_ids: torch.Tensor,
85 - ) -> torch.Tensor:
82 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
83 return self.embed_tokens(input_ids)
84
85 def forward(
@@ -194,6 +191,11 @@ class EagleLlama4ForCausalLM(Llama4ForCausalLM):
191 self.logits_processor = LogitsProcessor(self.config.vocab_size,
192 scale=logit_scale)
193
194 + def get_language_model(self) -> torch.nn.Module:
195 + return self.model
196 +
197 + get_input_embeddings = SupportsMultiModal.get_input_embeddings # type: ignore
198 +
199 def forward(
200 self,
201 input_ids: torch.Tensor,
@@ -220,20 +222,3 @@ class EagleLlama4ForCausalLM(Llama4ForCausalLM):
222 skip_prefixes=(["lm_head."]),
223 )
224 loader.load_weights(map(transform, weights))
223 -
224 - def get_input_embeddings(
225 - self,
226 - input_ids: torch.Tensor,
227 - multimodal_embeddings: Optional[NestedTensors] = None,
228 - ) -> torch.Tensor:
229 - inputs_embeds = self.model.get_input_embeddings(input_ids)
230 -
231 - if multimodal_embeddings is not None:
232 - inputs_embeds = merge_multimodal_embeddings(
233 - input_ids,
234 - inputs_embeds,
235 - multimodal_embeddings,
236 - self.config.image_token_index,
237 - )
238 -
239 - return inputs_embeds
vllm/model_executor/models/llama_eagle.py
+6
@@ -73,6 +73,9 @@ class LlamaModel(nn.Module):
73 self.config.hidden_size,
74 bias=False)
75
76 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
77 + return self.embed_tokens(input_ids)
78 +
79 def forward(
80 self,
81 input_ids: torch.Tensor,
@@ -149,6 +152,9 @@ class EagleLlamaForCausalLM(LlamaForCausalLM):
152 self.logits_processor = LogitsProcessor(self.config.vocab_size,
153 scale=logit_scale)
154
155 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
156 + return self.model.get_input_embeddings(input_ids)
157 +
158 def forward(
159 self,
160 input_ids: torch.Tensor,
vllm/model_executor/models/llama_eagle3.py
+4 -13
@@ -18,7 +18,6 @@ from vllm.model_executor.layers.quantization.base_config import (
18 from vllm.model_executor.layers.vocab_parallel_embedding import (
19 DEFAULT_VOCAB_PADDING_SIZE, ParallelLMHead, VocabParallelEmbedding)
20 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
21 -from vllm.model_executor.models.interfaces import MultiModalEmbeddings
21 from vllm.model_executor.models.llama import (LlamaDecoderLayer,
22 LlamaForCausalLM)
23
@@ -144,10 +143,7 @@ class LlamaModel(nn.Module):
143 eps=self.config.rms_norm_eps,
144 )
145
147 - def get_input_embeddings(
148 - self,
149 - input_ids: torch.Tensor,
150 - ) -> torch.Tensor:
146 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
147 return self.embed_tokens(input_ids)
148
149 def forward(
@@ -239,6 +235,9 @@ class Eagle3LlamaForCausalLM(LlamaForCausalLM):
235 requires_grad=False,
236 )
237
238 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
239 + return self.model.get_input_embeddings(input_ids)
240 +
241 def forward(
242 self,
243 input_ids: torch.Tensor,
@@ -302,11 +301,3 @@ class Eagle3LlamaForCausalLM(LlamaForCausalLM):
301 skip_substrs=skip_substrs,
302 )
303 loader.load_weights(model_weights.items())
305 -
306 - def get_input_embeddings(
307 - self,
308 - input_ids: torch.Tensor,
309 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
310 - ) -> torch.Tensor:
311 - inputs_embeds = self.model.get_input_embeddings(input_ids)
312 - return inputs_embeds
vllm/model_executor/models/llava.py
+6 -20
@@ -41,8 +41,7 @@ from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
41 from .pixtral import PixtralHFEncoderInfo, PixtralHFVisionModel
42 from .siglip import SiglipVisionModel
43 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
44 - init_vllm_registered_model, maybe_prefix,
45 - merge_multimodal_embeddings)
44 + init_vllm_registered_model, maybe_prefix)
45 from .vision import get_vision_encoder_info
46
47
@@ -676,22 +675,6 @@ class LlavaForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP):
675
676 return self._process_image_input(image_input)
677
679 - def get_input_embeddings(
680 - self,
681 - input_ids: torch.Tensor,
682 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
683 - ) -> torch.Tensor:
684 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
685 - if multimodal_embeddings is not None \
686 - and len(multimodal_embeddings) != 0:
687 - inputs_embeds = merge_multimodal_embeddings(
688 - input_ids,
689 - inputs_embeds,
690 - multimodal_embeddings,
691 - self.config.image_token_index,
692 - )
693 - return inputs_embeds
694 -
678 def forward(
679 self,
680 input_ids: torch.Tensor,
@@ -744,8 +727,11 @@ class LlavaForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP):
727 # condition is for v0 compatibility.
728 elif inputs_embeds is None:
729 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
747 - inputs_embeds = self.get_input_embeddings(input_ids,
748 - vision_embeddings)
730 + inputs_embeds = self.get_input_embeddings(
731 + input_ids,
732 + vision_embeddings,
733 + is_multimodal=input_ids == self.config.image_token_index,
734 + )
735 input_ids = None
736
737 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/llava_next.py
+18 -13
@@ -25,8 +25,8 @@ from .llava import (BaseLlavaMultiModalProcessor, BaseLlavaProcessingInfo,
25 LlavaDummyInputsBuilder, LlavaLikeConfig,
26 LlavaMultiModalProjector, init_vision_tower_for_llava)
27 from .siglip import SiglipVisionModel
28 -from .utils import (AutoWeightsLoader, WeightsMapper, embed_multimodal,
29 - flatten_bn, init_vllm_registered_model, maybe_prefix)
28 +from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
29 + init_vllm_registered_model, maybe_prefix)
30
31
32 class LlavaNextImagePixelInputs(TensorSchema):
@@ -474,19 +474,21 @@ class LlavaNextForConditionalGeneration(nn.Module, SupportsMultiModal,
474 self,
475 input_ids: torch.Tensor,
476 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
477 + *,
478 + is_multimodal: Optional[torch.Tensor] = None,
479 + # Multi-modal token ID may exceed vocab size
480 + handle_oov_mm_token: bool = True,
481 ) -> torch.Tensor:
482 + # This is to satisfy the type checker for each overload
483 + if multimodal_embeddings is None or is_multimodal is None:
484 + return super().get_input_embeddings(input_ids)
485
479 - if multimodal_embeddings is None \
480 - or len(multimodal_embeddings) == 0:
481 - return self.language_model.get_input_embeddings(input_ids)
482 -
483 - inputs_embeds = embed_multimodal(
486 + return super().get_input_embeddings(
487 input_ids,
485 - self.config.image_token_index,
486 - self.language_model.model.get_input_embeddings,
487 - multimodal_embeddings,
488 + multimodal_embeddings=multimodal_embeddings,
489 + is_multimodal=is_multimodal,
490 + handle_oov_mm_token=handle_oov_mm_token,
491 )
489 - return inputs_embeds
492
493 def forward(
494 self,
@@ -549,8 +551,11 @@ model_executor.models.llava_next.LlavaNextProcessingInfo.get_num_image_tokens].
551 # condition is for v0 compatibility.
552 elif inputs_embeds is None:
553 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
552 - inputs_embeds = self.get_input_embeddings(input_ids,
553 - vision_embeddings)
554 + inputs_embeds = self.get_input_embeddings(
555 + input_ids,
556 + vision_embeddings,
557 + is_multimodal=input_ids == self.config.image_token_index,
558 + )
559 input_ids = None
560
561 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/llava_next_video.py
+6 -17
@@ -30,8 +30,7 @@ from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
30 from .llava import init_vision_tower_for_llava
31 from .siglip import SiglipVisionModel
32 from .utils import (AutoWeightsLoader, WeightsMapper,
33 - init_vllm_registered_model, maybe_prefix,
34 - merge_multimodal_embeddings)
33 + init_vllm_registered_model, maybe_prefix)
34 from .vision import get_vision_encoder_info
35
36
@@ -415,19 +414,6 @@ class LlavaNextVideoForConditionalGeneration(nn.Module, SupportsMultiModal,
414 vision_embeddings = self._process_video_pixels(video_input)
415 return vision_embeddings
416
418 - def get_input_embeddings(
419 - self,
420 - input_ids: torch.Tensor,
421 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
422 - ) -> torch.Tensor:
423 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
424 - if multimodal_embeddings is not None \
425 - and len(multimodal_embeddings) != 0:
426 - inputs_embeds = merge_multimodal_embeddings(
427 - input_ids, inputs_embeds, multimodal_embeddings,
428 - self.config.video_token_index)
429 - return inputs_embeds
430 -
417 def forward(
418 self,
419 input_ids: torch.Tensor,
@@ -449,8 +435,11 @@ class LlavaNextVideoForConditionalGeneration(nn.Module, SupportsMultiModal,
435 # condition is for v0 compatibility.
436 elif inputs_embeds is None:
437 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
452 - inputs_embeds = self.get_input_embeddings(input_ids,
453 - vision_embeddings)
438 + inputs_embeds = self.get_input_embeddings(
439 + input_ids,
440 + vision_embeddings,
441 + is_multimodal=input_ids == self.config.video_token_index,
442 + )
443 input_ids = None
444
445 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/llava_onevision.py
-13
@@ -850,19 +850,6 @@ class LlavaOnevisionForConditionalGeneration(nn.Module, SupportsMultiModal,
850
851 return multimodal_embeddings
852
853 - def get_input_embeddings(
854 - self,
855 - input_ids: torch.Tensor,
856 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
857 - ) -> torch.Tensor:
858 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
859 - if multimodal_embeddings is not None \
860 - and len(multimodal_embeddings) != 0:
861 - inputs_embeds = merge_multimodal_embeddings(
862 - input_ids, inputs_embeds, multimodal_embeddings,
863 - [self.config.image_token_index, self.config.video_token_index])
864 - return inputs_embeds
865 -
853 def get_input_embeddings_v0(
854 self,
855 input_ids: torch.Tensor,
vllm/model_executor/models/midashenglm.py
+6 -19
@@ -54,8 +54,7 @@ from vllm.sequence import IntermediateTensors
54 from vllm.transformers_utils.configs.midashenglm import DashengConfig
55
56 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
57 -from .utils import (AutoWeightsLoader, init_vllm_registered_model,
58 - maybe_prefix, merge_multimodal_embeddings)
57 +from .utils import AutoWeightsLoader, init_vllm_registered_model, maybe_prefix
58
59 _Tuple2 = Union[int, tuple[int, int], Sequence[int]]
60
@@ -744,21 +743,6 @@ class MiDashengLMModel(nn.Module, SupportsMultiModal, SupportsPP):
743 return []
744 return self._process_audio_input(audio_input)
745
747 - def get_input_embeddings(
748 - self,
749 - input_ids: torch.Tensor,
750 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
751 - ) -> torch.Tensor:
752 - inputs_embeds = self.decoder.get_input_embeddings(input_ids)
753 - if multimodal_embeddings and len(multimodal_embeddings) > 0:
754 - inputs_embeds = merge_multimodal_embeddings(
755 - input_ids,
756 - inputs_embeds,
757 - multimodal_embeddings,
758 - self.config.audio_token_id,
759 - )
760 - return inputs_embeds
761 -
746 def forward(
747 self,
748 input_ids: torch.Tensor,
@@ -771,8 +755,11 @@ class MiDashengLMModel(nn.Module, SupportsMultiModal, SupportsPP):
755 inputs_embeds = None
756 elif inputs_embeds is None:
757 multimodal_embeddings = self.get_multimodal_embeddings(**kwargs)
774 - inputs_embeds = self.get_input_embeddings(input_ids,
775 - multimodal_embeddings)
758 + inputs_embeds = self.get_input_embeddings(
759 + input_ids,
760 + multimodal_embeddings,
761 + is_multimodal=input_ids == self.config.audio_token_id,
762 + )
763 input_ids = None
764
765 return self.decoder.model(input_ids,
vllm/model_executor/models/mimo_mtp.py
+6
@@ -117,6 +117,9 @@ class MiMoMultiTokenPredictor(nn.Module):
117
118 self.logits_processor = LogitsProcessor(config.vocab_size)
119
120 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
121 + return self.embed_tokens(input_ids)
122 +
123 def forward(
124 self,
125 input_ids: torch.Tensor,
@@ -158,6 +161,9 @@ class MiMoMTP(nn.Module):
161 self.config.hidden_size,
162 prefix=maybe_prefix(prefix, "lm_head"))
163
164 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
165 + return self.model.get_input_embeddings(input_ids)
166 +
167 def forward(
168 self,
169 input_ids: torch.Tensor,
vllm/model_executor/models/minicpmv.py
+6 -21
@@ -71,8 +71,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
71 from .idefics2_vision_model import Idefics2VisionTransformer
72 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
73 SupportsMultiModal, SupportsPP)
74 -from .utils import (AutoWeightsLoader, flatten_bn, maybe_prefix,
75 - merge_multimodal_embeddings)
74 +from .utils import AutoWeightsLoader, flatten_bn, isin_list, maybe_prefix
75
76 # For profile run
77 _MAX_FRAMES_PER_VIDEO = 16
@@ -1144,23 +1143,6 @@ class MiniCPMVBaseModel(nn.Module, SupportsMultiModal, SupportsPP):
1143
1144 return self._process_multimodal_inputs(modalities)
1145
1147 - def get_input_embeddings(
1148 - self,
1149 - input_ids: torch.Tensor,
1150 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1151 - ) -> torch.Tensor:
1152 - inputs_embeds = self.llm.get_input_embeddings(input_ids)
1153 - if multimodal_embeddings is not None \
1154 - and len(multimodal_embeddings) != 0:
1155 - assert len(self.mm_token_ids) > 0
1156 - inputs_embeds = merge_multimodal_embeddings(
1157 - input_ids,
1158 - inputs_embeds,
1159 - multimodal_embeddings,
1160 - list(self.mm_token_ids),
1161 - )
1162 - return inputs_embeds
1163 -
1146 def forward(
1147 self,
1148 input_ids: torch.Tensor,
@@ -1178,8 +1160,11 @@ class MiniCPMVBaseModel(nn.Module, SupportsMultiModal, SupportsPP):
1160 elif inputs_embeds is None:
1161 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
1162
1181 - inputs_embeds = self.get_input_embeddings(input_ids,
1182 - vision_embeddings)
1163 + inputs_embeds = self.get_input_embeddings(
1164 + input_ids,
1165 + vision_embeddings,
1166 + is_multimodal=isin_list(input_ids, list(self.mm_token_ids)),
1167 + )
1168 input_ids = None
1169
1170 hidden_states = self.llm.model(
vllm/model_executor/models/minimax_text_01.py
+2 -8
@@ -592,10 +592,7 @@ class MiniMaxText01Model(nn.Module):
592 dtype=torch.long)
593 minimax_cache_tensors[:, slots_tensor, ...] = 0
594
595 - def get_input_embeddings(
596 - self,
597 - input_ids: torch.Tensor,
598 - ) -> torch.Tensor:
595 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
596 return self.embed_tokens(input_ids)
597
598 def forward(self,
@@ -687,10 +684,7 @@ class MiniMaxText01ForCausalLM(nn.Module, HasInnerState, IsHybrid):
684 return self.model.minimax_cache.get_seqlen_agnostic_capture_inputs(
685 batch_size)
686
690 - def get_input_embeddings(
691 - self,
692 - input_ids: torch.Tensor,
693 - ) -> torch.Tensor:
687 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
688 return self.model.get_input_embeddings(input_ids)
689
690 def forward(self,
vllm/model_executor/models/minimax_vl_01.py
+6 -19
@@ -28,7 +28,7 @@ from .llava_next import LlavaNextProcessingInfo
28 from .pixtral import PixtralHFVisionModel
29 from .siglip import SiglipVisionModel
30 from .utils import (AutoWeightsLoader, flatten_bn, init_vllm_registered_model,
31 - maybe_prefix, merge_multimodal_embeddings)
31 + maybe_prefix)
32
33
34 class MiniMaxVL01ImagePixelInputs(TensorSchema):
@@ -218,22 +218,6 @@ class MiniMaxVL01ForConditionalGeneration(nn.Module, SupportsMultiModal,
218 self.make_empty_intermediate_tensors = (
219 self.language_model.make_empty_intermediate_tensors)
220
221 - def get_input_embeddings(
222 - self,
223 - input_ids: torch.Tensor,
224 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
225 - ) -> torch.Tensor:
226 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
227 - if multimodal_embeddings is not None \
228 - and len(multimodal_embeddings) != 0:
229 - inputs_embeds = merge_multimodal_embeddings(
230 - input_ids,
231 - inputs_embeds,
232 - multimodal_embeddings,
233 - self.config.image_token_index,
234 - )
235 - return inputs_embeds
236 -
221 def get_language_model(self) -> torch.nn.Module:
222 return self.language_model
223
@@ -403,8 +387,11 @@ class MiniMaxVL01ForConditionalGeneration(nn.Module, SupportsMultiModal,
387 inputs_embeds = None
388 elif inputs_embeds is None:
389 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
406 - inputs_embeds = self.get_input_embeddings(input_ids,
407 - vision_embeddings)
390 + inputs_embeds = self.get_input_embeddings(
391 + input_ids,
392 + vision_embeddings,
393 + is_multimodal=input_ids == self.config.image_token_index,
394 + )
395 input_ids = None
396
397 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/mistral3.py
+6 -20
@@ -38,8 +38,7 @@ from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
38 SupportsMultiModal, SupportsPP)
39 from .pixtral import PixtralHFEncoderInfo, PixtralHFVisionModel
40 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
41 - init_vllm_registered_model, maybe_prefix,
42 - merge_multimodal_embeddings)
41 + init_vllm_registered_model, maybe_prefix)
42 from .vision import get_vision_encoder_info
43
44
@@ -524,22 +523,6 @@ class Mistral3ForConditionalGeneration(nn.Module, SupportsLoRA,
523
524 return vision_embeddings
525
527 - def get_input_embeddings(
528 - self,
529 - input_ids: torch.Tensor,
530 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
531 - ) -> torch.Tensor:
532 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
533 - if multimodal_embeddings is not None \
534 - and len(multimodal_embeddings) != 0:
535 - inputs_embeds = merge_multimodal_embeddings(
536 - input_ids,
537 - inputs_embeds,
538 - multimodal_embeddings,
539 - self.config.image_token_index,
540 - )
541 - return inputs_embeds
542 -
526 def forward(
527 self,
528 input_ids: torch.Tensor,
@@ -592,8 +575,11 @@ class Mistral3ForConditionalGeneration(nn.Module, SupportsLoRA,
575 # condition is for v0 compatibility.
576 elif inputs_embeds is None:
577 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
595 - inputs_embeds = self.get_input_embeddings(input_ids,
596 - vision_embeddings)
578 + inputs_embeds = self.get_input_embeddings(
579 + input_ids,
580 + vision_embeddings,
581 + is_multimodal=input_ids == self.config.image_token_index,
582 + )
583 input_ids = None
584
585 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/mllama4.py
+7 -23
@@ -42,7 +42,7 @@ from vllm.model_executor.model_loader.utils import initialize_model
42 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
43 from vllm.multimodal import MULTIMODAL_REGISTRY
44 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
45 - MultiModalKwargsItems, NestedTensors)
45 + MultiModalKwargsItems)
46 from vllm.multimodal.parse import (ImageProcessorItems, ImageSize,
47 MultiModalDataItems)
48 from vllm.multimodal.processing import (BaseMultiModalProcessor,
@@ -56,8 +56,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
56
57 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
58 from .llama4 import Llama4ForCausalLM
59 -from .utils import (AutoWeightsLoader, flatten_bn, maybe_prefix,
60 - merge_multimodal_embeddings)
59 +from .utils import AutoWeightsLoader, flatten_bn, maybe_prefix
60 from .vision import run_dp_sharded_vision_model
61
62
@@ -813,24 +812,6 @@ class Llama4ForConditionalGeneration(nn.Module, SupportsMultiModal,
812
813 return self._process_image_input(image_input)
814
816 - def get_input_embeddings(
817 - self,
818 - input_ids: torch.Tensor,
819 - multimodal_embeddings: Optional[NestedTensors] = None,
820 - ) -> torch.Tensor:
821 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
822 -
823 - if multimodal_embeddings is not None and len(
824 - multimodal_embeddings) != 0:
825 - inputs_embeds = merge_multimodal_embeddings(
826 - input_ids,
827 - inputs_embeds,
828 - multimodal_embeddings,
829 - self.config.image_token_index,
830 - )
831 -
832 - return inputs_embeds
833 -
815 def forward(
816 self,
817 input_ids: torch.Tensor,
@@ -846,8 +827,11 @@ class Llama4ForConditionalGeneration(nn.Module, SupportsMultiModal,
827 # this condition is for v0 compatibility.
828 elif inputs_embeds is None:
829 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
849 - inputs_embeds = self.get_input_embeddings(input_ids,
850 - vision_embeddings)
830 + inputs_embeds = self.get_input_embeddings(
831 + input_ids,
832 + vision_embeddings,
833 + is_multimodal=input_ids == self.config.image_token_index,
834 + )
835 input_ids = None
836
837 return self.language_model(input_ids, positions, intermediate_tensors,
vllm/model_executor/models/modernbert.py
+9
@@ -43,6 +43,9 @@ class ModernBertEmbeddings(nn.Module):
43 eps=config.layer_norm_eps,
44 bias=config.norm_bias)
45
46 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
47 + return self.tok_embeddings(input_ids)
48 +
49 def forward(
50 self,
51 input_ids: torch.Tensor,
@@ -220,6 +223,9 @@ class ModernBertModel(nn.Module):
223 eps=config.norm_eps,
224 bias=config.norm_bias)
225
226 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
227 + return self.embeddings.get_input_embeddings(input_ids)
228 +
229 def load_weights(self, weights: Iterable[tuple[str,
230 torch.Tensor]]) -> set[str]:
231 weights = self.hf_to_vllm_mapper.apply(weights)
@@ -333,6 +339,9 @@ class ModernBertForSequenceClassification(nn.Module, SupportsCrossEncoding):
339 ),
340 })
341
342 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
343 + return self.model.get_input_embeddings(input_ids)
344 +
345 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
346
347 self_weights = []
vllm/model_executor/models/molmo.py
+7 -25
@@ -58,7 +58,7 @@ from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
58 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
59 is_pp_missing_parameter,
60 make_empty_intermediate_tensors_factory, make_layers,
61 - maybe_prefix, merge_multimodal_embeddings)
61 + maybe_prefix)
62
63 # TODO: hard-coded for now. Consider making it configurable.
64 VIT_LAYERS = [-2, -9]
@@ -819,10 +819,7 @@ class MolmoModel(nn.Module, SupportsQuant):
819 make_empty_intermediate_tensors_factory(
820 ["hidden_states", "residual"], config.hidden_size))
821
822 - def get_input_embeddings(
823 - self,
824 - input_ids: torch.Tensor,
825 - ) -> torch.Tensor:
822 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
823 return self.embed_tokens(input_ids)
824
825 def forward(
@@ -1481,24 +1478,6 @@ class MolmoForCausalLM(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA,
1478
1479 return self._process_image_input(image_input)
1480
1484 - def get_input_embeddings(
1485 - self,
1486 - input_ids: torch.Tensor,
1487 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1488 - ) -> torch.Tensor:
1489 - inputs_embeds = self.model.get_input_embeddings(input_ids)
1490 - if multimodal_embeddings is not None \
1491 - and len(multimodal_embeddings) != 0:
1492 - assert self.img_patch_id is not None
1493 -
1494 - inputs_embeds = merge_multimodal_embeddings(
1495 - input_ids,
1496 - inputs_embeds,
1497 - multimodal_embeddings,
1498 - self.img_patch_id,
1499 - )
1500 - return inputs_embeds
1501 -
1481 def forward(
1482 self,
1483 input_ids: torch.LongTensor,
@@ -1515,8 +1494,11 @@ class MolmoForCausalLM(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA,
1494 # condition is for v0 compatibility.
1495 elif inputs_embeds is None:
1496 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
1518 - inputs_embeds = self.get_input_embeddings(input_ids,
1519 - vision_embeddings)
1497 + inputs_embeds = self.get_input_embeddings(
1498 + input_ids,
1499 + vision_embeddings,
1500 + is_multimodal=input_ids == self.img_patch_id,
1501 + )
1502 input_ids = None
1503
1504 hidden_states = self.model(input_ids,
vllm/model_executor/models/nano_nemotron_vl.py
+13 -30
@@ -35,8 +35,7 @@ from vllm.model_executor.models.nemotron_h import NemotronHForCausalLM
35 from vllm.model_executor.models.radio import RadioModel
36 from vllm.model_executor.models.utils import (flatten_bn,
37 init_vllm_registered_model,
38 - maybe_prefix,
39 - merge_multimodal_embeddings)
38 + isin_list, maybe_prefix)
39 from vllm.multimodal import MULTIMODAL_REGISTRY
40 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
41 MultiModalKwargs, MultiModalKwargsItems,
@@ -1096,8 +1095,8 @@ class NemotronH_Nano_VL_V2(nn.Module, HasInnerState, IsHybrid,
1095
1096 return modalities
1097
1099 - def get_multimodal_embeddings(
1100 - self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
1098 + def get_multimodal_embeddings(self,
1099 + **kwargs: object) -> MultiModalEmbeddings:
1100 # Validate the multimodal input keyword arguments
1101 modalities = self._parse_and_validate_multimodal_inputs(**kwargs)
1102 if modalities is None:
@@ -1121,30 +1120,6 @@ class NemotronH_Nano_VL_V2(nn.Module, HasInnerState, IsHybrid,
1120
1121 return multimodal_embeddings
1122
1124 - def get_input_embeddings(
1125 - self,
1126 - input_ids: torch.Tensor,
1127 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1128 - ) -> torch.Tensor:
1129 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1130 -
1131 - if (multimodal_embeddings is not None
1132 - and len(multimodal_embeddings) != 0):
1133 - context_token_ids = [
1134 - token_id for token_id in (self.img_context_token_id,
1135 - self.video_context_token_id)
1136 - if token_id is not None
1137 - ]
1138 - assert len(context_token_ids) >= 1
1139 - inputs_embeds = merge_multimodal_embeddings(
1140 - input_ids,
1141 - inputs_embeds,
1142 - multimodal_embeddings,
1143 - context_token_ids,
1144 - )
1145 -
1146 - return inputs_embeds
1147 -
1123 def get_language_model(self) -> torch.nn.Module:
1124 return self.language_model
1125
@@ -1163,9 +1138,17 @@ class NemotronH_Nano_VL_V2(nn.Module, HasInnerState, IsHybrid,
1138 # NOTE: In v1, inputs_embeds is always generated at model runner, this
1139 # condition is for v0 compatibility.
1140 elif inputs_embeds is None:
1141 + context_token_ids = [
1142 + token_id for token_id in (self.img_context_token_id,
1143 + self.video_context_token_id)
1144 + if token_id is not None
1145 + ]
1146 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
1167 - inputs_embeds = self.get_input_embeddings(input_ids,
1168 - vision_embeddings)
1147 + inputs_embeds = self.get_input_embeddings(
1148 + input_ids,
1149 + vision_embeddings,
1150 + is_multimodal=isin_list(input_ids, context_token_ids),
1151 + )
1152 input_ids = None
1153
1154 hidden_states = self.language_model(
vllm/model_executor/models/nemotron_vl.py
+22 -15
@@ -38,7 +38,7 @@ from vllm.transformers_utils.tokenizer import AnyTokenizer
38 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
39 SupportsMultiModal, SupportsPP)
40 from .utils import (AutoWeightsLoader, flatten_bn, init_vllm_registered_model,
41 - maybe_prefix, merge_multimodal_embeddings)
41 + maybe_prefix)
42
43 IMG_START = '<img>'
44 IMG_END = '</img>'
@@ -576,20 +576,24 @@ class LlamaNemotronVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
576 self,
577 input_ids: torch.Tensor,
578 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
579 + *,
580 + is_multimodal: Optional[torch.Tensor] = None,
581 + handle_oov_mm_token: bool = False,
582 ) -> torch.Tensor:
580 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
581 - if multimodal_embeddings is not None \
582 - and len(multimodal_embeddings) != 0:
583 - context_token_ids = [self.img_context_token_id]
584 - assert len(context_token_ids) >= 1
583 + if multimodal_embeddings is not None and len(
584 + multimodal_embeddings) > 0:
585 self._set_visual_token_mask(input_ids)
586 - inputs_embeds = merge_multimodal_embeddings(
587 - input_ids,
588 - inputs_embeds,
589 - multimodal_embeddings,
590 - context_token_ids,
591 - )
592 - return inputs_embeds
586 +
587 + # This is to satisfy the type checker for each overload
588 + if multimodal_embeddings is None or is_multimodal is None:
589 + return super().get_input_embeddings(input_ids)
590 +
591 + return super().get_input_embeddings(
592 + input_ids,
593 + multimodal_embeddings=multimodal_embeddings,
594 + is_multimodal=is_multimodal,
595 + handle_oov_mm_token=handle_oov_mm_token,
596 + )
597
598 def forward(
599 self,
@@ -608,8 +612,11 @@ class LlamaNemotronVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
612 # condition is for v0 compatibility.
613 elif inputs_embeds is None:
614 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
611 - inputs_embeds = self.get_input_embeddings(input_ids,
612 - vision_embeddings)
615 + inputs_embeds = self.get_input_embeddings(
616 + input_ids,
617 + vision_embeddings,
618 + is_multimodal=input_ids == self.img_context_token_id,
619 + )
620 input_ids = None
621
622 forward_kwargs = {
vllm/model_executor/models/olmo2.py
+6
@@ -295,6 +295,9 @@ class Olmo2Model(nn.Module):
295 make_empty_intermediate_tensors_factory(["hidden_states"],
296 self.config.hidden_size))
297
298 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
299 + return self.embed_tokens(input_ids)
300 +
301 def forward(
302 self,
303 input_ids: torch.Tensor,
@@ -408,6 +411,9 @@ class Olmo2ForCausalLM(nn.Module, SupportsPP, SupportsLoRA):
411 self.make_empty_intermediate_tensors = (
412 self.model.make_empty_intermediate_tensors)
413
414 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
415 + return self.model.get_input_embeddings(input_ids)
416 +
417 def forward(
418 self,
419 input_ids: torch.Tensor,
vllm/model_executor/models/ovis.py
+5 -16
@@ -48,7 +48,6 @@ from vllm.transformers_utils.processors.ovis import OvisProcessor
48 from vllm.utils.tensor_schema import TensorSchema, TensorShape
49
50 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
51 -from .utils import merge_multimodal_embeddings
51
52 # Cannot find the following number from hf config.
53 IMAGE_TOKEN = "<image>"
@@ -501,19 +500,6 @@ class Ovis(nn.Module, SupportsMultiModal, SupportsPP):
500
501 return image_features
502
504 - def get_input_embeddings(
505 - self,
506 - input_ids: torch.Tensor,
507 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
508 - ) -> torch.Tensor:
509 - inputs_embeds = self.llm.get_input_embeddings(input_ids)
510 - if multimodal_embeddings is not None \
511 - and len(multimodal_embeddings) != 0:
512 - inputs_embeds = merge_multimodal_embeddings(
513 - input_ids, inputs_embeds, multimodal_embeddings,
514 - self.image_pad_token_id)
515 - return inputs_embeds
516 -
503 def forward(
504 self,
505 input_ids: torch.Tensor,
@@ -529,8 +515,11 @@ class Ovis(nn.Module, SupportsMultiModal, SupportsPP):
515 # condition is for v0 compatibility.
516 elif inputs_embeds is None:
517 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
532 - inputs_embeds = self.get_input_embeddings(input_ids,
533 - vision_embeddings)
518 + inputs_embeds = self.get_input_embeddings(
519 + input_ids,
520 + vision_embeddings,
521 + is_multimodal=input_ids == self.image_pad_token_id,
522 + )
523 input_ids = None
524
525 # up until here we have an inputs_embeds 100% numerical identity
vllm/model_executor/models/ovis2_5.py
+5 -13
@@ -585,17 +585,6 @@ class Ovis2_5(nn.Module, SupportsMultiModal, SupportsPP):
585
586 return multimodal_embeddings
587
588 - def get_input_embeddings(
589 - self,
590 - input_ids: torch.Tensor,
591 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
592 - ) -> torch.Tensor:
593 - inputs_embeds = self.llm.get_input_embeddings(input_ids)
594 - if multimodal_embeddings is not None:
595 - tmp = torch.concat(multimodal_embeddings, dim=0)
596 - inputs_embeds[input_ids == self.image_pad_token_id] = tmp
597 - return inputs_embeds
598 -
588 def forward(
589 self,
590 input_ids: torch.Tensor,
@@ -612,8 +601,11 @@ class Ovis2_5(nn.Module, SupportsMultiModal, SupportsPP):
601 elif inputs_embeds is None:
602 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
603
615 - inputs_embeds = self.get_input_embeddings(input_ids,
616 - vision_embeddings)
604 + inputs_embeds = self.get_input_embeddings(
605 + input_ids,
606 + vision_embeddings,
607 + is_multimodal=input_ids == self.image_pad_token_id,
608 + )
609 input_ids = None
610
611 # up until here we have a inputs_embeds 100% numerical identity
vllm/model_executor/models/paligemma.py
+6 -17
@@ -26,8 +26,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
26 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
27 from .siglip import SiglipVisionModel
28 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
29 - init_vllm_registered_model, maybe_prefix,
30 - merge_multimodal_embeddings)
29 + init_vllm_registered_model, maybe_prefix)
30 from .vision import get_vision_encoder_info
31
32 logger = init_logger(__name__)
@@ -362,19 +361,6 @@ class PaliGemmaForConditionalGeneration(nn.Module, SupportsMultiModal,
361 vision_embeddings = vision_embeddings * (self.config.hidden_size**-0.5)
362 return vision_embeddings
363
365 - def get_input_embeddings(
366 - self,
367 - input_ids: torch.Tensor,
368 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
369 - ) -> torch.Tensor:
370 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
371 - if multimodal_embeddings is not None \
372 - and len(multimodal_embeddings) != 0:
373 - inputs_embeds = merge_multimodal_embeddings(
374 - input_ids, inputs_embeds, multimodal_embeddings,
375 - self.config.image_token_index)
376 - return inputs_embeds
377 -
364 def forward(self,
365 input_ids: torch.Tensor,
366 positions: torch.Tensor,
@@ -388,8 +374,11 @@ class PaliGemmaForConditionalGeneration(nn.Module, SupportsMultiModal,
374 # condition is for v0 compatibility.
375 elif inputs_embeds is None:
376 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
391 - inputs_embeds = self.get_input_embeddings(input_ids,
392 - vision_embeddings)
377 + inputs_embeds = self.get_input_embeddings(
378 + input_ids,
379 + vision_embeddings,
380 + is_multimodal=input_ids == self.config.image_token_index,
381 + )
382 input_ids = None
383
384 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/phi3v.py
+32 -12
@@ -51,9 +51,9 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
51 from .clip import CLIPVisionModel
52 from .interfaces import (MultiModalEmbeddings, SupportsMultiModal, SupportsPP,
53 SupportsQuant)
54 -from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
55 - init_vllm_registered_model, maybe_prefix,
56 - merge_multimodal_embeddings)
54 +from .utils import (AutoWeightsLoader, WeightsMapper,
55 + _merge_multimodal_embeddings, flatten_bn,
56 + init_vllm_registered_model, maybe_prefix)
57
58 logger = init_logger(__name__)
59
@@ -643,14 +643,31 @@ class Phi3VForCausalLM(nn.Module, SupportsMultiModal, SupportsPP,
643 self,
644 input_ids: torch.Tensor,
645 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
646 + *,
647 + is_multimodal: Optional[torch.Tensor] = None,
648 + handle_oov_mm_token: bool = False,
649 ) -> torch.Tensor:
647 - inputs_embeds = self.embed_tokens(input_ids)
648 - if multimodal_embeddings is not None \
649 - and len(multimodal_embeddings) != 0:
650 - inputs_embeds = merge_multimodal_embeddings(
651 - input_ids, inputs_embeds, multimodal_embeddings,
652 - self.image_token_id)
653 - return inputs_embeds
650 + inputs_embeds = self._get_text_embeddings(
651 + input_ids,
652 + self.embed_tokens,
653 + is_multimodal=is_multimodal,
654 + handle_oov_mm_token=handle_oov_mm_token,
655 + )
656 +
657 + if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
658 + return inputs_embeds
659 +
660 + if is_multimodal is None:
661 + raise ValueError(
662 + "`get_input_embeddings` now requires `is_multimodal` arg, "
663 + "please update your model runner according to "
664 + "https://github.com/vllm-project/vllm/pull/16229.")
665 +
666 + return _merge_multimodal_embeddings(
667 + inputs_embeds=inputs_embeds,
668 + multimodal_embeddings=multimodal_embeddings,
669 + is_multimodal=is_multimodal,
670 + )
671
672 def forward(self,
673 input_ids: torch.Tensor,
@@ -666,8 +683,11 @@ class Phi3VForCausalLM(nn.Module, SupportsMultiModal, SupportsPP,
683 # condition is for v0 compatibility
684 elif inputs_embeds is None:
685 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
669 - inputs_embeds = self.get_input_embeddings(input_ids,
670 - vision_embeddings)
686 + inputs_embeds = self.get_input_embeddings(
687 + input_ids,
688 + vision_embeddings,
689 + is_multimodal=self.image_token_id,
690 + )
691 input_ids = None
692
693 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/phi4_multimodal.py
+3 -15
@@ -1342,12 +1342,12 @@ class Phi4MultimodalForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal):
1342 image_attention_mask)
1343 return image_embeds
1344
1345 - def get_multimodal_embeddings(
1346 - self, **kwargs: object) -> Optional[MultiModalEmbeddings]:
1345 + def get_multimodal_embeddings(self,
1346 + **kwargs: object) -> MultiModalEmbeddings:
1347
1348 modalities = self._parse_and_validate_multimodal_inputs(**kwargs)
1349 if not modalities:
1350 - return None
1350 + return []
1351
1352 # The result multimodal_embeddings is tuple of tensors, with each
1353 # tensor corresponding to a multimodal data item (image or video).
@@ -1371,18 +1371,6 @@ class Phi4MultimodalForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal):
1371
1372 return multimodal_embeddings
1373
1374 - def get_input_embeddings(
1375 - self,
1376 - input_ids: torch.Tensor,
1377 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1378 - ) -> torch.Tensor:
1379 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1380 - if multimodal_embeddings is not None:
1381 - inputs_embeds = merge_multimodal_embeddings(
1382 - input_ids, inputs_embeds, multimodal_embeddings,
1383 - [_IMAGE_PLACEHOLDER_TOKEN_ID, _AUDIO_PLACEHOLDER_TOKEN_ID])
1384 - return inputs_embeds
1385 -
1374 def get_input_embeddings_v0(
1375 self,
1376 input_ids: torch.Tensor,
vllm/model_executor/models/phi4mm.py
-14
@@ -1151,7 +1151,6 @@ class Phi4MMForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal):
1151 modalities = self._parse_and_validate_multimodal_inputs(**kwargs)
1152 if not modalities:
1153 return []
1154 - return None
1154
1155 # The result multimodal_embeddings is tuple of tensors, with each
1156 # tensor corresponding to a multimodal data item (image or video).
@@ -1175,19 +1174,6 @@ class Phi4MMForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal):
1174
1175 return multimodal_embeddings
1176
1178 - def get_input_embeddings(
1179 - self,
1180 - input_ids: torch.Tensor,
1181 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1182 - ) -> torch.Tensor:
1183 - inputs_embeds = self.model.embed_tokens(input_ids)
1184 - if multimodal_embeddings is not None and len(
1185 - multimodal_embeddings) != 0:
1186 - inputs_embeds = merge_multimodal_embeddings(
1187 - input_ids, inputs_embeds, multimodal_embeddings,
1188 - [_IMAGE_PLACEHOLDER_TOKEN_ID, _AUDIO_PLACEHOLDER_TOKEN_ID])
1189 - return inputs_embeds
1190 -
1177 def get_input_embeddings_v0(
1178 self,
1179 input_ids: torch.Tensor,
vllm/model_executor/models/pixtral.py
+6 -20
@@ -50,8 +50,7 @@ from vllm.transformers_utils.tokenizer import (MistralTokenizer,
50 from vllm.utils.tensor_schema import TensorSchema, TensorShape
51
52 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
53 -from .utils import (flatten_bn, init_vllm_registered_model, maybe_prefix,
54 - merge_multimodal_embeddings)
53 +from .utils import flatten_bn, init_vllm_registered_model, maybe_prefix
54 from .vision import VisionEncoderInfo, resolve_visual_encoder_outputs
55
56 try:
@@ -433,22 +432,6 @@ class PixtralForConditionalGeneration(nn.Module, SupportsMultiModal,
432
433 return self._process_image_input(image_input)
434
436 - def get_input_embeddings(
437 - self,
438 - input_ids: torch.Tensor,
439 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
440 - ) -> torch.Tensor:
441 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
442 - if multimodal_embeddings is not None \
443 - and len(multimodal_embeddings) != 0:
444 - inputs_embeds = merge_multimodal_embeddings(
445 - input_ids,
446 - inputs_embeds,
447 - multimodal_embeddings,
448 - self.vision_args.image_token_id,
449 - )
450 - return inputs_embeds
451 -
435 def forward(
436 self,
437 input_ids: torch.Tensor,
@@ -465,8 +448,11 @@ class PixtralForConditionalGeneration(nn.Module, SupportsMultiModal,
448 # condition is for v0 compatibility.
449 elif inputs_embeds is None:
450 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
468 - inputs_embeds = self.get_input_embeddings(input_ids,
469 - vision_embeddings)
451 + inputs_embeds = self.get_input_embeddings(
452 + input_ids,
453 + vision_embeddings,
454 + is_multimodal=input_ids == self.vision_args.image_token_id,
455 + )
456 input_ids = None
457
458 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/qwen2_5_omni_thinker.py
+15 -13
@@ -865,24 +865,26 @@ class Qwen2_5OmniThinkerForConditionalGeneration(
865 multimodal_embeddings += audio_embeddings
866 return multimodal_embeddings
867
868 + # TODO (ywang96): support overlapping modality embeddings so that
869 + # `use_audio_in_video` will work on V1.
870 def get_input_embeddings(
871 self,
872 input_ids: torch.Tensor,
873 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
874 + *,
875 + is_multimodal: Optional[torch.Tensor] = None,
876 + handle_oov_mm_token: bool = False,
877 ) -> torch.Tensor:
873 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
874 - if multimodal_embeddings is not None \
875 - and len(multimodal_embeddings) != 0:
876 -
877 - # TODO (ywang96): support overlapping modality embeddings so that
878 - # `use_audio_in_video` will work on V1.
879 - inputs_embeds = merge_multimodal_embeddings(
880 - input_ids, inputs_embeds, multimodal_embeddings, [
881 - self.config.image_token_index,
882 - self.config.video_token_index,
883 - self.config.audio_token_index
884 - ])
885 - return inputs_embeds
878 + # This is to satisfy the type checker for each overload
879 + if multimodal_embeddings is None or is_multimodal is None:
880 + return super().get_input_embeddings(input_ids)
881 +
882 + return super().get_input_embeddings(
883 + input_ids,
884 + multimodal_embeddings=multimodal_embeddings,
885 + is_multimodal=is_multimodal,
886 + handle_oov_mm_token=handle_oov_mm_token,
887 + )
888
889 def get_multimodal_embeddings_v0(
890 self, **kwargs: object) -> Optional[NestedTensors]:
vllm/model_executor/models/qwen2_5_vl.py
-13
@@ -1365,19 +1365,6 @@ class Qwen2_5_VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1365 multimodal_embeddings += video_embeddings
1366 return multimodal_embeddings
1367
1368 - def get_input_embeddings(
1369 - self,
1370 - input_ids: torch.Tensor,
1371 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1372 - ) -> torch.Tensor:
1373 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1374 - if multimodal_embeddings is not None \
1375 - and len(multimodal_embeddings) != 0:
1376 - inputs_embeds = merge_multimodal_embeddings(
1377 - input_ids, inputs_embeds, multimodal_embeddings,
1378 - [self.config.image_token_id, self.config.video_token_id])
1379 - return inputs_embeds
1380 -
1368 def get_input_embeddings_v0(
1369 self,
1370 input_ids: torch.Tensor,
vllm/model_executor/models/qwen2_audio.py
+6 -17
@@ -49,8 +49,7 @@ from vllm.sequence import IntermediateTensors
49 from vllm.utils.tensor_schema import TensorSchema, TensorShape
50
51 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
52 -from .utils import (AutoWeightsLoader, init_vllm_registered_model,
53 - maybe_prefix, merge_multimodal_embeddings)
52 +from .utils import AutoWeightsLoader, init_vllm_registered_model, maybe_prefix
53
54
55 # # === Audio Inputs === #
@@ -438,19 +437,6 @@ class Qwen2AudioForConditionalGeneration(nn.Module, SupportsMultiModal,
437 masked_audio_features = self._process_audio_input(audio_input)
438 return masked_audio_features
439
441 - def get_input_embeddings(
442 - self,
443 - input_ids: torch.Tensor,
444 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
445 - ) -> torch.Tensor:
446 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
447 - if multimodal_embeddings is not None \
448 - and len(multimodal_embeddings) != 0:
449 - inputs_embeds = merge_multimodal_embeddings(
450 - input_ids, inputs_embeds, multimodal_embeddings,
451 - self.config.audio_token_index)
452 - return inputs_embeds
453 -
440 def forward(
441 self,
442 input_ids: torch.Tensor,
@@ -467,8 +453,11 @@ class Qwen2AudioForConditionalGeneration(nn.Module, SupportsMultiModal,
453 # condition is for v0 compatibility.
454 elif inputs_embeds is None:
455 multimodal_embeddings = self.get_multimodal_embeddings(**kwargs)
470 - inputs_embeds = self.get_input_embeddings(input_ids,
471 - multimodal_embeddings)
456 + inputs_embeds = self.get_input_embeddings(
457 + input_ids,
458 + multimodal_embeddings,
459 + is_multimodal=input_ids == self.config.audio_token_index,
460 + )
461 input_ids = None
462
463 hidden_states = self.language_model.model(input_ids,
vllm/model_executor/models/qwen2_vl.py
-13
@@ -1459,19 +1459,6 @@ class Qwen2VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1459
1460 return multimodal_embeddings
1461
1462 - def get_input_embeddings(
1463 - self,
1464 - input_ids: torch.Tensor,
1465 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1466 - ) -> torch.Tensor:
1467 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1468 - if multimodal_embeddings is not None \
1469 - and len(multimodal_embeddings) != 0:
1470 - inputs_embeds = merge_multimodal_embeddings(
1471 - input_ids, inputs_embeds, multimodal_embeddings,
1472 - [self.config.image_token_id, self.config.video_token_id])
1473 - return inputs_embeds
1474 -
1462 def get_input_embeddings_v0(
1463 self,
1464 input_ids: torch.Tensor,
vllm/model_executor/models/qwen3_vl.py
+59 -30
@@ -79,7 +79,8 @@ from .qwen2_5_vl import (Qwen2_5_VisionAttention,
79 from .qwen2_vl import Qwen2VLProcessingInfo
80 from .qwen3 import Qwen3ForCausalLM, Qwen3Model
81 from .utils import (AutoWeightsLoader, PPMissingLayer, WeightsMapper,
82 - maybe_prefix, merge_multimodal_embeddings)
82 + _merge_multimodal_embeddings, maybe_prefix,
83 + merge_multimodal_embeddings)
84 from .vision import get_vit_attn_backend, run_dp_sharded_mrope_vision_model
85
86 logger = init_logger(__name__)
@@ -1324,17 +1325,22 @@ class Qwen3VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1325 return multimodal_embeddings
1326
1327 def _compute_deepstack_embeds(
1327 - self, input_ids: torch.Tensor, inputs_embeds: torch.Tensor,
1328 - multimodal_embeddings: MultiModalEmbeddings) -> torch.Tensor:
1329 - visual_lens = [
1330 - x.shape[0] if isinstance(x, torch.Tensor) else len(x)
1331 - for x in multimodal_embeddings
1332 - ]
1328 + self,
1329 + inputs_embeds: torch.Tensor,
1330 + multimodal_embeddings: MultiModalEmbeddings,
1331 + is_multimodal: torch.Tensor,
1332 + ) -> tuple[torch.Tensor, MultiModalEmbeddings]:
1333 + visual_lens = [len(x) for x in multimodal_embeddings]
1334 multimodal_embeddings_cat = torch.cat(multimodal_embeddings, dim=0)
1335
1335 - multimodal_embeddings_main, multimodal_embeddings_multiscale = torch.split( # noqa:E501
1336 - multimodal_embeddings_cat, [self.visual_dim, self.multiscale_dim],
1337 - dim=-1)
1336 + (
1337 + multimodal_embeddings_main,
1338 + multimodal_embeddings_multiscale,
1339 + ) = torch.split(
1340 + multimodal_embeddings_cat,
1341 + [self.visual_dim, self.multiscale_dim],
1342 + dim=-1,
1343 + )
1344
1345 multimodal_embeddings = torch.split(multimodal_embeddings_main,
1346 visual_lens,
@@ -1346,39 +1352,62 @@ class Qwen3VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1352 inputs_embeds.size(0),
1353 self.deepstack_num_level * inputs_embeds.size(1))
1354
1349 - deepstack_input_embeds = merge_multimodal_embeddings(
1350 - input_ids,
1351 - deepstack_input_embeds,
1352 - multimodal_embeddings_multiscale,
1353 - placeholder_token_id=[
1354 - self.config.image_token_id, self.config.video_token_id
1355 - ],
1355 + deepstack_input_embeds = _merge_multimodal_embeddings(
1356 + inputs_embeds=deepstack_input_embeds,
1357 + multimodal_embeddings=multimodal_embeddings_multiscale,
1358 + is_multimodal=is_multimodal,
1359 )
1360 deepstack_input_embeds = deepstack_input_embeds.view(
1361 inputs_embeds.shape[0], self.deepstack_num_level, self.visual_dim)
1362 deepstack_input_embeds = deepstack_input_embeds.permute(1, 0, 2)
1363 +
1364 return deepstack_input_embeds, multimodal_embeddings
1365
1366 def get_input_embeddings(
1367 self,
1368 input_ids: torch.Tensor,
1369 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1370 + *,
1371 + is_multimodal: Optional[torch.Tensor] = None,
1372 + handle_oov_mm_token: bool = False,
1373 ) -> torch.Tensor:
1367 - deepstack_input_embeds = None
1368 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
1369 - if multimodal_embeddings is not None:
1370 - if self.use_deepstack:
1371 - deepstack_input_embeds, multimodal_embeddings = self._compute_deepstack_embeds( # noqa:E501
1372 - input_ids, inputs_embeds, multimodal_embeddings)
1373 - inputs_embeds = merge_multimodal_embeddings(
1374 - input_ids, inputs_embeds, multimodal_embeddings,
1375 - [self.config.image_token_id, self.config.video_token_id])
1374 + inputs_embeds = self._get_text_embeddings(
1375 + input_ids,
1376 + self.language_model.get_input_embeddings,
1377 + is_multimodal=is_multimodal,
1378 + handle_oov_mm_token=handle_oov_mm_token,
1379 + )
1380 +
1381 + if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
1382 + return inputs_embeds
1383 +
1384 + if is_multimodal is None:
1385 + raise ValueError(
1386 + "`get_input_embeddings` now requires `is_multimodal` arg, "
1387 + "please update your model runner according to "
1388 + "https://github.com/vllm-project/vllm/pull/16229.")
1389
1390 if self.use_deepstack:
1378 - if deepstack_input_embeds is None:
1379 - deepstack_input_embeds = torch.zeros_like(
1380 - inputs_embeds).unsqueeze(0).repeat(
1381 - self.deepstack_num_level, 1, 1).contiguous()
1391 + (
1392 + deepstack_input_embeds,
1393 + multimodal_embeddings,
1394 + ) = self._compute_deepstack_embeds(
1395 + inputs_embeds=inputs_embeds,
1396 + multimodal_embeddings=multimodal_embeddings,
1397 + is_multimodal=is_multimodal,
1398 + )
1399 + else:
1400 + deepstack_input_embeds = None
1401 +
1402 + inputs_embeds = _merge_multimodal_embeddings(
1403 + inputs_embeds=inputs_embeds,
1404 + multimodal_embeddings=multimodal_embeddings,
1405 + is_multimodal=is_multimodal,
1406 + )
1407 +
1408 + if deepstack_input_embeds is not None:
1409 + deepstack_input_embeds = torch.zeros_like(inputs_embeds).unsqueeze(
1410 + 0).repeat(self.deepstack_num_level, 1, 1).contiguous()
1411 self._set_deepstack_input_embeds(deepstack_input_embeds)
1412
1413 return inputs_embeds
vllm/model_executor/models/qwen_vl.py
+7 -18
@@ -45,7 +45,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
45 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
46 SupportsMultiModal, SupportsPP)
47 from .qwen import QWenBaseModel, QWenModel
48 -from .utils import flatten_bn, merge_multimodal_embeddings
48 +from .utils import flatten_bn
49
50
51 class QwenImagePixelInputs(TensorSchema):
@@ -756,21 +756,6 @@ class QwenVLForConditionalGeneration(QWenBaseModel, SupportsPP, SupportsLoRA,
756 vision_embeddings = self._process_image_input(image_input)
757 return vision_embeddings
758
759 - def get_input_embeddings(
760 - self,
761 - input_ids: torch.Tensor,
762 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
763 - ) -> torch.Tensor:
764 - inputs_embeds = self.transformer.get_input_embeddings(input_ids)
765 -
766 - if multimodal_embeddings is not None \
767 - and len(multimodal_embeddings) != 0:
768 - inputs_embeds = merge_multimodal_embeddings(
769 - input_ids, inputs_embeds, multimodal_embeddings,
770 - self.transformer.visual.image_pad_id)
771 -
772 - return inputs_embeds
773 -
759 def forward(
760 self,
761 input_ids: torch.Tensor,
@@ -786,8 +771,12 @@ class QwenVLForConditionalGeneration(QWenBaseModel, SupportsPP, SupportsLoRA,
771 # condition is for v0 compatibility.
772 elif inputs_embeds is None:
773 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
789 - inputs_embeds = self.get_input_embeddings(input_ids,
790 - vision_embeddings)
774 + inputs_embeds = self.get_input_embeddings(
775 + input_ids,
776 + vision_embeddings,
777 + is_multimodal=input_ids ==
778 + self.transformer.visual.image_pad_id,
779 + )
780 input_ids = None
781
782 hidden_states = self.transformer(input_ids, positions,
vllm/model_executor/models/roberta.py
+3
@@ -218,6 +218,9 @@ class RobertaForSequenceClassification(nn.Module, SupportsCrossEncoding):
218 loader = AutoWeightsLoader(self)
219 return loader.load_weights(weights, mapper=self.jina_to_vllm_mapper)
220
221 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
222 + return self.roberta.get_input_embeddings(input_ids)
223 +
224 def forward(
225 self,
226 input_ids: Optional[torch.Tensor],
vllm/model_executor/models/skyworkr1v.py
+22 -14
@@ -38,7 +38,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
38
39 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
40 from .utils import (AutoWeightsLoader, flatten_bn, init_vllm_registered_model,
41 - maybe_prefix, merge_multimodal_embeddings)
41 + maybe_prefix)
42
43 IMG_START = '<img>'
44 IMG_END = '</img>'
@@ -842,19 +842,24 @@ class SkyworkR1VChatModel(nn.Module, SupportsMultiModal, SupportsPP):
842 self,
843 input_ids: torch.Tensor,
844 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
845 + *,
846 + is_multimodal: Optional[torch.Tensor] = None,
847 + handle_oov_mm_token: bool = False,
848 ) -> torch.Tensor:
846 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
847 - if multimodal_embeddings is not None \
848 - and len(multimodal_embeddings) != 0:
849 - assert self.img_context_token_id is not None
849 + if multimodal_embeddings is not None and len(
850 + multimodal_embeddings) > 0:
851 self._set_visual_token_mask(input_ids)
851 - inputs_embeds = merge_multimodal_embeddings(
852 - input_ids,
853 - inputs_embeds,
854 - multimodal_embeddings,
855 - self.img_context_token_id,
856 - )
857 - return inputs_embeds
852 +
853 + # This is to satisfy the type checker for each overload
854 + if multimodal_embeddings is None or is_multimodal is None:
855 + return super().get_input_embeddings(input_ids)
856 +
857 + return super().get_input_embeddings(
858 + input_ids,
859 + multimodal_embeddings=multimodal_embeddings,
860 + is_multimodal=is_multimodal,
861 + handle_oov_mm_token=handle_oov_mm_token,
862 + )
863
864 def forward(
865 self,
@@ -873,8 +878,11 @@ class SkyworkR1VChatModel(nn.Module, SupportsMultiModal, SupportsPP):
878 # condition is for v0 compatibility.
879 elif inputs_embeds is None:
880 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
876 - inputs_embeds = self.get_input_embeddings(input_ids,
877 - vision_embeddings)
881 + inputs_embeds = self.get_input_embeddings(
882 + input_ids,
883 + vision_embeddings,
884 + is_multimodal=input_ids == self.img_context_token_id,
885 + )
886 input_ids = None
887
888 forward_kwargs = {
vllm/model_executor/models/solar.py
+3
@@ -483,6 +483,9 @@ class SolarForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
483 self.make_empty_intermediate_tensors = (
484 self.model.make_empty_intermediate_tensors)
485
486 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
487 + return self.model.get_input_embeddings(input_ids)
488 +
489 def forward(
490 self,
491 input_ids: torch.Tensor,
vllm/model_executor/models/step3_text.py
+3
@@ -395,6 +395,9 @@ class Step3TextForCausalLM(nn.Module, SupportsPP):
395 self.make_empty_intermediate_tensors = (
396 self.model.make_empty_intermediate_tensors)
397
398 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
399 + return self.model.get_input_embeddings(input_ids)
400 +
401 def forward(self,
402 input_ids: torch.Tensor,
403 positions: torch.Tensor,
vllm/model_executor/models/step3_vl.py
+26 -26
@@ -25,7 +25,7 @@ from vllm.model_executor.layers.linear import (ColumnParallelLinear,
25 from vllm.model_executor.layers.quantization import QuantizationConfig
26 from vllm.multimodal import MULTIMODAL_REGISTRY
27 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
28 - MultiModalKwargsItems, NestedTensors)
28 + MultiModalKwargsItems)
29 from vllm.multimodal.parse import ImageSize, MultiModalDataItems
30 from vllm.multimodal.processing import (BaseMultiModalProcessor,
31 BaseProcessingInfo, PromptReplacement,
@@ -37,8 +37,7 @@ from vllm.transformers_utils.tokenizer import AnyTokenizer
37
38 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
39 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
40 - init_vllm_registered_model, maybe_prefix,
41 - merge_multimodal_embeddings)
40 + init_vllm_registered_model, maybe_prefix)
41 from .vision import run_dp_sharded_vision_model
42
43
@@ -996,10 +995,13 @@ class Step3VLForConditionalGeneration(nn.Module, SupportsMultiModal,
995 1 else cur_feature[0])
996 return merged_image_features
997
999 - def get_multimodal_embeddings(self, **kwargs) -> Optional[NestedTensors]:
998 + def get_language_model(self) -> torch.nn.Module:
999 + return self.language_model
1000 +
1001 + def get_multimodal_embeddings(self, **kwargs) -> MultiModalEmbeddings:
1002 image_input = self._parse_and_validate_image_input(**kwargs)
1003 if image_input is None:
1002 - return None
1004 + return []
1005 vision_embeddings = self._process_image_input(image_input)
1006 return vision_embeddings
1007
@@ -1007,24 +1009,21 @@ class Step3VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1009 self,
1010 input_ids: torch.Tensor,
1011 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
1012 + *,
1013 + is_multimodal: Optional[torch.Tensor] = None,
1014 + # Multi-modal token ID may exceed vocab size
1015 + handle_oov_mm_token: bool = True,
1016 ) -> torch.Tensor:
1011 - if multimodal_embeddings is None:
1012 - inputs_embeds = self.language_model.model.get_input_embeddings(
1013 - input_ids)
1014 - else:
1015 - is_text = input_ids != self.config.image_token_id
1016 - text_ids = input_ids[is_text]
1017 - text_embeds = self.language_model.model.get_input_embeddings(
1018 - text_ids)
1019 - inputs_embeds = torch.empty(input_ids.shape[0],
1020 - text_embeds.shape[-1],
1021 - dtype=text_embeds.dtype,
1022 - device=text_embeds.device)
1023 - inputs_embeds[is_text] = text_embeds
1024 - inputs_embeds = merge_multimodal_embeddings(
1025 - input_ids, inputs_embeds, multimodal_embeddings,
1026 - self.config.image_token_id)
1027 - return inputs_embeds
1017 + # This is to satisfy the type checker for each overload
1018 + if multimodal_embeddings is None or is_multimodal is None:
1019 + return super().get_input_embeddings(input_ids)
1020 +
1021 + return super().get_input_embeddings(
1022 + input_ids,
1023 + multimodal_embeddings=multimodal_embeddings,
1024 + is_multimodal=is_multimodal,
1025 + handle_oov_mm_token=handle_oov_mm_token,
1026 + )
1027
1028 def forward(
1029 self,
@@ -1038,10 +1037,11 @@ class Step3VLForConditionalGeneration(nn.Module, SupportsMultiModal,
1037 inputs_embeds = None
1038 elif inputs_embeds is None:
1039 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
1041 - # always pass the input via `inputs_embeds`
1042 - # to make sure the computation graph is consistent
1043 - inputs_embeds = self.get_input_embeddings(input_ids,
1044 - vision_embeddings)
1040 + inputs_embeds = self.get_input_embeddings(
1041 + input_ids,
1042 + vision_embeddings,
1043 + is_multimodal=input_ids == self.config.image_token_id,
1044 + )
1045 input_ids = None
1046
1047 hidden_states = self.language_model(input_ids,
vllm/model_executor/models/tarsier.py
+6 -19
@@ -40,7 +40,7 @@ from .clip import CLIPVisionModel
40 from .interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP
41 from .siglip import SiglipVisionModel
42 from .utils import (AutoWeightsLoader, flatten_bn, init_vllm_registered_model,
43 - maybe_prefix, merge_multimodal_embeddings)
43 + maybe_prefix)
44 from .vision import VisionEncoderInfo, get_vision_encoder_info
45
46
@@ -589,22 +589,6 @@ class TarsierForConditionalGeneration(nn.Module, SupportsMultiModal,
589 return []
590 return self._process_image_input(image_input)
591
592 - def get_input_embeddings(
593 - self,
594 - input_ids: torch.Tensor,
595 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
596 - ) -> torch.Tensor:
597 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
598 - if multimodal_embeddings is not None \
599 - and len(multimodal_embeddings) != 0:
600 - inputs_embeds = merge_multimodal_embeddings(
601 - input_ids,
602 - inputs_embeds,
603 - multimodal_embeddings,
604 - self.config.image_token_index,
605 - )
606 - return inputs_embeds
607 -
592 def forward(
593 self,
594 input_ids: torch.Tensor,
@@ -617,8 +601,11 @@ class TarsierForConditionalGeneration(nn.Module, SupportsMultiModal,
601 inputs_embeds = None
602 elif inputs_embeds is None:
603 vision_embeddings = self.get_multimodal_embeddings(**kwargs)
620 - inputs_embeds = self.get_input_embeddings(input_ids,
621 - vision_embeddings)
604 + inputs_embeds = self.get_input_embeddings(
605 + input_ids,
606 + vision_embeddings,
607 + is_multimodal=input_ids == self.config.image_token_index,
608 + )
609 input_ids = None
610 hidden_states = self.language_model.model(
611 input_ids=input_ids,
vllm/model_executor/models/terratorch.py
+3
@@ -233,6 +233,9 @@ class Terratorch(nn.Module, IsAttentionFree, SupportsMultiModal):
233 self,
234 input_ids: torch.Tensor,
235 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
236 + *,
237 + is_multimodal: Optional[torch.Tensor] = None,
238 + handle_oov_mm_token: bool = False,
239 ) -> torch.Tensor:
240 # We do not really use any input tokens and therefore no embeddings
241 # to be calculated. However, due to the mandatory token ids in
vllm/model_executor/models/transformers.py
+50 -14
@@ -52,8 +52,8 @@ from vllm.multimodal.profiling import BaseDummyInputsBuilder
52 from vllm.sequence import IntermediateTensors
53 from vllm.utils import is_list_of
54
55 -from .interfaces import (SupportsLoRA, SupportsMultiModal, SupportsPP,
56 - SupportsQuant)
55 +from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
56 + SupportsMultiModal, SupportsPP, SupportsQuant)
57 from .utils import (AutoWeightsLoader, PPMissingLayer, WeightsMapper,
58 flatten_bn, make_empty_intermediate_tensors_factory,
59 maybe_prefix)
@@ -797,6 +797,9 @@ class TransformersForCausalLM(TransformersBase):
797 else:
798 self.lm_head = PPMissingLayer()
799
800 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
801 + return self.model.get_input_embeddings()(input_ids)
802 +
803 def compute_logits(
804 self,
805 hidden_states: torch.Tensor,
@@ -873,13 +876,19 @@ class TransformersForMultimodalLM(TransformersForCausalLM, SupportsMultiModal):
876 multimodal_embeds = self.get_multimodal_embeddings(**kwargs)
877 if multimodal_embeds is not None:
878 inputs_embeds = self.get_input_embeddings(
876 - input_ids, multimodal_embeds)
879 + input_ids,
880 + multimodal_embeds,
881 + is_multimodal=input_ids == self.config.image_token_id,
882 + )
883 input_ids = None
884
885 model_output = super().forward(input_ids, positions,
886 intermediate_tensors, inputs_embeds)
887 return model_output
888
889 + def get_language_model(self) -> torch.nn.Module:
890 + return self.model
891 +
892 def get_multimodal_embeddings(self, **kwargs):
893 pixel_values = kwargs.pop("pixel_values", None)
894 pixel_values = pixel_values if pixel_values is not None else kwargs.pop(
@@ -934,15 +943,42 @@ class TransformersForMultimodalLM(TransformersForCausalLM, SupportsMultiModal):
943 def get_input_embeddings(
944 self,
945 input_ids: torch.Tensor,
937 - multimodal_embeddings=None,
946 + multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
947 + *,
948 + is_multimodal: Optional[torch.Tensor] = None,
949 + handle_oov_mm_token: bool = False,
950 ) -> torch.Tensor:
939 - inputs_embeds = self.model.get_input_embeddings()(input_ids)
940 - if (multimodal_embeddings is not None
941 - and len(multimodal_embeddings) != 0):
942 - mask = (input_ids == self.config.image_token_id)
943 - mask = mask.unsqueeze(-1).expand_as(inputs_embeds)
944 - multimodal_embeddings = torch.cat(multimodal_embeddings)
945 -
946 - inputs_embeds = inputs_embeds.masked_scatter(
947 - mask, multimodal_embeddings)
948 - return inputs_embeds
951 + """
952 + Apply token embeddings to `input_ids`.
953 +
954 + If `multimodal_embeddings` is passed, scatter them into
955 + `input_ids` according to the mask `is_multimodal`.
956 +
957 + In case the multi-modal token IDs exceed the vocabulary size of
958 + the language model, you can set `handle_oov_mm_token=False`
959 + to avoid calling the language model's `get_input_embeddings` method
960 + on those tokens.
961 + """
962 + from .utils import _merge_multimodal_embeddings
963 +
964 + inputs_embeds = self._get_text_embeddings(
965 + input_ids,
966 + self.model.get_input_embeddings(),
967 + is_multimodal=is_multimodal,
968 + handle_oov_mm_token=handle_oov_mm_token,
969 + )
970 +
971 + if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
972 + return inputs_embeds
973 +
974 + if is_multimodal is None:
975 + raise ValueError(
976 + "`get_input_embeddings` now requires `is_multimodal` arg, "
977 + "please update your model runner according to "
978 + "https://github.com/vllm-project/vllm/pull/16229.")
979 +
980 + return _merge_multimodal_embeddings(
981 + inputs_embeds=inputs_embeds,
982 + multimodal_embeddings=multimodal_embeddings,
983 + is_multimodal=is_multimodal,
984 + )
vllm/model_executor/models/ultravox.py
+20 -16
@@ -33,8 +33,7 @@ from vllm.utils.tensor_schema import TensorSchema, TensorShape
33 from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
34 SupportsMultiModal, SupportsPP)
35 from .utils import (AutoWeightsLoader, WeightsMapper, flatten_bn,
36 - init_vllm_registered_model, maybe_prefix,
37 - merge_multimodal_embeddings)
36 + init_vllm_registered_model, maybe_prefix)
37
38 _AUDIO_PLACEHOLDER_OVERRIDE = "<|audio|>"
39 _MAX_ENCODER_BATCH_SIZE = 16
@@ -555,19 +554,21 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
554 self,
555 input_ids: torch.Tensor,
556 multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
557 + *,
558 + is_multimodal: Optional[torch.Tensor] = None,
559 + # Multi-modal token ID may exceed vocab size
560 + handle_oov_mm_token: bool = True,
561 ) -> torch.Tensor:
559 - # The audio token index is not included in the embedding table
560 - # We need to remove it before embedding lookup
561 - safe_input_ids = input_ids.clone()
562 - safe_input_ids[safe_input_ids == self.config.audio_token_index] = 0
563 - inputs_embeds = self.language_model.get_input_embeddings(
564 - safe_input_ids)
565 - if multimodal_embeddings is not None and len(
566 - multimodal_embeddings) > 0:
567 - inputs_embeds = merge_multimodal_embeddings(
568 - input_ids, inputs_embeds, multimodal_embeddings,
569 - self.config.audio_token_index)
570 - return inputs_embeds
562 + # This is to satisfy the type checker for each overload
563 + if multimodal_embeddings is None or is_multimodal is None:
564 + return super().get_input_embeddings(input_ids)
565 +
566 + return super().get_input_embeddings(
567 + input_ids,
568 + multimodal_embeddings=multimodal_embeddings,
569 + is_multimodal=is_multimodal,
570 + handle_oov_mm_token=handle_oov_mm_token,
571 + )
572
573 def forward(self,
574 input_ids: torch.Tensor,
@@ -601,8 +602,11 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
602 elif inputs_embeds is None:
603 multimodal_embeddings = self.get_multimodal_embeddings(**kwargs)
604
604 - inputs_embeds = self.get_input_embeddings(input_ids,
605 - multimodal_embeddings)
605 + inputs_embeds = self.get_input_embeddings(
606 + input_ids,
607 + multimodal_embeddings,
608 + is_multimodal=input_ids == self.config.audio_token_index,
609 + )
610 input_ids = None
611
612 language_model = self.language_model
vllm/model_executor/models/utils.py
+37 -57
@@ -4,7 +4,7 @@
4 import itertools
5 from collections.abc import Iterable, Mapping
6 from dataclasses import dataclass, field
7 -from typing import Any, Callable, Literal, Optional, Protocol, Union, overload
7 +from typing import Any, Literal, Optional, Protocol, Union, overload
8
9 import torch
10 import torch.nn as nn
@@ -391,8 +391,8 @@ def _embedding_count_expression(embeddings: NestedTensors) -> str:
391
392 def _merge_multimodal_embeddings(
393 inputs_embeds: torch.Tensor,
394 - is_multimodal: torch.Tensor,
394 multimodal_embeddings: NestedTensors,
395 + is_multimodal: torch.Tensor,
396 ) -> torch.Tensor:
397 """
398 Merge ``multimodal_embeddings`` into ``inputs_embeds`` by overwriting the
@@ -402,61 +402,35 @@ def _merge_multimodal_embeddings(
402 Note:
403 This updates ``inputs_embeds`` in place.
404 """
405 - flattened = _flatten_embeddings(multimodal_embeddings)
405 + if len(multimodal_embeddings) == 0:
406 + return inputs_embeds
407 +
408 + mm_embeds_flat = _flatten_embeddings(multimodal_embeddings)
409 + input_dtype = inputs_embeds.dtype
410 +
411 try:
407 - # This is equivalent to: inputs_embeds[is_multimodal] = flattened.
412 + # For debugging
413 + # inputs_embeds[is_multimodal] = mm_embeds_flat.to(dtype=input_dtype)
414 +
415 + # NOTE: This can avoid D2H sync (#22105), but fails to
416 + # raise an error if is_multimodal.sum() < len(mm_embeds_flat)
417 inputs_embeds.masked_scatter_(is_multimodal.unsqueeze(-1),
409 - flattened.to(dtype=inputs_embeds.dtype))
418 + mm_embeds_flat.to(dtype=input_dtype))
419 except RuntimeError as e:
420 + num_actual_tokens = len(mm_embeds_flat)
421 num_expected_tokens = is_multimodal.sum().item()
412 - assert isinstance(num_expected_tokens, int)
422
414 - if flattened.shape[0] != num_expected_tokens:
423 + if num_actual_tokens != num_expected_tokens:
424 expr = _embedding_count_expression(multimodal_embeddings)
425 +
426 raise ValueError(
417 - f"Attempted to assign {expr} = {flattened.shape[0]} "
427 + f"Attempted to assign {expr} = {num_actual_tokens} "
428 f"multimodal tokens to {num_expected_tokens} placeholders"
429 ) from e
420 - else:
421 - raise ValueError("Error during masked scatter operation") from e
422 -
423 - return inputs_embeds
424 -
425 -
426 -def embed_multimodal(
427 - input_ids: torch.Tensor,
428 - multimodal_token_id: int,
429 - get_text_embeds: Callable[[torch.Tensor], torch.Tensor],
430 - multimodal_embeds: NestedTensors,
431 -) -> torch.Tensor:
432 - """
433 - Embed token IDs and multimodal inputs and combine their embeddings.
434 -
435 - ``multimodal_token_id`` is used to determine whether a token ID should
436 - be embedded using ``get_text_embeds`` or ``get_multimodal_embeds``.
430
438 - Compared to ``merge_multimodal_embeddings`, this avoids running
439 - ``get_text_embeds`` on ``input_ids[input_ids == multimodal_token_id]``
440 - which causes issues when the placeholder token ID exceeds the
441 - vocabulary size of the language model.
442 - """
443 - is_multimodal = input_ids == multimodal_token_id
444 - is_text = ~is_multimodal
445 -
446 - text_embeds = get_text_embeds(input_ids[is_text])
447 - merged_embeds = torch.empty(
448 - (input_ids.shape[0], text_embeds.shape[1]),
449 - dtype=text_embeds.dtype,
450 - device=text_embeds.device,
451 - )
431 + raise ValueError("Error during masked scatter operation") from e
432
453 - merged_embeds[is_text] = text_embeds
454 -
455 - return _merge_multimodal_embeddings(
456 - merged_embeds,
457 - is_multimodal,
458 - multimodal_embeds,
459 - )
433 + return inputs_embeds
434
435
436 def merge_multimodal_embeddings(
@@ -491,23 +465,29 @@ def merge_multimodal_embeddings(
465 This updates ``inputs_embeds`` in place.
466 """
467 if isinstance(placeholder_token_id, list):
494 - placeholder_token_id = torch.tensor(
495 - placeholder_token_id,
496 - pin_memory=is_pin_memory_available()).to(device=input_ids.device,
497 - non_blocking=True)
498 - return _merge_multimodal_embeddings(
499 - inputs_embeds,
500 - torch.isin(input_ids, placeholder_token_id),
501 - multimodal_embeddings,
502 - )
468 + is_multimodal = isin_list(input_ids, placeholder_token_id)
469 + else:
470 + is_multimodal = (input_ids == placeholder_token_id)
471
472 return _merge_multimodal_embeddings(
473 inputs_embeds,
506 - (input_ids == placeholder_token_id),
507 - multimodal_embeddings,
474 + multimodal_embeddings=multimodal_embeddings,
475 + is_multimodal=is_multimodal,
476 )
477
478
479 +def isin_list(
480 + elements: torch.Tensor,
481 + test_elements_list: list[int],
482 +) -> torch.Tensor:
483 + test_elements = torch.tensor(
484 + test_elements_list,
485 + pin_memory=is_pin_memory_available(),
486 + ).to(device=elements.device, non_blocking=True)
487 +
488 + return torch.isin(elements, test_elements)
489 +
490 +
491 class LayerFn(Protocol):
492
493 def __call__(self, prefix: str) -> torch.nn.Module:
vllm/model_executor/models/voxtral.py
+9 -20
@@ -45,10 +45,8 @@ from vllm.sequence import IntermediateTensors
45 from vllm.transformers_utils.tokenizer import (MistralTokenizer,
46 cached_tokenizer_from_config)
47
48 -from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
49 - SupportsMultiModal, SupportsTranscription)
50 -from .utils import (flatten_bn, init_vllm_registered_model, maybe_prefix,
51 - merge_multimodal_embeddings)
48 +from .interfaces import SupportsLoRA, SupportsMultiModal, SupportsTranscription
49 +from .utils import flatten_bn, init_vllm_registered_model, maybe_prefix
50
51 logger = init_logger(__name__)
52
@@ -376,9 +374,14 @@ class VoxtralForConditionalGeneration(nn.Module, SupportsMultiModal,
374 # NOTE: In v1, inputs_embeds is always generated at model runner, this
375 # condition is for v0 compatibility.
376 elif inputs_embeds is None:
377 + audio_encoder = self.tokenizer.instruct.audio_encoder
378 + audio_tok_id = audio_encoder.audio_token
379 audio_embeddings = self.get_multimodal_embeddings(**kwargs)
380 - inputs_embeds = self.get_input_embeddings(input_ids,
381 - audio_embeddings)
380 + inputs_embeds = self.get_input_embeddings(
381 + input_ids,
382 + audio_embeddings,
383 + is_multimodal=input_ids == audio_tok_id,
384 + )
385 input_ids = None
386
387 hidden_states = self.language_model.model(input_ids,
@@ -421,20 +424,6 @@ class VoxtralForConditionalGeneration(nn.Module, SupportsMultiModal,
424
425 return audio_embeddings
426
424 - def get_input_embeddings(
425 - self,
426 - input_ids: torch.Tensor,
427 - multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
428 - ) -> torch.Tensor:
429 - audio_encoder = self.tokenizer.instruct.audio_encoder
430 - audio_tok_id = audio_encoder.audio_token
431 -
432 - inputs_embeds = self.language_model.get_input_embeddings(input_ids)
433 - if multimodal_embeddings is not None:
434 - inputs_embeds = merge_multimodal_embeddings(
435 - input_ids, inputs_embeds, multimodal_embeddings, audio_tok_id)
436 - return inputs_embeds
437 -
427 def _parse_and_validate_audio_arrays(
428 self, **kwargs: object) -> Union[list[torch.Tensor], None]:
429 audio_arrays = kwargs.pop("audio_arrays", None)
vllm/model_executor/models/whisper.py
+5 -5
@@ -579,10 +579,7 @@ class WhisperDecoder(nn.Module):
579 hidden_states = self.layer_norm(hidden_states)
580 return hidden_states
581
582 - def get_input_embeddings(
583 - self,
584 - input_ids: torch.Tensor,
585 - ) -> torch.Tensor:
582 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
583 return self.embed_tokens(input_ids)
584
585
@@ -916,7 +913,10 @@ class WhisperForConditionalGeneration(nn.Module, SupportsTranscription,
913 def get_input_embeddings(
914 self,
915 input_ids: torch.Tensor,
919 - multimodal_embeddings: Optional[NestedTensors] = None,
916 + multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
917 + *,
918 + is_multimodal: Optional[torch.Tensor] = None,
919 + handle_oov_mm_token: bool = False,
920 ) -> torch.Tensor:
921 # This method just returns the decoder sequence embeddings since
922 # Whisper does not have encoder text tokens.
vllm/v1/spec_decode/eagle.py
+27 -19
@@ -18,6 +18,7 @@ from vllm.logger import init_logger
18 from vllm.model_executor.model_loader import get_model
19 from vllm.model_executor.models import supports_multimodal
20 from vllm.model_executor.models.llama_eagle3 import Eagle3LlamaForCausalLM
21 +from vllm.multimodal import MULTIMODAL_REGISTRY
22 from vllm.platforms import current_platform
23 from vllm.utils import is_pin_memory_available
24 from vllm.v1.attention.backends.flash_attn import FlashAttentionMetadata
@@ -64,8 +65,10 @@ class EagleProposer:
65 # hidden size (e.g., Llama 3.3 70B).
66 self.hidden_size = self.draft_model_config.get_hidden_size()
67
67 - self.is_multimodal_model = vllm_config.model_config \
68 - .is_multimodal_model
68 + # Multi-modal data support
69 + self.mm_registry = MULTIMODAL_REGISTRY
70 + self.supports_mm_inputs = self.mm_registry.supports_multimodal_inputs(
71 + vllm_config.model_config)
72
73 self.attn_metadata_builder: Optional[AttentionMetadataBuilder] = None
74
@@ -175,7 +178,8 @@ class EagleProposer:
178 last_token_indices: Optional[torch.Tensor],
179 common_attn_metadata: CommonAttentionMetadata,
180 sampling_metadata: SamplingMetadata,
178 - mm_embeds: Optional[list[torch.Tensor]] = None,
181 + mm_embed_inputs: Optional[tuple[list[torch.Tensor],
182 + torch.Tensor]] = None,
183 ) -> torch.Tensor:
184 num_tokens = target_token_ids.shape[0]
185 batch_size = next_token_ids.shape[0]
@@ -219,18 +223,21 @@ class EagleProposer:
223 # copy inputs to buffer for cudagraph
224 self._set_positions(num_tokens, target_positions)
225 self.hidden_states[:num_tokens] = target_hidden_states
222 - if self.is_multimodal_model:
223 - input_ids = self.input_ids[:num_tokens]
224 - inputs_embeds = self.model.get_input_embeddings(
225 - input_ids,
226 - multimodal_embeddings=mm_embeds or None,
226 +
227 + if self.supports_mm_inputs:
228 + mm_embeds, is_mm_embed = mm_embed_inputs or (None, None)
229 +
230 + self.inputs_embeds[:num_tokens] = self.model.get_input_embeddings(
231 + self.input_ids[:num_tokens],
232 + multimodal_embeddings=mm_embeds,
233 + is_multimodal=is_mm_embed,
234 )
228 - self.inputs_embeds[:num_tokens] = inputs_embeds
229 - inputs_embeds = self.inputs_embeds[:num_input_tokens]
235 +
236 input_ids = None
237 + inputs_embeds = self.inputs_embeds[:num_input_tokens]
238 else:
232 - inputs_embeds = None
239 input_ids = self.input_ids[:num_input_tokens]
240 + inputs_embeds = None
241
242 with set_forward_context(per_layer_attn_metadata,
243 self.vllm_config,
@@ -372,14 +379,15 @@ class EagleProposer:
379 self.input_ids[:batch_size] = input_ids
380 self._set_positions(batch_size, clamped_positions)
381 self.hidden_states[:batch_size] = hidden_states
375 - if self.is_multimodal_model:
376 - inputs_embeds = self.model.get_input_embeddings(input_ids)
377 - self.inputs_embeds[:batch_size] = inputs_embeds
378 - inputs_embeds = self.inputs_embeds[:input_batch_size]
382 + if self.supports_mm_inputs:
383 + self.inputs_embeds[:batch_size] = \
384 + self.model.get_input_embeddings(input_ids)
385 +
386 input_ids = None
387 + inputs_embeds = self.inputs_embeds[:input_batch_size]
388 else:
381 - inputs_embeds = None
389 input_ids = self.input_ids[:input_batch_size]
390 + inputs_embeds = None
391
392 # Run the model.
393 with set_forward_context(per_layer_attn_metadata,
@@ -849,7 +857,7 @@ class EagleProposer:
857
858 self.attn_layer_names = list(draft_attn_layer_names)
859
852 - if self.is_multimodal_model:
860 + if self.supports_mm_inputs:
861 # Even if the target model is multimodal, we can also use
862 # text-only draft models
863 try:
@@ -861,7 +869,7 @@ class EagleProposer:
869 logger.warning(
870 "Draft model does not support multimodal inputs, "
871 "falling back to text-only mode")
864 - self.is_multimodal_model = False
872 + self.supports_mm_inputs = False
873
874 if supports_multimodal(target_model):
875 # handle multimodality
@@ -933,7 +941,7 @@ class EagleProposer:
941 ) -> None:
942 with set_forward_context(None, self.vllm_config,
943 num_tokens=num_tokens):
936 - if self.is_multimodal_model:
944 + if self.supports_mm_inputs:
945 input_ids = None
946 inputs_embeds = self.inputs_embeds[:num_tokens]
947 else:
vllm/v1/worker/gpu_model_runner.py
+39 -13
@@ -368,6 +368,11 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
368 self.num_accepted_tokens = self._make_buffer(self.max_num_reqs,
369 dtype=torch.int64)
370
371 + # Only relevant for multimodal models
372 + if self.supports_mm_inputs:
373 + self.is_mm_embed = self._make_buffer(self.max_num_tokens,
374 + dtype=torch.bool)
375 +
376 # Only relevant for models using M-RoPE (e.g, Qwen2-VL)
377 if self.uses_mrope:
378 # NOTE: `mrope_positions` is implemented with one additional dummy
@@ -1627,9 +1632,16 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1632 self,
1633 scheduler_output: "SchedulerOutput",
1634 shift_computed_tokens: int = 0,
1630 - ) -> list[torch.Tensor]:
1635 + ) -> tuple[list[torch.Tensor], torch.Tensor]:
1636 + total_num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens
1637 +
1638 + mm_embeds = list[torch.Tensor]()
1639 + is_mm_embed = self.is_mm_embed.cpu
1640 + is_mm_embed[:total_num_scheduled_tokens] = False
1641 +
1642 + req_start_idx = 0
1643 should_sync_mrope_positions = False
1632 - mm_embeds: list[torch.Tensor] = []
1644 +
1645 for req_id in self.input_batch.req_ids:
1646 mm_embeds_req: list[torch.Tensor] = []
1647
@@ -1638,6 +1650,7 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1650 req_state = self.requests[req_id]
1651 num_computed_tokens = \
1652 req_state.num_computed_tokens + shift_computed_tokens
1653 +
1654 for mm_feature in req_state.mm_features:
1655 pos_info = mm_feature.mm_position
1656 start_pos = pos_info.offset
@@ -1670,6 +1683,10 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1683 if (is_embed := pos_info.is_embed) is not None:
1684 is_embed = is_embed[start_idx:end_idx]
1685
1686 + req_start_pos = req_start_idx + start_pos - num_computed_tokens
1687 + is_mm_embed[req_start_pos+start_idx:req_start_pos + end_idx] \
1688 + = True if is_embed is None else is_embed
1689 +
1690 mm_embeds_item = gather_mm_placeholders(
1691 encoder_output[start_idx:end_idx],
1692 is_embed=is_embed,
@@ -1677,6 +1694,7 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1694 mm_embeds_req.append(mm_embeds_item)
1695
1696 if self.is_multimodal_pruning_enabled and self.uses_mrope:
1697 + assert req_state.mrope_positions is not None
1698 should_sync_mrope_positions = True
1699 mm_embeds_req, new_mrope_positions, new_delta = (
1700 self.model.recompute_mrope_positions(
@@ -1685,18 +1703,19 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1703 mrope_positions=req_state.mrope_positions,
1704 num_computed_tokens=req_state.num_computed_tokens,
1705 ))
1688 - assert req_state.mrope_positions is not None
1706 req_state.mrope_positions.copy_(new_mrope_positions)
1707 req_state.mrope_position_delta = new_delta
1708
1709 mm_embeds.extend(mm_embeds_req)
1710 + req_start_idx += num_scheduled_tokens
1711 +
1712 + is_mm_embed = self.is_mm_embed.copy_to_gpu(total_num_scheduled_tokens)
1713
1714 if should_sync_mrope_positions:
1715 self._calc_mrope_positions(scheduler_output)
1696 - self.mrope_positions.copy_to_gpu(
1697 - scheduler_output.total_num_scheduled_tokens)
1716 + self.mrope_positions.copy_to_gpu(total_num_scheduled_tokens)
1717
1699 - return mm_embeds
1718 + return mm_embeds, is_mm_embed
1719
1720 def _extract_encoder_inputs(
1721 self,
@@ -1990,14 +2009,16 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2009 and not self.model_config.is_encoder_decoder):
2010 # Run the multimodal encoder if any.
2011 self._execute_mm_encoder(scheduler_output)
1993 - mm_embeds = self._gather_mm_embeddings(scheduler_output)
2012 + mm_embeds, is_mm_embed = self._gather_mm_embeddings(
2013 + scheduler_output)
2014
2015 # NOTE(woosuk): To unify token ids and soft tokens (vision
2016 # embeddings), we always use embeddings (rather than token ids)
2017 # as input to the multimodal model, even when the input is text.
2018 inputs_embeds_scheduled = self.model.get_input_embeddings(
1999 - input_ids=self.input_ids.gpu[:num_scheduled_tokens],
2000 - multimodal_embeddings=mm_embeds or None,
2019 + self.input_ids.gpu[:num_scheduled_tokens],
2020 + multimodal_embeddings=mm_embeds,
2021 + is_multimodal=is_mm_embed,
2022 )
2023
2024 # TODO(woosuk): Avoid the copy. Optimize.
@@ -2586,10 +2607,14 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2607 [h[token_indices] for h in aux_hidden_states], dim=-1)
2608 else:
2609 target_hidden_states = hidden_states[token_indices]
2589 - mm_embeds = None
2610 +
2611 if self.supports_mm_inputs:
2591 - mm_embeds = self._gather_mm_embeddings(scheduler_output,
2592 - shift_computed_tokens=1)
2612 + mm_embed_inputs = self._gather_mm_embeddings(
2613 + scheduler_output,
2614 + shift_computed_tokens=1,
2615 + )
2616 + else:
2617 + mm_embed_inputs = None
2618
2619 draft_token_ids = self.drafter.propose(
2620 target_token_ids=target_token_ids,
@@ -2599,8 +2624,9 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2624 last_token_indices=token_indices_to_sample,
2625 sampling_metadata=sampling_metadata,
2626 common_attn_metadata=common_attn_metadata,
2602 - mm_embeds=mm_embeds,
2627 + mm_embed_inputs=mm_embed_inputs,
2628 )
2629 +
2630 return draft_token_ids
2631
2632 def update_config(self, overrides: dict[str, Any]) -> None:
vllm/v1/worker/tpu_model_runner.py
+66 -13
@@ -263,6 +263,13 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
263 pin_memory=self.pin_memory)
264 self.seq_lens_np = self.seq_lens_cpu.numpy()
265
266 + # Only relevant for multimodal models
267 + if self.supports_mm_inputs:
268 + self.is_mm_embed_cpu = torch.zeros(self.max_num_tokens,
269 + dtype=torch.bool,
270 + device="cpu",
271 + pin_memory=self.pin_memory)
272 +
273 # Range tensor with values [0 .. self.max_num_tokens - 1].
274 # Used to initialize positions / context_lens / seq_lens
275 # Keep in int64 to avoid overflow with long context
@@ -879,13 +886,22 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
886 def _gather_mm_embeddings(
887 self,
888 scheduler_output: "SchedulerOutput",
882 - ) -> list[torch.Tensor]:
883 - mm_embeds: list[torch.Tensor] = []
889 + ) -> tuple[list[torch.Tensor], torch.Tensor]:
890 + total_num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens
891 + padded_total_num_scheduled_tokens = _get_padded_token_len(
892 + self.num_tokens_paddings, total_num_scheduled_tokens)
893 +
894 + is_mm_embed = self.is_mm_embed_cpu
895 + is_mm_embed[:padded_total_num_scheduled_tokens] = False
896 + mm_embeds = list[torch.Tensor]()
897 + req_start_idx = 0
898 +
899 for req_id in self.input_batch.req_ids:
900 num_scheduled_tokens = scheduler_output.num_scheduled_tokens[
901 req_id]
902 req_state = self.requests[req_id]
903 num_computed_tokens = req_state.num_computed_tokens
904 +
905 # TODO unroll loop and assume/enforce --disable_chunked_mm_input
906 # NOTE (NickLucche) here we diverge from logic in other runners, as
907 # we assume to only have whole mm items to process. Hence we avoid
@@ -906,26 +922,53 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
922 # The encoder output is already processed and stored
923 # in the decoder's KV cache.
924 continue
925 +
926 + start_idx = max(num_computed_tokens - start_pos, 0)
927 + end_idx = min(
928 + num_computed_tokens - start_pos + num_scheduled_tokens,
929 + num_encoder_tokens,
930 + )
931 + assert start_idx < end_idx
932 +
933 mm_hash = mm_feature.identifier
934 encoder_output = self.encoder_cache.get(mm_hash, None)
935 assert encoder_output is not None,\
936 f"Encoder cache miss for {mm_hash}."
937 +
938 assert pos_info.is_embed is None, "Expected all positions to"\
939 " be contiguous and embeddings."
915 - encoder_output = self.encoder_cache[mm_hash]
940 +
941 + req_start_pos = req_start_idx + start_pos - num_computed_tokens
942 + is_mm_embed[req_start_pos+start_idx:req_start_pos + end_idx] \
943 + = True
944 +
945 + # Only whole mm items are processed
946 mm_embeds.append(encoder_output)
917 - return mm_embeds
947
919 - def _get_model_inputs(self, input_ids: torch.Tensor,
920 - mm_embeds: list[torch.Tensor]):
948 + req_start_idx += num_scheduled_tokens
949 +
950 + is_mm_embed = is_mm_embed[:padded_total_num_scheduled_tokens] \
951 + .to(self.device)
952 +
953 + return mm_embeds, is_mm_embed
954 +
955 + def _get_model_inputs(
956 + self,
957 + input_ids: torch.Tensor,
958 + mm_embed_inputs: Optional[tuple[list[torch.Tensor], torch.Tensor]],
959 + ):
960 if self.supports_mm_inputs:
961 + mm_embeds, is_mm_embed = mm_embed_inputs or (None, None)
962 +
963 # NOTE(woosuk): To unify token ids and soft tokens (vision
964 # embeddings), we always use embeddings (rather than token ids)
965 # as input to the multimodal model, even when the input is text.
966 inputs_embeds = self.model.get_input_embeddings(
926 - input_ids=input_ids,
967 + input_ids,
968 multimodal_embeddings=mm_embeds,
969 + is_multimodal=is_mm_embed,
970 )
971 +
972 return None, inputs_embeds
973 else:
974 # For text-only models, we use token ids as input.
@@ -953,9 +996,10 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
996 if self.supports_mm_inputs:
997 # Run the multimodal encoder if any.
998 self._execute_mm_encoder(scheduler_output)
956 - mm_embeds = self._gather_mm_embeddings(scheduler_output)
999 + mm_embed_inputs = self._gather_mm_embeddings(scheduler_output)
1000 else:
958 - mm_embeds = []
1001 + mm_embed_inputs = None
1002 +
1003 torch_xla.sync(wait=False)
1004 # Prepare inputs, the requests might be split into multiple
1005 # executions, combine the result of each execution.
@@ -972,7 +1016,7 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1016 attn_metadata, logits_indices, padded_num_reqs, num_reqs,\
1017 end_index = self._prepare_inputs(scheduler_output, start_index)
1018 input_ids, inputs_embeds = self._get_model_inputs(
975 - self.input_ids, mm_embeds)
1019 + self.input_ids, mm_embed_inputs)
1020 torch_xla.sync(wait=False)
1021 # Run the decoder
1022 with set_forward_context(
@@ -1325,9 +1369,15 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1369 hf_config.image_token_index
1370
1371 placeholders_ids = placeholders_ids.to(self.device)
1372 +
1373 + mm_mask = torch.tensor([False] * num_tokens)
1374 + mm_mask[:items_size] = True
1375 + mm_mask = mm_mask.to(self.device)
1376 # Assign outputs or the graph will be cut short.
1329 - a, b = self._get_model_inputs(placeholders_ids,
1330 - [mm_embeds])
1377 + a, b = self._get_model_inputs(
1378 + placeholders_ids,
1379 + mm_embed_inputs=([mm_embeds], mm_mask),
1380 + )
1381 assert a is None
1382 torch_xla.sync(wait=False)
1383
@@ -1338,7 +1388,10 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1388 dtype=torch.int32,
1389 device="cpu")
1390 placeholders_ids = placeholders_ids.to(self.device)
1341 - a, b = self._get_model_inputs(placeholders_ids, [])
1391 + a, b = self._get_model_inputs(
1392 + placeholders_ids,
1393 + mm_embed_inputs=None,
1394 + )
1395 assert a is None
1396 torch_xla.sync(wait=False)
1397