test: guard prompt-update helpers against upstream removal
The MiniCPM-o select_text breakage cost eight CI jobs and surfaced only as "Engine core initialization failed", because the replacement closures that call these helpers are not driven by any unit test. A static check is what actually catches the next removal. Resolves each PromptUpdateDetails attribute referenced under vllm_omni/ against the installed class, so it tracks upstream instead of a hard-coded list, and names the offending file in the failure. Verified by reintroducing select_text: the guard fails with that file named, and passes once reverted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146Xu5Sgj4vXxLxvLjV5YPj Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
This commit is contained in:
1 parent
c1e32fc01d
commit
85d1f92ee5
1 file changed
+64
@@ -0,0 +1,64 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
"""Guard that Omni only calls prompt-update helpers upstream still provides.
|
||||
|
||||
vLLM 0.29 removed ``PromptUpdateDetails.select_text`` in favour of the
|
||||
token-oriented ``select_token_id`` / ``select_token_ids``. Three MiniCPM-o call
|
||||
sites survived the rebase and only failed at engine-core startup, where the
|
||||
traceback surfaced as an opaque "Engine core initialization failed" across eight
|
||||
CI jobs.
|
||||
|
||||
These helpers are referenced from replacement closures that no unit test drives,
|
||||
so a static check is what actually catches the next removal. It resolves the
|
||||
attribute against the installed class rather than a hard-coded list, so it
|
||||
follows upstream automatically.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from vllm.multimodal.processing.processor import PromptUpdateDetails
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
_OMNI_ROOT = Path(__file__).resolve().parents[2] / "vllm_omni"
|
||||
_GUARDED = "PromptUpdateDetails"
|
||||
|
||||
|
||||
def _referenced_attributes() -> dict[str, set[str]]:
|
||||
"""Map ``PromptUpdateDetails.<attr>`` -> source files referencing it."""
|
||||
found: dict[str, set[str]] = {}
|
||||
for path in _OMNI_ROOT.rglob("*.py"):
|
||||
try:
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||
except (SyntaxError, UnicodeDecodeError): # pragma: no cover - defensive
|
||||
continue
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name) and node.value.id == _GUARDED:
|
||||
found.setdefault(node.attr, set()).add(str(path.relative_to(_OMNI_ROOT.parent)))
|
||||
return found
|
||||
|
||||
|
||||
def test_omni_only_uses_prompt_update_helpers_that_exist_upstream() -> None:
|
||||
referenced = _referenced_attributes()
|
||||
assert referenced, "expected Omni to reference PromptUpdateDetails somewhere"
|
||||
|
||||
missing = {attr: sorted(files) for attr, files in referenced.items() if not hasattr(PromptUpdateDetails, attr)}
|
||||
|
||||
assert not missing, (
|
||||
"Omni calls PromptUpdateDetails helpers that the installed vLLM does not "
|
||||
f"provide: {missing}. Upstream moved prompt updates to token ids; use "
|
||||
"select_token_id/select_token_ids with the encoded text."
|
||||
)
|
||||
|
||||
|
||||
def test_token_oriented_helpers_are_the_ones_upstream_exposes() -> None:
|
||||
"""Pin the replacements, so their removal fails here and not at startup."""
|
||||
assert hasattr(PromptUpdateDetails, "select_token_id")
|
||||
assert hasattr(PromptUpdateDetails, "select_token_ids")
|
||||
assert not hasattr(PromptUpdateDetails, "select_text"), (
|
||||
"select_text is back upstream; re-check the MiniCPM-o replacements."
|
||||
)
|
||||
Reference in new issue
Block a user