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 4, 21:15 UTC
[Bugfix] Normalize malformed dict prompts that carry token IDs in `prompt` (#40339)
Signed-off-by: Alchuang22-dev <2584829494@qq.com>
Zeyu Zhang committed
Apr 21, 2026 at 15:44 UTC
5a94a198246bfee24e6f40adba8eed28756fca3c
3 files changed
+57
-2
tests/renderers/inputs/test_preprocess.py
+27
-1
@@ -1,6 +1,12 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-from vllm.renderers.inputs.preprocess import prompt_to_seq
3
+import pytest
4
+
5
+from vllm.renderers.inputs.preprocess import (
6
+ parse_dec_only_prompt,
7
+ parse_enc_dec_prompt,
8
+ prompt_to_seq,
9
+)
10
11
12
def test_empty_input():
@@ -39,3 +45,23 @@ def test_dict_input():
45
{"prompt": "foo"},
46
{"prompt_token_ids": [1, 2]},
47
]
48
+
49
+
50
+def test_parse_dec_only_prompt_rejects_non_string_prompt_field():
51
+ with pytest.raises(TypeError, match="Prompt text should be a string"):
52
+ parse_dec_only_prompt({"prompt": [1, 2, 3], "cache_salt": "abc"})
53
+
54
+
55
+def test_parse_dec_only_prompt_rejects_non_string_prompt_list():
56
+ with pytest.raises(TypeError, match="Prompt text should be a string"):
57
+ parse_dec_only_prompt({"prompt": [1, "x"]})
58
+
59
+
60
+def test_parse_enc_dec_prompt_rejects_nested_non_string_prompt_field():
61
+ with pytest.raises(TypeError, match="Prompt text should be a string"):
62
+ parse_enc_dec_prompt(
63
+ {
64
+ "encoder_prompt": {"prompt": [1, 2, 3]},
65
+ "decoder_prompt": {"prompt": [4, 5]},
66
+ }
67
+ )
vllm/renderers/base.py
+10
@@ -435,6 +435,11 @@ class BaseRenderer(ABC, Generic[_T]):
435
params: TokenizeParams,
436
) -> SingletonTokPrompt:
437
if "prompt_token_ids" not in prompt and "prompt_embeds" not in prompt:
438
+ if not isinstance(prompt.get("prompt"), str):
439
+ raise TypeError(
440
+ "Expected prompt['prompt'] to be a string before tokenization; "
441
+ "use 'prompt_token_ids' for token ID inputs"
442
+ )
443
prompt = params.apply_pre_tokenization(self.tokenizer, prompt) # type: ignore[arg-type]
444
prompt = self._tokenize_prompt(prompt, params)
445
@@ -466,6 +471,11 @@ class BaseRenderer(ABC, Generic[_T]):
471
params: TokenizeParams,
472
) -> SingletonTokPrompt:
473
if "prompt_token_ids" not in prompt and "prompt_embeds" not in prompt:
474
+ if not isinstance(prompt.get("prompt"), str):
475
+ raise TypeError(
476
+ "Expected prompt['prompt'] to be a string before tokenization; "
477
+ "use 'prompt_token_ids' for token ID inputs"
478
+ )
479
prompt = params.apply_pre_tokenization(self.tokenizer, prompt) # type: ignore[arg-type]
480
prompt = await self._tokenize_prompt_async(prompt, params)
481
vllm/renderers/inputs/preprocess.py
+20
-1
@@ -4,7 +4,7 @@ Schemas and utilities for preprocessing inputs.
4
5
# SPDX-License-Identifier: Apache-2.0
6
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
7
-from collections.abc import Sequence
7
+from collections.abc import Mapping, Sequence
8
from typing import TYPE_CHECKING, NamedTuple, TypeAlias, TypedDict, overload
9
10
from vllm.inputs import (
@@ -116,6 +116,19 @@ that has been standardized into a dictionary.
116
"""
117
118
119
+def _validate_prompt_dict(prompt: Mapping[str, object]) -> None:
120
+ """Reject malformed dict prompts before renderer tokenization."""
121
+ if (
122
+ "prompt" not in prompt
123
+ or "prompt_token_ids" in prompt
124
+ or "prompt_embeds" in prompt
125
+ ):
126
+ return
127
+
128
+ if not isinstance(prompt["prompt"], str):
129
+ raise TypeError("Prompt text should be a string")
130
+
131
+
132
def parse_dec_only_prompt(prompt: PromptType | object) -> DecoderOnlyDictPrompt:
133
"""
134
Parse a prompt for a decoder-only model and normalize it to a dictionary.
@@ -133,6 +146,8 @@ def parse_dec_only_prompt(prompt: PromptType | object) -> DecoderOnlyDictPrompt:
146
if "encoder_prompt" in prompt:
147
raise TypeError("Cannot pass encoder-decoder prompt to decoder-only models")
148
149
+ _validate_prompt_dict(prompt)
150
+
151
if (
152
"prompt" in prompt
153
or "prompt_token_ids" in prompt
@@ -156,6 +171,8 @@ def _parse_enc_prompt(prompt: PromptType | object) -> EncoderDictPrompt:
171
return TokensPrompt(prompt_token_ids=prompt)
172
173
if isinstance(prompt, dict):
174
+ _validate_prompt_dict(prompt)
175
+
176
if "prompt_embeds" in prompt:
177
raise TypeError("Cannot pass embeddings prompt to encoder-decoder models")
178
@@ -178,6 +195,8 @@ def _parse_dec_prompt(prompt: PromptType | object) -> DecoderDictPrompt:
195
return TokensPrompt(prompt_token_ids=prompt)
196
197
if isinstance(prompt, dict):
198
+ _validate_prompt_dict(prompt)
199
+
200
if "prompt_embeds" in prompt:
201
raise TypeError("Cannot pass embeddings prompt to encoder-decoder models")
202