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

[Multimodal] Consolidate mm inputs into MultiModalFeatureSpec (#23779)

Signed-off-by: sfeng33 <4florafeng@gmail.com>

Flora Feng committed Aug 29, 2025 at 03:36 UTC 69f46359dd5b36c1a059a0a8b729be1bd86394e8
16 files changed +143 -146
tests/tokenization/test_detokenize.py
-2
@@ -64,8 +64,6 @@ def _run_incremental_decode(tokenizer,
64 request = EngineCoreRequest("",
65 prompt_token_ids,
66 None,
67 - None,
68 - None,
67 params,
68 None,
69 None,
tests/v1/core/test_kv_cache_utils.py
+13 -9
@@ -7,7 +7,8 @@ import pytest
7 import torch
8
9 from vllm.config import ModelConfig, SchedulerConfig, VllmConfig
10 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
10 +from vllm.multimodal.inputs import (MultiModalFeatureSpec,
11 + MultiModalKwargsItem, PlaceholderRange)
12 from vllm.sampling_params import SamplingParams
13 from vllm.utils import GiB_bytes, sha256, sha256_cbor_64bit
14 from vllm.v1.core.kv_cache_manager import KVCacheManager
@@ -37,17 +38,20 @@ def make_request(
38 mm_hashes: Optional[list[str]] = None,
39 cache_salt: Optional[str] = None,
40 ):
40 - if mm_positions is None:
41 - mm_kwargs = None
42 - else:
43 - mm_item = MultiModalKwargsItem.dummy("dummy_m")
44 - mm_kwargs = [mm_item] * len(mm_positions)
41 + mm_features = []
42 + if mm_positions is not None:
43 + for j, position in enumerate(mm_positions):
44 + identifier = mm_hashes[j] if mm_hashes else f"hash_{j}"
45 + mm_feature = MultiModalFeatureSpec(
46 + data=MultiModalKwargsItem.dummy("dummy_m"),
47 + mm_position=position,
48 + identifier=identifier,
49 + modality="image")
50 + mm_features.append(mm_feature)
51
52 return Request(request_id=request_id,
53 prompt_token_ids=prompt_token_ids,
48 - multi_modal_kwargs=mm_kwargs,
49 - multi_modal_hashes=mm_hashes,
50 - multi_modal_placeholders=mm_positions,
54 + mm_features=mm_features if mm_features else None,
55 sampling_params=SamplingParams(max_tokens=17),
56 pooling_params=None,
57 eos_token_id=100,
tests/v1/core/test_prefix_caching.py
+13 -9
@@ -9,7 +9,8 @@ import pytest
9 import torch
10
11 from vllm.distributed.kv_events import AllBlocksCleared, BlockRemoved
12 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
12 +from vllm.multimodal.inputs import (MultiModalFeatureSpec,
13 + MultiModalKwargsItem, PlaceholderRange)
14 from vllm.sampling_params import SamplingParams
15 from vllm.utils import sha256, sha256_cbor_64bit
16 from vllm.v1.core.block_pool import BlockPool
@@ -32,17 +33,20 @@ def make_request(
33 prompt_logprobs: Optional[int] = None,
34 cache_salt: Optional[str] = None,
35 ):
35 - if mm_positions is None:
36 - mm_kwargs = None
37 - else:
38 - mm_item = MultiModalKwargsItem.dummy("dummy_m")
39 - mm_kwargs = [mm_item] * len(mm_positions)
36 + mm_features = []
37 + if mm_positions is not None:
38 + for j, position in enumerate(mm_positions):
39 + identifier = mm_hashes[j] if mm_hashes else f"hash_{j}"
40 + mm_feature = MultiModalFeatureSpec(
41 + data=MultiModalKwargsItem.dummy("dummy_m"),
42 + mm_position=position,
43 + identifier=identifier,
44 + modality="image")
45 + mm_features.append(mm_feature)
46
47 return Request(request_id=request_id,
48 prompt_token_ids=prompt_token_ids,
43 - multi_modal_kwargs=mm_kwargs,
44 - multi_modal_hashes=mm_hashes,
45 - multi_modal_placeholders=mm_positions,
49 + mm_features=mm_features if mm_features else None,
50 sampling_params=SamplingParams(
51 max_tokens=17, prompt_logprobs=prompt_logprobs),
52 pooling_params=None,
tests/v1/core/test_scheduler.py
+14 -12
@@ -8,7 +8,8 @@ import torch
8
9 from vllm.config import (CacheConfig, KVTransferConfig, ModelConfig,
10 SchedulerConfig, SpeculativeConfig, VllmConfig)
11 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
11 +from vllm.multimodal.inputs import (MultiModalFeatureSpec,
12 + MultiModalKwargsItem, PlaceholderRange)
13 from vllm.sampling_params import GuidedDecodingParams, SamplingParams
14 from vllm.v1.core.sched.output import CachedRequestData, SchedulerOutput
15 from vllm.v1.core.sched.scheduler import Scheduler
@@ -1308,21 +1309,24 @@ def create_requests_with_priority(
1309 prompt_logprobs=prompt_logprobs)
1310 requests = []
1311 for i in range(num_requests):
1312 + mm_features = []
1313 if mm_positions is not None:
1314 mm_position = mm_positions[i]
1313 - mm_item = MultiModalKwargsItem.dummy("dummy_m")
1314 - mm_kwargs = [mm_item] * len(mm_position)
1315 - else:
1316 - mm_position = None
1317 - mm_kwargs = None
1315 + for j, position in enumerate(mm_position):
1316 + identifier = f"hash{i}_{j}"
1317 + mm_feature = MultiModalFeatureSpec(
1318 + data=MultiModalKwargsItem.dummy("dummy_m"),
1319 + mm_position=position,
1320 + identifier=identifier,
1321 + modality="image")
1322 + mm_features.append(mm_feature)
1323 +
1324 request = Request(
1325 request_id=f"{i + starting_idx}",
1326 prompt_token_ids=[i + starting_idx] * num_tokens,
1327 sampling_params=sampling_params,
1328 pooling_params=None,
1323 - multi_modal_kwargs=mm_kwargs,
1324 - multi_modal_placeholders=mm_position,
1325 - multi_modal_hashes=None,
1329 + mm_features=mm_features if mm_features else None,
1330 eos_token_id=EOS_TOKEN_ID,
1331 arrival_time=arrival_times[i],
1332 priority=priorities[i],
@@ -1801,9 +1805,7 @@ def test_schedule_skip_tokenizer_init_structured_output_request():
1805 request = Request(
1806 request_id="0",
1807 prompt_token_ids=[0, 1],
1804 - multi_modal_kwargs=None,
1805 - multi_modal_hashes=None,
1806 - multi_modal_placeholders=None,
1808 + mm_features=None,
1809 sampling_params=sampling_params,
1810 pooling_params=None,
1811 eos_token_id=EOS_TOKEN_ID,
tests/v1/core/utils.py
+15 -15
@@ -6,7 +6,8 @@ import torch
6
7 from vllm.config import (CacheConfig, KVTransferConfig, ModelConfig,
8 SchedulerConfig, SpeculativeConfig, VllmConfig)
9 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
9 +from vllm.multimodal.inputs import (MultiModalFeatureSpec,
10 + MultiModalKwargsItem, PlaceholderRange)
11 from vllm.sampling_params import SamplingParams
12 from vllm.v1.core.kv_cache_utils import (get_request_block_hasher,
13 init_none_hash)
@@ -139,19 +140,20 @@ def create_requests(
140 prompt_logprobs=prompt_logprobs)
141 requests = []
142 for i in range(num_requests):
143 + mm_features = []
144 if mm_positions is not None:
145 mm_position = mm_positions[i]
144 - mm_item = MultiModalKwargsItem.dummy("dummy_m")
145 - mm_kwargs = [mm_item] * len(mm_position)
146 - # Dummy hash for each mm item should be unique
147 - # since encoder cache tracks entries by hash
148 - mm_hashes = [
149 - "hash" + str(i) + "_" + str(j) for j in range(len(mm_position))
150 - ]
151 - else:
152 - mm_position = None
153 - mm_kwargs = None
154 - mm_hashes = None
146 + for j, position in enumerate(mm_position):
147 + # Dummy hash for each mm item should be unique
148 + # since encoder cache tracks entries by hash
149 + identifier = f"hash{i}_{j}"
150 + mm_feature = MultiModalFeatureSpec(
151 + data=MultiModalKwargsItem.dummy("dummy_m"),
152 + mm_position=position,
153 + identifier=identifier,
154 + modality="image")
155 + mm_features.append(mm_feature)
156 +
157 prompt_token_ids = ([0] * num_tokens if same_prompt else [i] *
158 num_tokens)
159 request = Request(
@@ -159,9 +161,7 @@ def create_requests(
161 prompt_token_ids=prompt_token_ids,
162 sampling_params=sampling_params,
163 pooling_params=None,
162 - multi_modal_kwargs=mm_kwargs,
163 - multi_modal_placeholders=mm_position,
164 - multi_modal_hashes=mm_hashes,
164 + mm_features=mm_features if mm_features else None,
165 eos_token_id=EOS_TOKEN_ID,
166 block_hasher=block_hasher,
167 )
tests/v1/engine/test_engine_core.py
+1 -3
@@ -35,9 +35,7 @@ def make_request() -> EngineCoreRequest:
35 return EngineCoreRequest(
36 request_id=str(uuid.uuid4()),
37 prompt_token_ids=PROMPT_TOKENS,
38 - mm_kwargs=None,
39 - mm_hashes=None,
40 - mm_placeholders=None,
38 + mm_features=None,
39 sampling_params=SamplingParams(),
40 pooling_params=None,
41 eos_token_id=None,
tests/v1/engine/test_engine_core_client.py
+1 -3
@@ -52,9 +52,7 @@ def make_request(
52 return EngineCoreRequest(
53 request_id=str(uuid.uuid4()),
54 prompt_token_ids=prompt_tokens_ids,
55 - mm_kwargs=None,
56 - mm_hashes=None,
57 - mm_placeholders=None,
55 + mm_features=None,
56 sampling_params=params,
57 pooling_params=None,
58 eos_token_id=None,
tests/v1/engine/test_fast_incdec_prefix_err.py
+8 -10
@@ -26,16 +26,14 @@ def test_fast_inc_detok_invalid_utf8_err_case():
26 prompt_token_ids = [107, 4606, 236787, 107]
27 params = SamplingParams(skip_special_tokens=True)
28 request = EngineCoreRequest(
29 - "test",
30 - prompt_token_ids,
31 - None,
32 - None,
33 - None,
34 - params,
35 - None,
36 - None,
37 - 0.0,
38 - None,
29 + request_id="test",
30 + prompt_token_ids=prompt_token_ids,
31 + mm_features=None,
32 + sampling_params=params,
33 + pooling_params=None,
34 + eos_token_id=None,
35 + arrival_time=0.0,
36 + lora_request=None,
37 cache_salt=None,
38 data_parallel_rank=None,
39 )
tests/v1/engine/test_output_processor.py
+10 -20
@@ -52,11 +52,9 @@ def test_incremental_detokenization(request_output_kind: RequestOutputKind,
52 requests = [
53 EngineCoreRequest(request_id=f"request-{idx}",
54 prompt_token_ids=prompt_tokens,
55 - arrival_time=0,
56 - mm_kwargs=None,
57 - mm_hashes=None,
58 - mm_placeholders=None,
55 + mm_features=None,
56 eos_token_id=None,
57 + arrival_time=0,
58 lora_request=None,
59 cache_salt=None,
60 data_parallel_rank=None,
@@ -401,11 +399,9 @@ def test_logprobs_processor(request_output_kind: RequestOutputKind,
399 requests = [
400 EngineCoreRequest(request_id=request_id_list[idx],
401 prompt_token_ids=prompt_tokens,
404 - arrival_time=0,
405 - mm_kwargs=None,
406 - mm_hashes=None,
407 - mm_placeholders=None,
402 + mm_features=None,
403 eos_token_id=None,
404 + arrival_time=0,
405 lora_request=None,
406 cache_salt=None,
407 data_parallel_rank=None,
@@ -566,11 +562,9 @@ def test_stop_token(include_stop_str_in_output: bool,
562 request = EngineCoreRequest(
563 request_id=request_id,
564 prompt_token_ids=prompt_tokens,
569 - arrival_time=0,
570 - mm_kwargs=None,
571 - mm_hashes=None,
572 - mm_placeholders=None,
565 + mm_features=None,
566 eos_token_id=eos_token_id,
567 + arrival_time=0,
568 lora_request=None,
569 cache_salt=None,
570 data_parallel_rank=None,
@@ -665,11 +659,9 @@ def test_stop_string(include_stop_str_in_output: bool,
659 EngineCoreRequest(
660 request_id=request_id_list[idx],
661 prompt_token_ids=prompt_tokens,
668 - arrival_time=0,
669 - mm_kwargs=None,
670 - mm_hashes=None,
671 - mm_placeholders=None,
662 + mm_features=None,
663 eos_token_id=None,
664 + arrival_time=0,
665 lora_request=None,
666 cache_salt=None,
667 data_parallel_rank=None,
@@ -781,11 +773,9 @@ def test_iteration_stats(dummy_test_vectors):
773 EngineCoreRequest(
774 request_id=f"request-{idx}",
775 prompt_token_ids=prompt_tokens,
784 - arrival_time=0,
785 - mm_kwargs=None,
786 - mm_hashes=None,
787 - mm_placeholders=None,
776 + mm_features=None,
777 eos_token_id=None,
778 + arrival_time=0,
779 lora_request=None,
780 cache_salt=None,
781 data_parallel_rank=None,
tests/v1/kv_connector/unit/utils.py
+1 -3
@@ -162,9 +162,7 @@ def create_request(request_id: int,
162 prompt_token_ids=prompt_token_ids,
163 sampling_params=sampling_params,
164 pooling_params=None,
165 - multi_modal_kwargs=None,
166 - multi_modal_placeholders=None,
167 - multi_modal_hashes=None,
165 + mm_features=None,
166 eos_token_id=EOS_TOKEN_ID,
167 block_hasher=get_request_block_hasher(block_size, hash_fn),
168 )
vllm/multimodal/cache.py
+13 -3
@@ -12,9 +12,9 @@ from vllm.logger import init_logger
12 from vllm.utils import GiB_bytes, LRUCache
13 from vllm.utils.jsontree import json_map_leaves, json_reduce_leaves
14
15 -from .inputs import (MultiModalFieldElem, MultiModalKwargs,
16 - MultiModalKwargsItem, MultiModalKwargsItems,
17 - NestedTensors)
15 +from .inputs import (MultiModalFeatureSpec, MultiModalFieldElem,
16 + MultiModalKwargs, MultiModalKwargsItem,
17 + MultiModalKwargsItems, NestedTensors)
18
19 if TYPE_CHECKING:
20 from vllm.config import ModelConfig, VllmConfig
@@ -418,6 +418,16 @@ class BaseMultiModalReceiverCache(
418 MultiModalKwargsItem]):
419 """The required interface for caches on P1."""
420
421 + def get_and_update_features(
422 + self,
423 + mm_features: list["MultiModalFeatureSpec"],
424 + ) -> list["MultiModalFeatureSpec"]:
425 + """Update multimodal features with cached encoder outputs."""
426 + for feature in mm_features:
427 + feature.data = self.get_and_update_item(feature.data,
428 + feature.identifier)
429 + return mm_features
430 +
431
432 class MultiModalReceiverCache(BaseMultiModalReceiverCache):
433 """
vllm/multimodal/inputs.py
+23
@@ -198,6 +198,29 @@ A dictionary containing nested tensors which have been batched via
198 """
199
200
201 +@dataclass
202 +class MultiModalFeatureSpec:
203 + """
204 + Represents a single multimodal input with its processed data and metadata.
205 +
206 + Used by the V1 engine to track multimodal data through processing and
207 + caching. A request containing multiple multimodal items will have one
208 + MultiModalFeatureSpec per item.
209 + """
210 +
211 + data: Optional["MultiModalKwargsItem"]
212 + """Multimodal data for this feature"""
213 +
214 + modality: str
215 + """Based on the input, e.g., "image", "audio", "video"."""
216 +
217 + identifier: str
218 + """mm_hash or uuid for caching encoder outputs."""
219 +
220 + mm_position: PlaceholderRange
221 + """e.g., PlaceholderRange(offset=2, length=336)"""
222 +
223 +
224 @dataclass
225 class MultiModalFieldElem:
226 """
vllm/v1/engine/__init__.py
+2 -5
@@ -3,14 +3,13 @@
3
4 import enum
5 import time
6 -from collections.abc import Sequence
6 from typing import Any, Optional, Union
7
8 import msgspec
9 import torch
10
11 from vllm.lora.request import LoRARequest
13 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
12 +from vllm.multimodal.inputs import MultiModalFeatureSpec
13 from vllm.pooling_params import PoolingParams
14 from vllm.sampling_params import SamplingParams
15 from vllm.v1.metrics.stats import SchedulerStats
@@ -48,9 +47,7 @@ class EngineCoreRequest(
47
48 request_id: str
49 prompt_token_ids: list[int]
51 - mm_kwargs: Optional[Sequence[Optional[MultiModalKwargsItem]]]
52 - mm_hashes: Optional[list[str]]
53 - mm_placeholders: Optional[list[PlaceholderRange]]
50 + mm_features: Optional[list[MultiModalFeatureSpec]]
51 sampling_params: Optional[SamplingParams]
52 pooling_params: Optional[PoolingParams]
53 eos_token_id: Optional[int]
vllm/v1/engine/core.py
+7 -9
@@ -434,15 +434,13 @@ class EngineCore:
434 This function could be directly used in input processing thread to allow
435 request initialization running in parallel with Model forward
436 """
437 - if request.mm_hashes is not None:
438 - assert request.mm_kwargs is not None
439 -
440 - # Note on thread safety: no race condition.
441 - # `mm_receiver_cache` is reset at the end of LLMEngine init,
442 - # and will only accessed in the input processing thread afterwards.
443 - if self.mm_receiver_cache is not None:
444 - request.mm_kwargs = self.mm_receiver_cache.get_and_update(
445 - request.mm_kwargs, request.mm_hashes)
437 + # Note on thread safety: no race condition.
438 + # `mm_receiver_cache` is reset at the end of LLMEngine init,
439 + # and will only accessed in the input processing thread afterwards.
440 + if self.mm_receiver_cache is not None and request.mm_features:
441 + request.mm_features = (
442 + self.mm_receiver_cache.get_and_update_features(
443 + request.mm_features))
444
445 req = Request.from_engine_core_request(request,
446 self.request_block_hasher)
vllm/v1/engine/processor.py
+12 -19
@@ -12,7 +12,7 @@ from vllm.inputs.preprocess import InputPreprocessor
12 from vllm.lora.request import LoRARequest
13 from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalRegistry
14 from vllm.multimodal.cache import processor_cache_from_config
15 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
15 +from vllm.multimodal.inputs import MultiModalFeatureSpec
16 from vllm.multimodal.processing import EncDecMultiModalProcessor
17 from vllm.multimodal.utils import argsort_mm_positions
18 from vllm.pooling_params import PoolingParams
@@ -346,9 +346,8 @@ class Processor:
346 pooling_params = params.clone()
347
348 # Multimodal related.
349 - sorted_mm_inputs: Optional[list[Optional[MultiModalKwargsItem]]] = None
350 - sorted_mm_positions: Optional[list[PlaceholderRange]] = None
351 - sorted_mm_hashes: Optional[list[str]] = None
349 + mm_features: Optional[list[MultiModalFeatureSpec]] = None
350 +
351 if decoder_inputs["type"] == "multimodal":
352 decoder_mm_inputs = decoder_inputs["mm_kwargs"]
353 decoder_mm_positions = decoder_inputs["mm_placeholders"]
@@ -359,25 +358,19 @@ class Processor:
358 # in the input sequence.
359 sorted_mm_idxs = argsort_mm_positions(decoder_mm_positions)
360
362 - sorted_mm_inputs = [
363 - decoder_mm_inputs[modality][idx]
364 - for modality, idx in sorted_mm_idxs
365 - ]
366 - sorted_mm_positions = [
367 - decoder_mm_positions[modality][idx]
368 - for modality, idx in sorted_mm_idxs
369 - ]
370 - sorted_mm_hashes = [
371 - decoder_mm_hashes[modality][idx]
372 - for modality, idx in sorted_mm_idxs
373 - ]
361 + mm_features = []
362 + for modality, idx in sorted_mm_idxs:
363 + mm_features.append(
364 + MultiModalFeatureSpec(
365 + data=decoder_mm_inputs[modality][idx],
366 + modality=modality,
367 + identifier=decoder_mm_hashes[modality][idx],
368 + mm_position=decoder_mm_positions[modality][idx]))
369
370 return decoder_inputs.get("prompt"), EngineCoreRequest(
371 request_id=request_id,
372 prompt_token_ids=decoder_inputs["prompt_token_ids"],
378 - mm_kwargs=sorted_mm_inputs,
379 - mm_hashes=sorted_mm_hashes,
380 - mm_placeholders=sorted_mm_positions,
373 + mm_features=mm_features,
374 sampling_params=sampling_params,
375 pooling_params=pooling_params,
376 eos_token_id=eos_token_id,
vllm/v1/request.py
+10 -24
@@ -6,10 +6,9 @@ import time
6 from functools import partial
7 from typing import TYPE_CHECKING, Any, Callable, Optional, Union
8
9 -from vllm.multimodal.inputs import MultiModalKwargsItem, PlaceholderRange
9 +from vllm.multimodal.inputs import MultiModalFeatureSpec
10 from vllm.pooling_params import PoolingParams
11 from vllm.sampling_params import SamplingParams
12 -from vllm.utils import is_list_of
12 from vllm.v1.engine import (EngineCoreEvent, EngineCoreEventType,
13 EngineCoreRequest, FinishReason)
14 from vllm.v1.structured_output.request import StructuredOutputRequest
@@ -26,14 +25,12 @@ class Request:
25 self,
26 request_id: str,
27 prompt_token_ids: list[int],
29 - multi_modal_kwargs: Optional[list[MultiModalKwargsItem]],
30 - multi_modal_hashes: Optional[list[str]],
31 - multi_modal_placeholders: Optional[list[PlaceholderRange]],
28 sampling_params: Optional[SamplingParams],
29 pooling_params: Optional[PoolingParams],
30 eos_token_id: Optional[int],
31 client_index: int = 0,
32 arrival_time: Optional[float] = None,
33 + mm_features: Optional[list[MultiModalFeatureSpec]] = None,
34 lora_request: Optional["LoRARequest"] = None,
35 structured_output_request: Optional["StructuredOutputRequest"] = None,
36 cache_salt: Optional[str] = None,
@@ -89,16 +86,14 @@ class Request:
86 self.cache_salt: Optional[str] = cache_salt
87
88 # Multi-modal related
92 - self.mm_positions = multi_modal_placeholders or []
93 - self.mm_kwargs = multi_modal_kwargs or []
94 - self.mm_hashes: list[str] = multi_modal_hashes or []
95 - self.num_encoder_inputs = len(self.mm_kwargs)
89 + self.mm_features = mm_features or []
90 + self.num_encoder_inputs = len(self.mm_features)
91 self.has_encoder_inputs = self.num_encoder_inputs > 0
97 -
98 - # Sanity check
99 - assert len(self.mm_kwargs) == len(self.mm_positions)
100 - if self.mm_hashes:
101 - assert len(self.mm_kwargs) == len(self.mm_hashes)
92 + # TODO(sfeng33): Remove these legacy fields after clearing out all
93 + # references in scheduler and model runner
94 + self.mm_positions = [f.mm_position for f in self.mm_features]
95 + self.mm_kwargs = [f.data for f in self.mm_features]
96 + self.mm_hashes = [f.identifier for f in self.mm_features]
97
98 # Read-only views
99 # Prevent directly appending to these lists since
@@ -126,20 +121,11 @@ class Request:
121 cls, request: EngineCoreRequest,
122 block_hasher: Optional[Callable[["Request"], list["BlockHash"]]]
123 ) -> "Request":
129 - if request.mm_kwargs is not None:
130 - mm_kwargs_lst = list(request.mm_kwargs)
131 - assert is_list_of(mm_kwargs_lst, MultiModalKwargsItem), (
132 - "mm_kwargs was not updated in EngineCore.add_request")
133 - else:
134 - mm_kwargs_lst = None
135 -
124 return cls(
125 request_id=request.request_id,
126 client_index=request.client_index,
127 prompt_token_ids=request.prompt_token_ids,
140 - multi_modal_kwargs=mm_kwargs_lst,
141 - multi_modal_hashes=request.mm_hashes,
142 - multi_modal_placeholders=request.mm_placeholders,
128 + mm_features=request.mm_features,
129 sampling_params=request.sampling_params,
130 pooling_params=request.pooling_params,
131 eos_token_id=request.eos_token_id,