Skip to content

[Refactor] Use rendered prompt for turn based /v1/realtime auto truncation - #8292

Closed
NickCao wants to merge 3 commits into
vllm-project:mainfrom
NickCao:codex/realtime-rendered-truncation
Closed

NickCao wants to merge 3 commits into
vllm-project:mainfrom
NickCao:codex/realtime-rendered-truncation

Conversation

@NickCao

@NickCao NickCao commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Purpose

So it works with all models not only qwen3 omni.

Test Plan

vLLM Version: 0.30.0

vLLM-Omni Commit: b5b7f27

pytest -sv tests/entrypoints tests/engine -m 'core_model and cpu'
pytest -sv tests/entrypoints -m "advanced_model and cuda and H100" --run-level "advanced_model" --deselect tests/entrypoints/openai_api/test_qwen3_omni_realtime_websocket.py::TestQwen3OmniRealtimeWebSocket::test_server_vad_multi_turn_without_client_commit

Test Result

3610 passed, 5 skipped, 134 deselected, 1 xfailed, 29 warnings in 186.64s (0:03:06)
15 passed, 2364 deselected, 22 warnings in 898.27s (0:14:58)

@vllm-omni-review-bot

Copy link
Copy Markdown

This PR appears to belong to: docs/design/module/profiling.md, docs/design/module/entrypoints.md, docs/design/module/observability.md.

Module owners: @alex-jw-brooks @linyueqian @gcanlin

Routing: @alex-jw-brooks via module of the changed files, semantic router, CODEOWNERS; @linyueqian via module of the changed files, semantic router, CODEOWNERS; @gcanlin via module of the changed files

@NickCao, please review your own changes and leave a short self-review comment describing what you checked. PRs without author self-review may not be assigned a reviewer.

Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment.

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot routing record

Assigned Direct under experiment vllm-omni-strict-5050-20260829.

@vllm-omni-review-bot vllm-omni-review-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omni ReviewBot review

PR description

Realtime /v1/realtime turn-based auto truncation no longer estimates token budget with Qwen3-Omni-specific audio math. It builds the chat prompt, renders it through the engine renderer with max_total_tokens=None, and truncates conversation history until the rendered prompt_token_ids length fits the model budget. The same rendered EngineInput is then passed to engine.generate, so admission and execution share one token count. A new CPU unit test pins truncation driven by renderer expansion rather than raw text tokens.

Change flow

flowchart TD
  A["[EXISTING] response.create handler"]:::existing
  B["[CHANGED] cancel then _prepare_engine_input_with_auto_truncation"]:::changed
  C["[NEW] _render_engine_input via render_cmpl_async"]:::new
  D["[CHANGED] generate uses rendered EngineInput"]:::changed
  E["[NEW] test_realtime_prompt_preflight"]:::new
  A --> B
  B --> C
  C --> D
  E -.-> C
  classDef existing fill:#e5e7eb,stroke:#6b7280,color:#111827
  classDef changed fill:#fef3c7,stroke:#d97706,color:#451a03,stroke-width:2px
  classDef new fill:#dcfce7,stroke:#16a34a,color:#052e16,stroke-width:2px
  classDef removed fill:#fee2e2,stroke:#dc2626,color:#450a0a,stroke-width:2px
Loading

CI at 2ce95e6014a5 (2026-09-29T20:10:57.237082+00:00): required check(s) blocking: buildkite/vllm-omni (missing).

See inline comments below.


🤖 This review was generated by InferMatrix Copilot, an open-source repo-maintenance agent for PR review, CI debugging and issue triage. Try it on your own repo, and ⭐ star it if it helped!

@vllm-omni-review-bot

Copy link
Copy Markdown

Omni ReviewBot: finding feedback

[p1] Cancels in-flight response before admission can fail — vllm_omni/entrypoints/openai/realtime/connection.py:577

_handle_response_create now calls _cancel_active_response() before _prepare_engine_input_with_auto_truncation. On the old path, truncation/admission ran first and only cancelled after a successful admit, so a rejected over-budget response.create left the prior generation running. On this head, an over-limit or VLLMValidationError path still aborts the active response (lines 577–590 return after cancel), so clients lose a valid in-progress turn solely because the replacement request cannot start. Restore cancel-after-successful-preflight (or re-admit / avoid cancel until engine_input is non-None), and add a regression that proves a failing create does not cancel an existing active response.

This finding appears in the bot's COMMENT review, but GitHub could not place it as an inline diff comment. If you are the PR author and disagree, react 👎 to this comment. The disagreement will be shown to the maintainer; it does not approve or merge the PR.

@vllm-omni-review-bot

Copy link
Copy Markdown

Omni ReviewBot: finding feedback

[p1] PR Test Plan is a subset of the covering CPU entrypoints guard — tests/entrypoints/openai_api/test_realtime_prompt_preflight.py:14

Changed production path vllm_omni/entrypoints/openai/realtime/connection.py and new test tests/entrypoints/openai_api/test_realtime_prompt_preflight.py (markers core_model + cpu) are covered by Buildkite ready step "Simple · Engine&Entrypoints Test" which runs pytest -sv tests/entrypoints tests/engine -m 'core_model and cpu' (.buildkite/cuda/test-ready.yml). The PR Test Result only reports pytest tests/entrypoints/openai_api/test_realtime_prompt_preflight.py (1 passed) and does not state that the rest of that guard selector was skipped. Either run the guard command (or an equivalent tests/entrypoints tests/engine -m 'core_model and cpu' sweep) or explicitly document the uncovered remainder in the Test Plan. Frozen CI note: required buildkite/vllm-omni was missing at checked_at 2026-09-29T20:10:57Z (not treated as a code defect).

This finding appears in the bot's COMMENT review, but GitHub could not place it as an inline diff comment. If you are the PR author and disagree, react 👎 to this comment. The disagreement will be shown to the maintainer; it does not approve or merge the PR.

@hsliuustc0106 hsliuustc0106 added frontend code related to entrypoint refactor refactoring for better code scalability and quality labels Sep 29, 2026
@hsliuustc0106 hsliuustc0106 added the high priority high priority issue, needs to be done asap label Sep 30, 2026 — with ChatGPT Codex Connector
NickCao and others added 3 commits September 30, 2026 09:20
…ation

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Run prompt preflight before cancelling an in-flight response so a rejected response.create leaves the current generation intact.

Add a handler regression for an over-limit replacement.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
@NickCao
NickCao force-pushed the codex/realtime-rendered-truncation branch from 88ff5eb to b5b7f27 Compare September 30, 2026 13:20
@NickCao NickCao added the ready label to trigger buildkite CI label Sep 30, 2026
max_model_len = getattr(self.engine.model_config, "max_model_len", None)
persistent = response.input is None
items: list[Any] = s.items if response.input is None else response.input
prompt = await self._build_full_prompt(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work generically, because_build_full_prompt still hardcodes AUDIO_PLACEHOLDER for qwen3omni. The render engine input eventually applies vLLM's multimodal processor (i.e., wraps around this), so because of that, it wont be able to do the multimodal token expansion correctly

instructions=response.instructions,
items=items,
)
engine_input = await self._render_engine_input(prompt)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you benchmarked this? Since rendering is a lot more complex than the arithmetic from before and will redo a lot of the multimodal processing, I think this + rerunning rendering later on will actually cause a lot of scaling issues

parts_text = []
for part in item.content:
if part.type == "input_audio" and part.audio:
parts_text.append(AUDIO_PLACEHOLDER)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it belongs in this PR or separately since things are already hardcoded, but this should probably use get_placeholder_str, because it's implemented on SupportsMultimodal here. So we should be able to look up the unexpanded placeholder for any modality through that, e.g., for qwen3omni it's this

instructions=response.instructions,
items=items,
)
engine_input = await self._render_engine_input(prompt)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, if we're calling rendering, I think it would be more ideal to try to reuse previous rendering. E.g., from the first render of everything, we should have the spans of expanded audio placeholders, and from there, be able to determine what needs to drop without iterative re-rendering with the caveat that things need to be correctly updated after dropping stuff unless you do one final render (since mm placeholders etc will be stale)

@NickCao

NickCao commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Replaced by #8339

@NickCao NickCao closed this Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend code related to entrypoint high priority high priority issue, needs to be done asap ready label to trigger buildkite CI refactor refactoring for better code scalability and quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants