[Refactor] Remove InputPreprocessor (#53064)
Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>
Cyrus Leung committed
Aug 20, 2026 at 16:39 UTC
4666a8ba9ed5d271bf751cf1384cab2d2dcc0cfa
8 files changed
+29
-367
.buildkite/test-amd.yaml
-2
@@ -172,7 +172,6 @@ steps:
172
source_file_dependencies:
173
- vllm/
174
- tests/test_envs.py
175
- - tests/test_inputs.py
175
- tests/test_outputs.py
176
- tests/test_pooling_params.py
177
- tests/test_ray_env.py
@@ -189,7 +188,6 @@ steps:
188
commands:
189
- python3 standalone_tests/lazy_imports.py
190
- pytest -v -s test_envs.py
192
- - pytest -v -s test_inputs.py
191
- pytest -v -s test_outputs.py
192
- pytest -v -s test_pooling_params.py
193
- pytest -v -s test_ray_env.py
.buildkite/test_areas/misc.yaml
-2
@@ -358,7 +358,6 @@ steps:
358
- vllm/utils/
359
- vllm/v1/
360
- tests/test_envs.py
361
- - tests/test_inputs.py
361
- tests/test_outputs.py
362
- tests/test_pooling_params.py
363
- tests/test_ray_env.py
@@ -376,7 +375,6 @@ steps:
375
commands:
376
- python3 standalone_tests/lazy_imports.py
377
- pytest -v -s test_envs.py
379
- - pytest -v -s test_inputs.py
378
- pytest -v -s test_outputs.py
379
- pytest -v -s test_pooling_params.py
380
- pytest -v -s test_ray_env.py
.github/CODEOWNERS
-1
@@ -107,7 +107,6 @@
107
/tests/multimodal @DarkLight1337 @ywang96 @NickLucche @shen-shanshan
108
/tests/multimodal/media @Isotr0py
109
/tests/quantization @mgoin @robertgshaw2-redhat @yewentao256 @pavanimajety @zyongye @AndreasKaratzas
110
-/tests/test_inputs.py @DarkLight1337 @ywang96
110
/tests/entrypoints/llm/test_struct_output_generate.py @mgoin @russellb @aarnphm
111
/tests/v1/structured_output @mgoin @russellb @aarnphm
112
/tests/v1/core @WoosukKwon @robertgshaw2-redhat @njhill @ywang96 @alexm-redhat @heheda12345 @ApostaC @orozery @ivanium
tests/renderers/test_process_multi_modal_uuids.py
+1
-2
@@ -54,11 +54,10 @@ def test_text_only_model_mm_data_maps_to_bad_request():
54
55
with pytest.raises(ValueError, match="text-only") as exc_info:
56
renderer._process_multimodal(
57
- prompt="What is in this image?",
57
+ prompt=[1],
58
mm_data={"image": [cherry_pil_image]},
59
mm_uuids=None,
60
mm_processor_kwargs=None,
61
- tokenization_kwargs=None,
61
)
62
63
error_response = create_error_response(exc_info.value)
tests/test_inputs.py
deleted
-31
@@ -1,31 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import pytest
5
-
6
-from vllm.config import ModelConfig, VllmConfig
7
-from vllm.inputs.preprocess import InputPreprocessor
8
-
9
-pytestmark = pytest.mark.cpu_test
10
-
11
-
12
-@pytest.mark.parametrize("model_id", ["facebook/chameleon-7b"])
13
-@pytest.mark.parametrize("prompt", ["", {"prompt_token_ids": []}])
14
-@pytest.mark.skip(
15
- reason=(
16
- "Applying huggingface processor on text inputs results in "
17
- "significant performance regression for multimodal models. "
18
- "See https://github.com/vllm-project/vllm/issues/26320"
19
- )
20
-)
21
-def test_preprocessor_always_mm_code_path(model_id, prompt):
22
- model_config = ModelConfig(model=model_id)
23
- vllm_config = VllmConfig(model_config=model_config)
24
- input_preprocessor = InputPreprocessor(vllm_config)
25
-
26
- # HF processor adds sep token
27
- tokenizer = input_preprocessor.get_tokenizer()
28
- sep_token_id = tokenizer.vocab[tokenizer.sep_token]
29
-
30
- processed_inputs = input_preprocessor.preprocess(prompt)
31
- assert sep_token_id in processed_inputs["prompt_token_ids"]
vllm/inputs/preprocess.py
deleted
-291
@@ -1,291 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-from collections.abc import Mapping
5
-from typing import Any, overload
6
-
7
-from typing_extensions import assert_never
8
-
9
-from vllm.config import VllmConfig
10
-from vllm.inputs import build_enc_dec_input
11
-from vllm.logger import init_logger
12
-from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalRegistry
13
-from vllm.renderers import BaseRenderer, renderer_from_config
14
-from vllm.renderers.inputs import (
15
- DecoderDictPrompt,
16
- DecoderOnlyDictPrompt,
17
- EncoderDecoderDictPrompt,
18
- EncoderDictPrompt,
19
- SingletonDictPrompt,
20
-)
21
-from vllm.renderers.inputs.preprocess import parse_dec_only_prompt, parse_enc_dec_prompt
22
-from vllm.tokenizers import TokenizerLike
23
-
24
-from .engine import (
25
- DecoderEngineInput,
26
- DecoderOnlyEngineInput,
27
- EmbedsInput,
28
- EncoderDecoderInput,
29
- EncoderInput,
30
- EngineInput,
31
- MultiModalInput,
32
- SingletonInput,
33
- TokensInput,
34
- tokens_input,
35
-)
36
-from .llm import (
37
- EmbedsPrompt,
38
- MultiModalDataDict,
39
- MultiModalUUIDDict,
40
- PromptType,
41
- TextPrompt,
42
- TokensPrompt,
43
-)
44
-
45
-logger = init_logger(__name__)
46
-
47
-
48
-class InputPreprocessor:
49
- def __init__(
50
- self,
51
- vllm_config: VllmConfig,
52
- renderer: BaseRenderer | None = None,
53
- mm_registry: MultiModalRegistry = MULTIMODAL_REGISTRY,
54
- ) -> None:
55
- super().__init__()
56
-
57
- self.model_config = vllm_config.model_config
58
- self.renderer = renderer or renderer_from_config(vllm_config)
59
- self.mm_registry = mm_registry
60
-
61
- @property
62
- def tokenizer(self) -> TokenizerLike | None:
63
- return self.renderer.tokenizer
64
-
65
- def get_tokenizer(self) -> TokenizerLike:
66
- return self.renderer.get_tokenizer()
67
-
68
- def _tokenize_prompt(
69
- self,
70
- prompt: str,
71
- tokenization_kwargs: dict[str, Any] | None = None,
72
- ) -> list[int]:
73
- """
74
- Apply the model's tokenizer to a text prompt, returning the
75
- corresponding token IDs.
76
- """
77
- renderer = self.renderer
78
-
79
- tok_params = renderer.default_cmpl_tok_params.with_kwargs(
80
- **(tokenization_kwargs or {})
81
- )
82
-
83
- tok_prompt = renderer._tokenize_singleton_prompt(
84
- TextPrompt(prompt=prompt),
85
- tok_params,
86
- )
87
-
88
- return tok_prompt["prompt_token_ids"]
89
-
90
- def _process_multimodal(
91
- self,
92
- prompt: str | list[int],
93
- mm_data: MultiModalDataDict,
94
- mm_processor_kwargs: Mapping[str, object] | None = None,
95
- tokenization_kwargs: dict[str, Any] | None = None,
96
- *,
97
- mm_uuids: MultiModalUUIDDict | None = None,
98
- ) -> MultiModalInput:
99
- """
100
- Apply the model's multi-modal processor to a multi-modal prompt,
101
- returning the corresponding token IDs and metadata.
102
- """
103
- return self.renderer._process_multimodal(
104
- prompt,
105
- mm_data,
106
- mm_uuids=mm_uuids,
107
- mm_processor_kwargs=mm_processor_kwargs,
108
- tokenization_kwargs=tokenization_kwargs,
109
- )
110
-
111
- def _process_embeds(
112
- self,
113
- parsed_content: EmbedsPrompt,
114
- ) -> EmbedsInput:
115
- return self.renderer._process_embeds(parsed_content)
116
-
117
- def _truncate_inputs(
118
- self, inputs: list[int], tokenization_kwargs: dict[str, Any] | None = None
119
- ) -> list[int]:
120
- renderer = self.renderer
121
-
122
- tok_params = renderer.default_cmpl_tok_params.with_kwargs(
123
- **(tokenization_kwargs or {})
124
- )
125
-
126
- tok_prompt = renderer._tokenize_singleton_prompt(
127
- TokensPrompt(prompt_token_ids=inputs),
128
- tok_params,
129
- )
130
-
131
- return tok_prompt["prompt_token_ids"]
132
-
133
- def _process_tokens(
134
- self,
135
- parsed_content: TokensPrompt,
136
- tokenization_kwargs: dict[str, Any] | None = None,
137
- ) -> TokensInput | MultiModalInput:
138
- prompt_token_ids = self._truncate_inputs(
139
- parsed_content["prompt_token_ids"], tokenization_kwargs
140
- )
141
-
142
- inputs: TokensInput | MultiModalInput
143
- if multi_modal_data := parsed_content.get("multi_modal_data"):
144
- inputs = self._process_multimodal(
145
- prompt_token_ids,
146
- multi_modal_data,
147
- parsed_content.get("mm_processor_kwargs"),
148
- tokenization_kwargs=tokenization_kwargs,
149
- mm_uuids=parsed_content.get("multi_modal_uuids"),
150
- )
151
- else:
152
- inputs = tokens_input(prompt_token_ids)
153
-
154
- if prompt_text := parsed_content.get("prompt"):
155
- inputs["prompt"] = prompt_text
156
- if cache_salt := parsed_content.get("cache_salt"):
157
- inputs["cache_salt"] = cache_salt
158
-
159
- return inputs
160
-
161
- def _process_text(
162
- self,
163
- parsed_content: TextPrompt,
164
- tokenization_kwargs: dict[str, Any] | None = None,
165
- ) -> TokensInput | MultiModalInput:
166
- prompt_text = parsed_content["prompt"]
167
-
168
- inputs: TokensInput | MultiModalInput
169
- if multi_modal_data := parsed_content.get("multi_modal_data"):
170
- inputs = self._process_multimodal(
171
- prompt_text,
172
- multi_modal_data,
173
- parsed_content.get("mm_processor_kwargs") or {},
174
- tokenization_kwargs=tokenization_kwargs,
175
- )
176
- else:
177
- prompt_token_ids = self._tokenize_prompt(
178
- prompt_text,
179
- tokenization_kwargs=tokenization_kwargs,
180
- )
181
- inputs = tokens_input(prompt_token_ids)
182
-
183
- inputs["prompt"] = prompt_text
184
-
185
- if cache_salt := parsed_content.get("cache_salt"):
186
- inputs["cache_salt"] = cache_salt
187
-
188
- return inputs
189
-
190
- @overload
191
- def _prompt_to_llm_inputs(
192
- self,
193
- prompt: EncoderDictPrompt,
194
- tokenization_kwargs: dict[str, Any] | None = None,
195
- ) -> EncoderInput: ...
196
-
197
- @overload
198
- def _prompt_to_llm_inputs( # type: ignore[misc]
199
- self,
200
- prompt: DecoderDictPrompt,
201
- tokenization_kwargs: dict[str, Any] | None = None,
202
- ) -> DecoderEngineInput: ...
203
-
204
- @overload
205
- def _prompt_to_llm_inputs( # type: ignore[misc]
206
- self,
207
- prompt: DecoderOnlyDictPrompt,
208
- tokenization_kwargs: dict[str, Any] | None = None,
209
- ) -> DecoderOnlyEngineInput: ...
210
-
211
- def _prompt_to_llm_inputs(
212
- self,
213
- prompt: SingletonDictPrompt,
214
- tokenization_kwargs: dict[str, Any] | None = None,
215
- ) -> SingletonInput:
216
- if "prompt_embeds" in prompt:
217
- return self._process_embeds(prompt) # type: ignore[arg-type]
218
-
219
- if "prompt_token_ids" in prompt:
220
- return self._process_tokens(prompt) # type: ignore[arg-type]
221
-
222
- if "prompt" in prompt:
223
- return self._process_text(
224
- prompt, # type: ignore[arg-type]
225
- tokenization_kwargs=tokenization_kwargs,
226
- )
227
-
228
- assert_never(prompt) # type: ignore[arg-type]
229
-
230
- def _process_encoder_decoder_prompt(
231
- self,
232
- prompt: EncoderDecoderDictPrompt,
233
- tokenization_kwargs: dict[str, Any] | None = None,
234
- ) -> EncoderDecoderInput:
235
- encoder_prompt = prompt["encoder_prompt"]
236
- decoder_prompt = prompt["decoder_prompt"]
237
-
238
- skip_decoder_start_token = False
239
- if self.renderer.mm_processor is not None:
240
- from vllm.multimodal.processing import EncDecMultiModalProcessor
241
-
242
- if isinstance(self.renderer.mm_processor, EncDecMultiModalProcessor):
243
- skip_decoder_start_token = (
244
- self.renderer.mm_processor.skip_decoder_start_token
245
- )
246
-
247
- return build_enc_dec_input(
248
- encoder_input=self._prompt_to_llm_inputs(
249
- encoder_prompt,
250
- tokenization_kwargs=tokenization_kwargs,
251
- ),
252
- decoder_input=(
253
- None
254
- if decoder_prompt is None
255
- else self._prompt_to_llm_inputs(
256
- decoder_prompt,
257
- tokenization_kwargs=tokenization_kwargs,
258
- )
259
- ),
260
- decoder_start_token_id=self.renderer.get_dec_start_token_id(),
261
- skip_decoder_start_token=skip_decoder_start_token,
262
- )
263
-
264
- def _process_decoder_only_prompt(
265
- self,
266
- prompt: DecoderOnlyDictPrompt,
267
- tokenization_kwargs: dict[str, Any] | None = None,
268
- ) -> DecoderOnlyEngineInput:
269
- return self._prompt_to_llm_inputs(
270
- prompt,
271
- tokenization_kwargs=tokenization_kwargs,
272
- )
273
-
274
- def preprocess(
275
- self,
276
- prompt: PromptType,
277
- tokenization_kwargs: dict[str, Any] | None = None,
278
- ) -> EngineInput:
279
- """Preprocess the input prompt."""
280
- if self.model_config.is_encoder_decoder:
281
- # Encoder-decoder model requires special mapping of
282
- # input prompts to encoder & decoder.
283
- return self._process_encoder_decoder_prompt(
284
- parse_enc_dec_prompt(prompt),
285
- tokenization_kwargs,
286
- )
287
-
288
- return self._process_decoder_only_prompt(
289
- parse_dec_only_prompt(prompt),
290
- tokenization_kwargs=tokenization_kwargs,
291
- )
vllm/renderers/base.py
+1
-6
@@ -725,14 +725,12 @@ class BaseRenderer(ABC, Generic[_T]):
725
726
return mm_uuid_items
727
728
- # TODO: Remove str and tokenization_kwargs after deprecating InputPreprocessor
728
def _process_multimodal(
729
self,
731
- prompt: list[int] | str,
730
+ prompt: list[int],
731
mm_data: MultiModalDataDict,
732
mm_uuids: MultiModalUUIDDict | None,
733
mm_processor_kwargs: Mapping[str, object] | None,
735
- tokenization_kwargs: dict[str, Any] | None,
734
*,
735
skip_mm_cache: bool = False,
736
) -> "MultiModalInput":
@@ -755,7 +753,6 @@ class BaseRenderer(ABC, Generic[_T]):
753
mm_data_items,
754
mm_uuid_items,
755
hf_processor_mm_kwargs=mm_processor_kwargs or {},
758
- tokenization_kwargs=tokenization_kwargs or {},
756
)
757
mm_timing_ctx = self._mm_timing_registry.get(mm_req_id)
758
@@ -783,7 +780,6 @@ class BaseRenderer(ABC, Generic[_T]):
780
prompt_token_ids,
781
multi_modal_data,
782
mm_processor_kwargs=prompt.get("mm_processor_kwargs"),
786
- tokenization_kwargs=None, # Tokenization already done in Step 2
783
mm_uuids=prompt.get("multi_modal_uuids"),
784
skip_mm_cache=skip_mm_cache,
785
)
@@ -846,7 +842,6 @@ class BaseRenderer(ABC, Generic[_T]):
842
prompt_token_ids,
843
multi_modal_data,
844
mm_processor_kwargs=prompt.get("mm_processor_kwargs"),
849
- tokenization_kwargs=None,
845
mm_uuids=prompt.get("multi_modal_uuids"),
846
skip_mm_cache=skip_mm_cache,
847
)
vllm/v1/engine/input_processor.py
+27
-32
@@ -14,7 +14,6 @@ from vllm.inputs import (
14
SingletonInput,
15
split_enc_dec_input,
16
)
17
-from vllm.inputs.preprocess import InputPreprocessor
17
from vllm.logger import init_logger
18
from vllm.lora.request import LoRARequest
19
from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalRegistry
@@ -24,6 +23,7 @@ from vllm.multimodal.utils import argsort_mm_positions
23
from vllm.platforms import current_platform
24
from vllm.pooling_params import PoolingParams
25
from vllm.renderers import BaseRenderer, renderer_from_config
26
+from vllm.renderers.inputs.preprocess import parse_model_prompt
27
from vllm.sampling_params import SamplingParams
28
from vllm.tasks import GENERATION_TASKS, POOLING_TASKS, SupportedTask
29
from vllm.tokenizers import TokenizerLike
@@ -67,12 +67,6 @@ class InputProcessor:
67
)
68
mm_budget.reset_cache() # Not used anymore
69
70
- self.input_preprocessor = InputPreprocessor(
71
- vllm_config,
72
- renderer=renderer,
73
- mm_registry=mm_registry,
74
- )
75
-
70
# Raw-prompt preprocessing (tokenization and multimodal processing)
71
# is blocking, so async callers should run it on the renderer's
72
# thread pool to keep their event loop responsive.
@@ -313,44 +307,45 @@ class InputProcessor:
307
)
308
309
if isinstance(prompt, dict) and "type" in prompt:
316
- if tokenization_kwargs:
317
- logger.warning_once(
318
- "Passing tokenization_kwargs to InputProcessor is deprecated "
319
- "and will be removed in v0.18. You should instead pass "
320
- "them to Renderer.render_cmpl() or Renderer.render_chat()."
321
- )
322
-
310
if arrival_time is None:
311
arrival_time = prompt.get("arrival_time", time.time()) # type: ignore[assignment]
312
326
- processed_inputs: EngineInput = prompt # type: ignore[assignment]
313
+ engine_input: EngineInput = prompt # type: ignore[assignment]
314
else:
315
logger.warning_once(
316
"Passing raw prompts to InputProcessor is deprecated "
330
- "and will be removed in v0.18. You should instead pass "
317
+ "and will be removed in the future. You should instead pass "
318
"the outputs of Renderer.render_cmpl() or Renderer.render_chat()."
319
)
320
321
if arrival_time is None:
322
arrival_time = time.time()
323
337
- processed_inputs = self.input_preprocessor.preprocess(
338
- prompt,
339
- tokenization_kwargs=tokenization_kwargs,
324
+ renderer = self.renderer
325
+ model_config = self.model_config
326
+
327
+ parsed_prompt = parse_model_prompt(model_config, prompt)
328
+ tok_params = renderer.default_cmpl_tok_params.with_kwargs(
329
+ **(tokenization_kwargs or {})
330
+ )
331
+
332
+ (engine_input,) = renderer.render_cmpl(
333
+ [parsed_prompt],
334
+ tok_params,
335
)
336
342
- current_platform.validate_request(processed_inputs, params)
337
+ current_platform.validate_request(engine_input, params)
338
344
- encoder_inputs, decoder_inputs = split_enc_dec_input(processed_inputs)
345
- self._validate_model_inputs(encoder_inputs, decoder_inputs)
339
+ encoder_input, decoder_input = split_enc_dec_input(engine_input)
340
+ self._validate_model_inputs(encoder_input, decoder_input)
341
342
# Mypy can be conservative for TypedDict unions; normalize access.
348
- if decoder_inputs["type"] == "embeds":
349
- prompt_embeds = decoder_inputs["prompt_embeds"]
350
- prompt_token_ids = decoder_inputs.get("prompt_token_ids")
351
- prompt_is_token_ids = decoder_inputs.get("is_token_ids")
343
+ if decoder_input["type"] == "embeds":
344
+ prompt_embeds = decoder_input["prompt_embeds"]
345
+ prompt_token_ids = decoder_input.get("prompt_token_ids")
346
+ prompt_is_token_ids = decoder_input.get("is_token_ids")
347
else:
353
- prompt_token_ids = decoder_inputs["prompt_token_ids"]
348
+ prompt_token_ids = decoder_input["prompt_token_ids"]
349
prompt_embeds = None
350
prompt_is_token_ids = None
351
@@ -385,10 +380,10 @@ class InputProcessor:
380
# Multimodal related.
381
mm_features: list[MultiModalFeatureSpec] | None = None
382
388
- if decoder_inputs["type"] == "multimodal":
389
- decoder_mm_inputs = decoder_inputs["mm_kwargs"]
390
- decoder_mm_positions = decoder_inputs["mm_placeholders"]
391
- decoder_mm_hashes = decoder_inputs["mm_hashes"]
383
+ if decoder_input["type"] == "multimodal":
384
+ decoder_mm_inputs = decoder_input["mm_kwargs"]
385
+ decoder_mm_positions = decoder_input["mm_placeholders"]
386
+ decoder_mm_hashes = decoder_input["mm_hashes"]
387
388
if not all(
389
isinstance(leaf, str) for leaf in json_iter_leaves(decoder_mm_hashes)
@@ -430,7 +425,7 @@ class InputProcessor:
425
pooling_params=pooling_params,
426
arrival_time=arrival_time,
427
lora_request=lora_request,
433
- cache_salt=decoder_inputs.get("cache_salt"),
428
+ cache_salt=decoder_input.get("cache_salt"),
429
priority=priority,
430
data_parallel_rank=data_parallel_rank,
431
trace_headers=trace_headers,