@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / b5dcb372e4
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 8, 12:30 UTC

[Misc] Clean up validation logic in input processor (#34144)

Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>

Cyrus Leung committed Feb 11, 2026 at 11:29 UTC b5dcb372e4ba04043a012475cea7cc901412f25a
3 files changed +72 -86
tests/v1/engine/test_process_multi_modal_uuids.py
-1
@@ -20,7 +20,6 @@ def _build_input_processor(
20 ) -> InputProcessor:
21 model_config = ModelConfig(
22 model="Qwen/Qwen2.5-VL-3B-Instruct",
23 - skip_tokenizer_init=True,
23 max_model_len=128,
24 mm_processor_cache_gb=mm_cache_gb,
25 )
vllm/multimodal/encoder_budget.py
+1
@@ -62,6 +62,7 @@ class MultiModalBudget:
62 processor = mm_registry.create_processor(model_config, cache=cache)
63
64 self.cache = cache
65 + self.processor = processor
66 mm_config = model_config.get_multimodal_config()
67 enable_mm_embeds = mm_config is not None and mm_config.enable_mm_embeds
68
vllm/v1/engine/input_processor.py
+71 -85
@@ -72,13 +72,15 @@ class InputProcessor:
72 self.mm_registry = mm_registry
73 self.mm_processor_cache = mm_registry.processor_cache_from_config(vllm_config)
74
75 - self.mm_encoder_cache_size: int | None = None
76 - if (
77 - mm_registry.supports_multimodal_inputs(model_config)
78 - and not model_config.skip_tokenizer_init
79 - ):
75 + self.supports_mm_inputs = mm_registry.supports_multimodal_inputs(model_config)
76 + self.mm_encoder_cache_size = 0
77 + self.skip_prompt_length_check = False
78 + if self.supports_mm_inputs:
79 mm_budget = MultiModalBudget(vllm_config, mm_registry)
80 self.mm_encoder_cache_size = mm_budget.encoder_cache_size
81 + self.skip_prompt_length_check = (
82 + mm_budget.processor.info.skip_prompt_length_check
83 + )
84 mm_budget.reset_cache() # Not used anymore
85
86 self.input_preprocessor = InputPreprocessor(
@@ -670,76 +672,25 @@ class InputProcessor:
672 resumable=resumable,
673 )
674
673 - def _validate_model_inputs(
674 - self, encoder_inputs: SingletonInputs | None, decoder_inputs: SingletonInputs
675 - ):
676 - if encoder_inputs is not None:
677 - self._validate_model_input(encoder_inputs, prompt_type="encoder")
678 -
679 - self._validate_model_input(decoder_inputs, prompt_type="decoder")
680 -
681 - def _validate_model_input(
675 + def _validate_prompt_len(
676 self,
683 - prompt_inputs: SingletonInputs,
684 - *,
677 + prompt_len: int,
678 prompt_type: Literal["encoder", "decoder"],
679 ):
687 - model_config = self.model_config
688 -
689 - prompt_ids = (
690 - None
691 - if prompt_inputs["type"] == "embeds"
692 - else prompt_inputs["prompt_token_ids"]
693 - )
694 - prompt_embeds = (
695 - prompt_inputs["prompt_embeds"]
696 - if prompt_inputs["type"] == "embeds"
697 - else None
698 - )
699 - prompt_len = length_from_prompt_token_ids_or_embeds(prompt_ids, prompt_embeds)
700 - if not prompt_ids:
701 - if prompt_type == "encoder" and model_config.is_multimodal_model:
702 - pass # Mllama may have empty encoder inputs for text-only data
703 - elif prompt_inputs["type"] == "embeds":
704 - pass # Prompt embeds should not have prompt_ids.
705 - else:
706 - raise ValueError(f"The {prompt_type} prompt cannot be empty")
707 -
708 - tokenizer = self.tokenizer
709 - if tokenizer is not None:
710 - max_input_id = max(prompt_ids or (), default=0)
711 -
712 - # NOTE: tokenizer.max_token_id is the tokenizer’s vocab size while
713 - # self.model_config.get_vocab_size() is the model’s vocab size.
714 - # For Qwen3 models, the language model has extra tokens that do
715 - # not exist in the tokenizer, and vice versa for multimodal
716 - # placeholder tokens in some multimodal models.
717 - # See https://github.com/QwenLM/Qwen3/issues/29#issuecomment-1933720399 # noqa: E501
718 - # and https://github.com/vllm-project/vllm/pull/22471#discussion_r2312251421 # noqa: E501
680 + if self.skip_prompt_length_check:
681 + return
682
720 - # Here we take the max of the two to determine if a token id is
721 - # truly out-of-vocabulary.
722 - if max_input_id > max(
723 - tokenizer.max_token_id, self.model_config.get_vocab_size() - 1
724 - ):
725 - raise ValueError(f"Token id {max_input_id} is out of vocabulary")
683 + if prompt_len == 0 and prompt_type == "decoder":
684 + raise ValueError(f"The {prompt_type} prompt cannot be empty")
685
727 - max_prompt_len = self.model_config.max_model_len
686 + model_config = self.model_config
687 + max_prompt_len = (
688 + model_config.max_model_len
689 + if prompt_type == "decoder"
690 + else self.mm_encoder_cache_size
691 + )
692 if prompt_len > max_prompt_len:
729 - if model_config.is_multimodal_model:
730 - mm_registry = self.input_preprocessor.mm_registry
731 - model_cls = mm_registry._get_model_cls(model_config)
732 - factories = model_cls._processor_factory
733 - ctx = mm_registry._create_processing_ctx(
734 - model_config,
735 - tokenizer=tokenizer,
736 - )
737 - mm_info = factories.info(ctx)
738 -
739 - if mm_info.skip_prompt_length_check:
740 - return
741 -
742 - if model_config.is_multimodal_model:
693 + if self.supports_mm_inputs:
694 suggestion = (
695 "Make sure that `max_model_len` is no smaller than the "
696 "number of text tokens plus multimodal tokens. For image "
@@ -757,17 +708,7 @@ class InputProcessor:
708 f"longer than the maximum model length of {max_prompt_len}. "
709 f"{suggestion}"
710 )
760 -
761 - # TODO: Find out how many placeholder tokens are there so we can
762 - # check that chunked prefill does not truncate them
763 - # max_batch_len = self.scheduler_config.max_num_batched_tokens
764 -
765 - if (
766 - prompt_len == max_prompt_len
767 - and prompt_type == "decoder"
768 - and not model_config.is_multimodal_model
769 - and self.model_config.runner_type != "pooling"
770 - ):
711 + elif prompt_len == max_prompt_len and model_config.runner_type == "generate":
712 suggestion = (
713 "Make sure that `max_model_len` is no smaller than the "
714 "number of text tokens (prompt + requested output tokens)."
@@ -778,11 +719,29 @@ class InputProcessor:
719 f"model length of {max_prompt_len}. {suggestion}"
720 )
721
781 - if (
782 - prompt_type == "decoder"
783 - and prompt_inputs["type"] == "multimodal"
784 - and self.mm_encoder_cache_size is not None
785 - ):
722 + def _validate_model_input(
723 + self,
724 + prompt_inputs: SingletonInputs,
725 + prompt_type: Literal["encoder", "decoder"],
726 + ) -> None:
727 + model_config = self.model_config
728 + tokenizer = self.tokenizer
729 +
730 + prompt_ids = (
731 + None
732 + if prompt_inputs["type"] == "embeds"
733 + else prompt_inputs["prompt_token_ids"]
734 + )
735 + prompt_embeds = (
736 + prompt_inputs["prompt_embeds"]
737 + if prompt_inputs["type"] == "embeds"
738 + else None
739 + )
740 +
741 + prompt_len = length_from_prompt_token_ids_or_embeds(prompt_ids, prompt_embeds)
742 + self._validate_prompt_len(prompt_len, prompt_type)
743 +
744 + if prompt_inputs["type"] == "multimodal":
745 decoder_mm_positions = prompt_inputs["mm_placeholders"]
746 for modality, mm_positions in decoder_mm_positions.items():
747 for mm_position in mm_positions:
@@ -797,6 +756,33 @@ class InputProcessor:
756 f"by setting --limit-mm-per-prompt at startup."
757 )
758
759 + if prompt_ids and tokenizer is not None:
760 + max_input_id = max(prompt_ids, default=0)
761 +
762 + # NOTE: tokenizer.max_token_id is the tokenizer’s vocab size while
763 + # self.model_config.get_vocab_size() is the model’s vocab size.
764 + # For Qwen3 models, the language model has extra tokens that do
765 + # not exist in the tokenizer, and vice versa for multimodal
766 + # placeholder tokens in some multimodal models.
767 + # See https://github.com/QwenLM/Qwen3/issues/29#issuecomment-1933720399 # noqa: E501
768 + # and https://github.com/vllm-project/vllm/pull/22471#discussion_r2312251421 # noqa: E501
769 +
770 + # Here we take the max of the two to determine if a token id is
771 + # truly out-of-vocabulary.
772 + model_vocab_size = model_config.get_vocab_size()
773 + if max_input_id > max(tokenizer.max_token_id, model_vocab_size - 1):
774 + raise ValueError(f"Token id {max_input_id} is out of vocabulary")
775 +
776 + def _validate_model_inputs(
777 + self,
778 + encoder_inputs: SingletonInputs | None,
779 + decoder_inputs: SingletonInputs,
780 + ):
781 + if encoder_inputs is not None:
782 + self._validate_model_input(encoder_inputs, prompt_type="encoder")
783 +
784 + self._validate_model_input(decoder_inputs, prompt_type="decoder")
785 +
786 def stat_mm_cache(self) -> MultiModalCacheStats | None:
787 return self.input_preprocessor.stat_mm_cache()
788