Repository navigation
[Bugfix] Consume prompt embedding sentinel IDs before model execution - #64367
DarkLight1337 merged 4 commits into
Conversation
Replace mixed prompt embedding sentinel IDs with safe dummy IDs after locating their spans, preserving text IDs and embedding offsets. Add renderer and TP=1 serving regressions, and document mixed image and prompt embedding inputs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Sansom <andrew@protopia.ai>
|
Documentation preview: https://vllm--64367.org.readthedocs.build/en/64367/ |
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the changes supporting mixed prompt-embedding and image requests.
No open issues 🎉
What changed in this PR
This PR fixes mixed prompt-embedding and image requests by replacing temporary embedding sentinel IDs in the Hugging Face renderer before token validation and model execution.
Changes:
- Normalize only prompt-embedding spans to safe dummy token IDs, preserving surrounding text, embedding tensors, and offsets.
- Add renderer and serving regressions covering vocabulary bounds, multiple embedding spans, and rejection of invalid ordinary token IDs.
- Document support for mixing prompt embeddings with images.
| File | Description |
|---|---|
vllm/renderers/hf.py |
Replaces embedding sentinel IDs with zeros in engine input. |
tests/renderers/test_hf.py |
Checks safe token IDs, unchanged text, and preserved embedding offsets. |
tests/entrypoints/openai/chat_completion/test_chat_completion_with_prompt_embeds.py |
Tests mixed image/embedding serving and invalid-token rejection. |
docs/features/prompt_embeds.md |
Explains mixed image inputs and dummy-token replacement. |
Review effort: Balanced
|
@DarkLight1337 This is a simple (2 line) patch in the HF Renderer to better handle the added prompt embedding placeholder token id in a few places (when validating token ids and also when sending those token ids to the parallel Vocab Embedding layer). I added several tests because this is kinda a weird code path. A bunch of independent conditions can each mask this issue (TP, sending only prompt embeds and/or text, a MM model that has extra token ids reserved in the model definition that aren't in the tokenizer, etc...), so I wanted to have very precise regression tests to make sure that this was exercised. |
|
/ci run |
|
✅ Triggered Buildkite CI #98204 for commit ℹ️ This PR is 14 commits behind upstream |
Overview
Fix chat requests that mix prompt embeddings and images when the prompt-embedding placeholder ID is outside the model vocabulary. Consume these temporary IDs during preprocessing instead of passing them to token validation and embedding lookup.
Discovered while helping validate #58658 and #63190, but this is a distinct issue: the fix consumes temporary placeholder IDs in the renderer, rather than changing tower/connector LoRA or prompt-embedding passthrough handling in the runner.
Claims
Validation
HF_HUB_OFFLINE=1 .venv/bin/python -m pytest \ tests/renderers/test_hf.py::test_apply_prompt_embeds_keeps_engine_token_ids_in_vocabulary -q PATH="$PWD/.venv/bin:$PATH" CUDA_VISIBLE_DEVICES=0 HF_HUB_OFFLINE=1 \ .venv/bin/python -m pytest \ tests/entrypoints/openai/chat_completion/test_chat_completion_with_prompt_embeds.py::test_prompt_embeds_with_image -vThe serving regression was also run with only the two-line normalization change present, with no tokenizer-cache changes: 1 passed. The relocated renderer regression passed: 1 passed. All commit hooks passed, including Ruff, mypy, Markdown lint, and test-tethering checks.
Model validation is limited to the generation smoke test and the submitter's Gemma and Nemotron reproducers; no accuracy or performance benchmark was run.
The original failure was reproduced locally with:
nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16Google/gemma-4-E2B-itHuggingFaceTB/SmolVLM-256M-InstructThe original failure was not observable with
Qwen/Qwen3.5-0.8B,Qwen/Qwen3.5-2B,LiquidAI/LFM2.5-VL-450M, orllava-hf/llava-onevision-qwen2-0.5b-ov-hf. Qwen3.5 and LFM2.5 have model-vocabulary headroom for the newly registered sentinel, so the stale cached vocabulary bound does not reject it. These are locally checked examples, not an exhaustive affected-model list.Thanks to @Gatedd and @ajf-codes for helping me identify the issue!
Manual reproducer: mixed prompt embeddings and image input
Start a server with
--enable-prompt-embeds, using the same model asMODEL_NAMEbelow. TP=1 also exposes the unsafe embedding lookup if the validation rejection is bypassed without consuming the sentinel.Save the following script locally and run it with
.venv/bin/python mre.py. It downloads the tokenizer and the checkpoint shard containing the token embedding weights, constructs real prompt embeddings, and sends them alongside an image. The image URL requires network access. SetMODEL_NAMEto another listed model and restart the server to compare models.Details
The mixed-input path already records the positions and tensors needed for embedding splicing, but leaves the temporary sentinel IDs in
prompt_token_ids. A newly registered sentinel can exceed the cached tokenizer's vocabulary bound, causing HTTP 400. If validation accepts the sentinel, an out-of-range model embedding lookup can instead crash before the supplied embedding is merged. TP>1 shard masking can hide that latter failure.After locating all spans, replace their token IDs with
0and write the updated list back to the engine input. The existing multimodal merge overwrites those dummy embeddings with the supplied tensors. Ordinary token IDs are not changed, so their validation remains intact.The production change is two added lines. Tests cover two embedding spans of different lengths and real TP=1 image/embedding serving; documentation describes mixed image and prompt-embedding inputs.
Duplicate-work checks found no matching open upstream PR. The related #51123 addresses LoRA batch-shape handling in the runner, not mixed-input sentinel consumption in the renderer.
AI assistance was used to investigate, implement, and test this change.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.