@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 79aa244678
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

[Multi Modal] Configurable MM Profiling (#25631)

Signed-off-by: wwl2755 <wangwenlong2755@gmail.com> Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>

Wenlong Wang committed Oct 3, 2025 at 03:59 UTC 79aa244678df497b52a047df8c1ca8ad12bbe2f7
60 files changed +654 -99
docs/contributing/model/multimodal.md
+10 -2
@@ -258,17 +258,21 @@ Assuming that the memory usage increases with the number of tokens, the dummy in
258 self,
259 seq_len: int,
260 mm_counts: Mapping[str, int],
261 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
262 ) -> MultiModalDataDict:
263 num_images = mm_counts.get("image", 0)
264
265 target_width, target_height = \
266 self.info.get_image_size_with_most_features()
267
268 + image_overrides = mm_options.get("image") if mm_options else None
269 +
270 return {
271 "image":
272 self._get_dummy_images(width=target_width,
273 height=target_height,
271 - num_images=num_images)
274 + num_images=num_images,
275 + overrides=image_overrides)
276 }
277 ```
278
@@ -438,16 +442,20 @@ Assuming that the memory usage increases with the number of tokens, the dummy in
442 self,
443 seq_len: int,
444 mm_counts: Mapping[str, int],
445 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
446 ) -> MultiModalDataDict:
447 target_width, target_height = \
448 self.info.get_image_size_with_most_features()
449 num_images = mm_counts.get("image", 0)
450
451 + image_overrides = mm_options.get("image") if mm_options else None
452 +
453 return {
454 "image":
455 self._get_dummy_images(width=target_width,
456 height=target_height,
450 - num_images=num_images)
457 + num_images=num_images,
458 + overrides=image_overrides)
459 }
460 ```
461
tests/models/multimodal/processing/test_common.py
+19 -3
@@ -12,6 +12,8 @@ from mistral_common.protocol.instruct.request import ChatCompletionRequest
12 from PIL import Image
13
14 from vllm.config import ModelConfig
15 +from vllm.config.multimodal import (AudioDummyOptions, BaseDummyOptions,
16 + ImageDummyOptions, VideoDummyOptions)
17 from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalDataDict
18 from vllm.multimodal.cache import MultiModalProcessorOnlyCache
19 from vllm.multimodal.inputs import MultiModalInputs
@@ -112,12 +114,26 @@ def _test_processing_correctness(
114
115 processing_info = factories.info(ctx)
116 supported_mm_limits = processing_info.get_supported_mm_limits()
115 - limit_mm_per_prompt = {
117 + # Keep integer limits for local data generation
118 + limit_mm_per_prompt_ints = {
119 modality: 3 if limit is None else limit
120 for modality, limit in supported_mm_limits.items()
121 }
122
120 - model_config.get_multimodal_config().limit_per_prompt = limit_mm_per_prompt
123 + def _to_dummy_options(modality: str, count: int) -> BaseDummyOptions:
124 + if modality == "video":
125 + return VideoDummyOptions(count=count)
126 + if modality == "image":
127 + return ImageDummyOptions(count=count)
128 + if modality == "audio":
129 + return AudioDummyOptions(count=count)
130 + return BaseDummyOptions(count=count)
131 +
132 + # Assign normalized DummyOptions to the model config
133 + model_config.get_multimodal_config().limit_per_prompt = {
134 + modality: _to_dummy_options(modality, count)
135 + for modality, count in limit_mm_per_prompt_ints.items()
136 + }
137
138 baseline_processor = factories.build_processor(ctx, cache=None)
139 cached_processor = factories.build_processor(ctx, cache=cache)
@@ -150,7 +166,7 @@ def _test_processing_correctness(
166 k:
167 [(input_to_hit[k] if rng.rand() < hit_rate else input_factory[k]())
168 for _ in range(rng.randint(limit + 1))]
153 - for k, limit in limit_mm_per_prompt.items()
169 + for k, limit in limit_mm_per_prompt_ints.items()
170 }
171
172 mm_counts = {k: len(vs) for k, vs in mm_data.items()}
tests/models/multimodal/processing/test_mllama4.py
+5 -5
@@ -17,23 +17,23 @@ def test_profiling(model_id: str, max_model_len: int):
17 model_config_kwargs = {
18 "max_model_len": max_model_len,
19 }
20 + mm_counts = {"image": 1}
21 ctx = build_model_context(
22 model_id,
23 model_config_kwargs=model_config_kwargs,
23 - limit_mm_per_prompt={"image": 1},
24 + limit_mm_per_prompt=mm_counts,
25 )
26
26 - mm_config = ctx.get_mm_config()
27 processor = MULTIMODAL_REGISTRY.create_processor(ctx.model_config)
28 profiler = MultiModalProfiler(processor)
29
30 decoder_dummy_data = profiler.get_decoder_dummy_data(
31 max_model_len,
32 - mm_counts=mm_config.limit_per_prompt,
32 + mm_counts=mm_counts,
33 )
34 dummy_mm_data = processor.dummy_inputs.get_dummy_processor_inputs(
35 max_model_len,
36 - mm_counts=mm_config.limit_per_prompt,
36 + mm_counts=mm_counts,
37 )
38
39 hf_config = ctx.get_hf_config(Llama4Config)
@@ -58,7 +58,7 @@ def test_profiling(model_id: str, max_model_len: int):
58
59 profiled_tokens = profiler.get_mm_max_contiguous_tokens(
60 max_model_len,
61 - mm_counts=mm_config.limit_per_prompt,
61 + mm_counts=mm_counts,
62 )
63
64 assert total_tokens == profiled_tokens["image"]
tests/models/multimodal/processing/test_tensor_schema.py
+16 -1
@@ -15,6 +15,8 @@ from mistral_common.protocol.instruct.request import ChatCompletionRequest
15 from PIL import Image
16
17 from vllm.config import ModelConfig, VllmConfig, set_current_vllm_config
18 +from vllm.config.multimodal import (AudioDummyOptions, BaseDummyOptions,
19 + ImageDummyOptions, VideoDummyOptions)
20 from vllm.distributed import (cleanup_dist_env_and_memory,
21 init_distributed_environment,
22 initialize_model_parallel)
@@ -236,7 +238,20 @@ def test_model_tensor_schema(model_arch: str, model_id: str):
238 modality: 3 if limit is None else limit
239 for modality, limit in supported_mm_limits.items()
240 }
239 - model_config.get_multimodal_config().limit_per_prompt = limit_mm_per_prompt
241 +
242 + def _to_dummy_options(modality: str, count: int) -> BaseDummyOptions:
243 + if modality == "video":
244 + return VideoDummyOptions(count=count)
245 + if modality == "image":
246 + return ImageDummyOptions(count=count)
247 + if modality == "audio":
248 + return AudioDummyOptions(count=count)
249 + return BaseDummyOptions(count=count)
250 +
251 + model_config.get_multimodal_config().limit_per_prompt = {
252 + modality: _to_dummy_options(modality, count)
253 + for modality, count in limit_mm_per_prompt.items()
254 + }
255 processor = factories.build_processor(ctx, cache=None)
256
257 with initialize_dummy_model(model_cls, model_config) as model:
vllm/config/model.py
+3 -1
@@ -276,7 +276,9 @@ class ModelConfig:
276 multimodal_config: Optional[MultiModalConfig] = None
277 """Configuration for multimodal model. If `None`, this will be inferred
278 from the architecture of `self.model`."""
279 - limit_mm_per_prompt: InitVar[Optional[dict[str, int]]] = None
279 + limit_mm_per_prompt: InitVar[Optional[dict[str, Union[int,
280 + dict[str,
281 + int]]]]] = None
282 media_io_kwargs: InitVar[Optional[dict[str, dict[str, Any]]]] = None
283 mm_processor_kwargs: InitVar[Optional[dict[str, Any]]] = None
284 mm_processor_cache_gb: InitVar[Optional[float]] = None
vllm/config/multimodal.py
+83 -12
@@ -4,15 +4,45 @@
4 import hashlib
5 from collections.abc import Mapping
6 from dataclasses import field
7 -from typing import Any, Literal, Optional
7 +from typing import Any, Literal, Optional, Union
8
9 +from pydantic import ConfigDict, Field, field_validator
10 from pydantic.dataclasses import dataclass
11
11 -import vllm.envs as envs
12 from vllm.config.utils import config
13
14 +
15 +@dataclass
16 +class BaseDummyOptions:
17 + """Base options for generating dummy data during profiling."""
18 + count: int = Field(999, ge=0)
19 +
20 +
21 +@dataclass(config=ConfigDict(extra="forbid"))
22 +class VideoDummyOptions(BaseDummyOptions):
23 + """Options for generating dummy video data during profiling."""
24 + num_frames: Optional[int] = Field(None, gt=0)
25 + width: Optional[int] = Field(None, gt=0)
26 + height: Optional[int] = Field(None, gt=0)
27 +
28 +
29 +@dataclass(config=ConfigDict(extra="forbid"))
30 +class ImageDummyOptions(BaseDummyOptions):
31 + """Options for generating dummy image data during profiling."""
32 + width: Optional[int] = Field(None, gt=0)
33 + height: Optional[int] = Field(None, gt=0)
34 +
35 +
36 +@dataclass(config=ConfigDict(extra="forbid"))
37 +class AudioDummyOptions(BaseDummyOptions):
38 + """Options for generating dummy audio data during profiling."""
39 + length: Optional[int] = Field(None, gt=0)
40 +
41 +
42 MMEncoderTPMode = Literal["weights", "data"]
43 MMCacheType = Literal["shm", "lru"]
44 +DummyOptions = Union[BaseDummyOptions, VideoDummyOptions, ImageDummyOptions,
45 + AudioDummyOptions]
46
47
48 @config
@@ -20,12 +50,22 @@ MMCacheType = Literal["shm", "lru"]
50 class MultiModalConfig:
51 """Controls the behavior of multimodal models."""
52
23 - limit_per_prompt: dict[str, int] = field(default_factory=dict)
24 - """The maximum number of input items allowed per prompt for each modality.
25 - Defaults to 1 (V0) or 999 (V1) for each modality.
53 + limit_per_prompt: dict[str, DummyOptions] = field(default_factory=dict)
54 + """The maximum number of input items and options allowed per
55 + prompt for each modality.
56 + Defaults to 999 for each modality.
57 +
58 + Legacy format (count only):
59 + {"image": 16, "video": 2}
60 +
61 + Configurable format (with options):
62 + {"video": {"count": 1, "num_frames": 32, "width": 512, "height": 512},
63 + "image": {"count": 5, "width": 512, "height": 512}}
64
27 - For example, to allow up to 16 images and 2 videos per prompt:
28 - `{"image": 16, "video": 2}`"""
65 + Mixed format (combining both):
66 + {"image": 16, "video": {"count": 1, "num_frames": 32, "width": 512,
67 + "height": 512}}
68 + """
69 media_io_kwargs: dict[str, dict[str, Any]] = field(default_factory=dict)
70 """Additional args passed to process media inputs, keyed by modalities.
71 For example, to set num_frames for video, set
@@ -84,6 +124,27 @@ class MultiModalConfig:
124 from each video to be pruned.
125 """
126
127 + @field_validator("limit_per_prompt", mode="before")
128 + @classmethod
129 + def _validate_limit_per_prompt(
130 + cls, value: dict[str, Union[int,
131 + dict[str,
132 + int]]]) -> dict[str, DummyOptions]:
133 + for k, v in value.items():
134 + # Handle legacy format where only count is specified
135 + if isinstance(v, int):
136 + v = {"count": v}
137 + # Convert to the appropriate DummyOptions subclass
138 + if k == "video":
139 + value[k] = VideoDummyOptions(**v)
140 + elif k == "image":
141 + value[k] = ImageDummyOptions(**v)
142 + elif k == "audio":
143 + value[k] = AudioDummyOptions(**v)
144 + else:
145 + value[k] = BaseDummyOptions(**v)
146 + return value
147 +
148 def compute_hash(self) -> str:
149 """
150 WARNING: Whenever a new field is added to this config,
@@ -106,12 +167,22 @@ class MultiModalConfig:
167 def get_limit_per_prompt(self, modality: str) -> int:
168 """
169 Get the maximum number of input items allowed per prompt
109 - for the given modality.
170 + for the given modality (backward compatible).
171 + """
172 + limit_data = self.limit_per_prompt.get(modality)
173 +
174 + if limit_data is None:
175 + # Unspecified modality is set to 999 by default
176 + return 999
177 + return limit_data.count
178 +
179 + def get_dummy_options(self, modality: str) -> Optional[BaseDummyOptions]:
180 + """
181 + Get the configurable dummy data options for a modality.
182 + Returns None if no options are configured for this modality.
183 """
111 - return self.limit_per_prompt.get(
112 - modality,
113 - 999 if envs.VLLM_USE_V1 else 1,
114 - )
184 + # All values are now DummyOptions after normalization
185 + return self.limit_per_prompt.get(modality)
186
187 def merge_mm_processor_kwargs(
188 self,
vllm/engine/arg_utils.py
+1 -1
@@ -376,7 +376,7 @@ class EngineArgs:
376 quantization: Optional[QuantizationMethods] = ModelConfig.quantization
377 enforce_eager: bool = ModelConfig.enforce_eager
378 disable_custom_all_reduce: bool = ParallelConfig.disable_custom_all_reduce
379 - limit_mm_per_prompt: dict[str, int] = \
379 + limit_mm_per_prompt: dict[str, Union[int, dict[str, int]]] = \
380 get_field(MultiModalConfig, "limit_per_prompt")
381 interleave_mm_strings: bool = MultiModalConfig.interleave_mm_strings
382 media_io_kwargs: dict[str, dict[str,
vllm/model_executor/models/aria.py
+6 -1
@@ -10,6 +10,7 @@ from transformers.models.aria.modeling_aria import AriaCrossAttention
10 from transformers.models.aria.processing_aria import AriaProcessor
11
12 from vllm.config import VllmConfig
13 +from vllm.config.multimodal import BaseDummyOptions
14 from vllm.distributed import get_tensor_model_parallel_rank
15 from vllm.model_executor.layers.activation import get_act_fn
16 from vllm.model_executor.layers.fused_moe import FusedMoE
@@ -431,17 +432,21 @@ class AriaDummyInputsBuilder(BaseDummyInputsBuilder[AriaProcessingInfo]):
432 self,
433 seq_len: int,
434 mm_counts: Mapping[str, int],
435 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
436 ) -> MultiModalDataDict:
437 vision_config = self.info.get_vision_config()
438
439 max_image_size = vision_config.image_size
440 num_images = mm_counts.get("image", 0)
441
442 + image_overrides = mm_options.get("image") if mm_options else None
443 +
444 return {
445 "image":
446 self._get_dummy_images(width=max_image_size,
447 height=max_image_size,
444 - num_images=num_images)
448 + num_images=num_images,
449 + overrides=image_overrides)
450 }
451
452
vllm/model_executor/models/aya_vision.py
+6 -1
@@ -16,6 +16,7 @@ from transformers.models.got_ocr2.image_processing_got_ocr2 import (
16 get_optimal_tiled_canvas)
17
18 from vllm.config import VllmConfig
19 +from vllm.config.multimodal import BaseDummyOptions
20 from vllm.multimodal import MULTIMODAL_REGISTRY
21 from vllm.multimodal.inputs import MultiModalDataDict, MultiModalKwargsItems
22 from vllm.multimodal.parse import (ImageProcessorItems, ImageSize,
@@ -166,16 +167,20 @@ class AyaVisionDummyInputsBuilder(
167 self,
168 seq_len: int,
169 mm_counts: Mapping[str, int],
170 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
171 ) -> MultiModalDataDict:
172 num_images = mm_counts.get("image", 0)
173 image_size = \
174 self.info.get_image_size_with_most_features()
175
176 + image_overrides = mm_options.get("image") if mm_options else None
177 +
178 return {
179 "image":
180 self._get_dummy_images(width=image_size.width,
181 height=image_size.height,
178 - num_images=num_images)
182 + num_images=num_images,
183 + overrides=image_overrides)
184 }
185
186
vllm/model_executor/models/blip2.py
+6 -1
@@ -10,6 +10,7 @@ from transformers import (BatchFeature, Blip2Config, Blip2QFormerConfig,
10 apply_chunking_to_forward)
11
12 from vllm.config import CacheConfig, VllmConfig
13 +from vllm.config.multimodal import BaseDummyOptions
14 from vllm.model_executor.layers.activation import get_act_fn
15 from vllm.model_executor.layers.quantization import QuantizationConfig
16 from vllm.multimodal import MULTIMODAL_REGISTRY
@@ -435,6 +436,7 @@ class Blip2DummyInputsBuilder(BaseDummyInputsBuilder[Blip2ProcessingInfo]):
436 self,
437 seq_len: int,
438 mm_counts: Mapping[str, int],
439 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
440 ) -> MultiModalDataDict:
441 hf_config = self.info.get_hf_config()
442 vision_config = hf_config.vision_config
@@ -442,11 +444,14 @@ class Blip2DummyInputsBuilder(BaseDummyInputsBuilder[Blip2ProcessingInfo]):
444 max_image_size = vision_config.image_size
445 num_images = mm_counts.get("image", 0)
446
447 + image_overrides = mm_options.get("image") if mm_options else None
448 +
449 return {
450 "image":
451 self._get_dummy_images(width=max_image_size,
452 height=max_image_size,
449 - num_images=num_images)
453 + num_images=num_images,
454 + overrides=image_overrides)
455 }
456
457
vllm/model_executor/models/chameleon.py
+6 -1
@@ -14,6 +14,7 @@ from transformers import (BatchFeature, ChameleonConfig, ChameleonProcessor,
14
15 from vllm.attention import Attention
16 from vllm.config import CacheConfig, VllmConfig
17 +from vllm.config.multimodal import BaseDummyOptions
18 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
19 from vllm.logger import init_logger
20 from vllm.model_executor.layers.activation import SiluAndMul
@@ -92,17 +93,21 @@ class ChameleonDummyInputsBuilder(
93 self,
94 seq_len: int,
95 mm_counts: Mapping[str, int],
96 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
97 ) -> MultiModalDataDict:
98 config = self.info.get_hf_config()
99
100 width = height = config.vq_config.resolution
101 num_images = mm_counts.get("image", 0)
102
103 + image_overrides = mm_options.get("image") if mm_options else None
104 +
105 return {
106 "image":
107 self._get_dummy_images(width=width,
108 height=height,
105 - num_images=num_images)
109 + num_images=num_images,
110 + overrides=image_overrides)
111 }
112
113
vllm/model_executor/models/cohere2_vision.py
+6 -1
@@ -16,6 +16,7 @@ from transformers.models.cohere2_vision.processing_cohere2_vision import (
16 Cohere2VisionProcessor)
17
18 from vllm.config import VllmConfig
19 +from vllm.config.multimodal import BaseDummyOptions
20 from vllm.model_executor.layers.activation import MulAndSilu
21 from vllm.model_executor.layers.linear import (MergedColumnParallelLinear,
22 RowParallelLinear)
@@ -209,16 +210,20 @@ class Cohere2VisionDummyInputsBuilder(
210 self,
211 seq_len: int,
212 mm_counts: Mapping[str, int],
213 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
214 ) -> MultiModalDataDict:
215 num_images = mm_counts.get("image", 0)
216 image_size = \
217 self.info.get_image_size_with_most_features()
218
219 + image_overrides = mm_options.get("image") if mm_options else None
220 +
221 return {
222 "image":
223 self._get_dummy_images(width=image_size.width,
224 height=image_size.height,
221 - num_images=num_images)
225 + num_images=num_images,
226 + overrides=image_overrides)
227 }
228
229
vllm/model_executor/models/deepseek_vl2.py
+6 -1
@@ -14,6 +14,7 @@ from einops import rearrange, repeat
14 from transformers import BatchFeature
15
16 from vllm.config import VllmConfig
17 +from vllm.config.multimodal import BaseDummyOptions
18 from vllm.distributed import get_tensor_model_parallel_world_size
19 from vllm.model_executor.layers.quantization import QuantizationConfig
20 from vllm.model_executor.model_loader.utils import set_default_torch_dtype
@@ -191,16 +192,20 @@ class DeepseekVL2DummyInputsBuilder(
192 self,
193 seq_len: int,
194 mm_counts: Mapping[str, int],
195 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
196 ) -> MultiModalDataDict:
197 num_images = mm_counts.get("image", 0)
198
199 max_image_size = self.info.get_image_size_with_most_features()
200
201 + image_overrides = mm_options.get("image") if mm_options else None
202 +
203 return {
204 "image":
205 self._get_dummy_images(width=max_image_size.width,
206 height=max_image_size.height,
203 - num_images=num_images)
207 + num_images=num_images,
208 + overrides=image_overrides)
209 }
210
211
vllm/model_executor/models/dots_ocr.py
+6 -1
@@ -13,6 +13,7 @@ from vllm.attention.backends.registry import _Backend
13 from vllm.attention.layer import (check_upstream_fa_availability,
14 maybe_get_vit_flash_attn_backend)
15 from vllm.config import VllmConfig
16 +from vllm.config.multimodal import BaseDummyOptions
17 from vllm.distributed import utils as dist_utils
18 from vllm.distributed.parallel_state import (
19 get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
@@ -91,17 +92,21 @@ class DotsOCRDummyInputsBuilder(Qwen2VLDummyInputsBuilder):
92 self,
93 seq_len: int,
94 mm_counts: Mapping[str, int],
95 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
96 ) -> MultiModalDataDict:
97 num_images = mm_counts.get("image", 0)
98
99 target_width, target_height = self.info.get_image_size_with_most_features( # noqa: E501
100 )
101
102 + image_overrides = mm_options.get("image") if mm_options else None
103 +
104 return {
105 "image":
106 self._get_dummy_images(width=target_width,
107 height=target_height,
104 - num_images=num_images),
108 + num_images=num_images,
109 + overrides=image_overrides),
110 }
111
112
vllm/model_executor/models/ernie45_vl.py
+9 -2
@@ -38,6 +38,7 @@ from vllm.attention.backends.registry import _Backend
38 from vllm.attention.layer import (check_upstream_fa_availability,
39 maybe_get_vit_flash_attn_backend)
40 from vllm.config import VllmConfig
41 +from vllm.config.multimodal import BaseDummyOptions
42 from vllm.distributed import parallel_state
43 from vllm.distributed import utils as dist_utils
44 from vllm.logger import init_logger
@@ -1184,6 +1185,7 @@ class Ernie4_5_VLDummyInputsBuilder(
1185 self,
1186 seq_len: int,
1187 mm_counts: Mapping[str, int],
1188 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
1189 ) -> MultiModalDataDict:
1190 num_images = mm_counts.get("image", 0)
1191 num_videos = mm_counts.get("video", 0)
@@ -1193,16 +1195,21 @@ class Ernie4_5_VLDummyInputsBuilder(
1195 target_num_frames = \
1196 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
1197
1198 + image_overrides = mm_options.get("image") if mm_options else None
1199 + video_overrides = mm_options.get("video") if mm_options else None
1200 +
1201 return {
1202 "image":
1203 self._get_dummy_images(width=target_width,
1204 height=target_height,
1200 - num_images=num_images),
1205 + num_images=num_images,
1206 + overrides=image_overrides),
1207 "video":
1208 self._get_dummy_videos(width=target_width,
1209 height=target_height,
1210 num_frames=target_num_frames,
1205 - num_videos=num_videos)
1211 + num_videos=num_videos,
1212 + overrides=video_overrides)
1213 }
1214
1215
vllm/model_executor/models/fuyu.py
+6 -1
@@ -27,6 +27,7 @@ from transformers import (BatchFeature, FuyuConfig, FuyuImageProcessor,
27 FuyuProcessor)
28
29 from vllm.config import VllmConfig
30 +from vllm.config.multimodal import BaseDummyOptions
31 from vllm.model_executor.layers.linear import ColumnParallelLinear
32 from vllm.model_executor.models.persimmon import PersimmonForCausalLM
33 from vllm.multimodal import MULTIMODAL_REGISTRY
@@ -136,16 +137,20 @@ class FuyuDummyInputsBuilder(BaseDummyInputsBuilder[FuyuProcessingInfo]):
137 self,
138 seq_len: int,
139 mm_counts: Mapping[str, int],
140 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
141 ) -> MultiModalDataDict:
142 target_width, target_height = \
143 self.info.get_image_size_with_most_features()
144 num_images = mm_counts.get("image", 0)
145
146 + image_overrides = mm_options.get("image") if mm_options else None
147 +
148 return {
149 "image":
150 self._get_dummy_images(width=target_width,
151 height=target_height,
148 - num_images=num_images)
152 + num_images=num_images,
153 + overrides=image_overrides)
154 }
155
156
vllm/model_executor/models/gemma3_mm.py
+6 -1
@@ -11,6 +11,7 @@ from transformers.models.gemma3.processing_gemma3 import Gemma3ProcessorKwargs
11
12 import vllm.envs as envs
13 from vllm.config import VllmConfig
14 +from vllm.config.multimodal import BaseDummyOptions
15 from vllm.logger import init_logger
16 from vllm.model_executor.layers.layernorm import GemmaRMSNorm
17 from vllm.model_executor.models.module_mapping import MultiModelKeys
@@ -241,17 +242,21 @@ class Gemma3DummyInputsBuilder(BaseDummyInputsBuilder[Gemma3ProcessingInfo]):
242 self,
243 seq_len: int,
244 mm_counts: Mapping[str, int],
245 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
246 ) -> MultiModalDataDict:
247 num_images = mm_counts.get("image", 0)
248
249 target_width, target_height = \
250 self.info.get_image_size_with_most_features()
251
252 + image_overrides = mm_options.get("image") if mm_options else None
253 +
254 return {
255 "image":
256 self._get_dummy_images(width=target_width,
257 height=target_height,
254 - num_images=num_images)
258 + num_images=num_images,
259 + overrides=image_overrides)
260 }
261
262
vllm/model_executor/models/gemma3n_mm.py
+10 -2
@@ -16,6 +16,7 @@ from transformers.models.gemma3n import (Gemma3nAudioConfig,
16 from transformers.models.siglip import SiglipImageProcessorFast
17
18 from vllm.config import ModelConfig, SpeechToTextConfig, VllmConfig
19 +from vllm.config.multimodal import BaseDummyOptions
20 from vllm.inputs.data import PromptType
21 from vllm.logger import init_logger
22 from vllm.model_executor.layers.layernorm import RMSNorm
@@ -153,6 +154,7 @@ class Gemma3nDummyInputsBuilder(BaseDummyInputsBuilder[Gemma3nProcessingInfo]):
154 self,
155 seq_len: int,
156 mm_counts: Mapping[str, int],
157 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
158 ) -> MultiModalDataDict:
159 num_images = mm_counts.get("image", 0)
160 num_audios = mm_counts.get("audio", 0)
@@ -163,13 +165,19 @@ class Gemma3nDummyInputsBuilder(BaseDummyInputsBuilder[Gemma3nProcessingInfo]):
165 img_width = image_processor.size.get("width", 224)
166 img_height = image_processor.size.get("height", 224)
167
168 + image_overrides = mm_options.get("image") if mm_options else None
169 + audio_overrides = mm_options.get("audio") if mm_options else None
170 +
171 return {
172 "image":
173 self._get_dummy_images(width=img_width,
174 height=img_height,
170 - num_images=num_images),
175 + num_images=num_images,
176 + overrides=image_overrides),
177 "audio":
172 - self._get_dummy_audios(length=audio_len, num_audios=num_audios)
178 + self._get_dummy_audios(length=audio_len,
179 + num_audios=num_audios,
180 + overrides=audio_overrides)
181 }
182
183
vllm/model_executor/models/glm4_1v.py
+34 -2
@@ -29,7 +29,7 @@
29 import math
30 from collections.abc import Iterable, Mapping, Sequence
31 from functools import partial
32 -from typing import Annotated, Any, Callable, Literal, Optional, Union
32 +from typing import Annotated, Any, Callable, Literal, Optional, Union, override
33
34 import numpy as np
35 import torch
@@ -50,6 +50,7 @@ from vllm.attention.backends.registry import _Backend
50 from vllm.attention.layer import (check_upstream_fa_availability,
51 maybe_get_vit_flash_attn_backend)
52 from vllm.config import VllmConfig
53 +from vllm.config.multimodal import BaseDummyOptions, VideoDummyOptions
54 from vllm.distributed import (get_tensor_model_parallel_world_size,
55 parallel_state)
56 from vllm.distributed import utils as dist_utils
@@ -1110,6 +1111,7 @@ class Glm4vDummyInputsBuilder(BaseDummyInputsBuilder[Glm4vProcessingInfo]):
1111 self,
1112 seq_len: int,
1113 mm_counts: Mapping[str, int],
1114 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
1115 ) -> MultiModalDataDict:
1116 num_images = mm_counts.get("image", 0)
1117 num_videos = mm_counts.get("video", 0)
@@ -1118,17 +1120,23 @@ class Glm4vDummyInputsBuilder(BaseDummyInputsBuilder[Glm4vProcessingInfo]):
1120 self.info.get_image_size_with_most_features())
1121 target_num_frames = self.info.get_num_frames_with_most_features(
1122 seq_len, mm_counts)
1123 +
1124 + image_overrides = mm_options.get("image") if mm_options else None
1125 + video_overrides = mm_options.get("video") if mm_options else None
1126 +
1127 return {
1128 "image":
1129 self._get_dummy_images(width=target_width,
1130 height=target_height,
1125 - num_images=num_images),
1131 + num_images=num_images,
1132 + overrides=image_overrides),
1133 "video":
1134 self._get_dummy_videos(
1135 width=target_width,
1136 height=target_height,
1137 num_frames=target_num_frames,
1138 num_videos=num_videos,
1139 + overrides=video_overrides,
1140 ),
1141 }
1142
@@ -1139,7 +1147,31 @@ class Glm4vDummyInputsBuilder(BaseDummyInputsBuilder[Glm4vProcessingInfo]):
1147 height: int,
1148 num_frames: int,
1149 num_videos: int,
1150 + overrides: Optional[VideoDummyOptions] = None,
1151 ) -> list[VideoItem]:
1152 + if overrides:
1153 + if overrides.num_frames:
1154 + if overrides.num_frames > num_frames:
1155 + logger.warning(
1156 + "video.num_frames override (%d) exceeds model's "
1157 + "maximum number of frames (%d), will be ignored",
1158 + overrides.num_frames, num_frames)
1159 + num_frames = min(num_frames, overrides.num_frames)
1160 + if overrides.width:
1161 + if overrides.width > width:
1162 + logger.warning(
1163 + "video.width override (%d) exceeds model's "
1164 + "maximum width (%d), will be ignored", overrides.width,
1165 + width)
1166 + width = min(width, overrides.width)
1167 + if overrides.height:
1168 + if overrides.height > height:
1169 + logger.warning(
1170 + "video.height override (%d) exceeds model's "
1171 + "maximum height (%d), will be ignored",
1172 + overrides.height, height)
1173 + height = min(height, override.height)
1174 +
1175 video = np.full((num_frames, width, height, 3), 255, dtype=np.uint8)
1176 video_items = []
1177 for i in range(num_videos):
vllm/model_executor/models/glm4v.py
+6 -1
@@ -19,6 +19,7 @@ from transformers.tokenization_utils_base import TextInput
19
20 from vllm.attention.layer import MultiHeadAttention
21 from vllm.config import VllmConfig
22 +from vllm.config.multimodal import BaseDummyOptions
23 from vllm.distributed import get_tensor_model_parallel_world_size
24 from vllm.model_executor.layers.activation import SiluAndMul, get_act_fn
25 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
@@ -465,6 +466,7 @@ class GLM4VDummyInputsBuilder(BaseDummyInputsBuilder[GLM4VProcessingInfo]):
466 self,
467 seq_len: int,
468 mm_counts: Mapping[str, int],
469 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
470 ) -> MultiModalDataDict:
471 hf_config = self.info.get_hf_config()
472 vision_config = hf_config.vision_config
@@ -472,11 +474,14 @@ class GLM4VDummyInputsBuilder(BaseDummyInputsBuilder[GLM4VProcessingInfo]):
474 target_width = target_height = vision_config["image_size"]
475 num_images = mm_counts.get("image", 0)
476
477 + image_overrides = mm_options.get("image") if mm_options else None
478 +
479 return {
480 "image":
481 self._get_dummy_images(width=target_width,
482 height=target_height,
479 - num_images=num_images)
483 + num_images=num_images,
484 + overrides=image_overrides)
485 }
486
487
vllm/model_executor/models/granite_speech.py
+5
@@ -33,6 +33,7 @@ from torch import nn
33 from transformers import BatchFeature, PretrainedConfig
34
35 from vllm.config import CacheConfig, VllmConfig
36 +from vllm.config.multimodal import BaseDummyOptions
37 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
38 RowParallelLinear)
39 from vllm.model_executor.layers.quantization import QuantizationConfig
@@ -181,13 +182,17 @@ class GraniteSpeechDummyInputsBuilder(
182 self,
183 seq_len: int,
184 mm_counts: Mapping[str, int],
185 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
186 ) -> MultiModalDataDict:
187 num_audios = mm_counts.get("audio", 0)
188 + audio_overrides = mm_options.get("audio") if mm_options else None
189 +
190 return {
191 "audio":
192 self._get_dummy_audios(
193 length=self.info.get_max_audio_len(),
194 num_audios=num_audios,
195 + overrides=audio_overrides,
196 )
197 }
198
vllm/model_executor/models/hyperclovax_vision.py
+8
@@ -29,6 +29,7 @@ from transformers import BatchFeature, CLIPVisionConfig, SiglipVisionConfig
29 from transformers.modeling_utils import no_init_weights
30
31 from vllm.config import VllmConfig
32 +from vllm.config.multimodal import BaseDummyOptions
33 from vllm.model_executor.layers.quantization import QuantizationConfig
34 from vllm.multimodal import MULTIMODAL_REGISTRY
35 from vllm.multimodal.cache import BaseMultiModalProcessorCache
@@ -149,6 +150,7 @@ class HCXVisionDummyInputsBuilder(
150 self,
151 seq_len: int,
152 mm_counts: Mapping[str, int],
153 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
154 ) -> MultiModalDataDict:
155 num_images = mm_counts.get("image", 0)
156 num_videos = mm_counts.get("video", 0)
@@ -156,12 +158,17 @@ class HCXVisionDummyInputsBuilder(
158 target_width, target_height = \
159 self.info.get_image_size_with_most_features()
160 target_num_frames = 32
161 +
162 + image_overrides = mm_options.get("image") if mm_options else None
163 + video_overrides = mm_options.get("video") if mm_options else None
164 +
165 return {
166 "image":
167 self._get_dummy_images(
168 width=target_width,
169 height=target_height,
170 num_images=num_images,
171 + overrides=image_overrides,
172 ),
173 "video":
174 self._get_dummy_videos(
@@ -169,6 +176,7 @@ class HCXVisionDummyInputsBuilder(
176 height=target_height - 1,
177 num_frames=target_num_frames,
178 num_videos=num_videos,
179 + overrides=video_overrides,
180 )
181 }
182
vllm/model_executor/models/idefics3.py
+6 -1
@@ -26,6 +26,7 @@ from transformers import (BatchFeature, Idefics3Config, Idefics3ImageProcessor,
26 Idefics3Processor)
27
28 from vllm.config import VllmConfig
29 +from vllm.config.multimodal import BaseDummyOptions
30 from vllm.model_executor.layers.linear import ReplicatedLinear
31 from vllm.model_executor.layers.logits_processor import LogitsProcessor
32 from vllm.model_executor.layers.quantization import QuantizationConfig
@@ -292,17 +293,21 @@ class Idefics3DummyInputsBuilder(BaseDummyInputsBuilder[Idefics3ProcessingInfo]
293 self,
294 seq_len: int,
295 mm_counts: Mapping[str, int],
296 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
297 ) -> MultiModalDataDict:
298 num_images = mm_counts.get("image", 0)
299 hf_processor = self.info.get_hf_processor()
300 image_processor: Idefics3ImageProcessor = hf_processor.image_processor
301 longest_edge = image_processor.max_image_size['longest_edge']
302
303 + image_overrides = mm_options.get("image") if mm_options else None
304 +
305 return {
306 "image":
307 self._get_dummy_images(width=longest_edge,
308 height=longest_edge,
305 - num_images=num_images)
309 + num_images=num_images,
310 + overrides=image_overrides)
311 }
312
313
vllm/model_executor/models/interns1.py
+9 -2
@@ -20,6 +20,7 @@ from transformers.models.internvl.video_processing_internvl import (
20 InternVLVideoProcessor)
21
22 from vllm.config import VllmConfig
23 +from vllm.config.multimodal import BaseDummyOptions
24 from vllm.model_executor.layers.quantization import QuantizationConfig
25 from vllm.model_executor.models.interns1_vit import InternS1VisionModel
26 from vllm.model_executor.models.module_mapping import MultiModelKeys
@@ -270,6 +271,7 @@ class InternS1DummyInputsBuilder(BaseDummyInputsBuilder[InternS1ProcessingInfo]
271 self,
272 seq_len: int,
273 mm_counts: Mapping[str, int],
274 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
275 ) -> MultiModalDataDict:
276 target_width, target_height = \
277 self.info.get_image_size_with_most_features()
@@ -281,16 +283,21 @@ class InternS1DummyInputsBuilder(BaseDummyInputsBuilder[InternS1ProcessingInfo]
283 config = self.info.get_hf_config()
284 image_size_h, image_size_w = config.vision_config.image_size
285
286 + image_overrides = mm_options.get("image") if mm_options else None
287 + video_overrides = mm_options.get("video") if mm_options else None
288 +
289 return {
290 "image":
291 self._get_dummy_images(width=target_width,
292 height=target_height,
288 - num_images=num_images),
293 + num_images=num_images,
294 + overrides=image_overrides),
295 "video":
296 self._get_dummy_videos(width=image_size_w,
297 height=image_size_h,
298 num_frames=target_num_frames,
293 - num_videos=num_videos),
299 + num_videos=num_videos,
300 + overrides=video_overrides),
301 }
302
303
vllm/model_executor/models/internvl.py
+12 -3
@@ -20,6 +20,7 @@ from PIL import Image
20 from transformers import BatchFeature, PretrainedConfig, TensorType
21
22 from vllm.config import VllmConfig
23 +from vllm.config.multimodal import BaseDummyOptions
24 from vllm.model_executor.layers.quantization import QuantizationConfig
25 from vllm.model_executor.layers.quantization.awq import AWQConfig
26 from vllm.model_executor.models.intern_vit import (InternVisionModel,
@@ -747,16 +748,20 @@ class BaseInternVLDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
748 self,
749 seq_len: int,
750 mm_counts: Mapping[str, int],
751 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
752 ) -> MultiModalDataDict:
753 target_width, target_height = \
754 self.info.get_image_size_with_most_features()
755 num_images = mm_counts.get("image", 0)
756
757 + image_overrides = mm_options.get("image") if mm_options else None
758 +
759 return {
760 "image":
761 self._get_dummy_images(width=target_width,
762 height=target_height,
759 - num_images=num_images)
763 + num_images=num_images,
764 + overrides=image_overrides)
765 }
766
767
@@ -913,21 +918,25 @@ class InternVLDummyInputsBuilder(
918 self,
919 seq_len: int,
920 mm_counts: Mapping[str, int],
921 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
922 ) -> MultiModalDataDict:
923 dummy_image = super().get_dummy_mm_data(seq_len=seq_len,
918 - mm_counts=mm_counts)
924 + mm_counts=mm_counts,
925 + mm_options=mm_options)
926 if self.info.supports_video:
927 config = self.info.get_hf_config()
928 image_size: int = config.vision_config.image_size
929 target_num_frames = \
930 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
931 num_videos = mm_counts.get("video", 0)
932 + video_overrides = mm_options.get("video") if mm_options else None
933 dummy_video = {
934 "video":
935 self._get_dummy_videos(width=image_size,
936 height=image_size,
937 num_frames=target_num_frames,
930 - num_videos=num_videos)
938 + num_videos=num_videos,
939 + overrides=video_overrides)
940 }
941 else:
942 dummy_video = {}
vllm/model_executor/models/keye.py
+7
@@ -20,6 +20,7 @@ from transformers.utils import torch_int
20 from vllm.attention.backends.registry import _Backend
21 from vllm.attention.layer import check_upstream_fa_availability
22 from vllm.config import VllmConfig
23 +from vllm.config.multimodal import BaseDummyOptions
24 from vllm.distributed import get_tensor_model_parallel_world_size
25 from vllm.logger import init_logger
26 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
@@ -1170,6 +1171,7 @@ class KeyeBaseDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
1171 self,
1172 seq_len: int,
1173 mm_counts: Mapping[str, int],
1174 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
1175 ) -> MultiModalDataDict:
1176 num_images = mm_counts.get("image", 0)
1177 num_videos = mm_counts.get("video", 0)
@@ -1179,12 +1181,16 @@ class KeyeBaseDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
1181 target_num_frames = self.info.get_num_frames_with_most_features(
1182 seq_len)
1183
1184 + image_overrides = mm_options.get("image") if mm_options else None
1185 + video_overrides = mm_options.get("video") if mm_options else None
1186 +
1187 mm_data = {
1188 "image":
1189 self._get_dummy_images(
1190 width=target_width,
1191 height=target_height,
1192 num_images=num_images,
1193 + overrides=image_overrides,
1194 ),
1195 "video":
1196 self._get_dummy_videos(
@@ -1192,6 +1198,7 @@ class KeyeBaseDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
1198 height=target_height,
1199 num_frames=target_num_frames,
1200 num_videos=num_videos,
1201 + overrides=video_overrides,
1202 ),
1203 }
1204
vllm/model_executor/models/kimi_vl.py
+6 -1
@@ -54,6 +54,7 @@ from transformers import BatchFeature
54 from transformers.activations import GELUActivation
55
56 from vllm.config import VllmConfig
57 +from vllm.config.multimodal import BaseDummyOptions
58 from vllm.distributed import get_pp_group
59 from vllm.model_executor.layers.fused_moe import FusedMoE
60 from vllm.model_executor.layers.linear import ReplicatedLinear
@@ -212,14 +213,18 @@ class KimiVLDummyInputsBuilder(BaseDummyInputsBuilder[KimiVLProcessingInfo]):
213 self,
214 seq_len: int,
215 mm_counts: Mapping[str, int],
216 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
217 ) -> MultiModalDataDict:
218 num_images = mm_counts.get("image", 0)
219
220 + image_overrides = mm_options.get("image") if mm_options else None
221 +
222 return {
223 "image":
224 self._get_dummy_images(width=MaxImageTokenMeta.width,
225 height=MaxImageTokenMeta.height,
222 - num_images=num_images)
226 + num_images=num_images,
227 + overrides=image_overrides)
228 }
229
230
vllm/model_executor/models/llava.py
+6 -1
@@ -15,6 +15,7 @@ from transformers.models.llava import LlavaProcessor
15 from transformers.models.pixtral import PixtralProcessor
16
17 from vllm.config import VllmConfig
18 +from vllm.config.multimodal import BaseDummyOptions
19 from vllm.model_executor.layers.activation import get_act_fn
20 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
21 RowParallelLinear)
@@ -195,17 +196,21 @@ class LlavaDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
196 self,
197 seq_len: int,
198 mm_counts: Mapping[str, int],
199 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
200 ) -> MultiModalDataDict:
201 num_images = mm_counts.get("image", 0)
202
203 target_width, target_height = \
204 self.info.get_image_size_with_most_features()
205
206 + image_overrides = mm_options.get("image") if mm_options else None
207 +
208 return {
209 "image":
210 self._get_dummy_images(width=target_width,
211 height=target_height,
208 - num_images=num_images)
212 + num_images=num_images,
213 + overrides=image_overrides)
214 }
215
216
vllm/model_executor/models/llava_next_video.py
+5
@@ -11,6 +11,7 @@ from transformers import (BatchFeature, LlavaNextVideoConfig,
11 LlavaNextVideoProcessor)
12
13 from vllm.config import VllmConfig
14 +from vllm.config.multimodal import BaseDummyOptions
15 from vllm.model_executor.layers.activation import get_act_fn
16 from vllm.model_executor.models.clip import CLIPVisionModel
17 from vllm.multimodal import MULTIMODAL_REGISTRY
@@ -150,6 +151,7 @@ class LlavaNextVideoDummyInputsBuilder(
151 self,
152 seq_len: int,
153 mm_counts: Mapping[str, int],
154 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
155 ) -> MultiModalDataDict:
156 num_videos = mm_counts.get("video", 0)
157
@@ -158,6 +160,8 @@ class LlavaNextVideoDummyInputsBuilder(
160 target_num_frames = \
161 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
162
163 + video_overrides = mm_options.get("video") if mm_options else None
164 +
165 return {
166 "video":
167 self._get_dummy_videos(
@@ -165,6 +169,7 @@ class LlavaNextVideoDummyInputsBuilder(
169 height=target_height,
170 num_frames=target_num_frames,
171 num_videos=num_videos,
172 + overrides=video_overrides,
173 )
174 }
175
vllm/model_executor/models/llava_onevision.py
+8 -1
@@ -13,6 +13,7 @@ from transformers.models.llava_onevision.modeling_llava_onevision import (
13 get_anyres_image_grid_shape, unpad_image)
14
15 from vllm.config import VllmConfig
16 +from vllm.config.multimodal import BaseDummyOptions
17 from vllm.model_executor.layers.activation import get_act_fn
18 from vllm.multimodal import MULTIMODAL_REGISTRY
19 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
@@ -254,6 +255,7 @@ class LlavaOnevisionDummyInputsBuilder(
255 self,
256 seq_len: int,
257 mm_counts: Mapping[str, int],
258 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
259 ) -> MultiModalDataDict:
260 num_images = mm_counts.get("image", 0)
261 num_videos = mm_counts.get("video", 0)
@@ -264,17 +266,22 @@ class LlavaOnevisionDummyInputsBuilder(
266 self.info.get_num_frames_with_most_features(seq_len,
267 mm_counts)
268
269 + image_overrides = mm_options.get("image") if mm_options else None
270 + video_overrides = mm_options.get("video") if mm_options else None
271 +
272 return {
273 "image":
274 self._get_dummy_images(width=target_width,
275 height=target_height,
271 - num_images=num_images),
276 + num_images=num_images,
277 + overrides=image_overrides),
278 "video":
279 self._get_dummy_videos(
280 width=target_width,
281 height=target_height,
282 num_frames=target_num_frames,
283 num_videos=num_videos,
284 + overrides=video_overrides,
285 )
286 }
287
vllm/model_executor/models/midashenglm.py
+6 -1
@@ -36,6 +36,7 @@ from torch.nn.functional import scaled_dot_product_attention
36 from transformers import BatchFeature
37
38 from vllm.config import VllmConfig
39 +from vllm.config.multimodal import BaseDummyOptions
40 from vllm.distributed import get_tensor_model_parallel_world_size
41 from vllm.model_executor.layers.activation import get_act_fn
42 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
@@ -539,13 +540,17 @@ class MiDashengLMDummyInputsBuilder(
540 self,
541 seq_len: int,
542 mm_counts: Mapping[str, int],
543 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
544 ) -> MultiModalDataDict:
545 num_audios = mm_counts.get("audio", 0)
546
547 + audio_overrides = mm_options.get("audio") if mm_options else None
548 +
549 return {
550 "audio":
551 self._get_dummy_audios(length=self.info.get_max_audio_len(),
548 - num_audios=num_audios)
552 + num_audios=num_audios,
553 + overrides=audio_overrides)
554 }
555
556
vllm/model_executor/models/minicpmo.py
+8 -2
@@ -36,6 +36,7 @@ from transformers.models.whisper.modeling_whisper import (ACT2FN,
36 WhisperEncoder)
37
38 from vllm.config import VllmConfig
39 +from vllm.config.multimodal import BaseDummyOptions
40 from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalKwargsItems
41 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
42 NestedTensors)
@@ -237,18 +238,23 @@ class MiniCPMODummyInputsBuilder(
238 self,
239 seq_len: int,
240 mm_counts: Mapping[str, int],
241 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
242 ) -> MultiModalDataDict:
243 num_audios = mm_counts.get("audio", 0)
244 audio_len = self.info.get_max_audio_chunks_with_most_features() * \
245 self.info.get_default_audio_sampling_rate()
246
247 + audio_overrides = mm_options.get("audio") if mm_options else None
248 +
249 audio_mm_data = {
250 "audio":
247 - self._get_dummy_audios(length=audio_len, num_audios=num_audios)
251 + self._get_dummy_audios(length=audio_len,
252 + num_audios=num_audios,
253 + overrides=audio_overrides)
254 }
255
256 return {
251 - **super().get_dummy_mm_data(seq_len, mm_counts),
257 + **super().get_dummy_mm_data(seq_len, mm_counts, mm_options),
258 **audio_mm_data,
259 }
260
vllm/model_executor/models/minicpmv.py
+9 -2
@@ -39,6 +39,7 @@ from transformers import BatchFeature, PretrainedConfig
39 from typing_extensions import TypeVar
40
41 from vllm.config import VllmConfig
42 +from vllm.config.multimodal import BaseDummyOptions
43 from vllm.model_executor.layers.quantization import QuantizationConfig
44 from vllm.model_executor.layers.quantization.awq import AWQConfig
45 from vllm.model_executor.layers.quantization.awq_marlin import AWQMarlinConfig
@@ -679,6 +680,7 @@ class MiniCPMVDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
680 self,
681 seq_len: int,
682 mm_counts: Mapping[str, int],
683 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
684 ) -> MultiModalDataDict:
685 num_images = mm_counts.get("image", 0)
686 num_videos = mm_counts.get("video", 0)
@@ -690,15 +692,20 @@ class MiniCPMVDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
692 num_video_frames = \
693 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
694
695 + image_overrides = mm_options.get("image") if mm_options else None
696 + video_overrides = mm_options.get("video") if mm_options else None
697 +
698 return {
699 "image":
700 self._get_dummy_images(width=image_width,
701 height=image_height,
697 - num_images=num_images),
702 + num_images=num_images,
703 + overrides=image_overrides),
704 "video": [
705 self._get_dummy_images(width=video_width,
706 height=video_height,
701 - num_images=num_video_frames)
707 + num_images=num_video_frames,
708 + overrides=video_overrides)
709 ] * num_videos,
710 }
711
vllm/model_executor/models/mistral3.py
+6 -1
@@ -13,6 +13,7 @@ from transformers import (BatchFeature, Mistral3Config, PixtralVisionConfig,
13 from transformers.models.pixtral import PixtralProcessor
14
15 from vllm.config import VllmConfig
16 +from vllm.config.multimodal import BaseDummyOptions
17 from vllm.model_executor.layers.activation import get_act_fn
18 from vllm.model_executor.layers.layernorm import RMSNorm
19 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
@@ -208,17 +209,21 @@ class Mistral3DummyInputsBuilder(BaseDummyInputsBuilder[_I]):
209 self,
210 seq_len: int,
211 mm_counts: Mapping[str, int],
212 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
213 ) -> MultiModalDataDict:
214 num_images = mm_counts.get("image", 0)
215
216 target_width, target_height = \
217 self.info.get_image_size_with_most_features()
218
219 + image_overrides = mm_options.get("image") if mm_options else None
220 +
221 return {
222 "image":
223 self._get_dummy_images(width=target_width,
224 height=target_height,
221 - num_images=num_images)
225 + num_images=num_images,
226 + overrides=image_overrides)
227 }
228
229
vllm/model_executor/models/mllama4.py
+6 -1
@@ -31,6 +31,7 @@ from transformers.models.llama4.image_processing_llama4_fast import (
31
32 from vllm.attention.layer import MultiHeadAttention
33 from vllm.config import VllmConfig
34 +from vllm.config.multimodal import BaseDummyOptions
35 from vllm.distributed import get_tensor_model_parallel_world_size
36 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
37 QKVParallelLinear,
@@ -689,17 +690,21 @@ class Mllama4DummyInputsBuilder(BaseDummyInputsBuilder[Mllama4ProcessingInfo]):
690 self,
691 seq_len: int,
692 mm_counts: Mapping[str, int],
693 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
694 ) -> MultiModalDataDict:
695 num_images = mm_counts.get("image", 0)
696
697 (target_width,
698 target_height) = self.info.get_image_size_with_most_features()
699
700 + image_overrides = mm_options.get("image") if mm_options else None
701 +
702 return {
703 "image":
704 self._get_dummy_images(width=target_width,
705 height=target_height,
702 - num_images=num_images)
706 + num_images=num_images,
707 + overrides=image_overrides)
708 }
709
710
vllm/model_executor/models/molmo.py
+6 -1
@@ -22,6 +22,7 @@ from vllm.attention import Attention
22 from vllm.attention.layer import MultiHeadAttention
23 from vllm.compilation.decorators import support_torch_compile
24 from vllm.config import CacheConfig, VllmConfig
25 +from vllm.config.multimodal import BaseDummyOptions
26 from vllm.distributed import (get_pp_group, get_tensor_model_parallel_rank,
27 get_tensor_model_parallel_world_size,
28 split_tensor_along_last_dim,
@@ -1226,16 +1227,20 @@ class MolmoDummyInputsBuilder(BaseDummyInputsBuilder[MolmoProcessingInfo]):
1227 self,
1228 seq_len: int,
1229 mm_counts: Mapping[str, int],
1230 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
1231 ) -> MultiModalDataDict:
1232 target_width, target_height = \
1233 self.info.get_image_size_with_most_features()
1234 num_images = mm_counts.get("image", 0)
1235
1236 + image_overrides = mm_options.get("image") if mm_options else None
1237 +
1238 return {
1239 "image":
1240 self._get_dummy_images(width=target_width,
1241 height=target_height,
1238 - num_images=num_images)
1242 + num_images=num_images,
1243 + overrides=image_overrides)
1244 }
1245
1246
vllm/model_executor/models/nano_nemotron_vl.py
+12 -3
@@ -21,6 +21,7 @@ from PIL import Image
21 from transformers import BatchFeature, PretrainedConfig, TensorType
22
23 from vllm.config import VllmConfig
24 +from vllm.config.multimodal import BaseDummyOptions
25 from vllm.model_executor.layers.activation import ReLUSquaredActivation
26 from vllm.model_executor.layers.layernorm import RMSNorm
27 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
@@ -809,6 +810,7 @@ class NanoNemotronVLDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
810 self,
811 seq_len: int,
812 mm_counts: Mapping[str, int],
813 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
814 ) -> MultiModalDataDict:
815 # Use default max_num_tiles for dummy data generation
816 max_num_tiles = 12
@@ -816,11 +818,14 @@ class NanoNemotronVLDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
818 self.info.get_image_size_with_most_features(max_num_tiles))
819 num_images = mm_counts.get("image", 0)
820
821 + image_overrides = mm_options.get("image") if mm_options else None
822 +
823 return {
824 "image":
825 self._get_dummy_images(width=target_width,
826 height=target_height,
823 - num_images=num_images)
827 + num_images=num_images,
828 + overrides=image_overrides)
829 }
830
831
@@ -837,21 +842,25 @@ class NanoNemotronVLDummyInputsBuilder(
842 self,
843 seq_len: int,
844 mm_counts: Mapping[str, int],
845 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
846 ) -> MultiModalDataDict:
847 dummy_image = super().get_dummy_mm_data(seq_len=seq_len,
842 - mm_counts=mm_counts)
848 + mm_counts=mm_counts,
849 + mm_options=mm_options)
850 if self.info.supports_video:
851 config = self.info.get_hf_config()
852 image_size: int = config.force_image_size
853 target_num_frames = \
854 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
855 num_videos = mm_counts.get("video", 0)
856 + video_overrides = mm_options.get("video") if mm_options else None
857 dummy_video = {
858 "video":
859 self._get_dummy_videos(width=image_size,
860 height=image_size,
861 num_frames=target_num_frames,
854 - num_videos=num_videos)
862 + num_videos=num_videos,
863 + overrides=video_overrides)
864 }
865 else:
866 dummy_video = {}
vllm/model_executor/models/nvlm_d.py
+6 -1
@@ -14,6 +14,7 @@ import torch
14 import torch.nn as nn
15 from transformers import PretrainedConfig
16
17 +from vllm.config.multimodal import BaseDummyOptions
18 from vllm.model_executor.layers.quantization import QuantizationConfig
19 from vllm.multimodal import MULTIMODAL_REGISTRY
20 from vllm.multimodal.inputs import MultiModalDataDict, MultiModalKwargsItems
@@ -86,16 +87,20 @@ class NVLMDummyInputsBuilder(BaseInternVLDummyInputsBuilder[NVLMProcessingInfo]
87 self,
88 seq_len: int,
89 mm_counts: Mapping[str, int],
90 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
91 ) -> MultiModalDataDict:
92 target_width, target_height = \
93 self.info.get_image_size_with_most_features()
94 num_images = mm_counts.get("image", 0)
95
96 + image_overrides = mm_options.get("image") if mm_options else None
97 +
98 return {
99 "image":
100 self._get_dummy_images(width=target_width,
101 height=target_height,
98 - num_images=num_images)
102 + num_images=num_images,
103 + overrides=image_overrides)
104 }
105
106
vllm/model_executor/models/ovis.py
+6 -1
@@ -28,6 +28,7 @@ from torch.nn.functional import gumbel_softmax, pad, softmax
28 from transformers import BatchFeature, PretrainedConfig
29
30 from vllm.config import VllmConfig
31 +from vllm.config.multimodal import BaseDummyOptions
32 from vllm.model_executor.layers.linear import ReplicatedLinear
33 from vllm.model_executor.layers.quantization import QuantizationConfig
34 from vllm.model_executor.models.aimv2 import AIMv2Model
@@ -283,17 +284,21 @@ class OvisDummyInputsBuilder(BaseDummyInputsBuilder[OvisProcessingInfo]):
284 self,
285 seq_len: int,
286 mm_counts: Mapping[str, int],
287 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
288 ) -> MultiModalDataDict:
289 num_images = mm_counts.get("image", 0)
290
291 target_width, target_height = \
292 self.info.get_image_size_with_most_features()
293
294 + image_overrides = mm_options.get("image") if mm_options else None
295 +
296 mm_data = {
297 "image":
298 self._get_dummy_images(width=target_width,
299 height=target_height,
296 - num_images=num_images),
300 + num_images=num_images,
301 + overrides=image_overrides),
302 }
303 return mm_data
304
vllm/model_executor/models/ovis2_5.py
+9 -1
@@ -10,6 +10,7 @@ import torch.nn as nn
10 from transformers import BaseImageProcessor, BatchFeature, PretrainedConfig
11
12 from vllm.config import VllmConfig
13 +from vllm.config.multimodal import BaseDummyOptions
14 from vllm.model_executor.layers.linear import ReplicatedLinear
15 from vllm.model_executor.layers.quantization import QuantizationConfig
16 from vllm.model_executor.models.ovis import (OvisImagePatchInputs,
@@ -290,6 +291,7 @@ class Ovis2_5DummyInputsBuilder(BaseDummyInputsBuilder[Ovis2_5ProcessingInfo]):
291 self,
292 seq_len: int,
293 mm_counts: Mapping[str, int],
294 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
295 ) -> MultiModalDataDict:
296 num_images = mm_counts.get("image", 0)
297 num_videos = mm_counts.get("video", 0)
@@ -298,17 +300,23 @@ class Ovis2_5DummyInputsBuilder(BaseDummyInputsBuilder[Ovis2_5ProcessingInfo]):
300 self.info.get_image_size_with_most_features()
301 target_num_frames = \
302 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
303 +
304 + image_overrides = mm_options.get("image") if mm_options else None
305 + video_overrides = mm_options.get("video") if mm_options else None
306 +
307 mm_data = {
308 "image":
309 self._get_dummy_images(width=target_width,
310 height=target_height,
305 - num_images=num_images),
311 + num_images=num_images,
312 + overrides=image_overrides),
313 "video":
314 self._get_dummy_videos(
315 width=target_width,
316 height=target_height,
317 num_frames=target_num_frames,
318 num_videos=num_videos,
319 + overrides=video_overrides,
320 )
321 }
322 return mm_data
vllm/model_executor/models/paligemma.py
+6 -1
@@ -8,6 +8,7 @@ from torch import nn
8 from transformers import BatchFeature, PaliGemmaConfig
9
10 from vllm.config import VllmConfig
11 +from vllm.config.multimodal import BaseDummyOptions
12 from vllm.logger import init_logger
13 from vllm.multimodal import MULTIMODAL_REGISTRY
14 from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
@@ -106,6 +107,7 @@ class PaliGemmaDummyInputsBuilder(
107 self,
108 seq_len: int,
109 mm_counts: Mapping[str, int],
110 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
111 ) -> MultiModalDataDict:
112 hf_config = self.info.get_hf_config()
113 vision_config = hf_config.vision_config
@@ -113,11 +115,14 @@ class PaliGemmaDummyInputsBuilder(
115
116 num_images = mm_counts.get("image", 0)
117
118 + image_overrides = mm_options.get("image") if mm_options else None
119 +
120 return {
121 "image":
122 self._get_dummy_images(width=max_image_size,
123 height=max_image_size,
120 - num_images=num_images)
124 + num_images=num_images,
125 + overrides=image_overrides)
126 }
127
128
vllm/model_executor/models/phi3v.py
+6 -1
@@ -25,6 +25,7 @@ from transformers import (BatchFeature, CLIPVisionConfig, PretrainedConfig,
25 ProcessorMixin)
26
27 from vllm.config import VllmConfig
28 +from vllm.config.multimodal import BaseDummyOptions
29 from vllm.logger import init_logger
30 from vllm.model_executor.layers.quantization import QuantizationConfig
31 from vllm.model_executor.layers.vocab_parallel_embedding import (
@@ -356,17 +357,21 @@ class Phi3VDummyInputsBuilder(BaseDummyInputsBuilder[Phi3VProcessingInfo]):
357 self,
358 seq_len: int,
359 mm_counts: Mapping[str, int],
360 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
361 ) -> MultiModalDataDict:
362 num_images = mm_counts.get("image", 0)
363
364 target_width, target_height = \
365 self.info.get_image_size_with_most_features()
366
367 + image_overrides = mm_options.get("image") if mm_options else None
368 +
369 return {
370 "image":
371 self._get_dummy_images(width=target_width,
372 height=target_height,
369 - num_images=num_images)
373 + num_images=num_images,
374 + overrides=image_overrides)
375 }
376
377
vllm/model_executor/models/phi4_multimodal.py
+9 -2
@@ -17,6 +17,7 @@ from transformers.models.phi4_multimodal.modeling_phi4_multimodal import (
17 Phi4MultimodalAudioRelativeAttentionBias, adaptive_enc_mask, unfold_tensor)
18
19 from vllm.config import VllmConfig
20 +from vllm.config.multimodal import BaseDummyOptions
21 from vllm.distributed import (divide, get_tensor_model_parallel_rank,
22 get_tensor_model_parallel_world_size)
23 from vllm.model_executor.layers.activation import MulAndSilu, get_act_fn
@@ -980,6 +981,7 @@ class Phi4MMDummyInputsBuilder(BaseDummyInputsBuilder[Phi4MMProcessingInfo]):
981 self,
982 seq_len: int,
983 mm_counts: Mapping[str, int],
984 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
985 ) -> MultiModalDataDict:
986 num_audios = mm_counts.get("audio", 0)
987 num_images = mm_counts.get("image", 0)
@@ -987,14 +989,19 @@ class Phi4MMDummyInputsBuilder(BaseDummyInputsBuilder[Phi4MMProcessingInfo]):
989 target_width, target_height = \
990 self.info.get_image_size_with_most_features()
991
992 + image_overrides = mm_options.get("image") if mm_options else None
993 + audio_overrides = mm_options.get("audio") if mm_options else None
994 +
995 mm_data = {
996 "image":
997 self._get_dummy_images(width=target_width,
998 height=target_height,
994 - num_images=num_images),
999 + num_images=num_images,
1000 + overrides=image_overrides),
1001 "audio":
1002 self._get_dummy_audios(length=_AUDIO_MAX_SOUNDFILE_SIZE,
997 - num_audios=num_audios),
1003 + num_audios=num_audios,
1004 + overrides=audio_overrides),
1005 }
1006
1007 return mm_data
vllm/model_executor/models/phi4mm.py
+9 -2
@@ -11,6 +11,7 @@ from transformers import (BatchFeature, PretrainedConfig, ProcessorMixin,
11 SequenceFeatureExtractor, SiglipVisionConfig)
12
13 from vllm.config import VllmConfig
14 +from vllm.config.multimodal import BaseDummyOptions
15 from vllm.distributed import get_pp_group
16 from vllm.model_executor.layers.logits_processor import LogitsProcessor
17 from vllm.model_executor.layers.quantization import QuantizationConfig
@@ -749,6 +750,7 @@ class Phi4MMDummyInputsBuilder(BaseDummyInputsBuilder[Phi4MMProcessingInfo]):
750 self,
751 seq_len: int,
752 mm_counts: Mapping[str, int],
753 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
754 ) -> MultiModalDataDict:
755 num_audios = mm_counts.get("audio", 0)
756 num_images = mm_counts.get("image", 0)
@@ -756,14 +758,19 @@ class Phi4MMDummyInputsBuilder(BaseDummyInputsBuilder[Phi4MMProcessingInfo]):
758 target_width, target_height = \
759 self.info.get_image_size_with_most_features()
760
761 + image_overrides = mm_options.get("image") if mm_options else None
762 + audio_overrides = mm_options.get("audio") if mm_options else None
763 +
764 mm_data = {
765 "image":
766 self._get_dummy_images(width=target_width,
767 height=target_height,
763 - num_images=num_images),
768 + num_images=num_images,
769 + overrides=image_overrides),
770 "audio":
771 self._get_dummy_audios(length=_AUDIO_MAX_SOUNDFILE_SIZE,
766 - num_audios=num_audios),
772 + num_audios=num_audios,
773 + overrides=audio_overrides),
774 }
775
776 return mm_data
vllm/model_executor/models/pixtral.py
+8 -2
@@ -24,6 +24,7 @@ from transformers.models.pixtral.modeling_pixtral import (
24 from transformers.tokenization_utils_base import TextInput
25
26 from vllm.config import VllmConfig
27 +from vllm.config.multimodal import BaseDummyOptions
28 from vllm.distributed import divide, get_tensor_model_parallel_world_size
29 from vllm.model_executor.layers.activation import get_act_and_mul_fn
30 from vllm.model_executor.layers.layernorm import RMSNorm
@@ -228,28 +229,33 @@ class PixtralDummyInputsBuilder(BaseDummyInputsBuilder[PixtralProcessingInfo]):
229 self,
230 seq_len: int,
231 mm_counts: Mapping[str, int],
232 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
233 ) -> MultiModalDataDict:
234 num_images = mm_counts.get("image", 0)
235
236 target_width, target_height = \
237 self.info.get_image_size_with_most_features()
238
239 + image_overrides = mm_options.get("image") if mm_options else None
240 +
241 return {
242 "image":
243 self._get_dummy_images(width=target_width,
244 height=target_height,
241 - num_images=num_images)
245 + num_images=num_images,
246 + overrides=image_overrides)
247 }
248
249 def get_dummy_processor_inputs(
250 self,
251 seq_len: int,
252 mm_counts: Mapping[str, int],
253 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
254 ) -> ProcessorInputs:
255 tokenizer = self.info.get_tokenizer()
256
257 dummy_text = self.get_dummy_text(mm_counts)
252 - dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts)
258 + dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts, mm_options)
259 dummy_images = dummy_mm_data.get("image", [])
260 tokenization_kwargs = {"truncation": False}
261
vllm/model_executor/models/qwen2_5_omni_thinker.py
+12 -3
@@ -39,6 +39,7 @@ from transformers.models.qwen2_5_omni.processing_qwen2_5_omni import (
39 from transformers.models.whisper import WhisperFeatureExtractor
40
41 from vllm.config import VllmConfig
42 +from vllm.config.multimodal import BaseDummyOptions
43 from vllm.logger import init_logger
44 from vllm.model_executor.layers.rotary_embedding import MRotaryEmbedding
45 from vllm.model_executor.models.module_mapping import MultiModelKeys
@@ -212,6 +213,7 @@ class Qwen2_5OmniThinkerDummyInputsBuilder(
213 self,
214 seq_len: int,
215 mm_counts: Mapping[str, int],
216 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
217 ) -> MultiModalDataDict:
218 num_audios = mm_counts.get("audio", 0)
219 num_images = mm_counts.get("image", 0)
@@ -228,19 +230,26 @@ class Qwen2_5OmniThinkerDummyInputsBuilder(
230 target_num_frames = \
231 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
232
233 + image_overrides = mm_options.get("image") if mm_options else None
234 + video_overrides = mm_options.get("video") if mm_options else None
235 + audio_overrides = mm_options.get("audio") if mm_options else None
236 +
237 mm_data = {
238 "audio":
239 self._get_dummy_audios(length=target_audio_length,
234 - num_audios=num_audios),
240 + num_audios=num_audios,
241 + overrides=audio_overrides),
242 "image":
243 self._get_dummy_images(width=target_width,
244 height=target_height,
238 - num_images=num_images),
245 + num_images=num_images,
246 + overrides=image_overrides),
247 "video":
248 self._get_dummy_videos(width=target_width,
249 height=target_height,
250 num_frames=target_num_frames,
243 - num_videos=num_videos),
251 + num_videos=num_videos,
252 + overrides=video_overrides),
253 }
254
255 return mm_data
vllm/model_executor/models/qwen2_audio.py
+7 -1
@@ -34,6 +34,7 @@ from transformers.models.qwen2_audio import (Qwen2AudioConfig,
34 from transformers.models.whisper import WhisperFeatureExtractor
35
36 from vllm.config import VllmConfig
37 +from vllm.config.multimodal import BaseDummyOptions
38 from vllm.multimodal import MULTIMODAL_REGISTRY
39 from vllm.multimodal.inputs import (AudioItem, ModalityData,
40 MultiModalDataDict, MultiModalFieldConfig,
@@ -144,6 +145,7 @@ class Qwen2AudioDummyInputsBuilder(
145 self,
146 seq_len: int,
147 mm_counts: Mapping[str, int],
148 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
149 ) -> MultiModalDataDict:
150 feature_extractor = self.info.get_feature_extractor()
151
@@ -151,9 +153,13 @@ class Qwen2AudioDummyInputsBuilder(
153 audio_len = feature_extractor.chunk_length * sampling_rate
154 num_audios = mm_counts.get("audio", 0)
155
156 + audio_overrides = mm_options.get("audio") if mm_options else None
157 +
158 return {
159 "audio":
156 - self._get_dummy_audios(length=audio_len, num_audios=num_audios)
160 + self._get_dummy_audios(length=audio_len,
161 + num_audios=num_audios,
162 + overrides=audio_overrides)
163 }
164
165
vllm/model_executor/models/qwen2_vl.py
+8 -1
@@ -45,6 +45,7 @@ from vllm.attention.backends.registry import _Backend
45 from vllm.attention.layer import (check_upstream_fa_availability,
46 maybe_get_vit_flash_attn_backend)
47 from vllm.config import VllmConfig
48 +from vllm.config.multimodal import BaseDummyOptions
49 from vllm.distributed import parallel_state, tensor_model_parallel_all_gather
50 from vllm.distributed import utils as dist_utils
51 from vllm.logger import init_logger
@@ -1034,6 +1035,7 @@ class Qwen2VLDummyInputsBuilder(BaseDummyInputsBuilder[Qwen2VLProcessingInfo]):
1035 self,
1036 seq_len: int,
1037 mm_counts: Mapping[str, int],
1038 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
1039 ) -> MultiModalDataDict:
1040 num_images = mm_counts.get("image", 0)
1041 num_videos = mm_counts.get("video", 0)
@@ -1043,17 +1045,22 @@ class Qwen2VLDummyInputsBuilder(BaseDummyInputsBuilder[Qwen2VLProcessingInfo]):
1045 target_num_frames = \
1046 self.info.get_num_frames_with_most_features(seq_len, mm_counts)
1047
1048 + image_overrides = mm_options.get("image") if mm_options else None
1049 + video_overrides = mm_options.get("video") if mm_options else None
1050 +
1051 return {
1052 "image":
1053 self._get_dummy_images(width=target_width,
1054 height=target_height,
1050 - num_images=num_images),
1055 + num_images=num_images,
1056 + overrides=image_overrides),
1057 "video":
1058 self._get_dummy_videos(
1059 width=target_width,
1060 height=target_height,
1061 num_frames=target_num_frames,
1062 num_videos=num_videos,
1063 + overrides=video_overrides,
1064 )
1065 }
1066
vllm/model_executor/models/qwen3_vl.py
+9 -1
@@ -47,6 +47,7 @@ from vllm.attention.backends.registry import _Backend
47 from vllm.attention.layer import check_upstream_fa_availability
48 from vllm.compilation.decorators import support_torch_compile
49 from vllm.config import VllmConfig
50 +from vllm.config.multimodal import BaseDummyOptions
51 from vllm.distributed import get_pp_group
52 from vllm.logger import init_logger
53 from vllm.model_executor.layers.activation import _ACTIVATION_REGISTRY
@@ -736,6 +737,7 @@ class Qwen3VLDummyInputsBuilder(BaseDummyInputsBuilder[Qwen3VLProcessingInfo]):
737 self,
738 seq_len: int,
739 mm_counts: Mapping[str, int],
740 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
741 ) -> MultiModalDataDict:
742 num_images = mm_counts.get("image", 0)
743 num_videos = mm_counts.get("video", 0)
@@ -750,17 +752,23 @@ class Qwen3VLDummyInputsBuilder(BaseDummyInputsBuilder[Qwen3VLProcessingInfo]):
752 num_frames=target_num_frames,
753 image_processor=self.info.get_video_processor(),
754 )
755 +
756 + image_overrides = mm_options.get("image") if mm_options else None
757 + video_overrides = mm_options.get("video") if mm_options else None
758 +
759 return {
760 "image":
761 self._get_dummy_images(width=target_width,
762 height=target_height,
757 - num_images=num_images),
763 + num_images=num_images,
764 + overrides=image_overrides),
765 "video":
766 self._get_dummy_videos(
767 width=target_video_size.width,
768 height=target_video_size.height,
769 num_frames=target_num_frames,
770 num_videos=num_videos,
771 + overrides=video_overrides,
772 ),
773 }
774
vllm/model_executor/models/qwen_vl.py
+6 -1
@@ -24,6 +24,7 @@ from transformers.image_utils import ImageInput
24 from transformers.tokenization_utils_base import TextInput
25
26 from vllm.config import VllmConfig
27 +from vllm.config.multimodal import BaseDummyOptions
28 from vllm.model_executor.layers.activation import get_act_fn
29 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
30 ReplicatedLinear,
@@ -567,6 +568,7 @@ class QwenVLDummyInputsBuilder(BaseDummyInputsBuilder[QwenVLProcessingInfo]):
568 self,
569 seq_len: int,
570 mm_counts: Mapping[str, int],
571 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
572 ) -> MultiModalDataDict:
573 hf_config = self.info.get_hf_config()
574 vision_config = hf_config.visual
@@ -574,11 +576,14 @@ class QwenVLDummyInputsBuilder(BaseDummyInputsBuilder[QwenVLProcessingInfo]):
576 target_width = target_height = vision_config["image_size"]
577 num_images = mm_counts.get("image", 0)
578
579 + image_overrides = mm_options.get("image") if mm_options else None
580 +
581 return {
582 "image":
583 self._get_dummy_images(width=target_width,
584 height=target_height,
581 - num_images=num_images)
585 + num_images=num_images,
586 + overrides=image_overrides)
587 }
588
589
vllm/model_executor/models/rvl.py
+7 -1
@@ -2,12 +2,14 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Mapping
5 +from typing import Optional
6
7 import torch
8 import torch.nn as nn
9 from transformers.activations import GELUActivation
10
11 from vllm.config import VllmConfig
12 +from vllm.config.multimodal import BaseDummyOptions
13 from vllm.multimodal import MULTIMODAL_REGISTRY
14 from vllm.multimodal.inputs import MultiModalDataDict
15
@@ -38,17 +40,21 @@ class RVLDummyInputsBuilder(LlavaDummyInputsBuilder[RVLProcessingInfo]):
40 self,
41 seq_len: int,
42 mm_counts: Mapping[str, int],
43 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
44 ) -> MultiModalDataDict:
45 num_images = mm_counts.get("image", 0)
46
47 target_width, target_height = (
48 self.info.get_image_size_with_most_features())
49
50 + image_overrides = mm_options.get("image") if mm_options else None
51 +
52 return {
53 "image":
54 self._get_dummy_images(width=target_width,
55 height=target_height,
51 - num_images=num_images),
56 + num_images=num_images,
57 + overrides=image_overrides),
58 }
59
60
vllm/model_executor/models/skyworkr1v.py
+6 -1
@@ -17,6 +17,7 @@ from PIL import Image
17 from transformers import BatchFeature, PretrainedConfig, TensorType
18
19 from vllm.config import VllmConfig
20 +from vllm.config.multimodal import BaseDummyOptions
21 from vllm.model_executor.layers.linear import ReplicatedLinear
22 from vllm.model_executor.layers.quantization import QuantizationConfig
23 from vllm.model_executor.layers.quantization.awq import AWQConfig
@@ -505,16 +506,20 @@ class SkyworkR1VDummyInputsBuilder(
506 self,
507 seq_len: int,
508 mm_counts: Mapping[str, int],
509 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
510 ) -> MultiModalDataDict:
511 target_width, target_height = \
512 self.info.get_image_size_with_most_features()
513 num_images = mm_counts.get("image", 0)
514
515 + image_overrides = mm_options.get("image") if mm_options else None
516 +
517 return {
518 "image":
519 self._get_dummy_images(width=target_width,
520 height=target_height,
517 - num_images=num_images)
521 + num_images=num_images,
522 + overrides=image_overrides)
523 }
524
525
vllm/model_executor/models/step3_vl.py
+6 -1
@@ -17,6 +17,7 @@ from transformers import BatchFeature, PretrainedConfig, TensorType
17
18 from vllm.attention.layer import MultiHeadAttention
19 from vllm.config import VllmConfig
20 +from vllm.config.multimodal import BaseDummyOptions
21 from vllm.distributed import get_tensor_model_parallel_world_size
22 from vllm.model_executor.layers.activation import get_act_fn
23 from vllm.model_executor.layers.linear import (ColumnParallelLinear,
@@ -496,16 +497,20 @@ class Step3VLDummyInputsBuilder(BaseDummyInputsBuilder[Step3VLProcessingInfo]):
497 self,
498 seq_len: int,
499 mm_counts: Mapping[str, int],
500 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
501 ) -> MultiModalDataDict:
502 target_width, target_height = \
503 self.info.get_image_size_with_most_features()
504 num_images = mm_counts.get("image", 0)
505
506 + image_overrides = mm_options.get("image") if mm_options else None
507 +
508 return {
509 "image":
510 self._get_dummy_images(width=target_width,
511 height=target_height,
508 - num_images=num_images)
512 + num_images=num_images,
513 + overrides=image_overrides)
514 }
515
516
vllm/model_executor/models/terratorch.py
+11
@@ -28,6 +28,8 @@ from terratorch.vllm import (DummyDataGenerator, InferenceRunner,
28 from transformers import BatchFeature
29
30 from vllm.config import VllmConfig
31 +from vllm.config.multimodal import BaseDummyOptions
32 +from vllm.logger import init_logger
33 from vllm.model_executor.layers.pooler import DispatchPooler, Pooler
34 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
35 from vllm.model_executor.models.utils import AutoWeightsLoader
@@ -48,6 +50,8 @@ from .interfaces import (IsAttentionFree, MultiModalEmbeddings,
50 SupportsMultiModal)
51 from .interfaces_base import default_pooling_type
52
53 +logger = init_logger(__name__)
54 +
55
56 def _terratorch_field_names(pretrained_cfg: dict):
57 input_definition = InputDefinition(**pretrained_cfg["input"])
@@ -97,9 +101,16 @@ class TerratorchInputBuilder(BaseDummyInputsBuilder[TerratorchProcessingInfo]):
101 self,
102 seq_len: int,
103 mm_counts: Mapping[str, int],
104 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
105 ) -> MultiModalDataDict:
106 # Dummy data is generated based on the 'input' section
107 # defined in the HF configuration file
108 +
109 + if mm_options:
110 + logger.warning("Configurable multimodal profiling "
111 + "options are not supported for Terratorch. "
112 + "They are ignored for now.")
113 +
114 return self.dummy_data_generator.get_dummy_mm_data()
115
116
vllm/model_executor/models/transformers.py
+6 -1
@@ -33,6 +33,7 @@ from vllm.attention import Attention, AttentionType
33 from vllm.compilation.decorators import support_torch_compile
34 from vllm.config import (CacheConfig, DeviceConfig, ModelConfig,
35 ParallelConfig, VllmConfig)
36 +from vllm.config.multimodal import BaseDummyOptions
37 from vllm.config.utils import getattr_iter
38 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
39 from vllm.distributed.utils import get_pp_indices
@@ -285,16 +286,20 @@ class MultiModalDummyInputsBuilder(
286 self,
287 seq_len: int,
288 mm_counts: Mapping[str, int],
289 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
290 ) -> MultiModalDataDict:
291 num_images = mm_counts.get("image", 0)
292
293 target_width, target_height = self.info.get_max_image_size()
294
295 + image_overrides = mm_options.get("image") if mm_options else None
296 +
297 return {
298 "image":
299 self._get_dummy_images(width=target_width,
300 height=target_height,
297 - num_images=num_images),
301 + num_images=num_images,
302 + overrides=image_overrides),
303 }
304
305
vllm/model_executor/models/ultravox.py
+7 -1
@@ -14,6 +14,7 @@ from transformers.models.whisper import WhisperFeatureExtractor
14 from transformers.models.whisper.modeling_whisper import WhisperEncoder
15
16 from vllm.config import VllmConfig
17 +from vllm.config.multimodal import BaseDummyOptions
18 from vllm.model_executor.layers.activation import MulAndSilu, get_act_fn
19 from vllm.model_executor.layers.layernorm import RMSNorm
20 from vllm.model_executor.model_loader import DefaultModelLoader
@@ -114,6 +115,7 @@ class UltravoxDummyInputsBuilder(BaseDummyInputsBuilder[UltravoxProcessingInfo]
115 self,
116 seq_len: int,
117 mm_counts: Mapping[str, int],
118 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
119 ) -> MultiModalDataDict:
120 feature_extractor = self.info.get_feature_extractor()
121
@@ -122,9 +124,13 @@ class UltravoxDummyInputsBuilder(BaseDummyInputsBuilder[UltravoxProcessingInfo]
124 _MAX_ENCODER_BATCH_SIZE)
125 num_audios = mm_counts.get("audio", 0)
126
127 + audio_overrides = mm_options.get("audio") if mm_options else None
128 +
129 return {
130 "audio":
127 - self._get_dummy_audios(length=audio_len, num_audios=num_audios)
131 + self._get_dummy_audios(length=audio_len,
132 + num_audios=num_audios,
133 + overrides=audio_overrides)
134 }
135
136
vllm/model_executor/models/voxtral.py
+9 -2
@@ -21,6 +21,7 @@ from transformers import BatchFeature, TensorType, WhisperConfig
21 from transformers.tokenization_utils_base import TextInput
22
23 from vllm.config import ModelConfig, SpeechToTextConfig, VllmConfig
24 +from vllm.config.multimodal import BaseDummyOptions
25 from vllm.inputs.data import PromptType
26 from vllm.logger import init_logger
27 from vllm.model_executor.layers.quantization import QuantizationConfig
@@ -204,25 +205,31 @@ class VoxtralDummyInputsBuilder(BaseDummyInputsBuilder[VoxtralProcessingInfo]):
205 self,
206 seq_len: int,
207 mm_counts: Mapping[str, int],
208 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
209 ) -> MultiModalDataDict:
210 num_audios = mm_counts.get("audio", 0)
211
212 target_length = self.info.get_max_audio_array_len()
213
214 + audio_overrides = mm_options.get("audio") if mm_options else None
215 +
216 return {
217 "audio":
214 - self._get_dummy_audios(length=target_length, num_audios=num_audios)
218 + self._get_dummy_audios(length=target_length,
219 + num_audios=num_audios,
220 + overrides=audio_overrides)
221 }
222
223 def get_dummy_processor_inputs(
224 self,
225 seq_len: int,
226 mm_counts: Mapping[str, int],
227 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
228 ) -> ProcessorInputs:
229 tokenizer = self.info.get_tokenizer()
230
231 dummy_text = self.get_dummy_text(mm_counts)
225 - dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts)
232 + dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts, mm_options)
233 dummy_audios = dummy_mm_data.get("audio", [])
234
235 audio_chunks: list[AudioChunk] = []
vllm/model_executor/models/whisper.py
+7 -1
@@ -18,6 +18,7 @@ from vllm.attention.layer import MultiHeadAttention
18 from vllm.attention.layers.cross_attention import CrossAttention
19 from vllm.config import (CacheConfig, ModelConfig, SpeechToTextConfig,
20 VllmConfig)
21 +from vllm.config.multimodal import BaseDummyOptions
22 from vllm.distributed import get_tensor_model_parallel_world_size
23 from vllm.inputs.data import PromptType
24 from vllm.logger import init_logger
@@ -691,6 +692,7 @@ class WhisperDummyInputsBuilder(BaseDummyInputsBuilder[WhisperProcessingInfo]):
692 self,
693 seq_len: int,
694 mm_counts: Mapping[str, int],
695 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
696 ) -> MultiModalDataDict:
697 feature_extractor = self.info.get_feature_extractor()
698
@@ -698,9 +700,13 @@ class WhisperDummyInputsBuilder(BaseDummyInputsBuilder[WhisperProcessingInfo]):
700 audio_len = feature_extractor.chunk_length * sampling_rate
701 num_audios = mm_counts.get("audio", 0)
702
703 + audio_overrides = mm_options.get("audio") if mm_options else None
704 +
705 return {
706 "audio":
703 - self._get_dummy_audios(length=audio_len, num_audios=num_audios)
707 + self._get_dummy_audios(length=audio_len,
708 + num_audios=num_audios,
709 + overrides=audio_overrides)
710 }
711
712
vllm/multimodal/profiling.py
+75 -5
@@ -10,6 +10,8 @@ import numpy.typing as npt
10 from PIL import Image
11
12 import vllm.envs as envs
13 +from vllm.config.multimodal import (AudioDummyOptions, BaseDummyOptions,
14 + ImageDummyOptions, VideoDummyOptions)
15 from vllm.logger import init_logger
16
17 from .inputs import (MultiModalDataDict, MultiModalEncDecInputs,
@@ -73,10 +75,19 @@ class BaseDummyInputsBuilder(ABC, Generic[_I]):
75 self,
76 seq_len: int,
77 mm_counts: Mapping[str, int],
78 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
79 ) -> MultiModalDataDict:
80 """
81 Build the multimodal input which, after processing, results in
82 the maximum possible number of placeholder tokens.
83 +
84 + Args:
85 + seq_len: Sequence length
86 + mm_counts: Count of items per modality
87 + mm_options: Configurable options per modality (optional).
88 + If None, use model defaults for backward compatibility.
89 + If provided, models can use these to customize dummy
90 + data generation.
91 """
92 raise NotImplementedError
93
@@ -84,13 +95,22 @@ class BaseDummyInputsBuilder(ABC, Generic[_I]):
95 self,
96 seq_len: int,
97 mm_counts: Mapping[str, int],
98 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
99 ) -> ProcessorInputs:
100 """
101 Build the input which, after processing, results in
102 the maximum possible number of placeholder tokens.
103 +
104 + Args:
105 + seq_len: Sequence length
106 + mm_counts: Count of items per modality
107 + mm_options: Configurable options per modality (optional)
108 """
109 dummy_text = self.get_dummy_text(mm_counts)
93 - dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts)
110 +
111 + # Use the unified function for both legacy and configurable cases
112 + dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts, mm_options)
113 +
114 tokenization_kwargs = {"truncation": False}
115
116 return ProcessorInputs(prompt=dummy_text,
@@ -102,9 +122,17 @@ class BaseDummyInputsBuilder(ABC, Generic[_I]):
122 *,
123 length: int,
124 num_audios: int,
125 + overrides: Optional[AudioDummyOptions] = None,
126 ) -> list[npt.NDArray]:
127 if num_audios == 0:
128 return []
129 + if overrides and overrides.length:
130 + if overrides.length > length:
131 + logger.warning(
132 + "audio.length override (%d) exceeds model's "
133 + "maximum length (%d), will be ignored", overrides.length,
134 + length)
135 + length = min(length, overrides.length)
136 audio = np.zeros((length, ))
137 return [audio] * num_audios
138
@@ -114,9 +142,25 @@ class BaseDummyInputsBuilder(ABC, Generic[_I]):
142 width: int,
143 height: int,
144 num_images: int,
145 + overrides: Optional[ImageDummyOptions] = None,
146 ) -> list[Image.Image]:
147 if num_images == 0:
148 return []
149 + if overrides:
150 + if overrides.width:
151 + if overrides.width > width:
152 + logger.warning(
153 + "image.width override (%d) exceeds model's "
154 + "maximum width (%d), will be ignored", overrides.width,
155 + width)
156 + width = min(width, overrides.width)
157 + if overrides.height:
158 + if overrides.height > height:
159 + logger.warning(
160 + "image.height override (%d) exceeds model's "
161 + "maximum height (%d), will be ignored",
162 + overrides.height, height)
163 + height = min(height, overrides.height)
164 image = Image.new("RGB", (width, height), color=255)
165 return [image] * num_images
166
@@ -127,9 +171,32 @@ class BaseDummyInputsBuilder(ABC, Generic[_I]):
171 height: int,
172 num_frames: int,
173 num_videos: int,
174 + overrides: Optional[VideoDummyOptions] = None,
175 ) -> list[npt.NDArray]:
176 if num_videos == 0:
177 return []
178 + if overrides:
179 + if overrides.num_frames:
180 + if overrides.num_frames > num_frames:
181 + logger.warning(
182 + "video.num_frames override (%d) exceeds model's "
183 + "maximum number of frames (%d), will be ignored",
184 + overrides.num_frames, num_frames)
185 + num_frames = min(num_frames, overrides.num_frames)
186 + if overrides.width:
187 + if overrides.width > width:
188 + logger.warning(
189 + "video.width override (%d) exceeds model's "
190 + "maximum width (%d), will be ignored", overrides.width,
191 + width)
192 + width = min(width, overrides.width)
193 + if overrides.height:
194 + if overrides.height > height:
195 + logger.warning(
196 + "video.height override (%d) exceeds model's "
197 + "maximum height (%d), will be ignored",
198 + overrides.height, height)
199 + height = min(height, overrides.height)
200 video = np.full((num_frames, width, height, 3), 255)
201 return [video] * num_videos
202
@@ -162,13 +229,14 @@ class MultiModalProfiler(Generic[_I]):
229 self,
230 seq_len: int,
231 mm_counts: Optional[Mapping[str, int]] = None,
232 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
233 ) -> MultiModalInputs:
234 if mm_counts is None:
235 mm_counts = self.get_mm_limits()
236
237 factory = self.dummy_inputs
238 processor_inputs = factory.get_dummy_processor_inputs(
171 - seq_len, mm_counts)
239 + seq_len, mm_counts, mm_options)
240
241 return self.processor.apply(
242 prompt=processor_inputs.prompt,
@@ -195,8 +263,9 @@ class MultiModalProfiler(Generic[_I]):
263 self,
264 seq_len: int,
265 mm_counts: Optional[Mapping[str, int]] = None,
266 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
267 ) -> DummyEncoderData:
199 - mm_inputs = self._get_dummy_mm_inputs(seq_len, mm_counts)
268 + mm_inputs = self._get_dummy_mm_inputs(seq_len, mm_counts, mm_options)
269 mm_inputs = cast(MultiModalEncDecInputs, mm_inputs)
270
271 # For encoder-decoder models, use encoder prompt token ids instead of
@@ -228,8 +297,9 @@ class MultiModalProfiler(Generic[_I]):
297 self,
298 seq_len: int,
299 mm_counts: Optional[Mapping[str, int]] = None,
300 + mm_options: Optional[Mapping[str, BaseDummyOptions]] = None,
301 ) -> DummyDecoderData:
232 - mm_inputs = self._get_dummy_mm_inputs(seq_len, mm_counts)
302 + mm_inputs = self._get_dummy_mm_inputs(seq_len, mm_counts, mm_options)
303
304 prompt_token_ids = mm_inputs["prompt_token_ids"]
305 total_len = len(prompt_token_ids)
@@ -274,7 +344,7 @@ class MultiModalProfiler(Generic[_I]):
344
345 `<im_start> [IMG] [IMG] [IMG] <row_break> [IMG] [IMG] [IMG] <im_end>`
346 Returns 9, even when the number of image embeddings is 6.
277 -
347 +
348 This is important to take into account when profiling and
349 initializing the encoder cache size.
350 """
vllm/multimodal/registry.py
+44 -7
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Generic, Optional, Protocol, TypeVar
6
7 import torch.nn as nn
8
9 +from vllm.config.multimodal import BaseDummyOptions
10 from vllm.logger import init_logger
11 from vllm.transformers_utils.tokenizer import (AnyTokenizer,
12 cached_tokenizer_from_config)
@@ -52,7 +53,7 @@ class DummyInputsBuilderFactory(Protocol[_I]): # type: ignore[misc]
53 ...
54
55
55 -class MultiModalProcessorFactory(Protocol[_I]):
56 +class MultiModalProcessorFactory(Protocol[_I]): # type: ignore[misc]
57 """
58 Constructs a
59 [`BaseMultiModalProcessor`][vllm.multimodal.processing.BaseMultiModalProcessor]
@@ -95,6 +96,28 @@ class MultiModalRegistry:
96 self._processor_factories = ClassRegistry[nn.Module,
97 _ProcessorFactories]()
98
99 + def _extract_mm_options(
100 + self,
101 + model_config: "ModelConfig",
102 + ) -> Optional[Mapping[str, BaseDummyOptions]]:
103 + """
104 + Extract multimodal dummy options from model config.
105 +
106 + Returns None if no configurable options are found, otherwise returns
107 + a mapping of modality names to their dummy options.
108 + """
109 + if not model_config.multimodal_config:
110 + return None
111 +
112 + mm_options = {
113 + m: opt
114 + for m in model_config.multimodal_config.limit_per_prompt
115 + if (opt := model_config.multimodal_config.get_dummy_options(m)
116 + ) is not None
117 + }
118 +
119 + return mm_options if len(mm_options) > 0 else None
120 +
121 def supports_multimodal_inputs(self, model_config: "ModelConfig") -> bool:
122 """
123 Checks if the model supports multimodal inputs.
@@ -135,7 +158,7 @@ class MultiModalRegistry:
158 return {}
159
160 processor = self.create_processor(model_config, cache=cache)
138 - profiler = MultiModalProfiler(processor)
161 + profiler: MultiModalProfiler = MultiModalProfiler(processor)
162
163 seq_len = model_config.max_model_len
164 mm_limits = self.get_mm_limits_per_prompt(model_config, cache=cache)
@@ -189,7 +212,7 @@ class MultiModalRegistry:
212 return {}
213
214 processor = self.create_processor(model_config, cache=cache)
192 - profiler = MultiModalProfiler(processor)
215 + profiler: MultiModalProfiler = MultiModalProfiler(processor)
216 return profiler.get_mm_limits()
217
218 def register_processor(
@@ -285,8 +308,15 @@ class MultiModalRegistry:
308 The model is identified by ``model_config``.
309 """
310 processor = self.create_processor(model_config, cache=cache)
288 - profiler = MultiModalProfiler(processor)
289 - dummy_data = profiler.get_decoder_dummy_data(seq_len, mm_counts)
311 + profiler: MultiModalProfiler = MultiModalProfiler(processor)
312 +
313 + # Extract configurable options from multimodal config.
314 + # Only include modalities that use advanced option types so legacy
315 + # count-only behavior remains unchanged.
316 + mm_options = self._extract_mm_options(model_config)
317 +
318 + dummy_data = profiler.get_decoder_dummy_data(seq_len, mm_counts,
319 + mm_options)
320
321 # Having more tokens is over-conservative but otherwise fine
322 token_ids = dummy_data.prompt_token_ids
@@ -311,8 +341,15 @@ class MultiModalRegistry:
341 The model is identified by ``model_config``.
342 """
343 processor = self.create_processor(model_config, cache=cache)
314 - profiler = MultiModalProfiler(processor)
315 - dummy_data = profiler.get_encoder_dummy_data(seq_len, mm_counts)
344 + profiler: MultiModalProfiler = MultiModalProfiler(processor)
345 +
346 + # Extract configurable options from multimodal config.
347 + # Only include modalities that use advanced option types so legacy
348 + # count-only behavior remains unchanged.
349 + mm_options = self._extract_mm_options(model_config)
350 +
351 + dummy_data = profiler.get_encoder_dummy_data(seq_len, mm_counts,
352 + mm_options)
353
354 # Having more tokens is over-conservative but otherwise fine
355 token_ids = dummy_data.prompt_token_ids