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