Repository navigation
fix(duplex): a control timeout maps to server_error, not invalid_request_error - #9082
alexisromtz-lab wants to merge 1 commit into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
This PR appears to belong to: docs/design/module/entrypoints.md. Module owners: @alex-jw-brooks @linyueqian @NickCao Routing: @alex-jw-brooks via module of the changed files, module named in the PR description, CODEOWNERS; @linyueqian via module of the changed files, module named in the PR description, CODEOWNERS; @NickCao via module of the changed files, module named in the PR description, CODEOWNERS @alexisromtz-lab, 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. |
Omni ReviewBot triage noteResolved as of |
|
Self-review complete. I verified the following:
Ready for maintainer review. |
hsliuustc0106
left a comment
There was a problem hiding this comment.
TODO: include the focused regressions in this fix, rather than a follow-up.
@alexisromtz-lab, head 5173de6d99bbafd02ac56ceefc24f1ba544f0690 changes only the timeout error mapping and serving open-session budget, with no regression tests in the diff.
- Add a test through the actual error-envelope serialization asserting a timeout is emitted as
server_error, and a serving-open test with a recording fake asserting the explicit 30-second budget reachesopen_session. - Show the focused command, environment, base-fail/head-pass result and fix SHA. Your disclosed macOS/runtime limitation is noted; if these cannot run locally, state the remaining execution gap rather than treating syntax checks as behavioral coverage.
Acceptance: each test fails when its corresponding production change is reverted. This is a scoped triage/evidence request, not a completed substantive review or a demonstrated runtime defect. No local tests executed. AI assistance: OpenAI assistant prepared this review.
5173de6 to
3b79991
Compare
|
Thanks @hsliuustc0106 — both points are addressed and the branch is rebased. Rebase. Now on 1. Focused regressions, in this PR
2. Base-fail / head-pass Focused command, macOS arm64 · CPython 3.14.7 · no
That check loads the real Remaining execution gap — stated, not papered over
Fix SHAs: Disclosure: the rebase, the two tests and the measurements above were prepared with an AI assistant; the diffs were read and the commands above were run and pasted as-is. |
5e90343 to
3e7827e
Compare
|
Thanks @hsliuustc0106 — the focused regression tests are on the branch now (head
Execution evidence — and exactly where it stopsThis workstation is macOS arm64, so there is no vLLM runtime here and the full suite cannot run. Ran, on the real sources, base vs head:
To get the first one to import I had to register Not run: the open-budget test. Its import chain reaches real vLLM through To close it on a machine with vLLM: pytest -q tests/engine/duplex/test_duplex_events.py -k timeout
pytest -q tests/entrypoints/duplex/test_duplex_serving.py -k open_budget
# base-fail: drop the explicit budget and re-run
git checkout c60936d~1 -- vllm_omni/entrypoints/duplex/serving.py
pytest -q tests/entrypoints/duplex/test_duplex_serving.py -k open_budget # expect FAILPoint me at the job that collects Two more things I am fixing now: the DCO check is red because none of the three commits carries a |
|
Regarding my previous comment, last paragraph: Correction to my previous comment: After I posted this, I squashed the commits locally and added the missing |
3e7827e to
6a55cd2
Compare
|
Correction to the fix SHAs in my two previous comments: the commits I cited there (
All three commits now carry a The remaining execution gap is unchanged and still open: the envelope regression was exercised against the real |
|
@hsliuustc0106 — the open-budget regression now has a red/green run behind it, not only an assertion. This closes the execution gap I left open in my previous two comments. Ran on head
What is and is not substituted. There is no vLLM wheel for macOS arm64, so Everything the two assertions touch is the real file: Nothing new to push with this comment — the branch already carries both tests ( Still open, unchanged: no PR job collects Disclosure: measured with an AI assistant; the commands are pasted as run. |
linyueqian
left a comment
There was a problem hiding this comment.
Reviewed 6a55cd2. Mapping the engine's timeout code to server_error is a real fix: duplex_omni_engine.py raises DuplexSessionError(code="timeout"), and without an entry realtime_error_type reported it as invalid_request_error. One point inline about the open budget overlapping with #9107.
Static review only; I did not run the tests locally.
| # allocates the id and announces it in ``session.created``. | ||
| try: | ||
| handle = await self._omni.open_session(session_payload) | ||
| handle = await self._omni.open_session(session_payload, timeout=_DEFAULT_OPEN_TIMEOUT_S) |
There was a problem hiding this comment.
[important] #9107 fixes the same cold-start open timeout (#9072) by raising the engine's default open budget to 60 s. Passing an explicit 30 s here overrides that default for every websocket session, so a cold first open that takes 30 to 60 s would still fail through this endpoint once both land. Could you drop the explicit timeout here and rely on the engine default, keeping the timeout error mapping, or coordinate one shared constant with #9107?
There was a problem hiding this comment.
@linyueqian done, and thank you — option one, not a shared constant.
The explicit budget is gone and serving.py is out of the diff entirely: main already calls open_session(session_payload) (line 254), so after rebasing onto c704a28 the only change left here is the timeout error mapping plus its regressions.
Why dropping it is the correct half of your point: #9107 is merged and open_session now defaults to _DEFAULT_OPEN_TIMEOUT_S = 60.0 (vllm_omni/entrypoints/duplex_omni.py:55), while _DEFAULT_CONTROL_TIMEOUT_S stayed at 10.0 for close/resume/touch. Passing 30 s from the endpoint would have shadowed the 60 s budget for every websocket session and reproduced #9072 through this entrypoint — exactly what you described.
One distinction kept on purpose: a bare timeout becomes server_error, but config_timeout stays invalid_request_error, because that one is the callers own config budget rather than the engine failing to answer. The test now pins both, so the next person cannot collapse them by accident.
…est_error `duplex_omni_engine.py` raises `DuplexSessionError(code="timeout")` when the open or an in-session control RPC runs out of budget. The table had no bare `timeout` entry, so `realtime_error_type()` fell back to `invalid_request_error`: a server-side timeout was reported to the client as a malformed request, and a correct retry policy backs off permanently on a condition that is transient. `config_timeout` is deliberately left as a client error: that one is the caller's own config budget, not the server failing to answer. The explicit 30 s open budget this branch also carried is dropped. vllm-project#9107 gave `open_session` its own 60 s default (`_DEFAULT_OPEN_TIMEOUT_S`), so passing 30 s from `serving.py` would override it for every websocket session; `main` now calls `open_session(session_payload)` and inherits the engine default, per @linyueqian's review. The fix in this PR is the error mapping only. The regressions ride with the fix, not in a follow-up: `test_error_event_maps_codes_to_openai_error_types_and_echoes_client_event_id` pins the table entry and the bucket as it leaves through `ErrorEvent.to_realtime()`, which is the shape a client actually reads. Both assertions fail when the production line is reverted. Focused run, exact assertions added to that test, executed against the real `vllm_omni.protocol.{duplex,realtime}` modules (`vllm` has no macOS wheel, so the pure-Python module unit is exercised directly instead of through pytest): - base `c704a28` (upstream/main): FAIL - `tabla[timeout] = None`, `wire.error.type = 'invalid_request_error'` (2 of 4 assertions). - head: PASS - 4/4. Not run: `pytest tests/engine/duplex/` needs a CUDA host and `vllm` installed. That execution gap remains; this commit does not claim it as coverage. Signed-off-by: Jose Alexis Roman Martinez <alexis_romtz@hotmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6a55cd2 to
4ccab26
Compare
|
Re: the two boxes in @hsliuustc0106's review — both are closed in head - [x] Regression tests ride with the fix, not in a follow-up. - [x] Focused command, environment, base-fail/head-pass, fix SHA. Environment: macOS, CPython 3.14, no CUDA. The base run is the acceptance criterion: revert the Remaining execution gap, stated rather than covered: there is still no run of AI assistance: Copilot (DeepSeek) drafted the code, the test and this text under my direction. I reviewed the diff, verified the base-fail/head-pass pair above, and I take responsibility for the change. |
What this PR does
vllm_omni/protocol/duplex/errors.py— adds"timeout": "server_error"toREALTIME_ERROR_TYPES_BY_CODE.duplex_omni_engine.pyraisesDuplexSessionError(code="timeout")when the open or an in-session control RPC runs out of budget. Onmainthe table has no baretimeoutkey (grep -c '^ "timeout":'= 0), sorealtime_error_type()falls back toinvalid_request_errorand a server-side timeout is reported to the client as a client error. A correct retry policy therefore backs off permanently on a condition that is transient.config_timeoutis deliberately left asinvalid_request_error: that one is the caller's own config budget, not the engine failing to answer. The test pins both so they cannot be collapsed by accident.Scope change since the first push
The earlier head (
6a55cd2) also opened the session with an explicittimeout=_DEFAULT_OPEN_TIMEOUT_S(30 s) inserving.py. That is gone:#9107merged and gaveopen_sessionits own budget,_DEFAULT_OPEN_TIMEOUT_S = 60.0(vllm_omni/entrypoints/duplex_omni.py:55), while_DEFAULT_CONTROL_TIMEOUT_Sstayed at 10.0 for close/resume/touch.mainalready callsopen_session(session_payload)with no override (serving.py:254), so after rebasing there is nothing to change there. Per @linyueqian's review, the PR is now the error mapping only.git diff --numstat upstream/main: 2 files, +17 / -0 (errors.py+6,test_duplex_events.py+11).Tests
tests/engine/duplex/test_duplex_events.py::test_error_event_maps_codes_to_openai_error_types_and_echoes_client_event_idasserts the table entry, then the bucket as it leaves throughErrorEvent.to_realtime()— the envelope a client actually parses, not only the dict lookup. Both assertions fail when theerrors.pyline is reverted.Evidence
Environment: macOS, CPython 3.14, no CUDA.
vllmhas no macOS wheel, sopytest tests/engine/duplex/cannot run here. The assertions were run against the realvllm_omni.protocol.{duplex,realtime}modules with the package root stubbed —vllm_omni/__init__.pyis the only thing that pullsvllm(viapatch.py), so the module under test is loaded unmodified:Execution gap, stated rather than covered: none of the checks attached to this PR runs
tests/engine/duplex/. That suite needs a CUDA host withvllminstalled, and the four assertions above are not a substitute for it.AI assistance: Copilot (DeepSeek) drafted the code, the test and this text under my direction. I reviewed the diff, verified the base-fail/head-pass pair above, and I take responsibility for the change.