Skip to content

[CI/Build][MiniCPM-o] Derive duplex admission-probe limit from the deploy config - #6678

Merged
amy-why-3459 merged 4 commits into
vllm-project:mainfrom
dshah1333:fix-6671-admission-limit
Aug 27, 2026
Merged

[CI/Build][MiniCPM-o] Derive duplex admission-probe limit from the deploy config#6678
amy-why-3459 merged 4 commits into
vllm-project:mainfrom
dshah1333:fix-6671-admission-limit

Conversation

@dshah1333

Copy link
Copy Markdown
Contributor

Purpose

Fixes #6671.

test_duplex_admission_and_expiry_reaper times out on both CUDA and NPU:

FAILED tests/e2e/online_serving/test_minicpmo_4_5_duplex_expansion.py::test_duplex_admission_and_expiry_reaper[three-stage-single-gpu] - TimeoutError

My read is the server is fine here and the test just carries a stale number.

The test hardcodes verify_admission_limit = 2, so the probe opens 2 sessions and waits for the 3rd to be rejected. #6619 repointed this test from minicpmo_4_5_duplex.yaml, which sets duplex_session.max_sessions to 2, to the shipping minicpmo_4_5.yaml, which sets it to 4. With room for 4 the 3rd session is admitted, no error event ever arrives, and _receive_until blocks until the timeout.

The capacity flows deploy yaml -> omni_config.py:1424 -> serving.py:916 -> duplex_session.py:404, and that last one only rejects once len(_sessions) >= max_sessions.

So this reads the limit from the deploy config the test starts the server with instead of hardcoding it. The number then lives in one place and a later capacity change can't turn into an unrelated probe timeout.

Test Plan

vLLM Version: 0.27.0

vLLM-Omni Commit: a57246e (see the note under Test Result)

Served MiniCPM-o 4.5 on 1x H100 80GB from the unmodified shipping yaml, then ran the test's own run_lifecycle_probes admission probe at both limits:

vllm-omni serve openbmb/MiniCPM-o-4_5 --omni \
  --deploy-config vllm_omni/deploy/minicpmo_4_5.yaml \
  --trust-remote-code --host 0.0.0.0 --port 8099
import asyncio, sys
sys.argv = ["drv", "--url", "ws://127.0.0.1:8099/v1/realtime?duplex=1",
            "--model", "openbmb/MiniCPM-o-4_5",
            "--input-wav", "tests/assets/minicpmo_4_5/response_required_16k.wav",
            "--ref-audio", "<ref_audio>.wav",
            "--verify-admission-limit", "2",   # then again with 4
            "--timeout-s", "40", "--output-dir", "/tmp/probe"]
from tests.e2e.online_serving.run_minicpmo_realtime_duplex_multi_session import (
    parse_args, run_lifecycle_probes)
print(asyncio.run(run_lifecycle_probes(parse_args())))

Test Result

probe limit what it means result
2 what the test hardcodes today TimeoutError, same as #6671
4 what the deploy yaml configures ok=True, overflow_error_code=resource_exhausted

Server log at the configured capacity:

open_duplex_session rejected: duplex_session_capacity_exhausted: limit=4
error: {'code': 'resource_exhausted', 'message': 'duplex_session_capacity_exhausted: limit=4', 'retryable': True}

ruff check and ruff format --check pass on the 3 changed files.

Two notes on how I validated this:

  • I ran it on a57246e with vLLM 0.27.0 rather than current main, because main needs vLLM 0.28.0 and my box wasn't on it yet. The capacity check in duplex_session.py is identical between that commit and main, and the shipping yaml has max_sessions: 4 in both, so I think the result carries over. Happy to rerun on main if you'd rather see that.
  • The limit-4 arm needs a freshly started server. A probe that ends in a timeout leaves one accepted session alive until the reaper runs, which eats capacity and skews the next arm. That caught me the first time round.

…ploy config

test_duplex_admission_and_expiry_reaper hardcoded verify_admission_limit=2, so
the admission probe opened 2 sessions and waited for the 3rd to be rejected.

PR vllm-project#6619 repointed this test from minicpmo_4_5_duplex.yaml, which sets
duplex_session.max_sessions to 2, to the shipping minicpmo_4_5.yaml, which sets
it to 4. With room for 4 the 3rd session is admitted, no error event is emitted,
and _receive_until blocks until the timeout:

    FAILED tests/e2e/online_serving/test_minicpmo_4_5_duplex_expansion.py::test_duplex_admission_and_expiry_reaper[three-stage-single-gpu] - TimeoutError

Read the capacity from the deploy config the test starts the server with, so the
limit lives in one place and a later capacity change cannot turn into an
unrelated admission-probe timeout.

Fixes vllm-project#6671

Signed-off-by: Deep Shah <deep@socratic.co>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@vllm-omni-review-bot

Copy link
Copy Markdown

This PR appears to be related to model: minicpm.

Model owners: @y-null

@dshah1333, 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.

@amy-why-3459

Copy link
Copy Markdown
Collaborator

@natureofnature @ZacheryAU PTAL

@amy-why-3459

Copy link
Copy Markdown
Collaborator

Approve with a couple of should-fixes.

The timeout in #6671 is the probe, not the server. _admission_probe opens limit sessions and then waits for the next one to emit error. The manager only rejects when len(_sessions) >= max_sessions. #6619 pointed this test at shipping minicpmo_4_5.yaml (duplex_session.max_sessions: 4) while the probe stayed hardcoded at 2, so session 3 is admitted and _receive_until(..., "error") blocks until the timeout. The H100 table (limit=2 reproduces, limit=4 returns resource_exhausted / limit=4) is enough. Reading the deploy config is the right fix — hardcoding 4 would just drift again.

Please load the same object the server uses. get_deploy_duplex_max_sessions safe_loads the named file and re-implements defaulting. That matches this yaml (the field is top-level, no base_config). It will not match an overlay such as minicpmo_4_5_duplex.yaml, or a file that inherits capacity from its base. Use load_deploy_config(get_deploy_config_path(rel_path)).duplex_session.max_sessions so merge + dataclass defaults stay in lockstep.

Also add a CPU unit test that does not boot a server: minicpmo_4_5.yaml → 4, a yaml with no duplex_session → 1 (and the duplex overlay → 2 if you switch to load_deploy_config). Otherwise the next capacity edit only shows up as another nightly timeout. Post the author self-review the bot asked for: 4 vs 2, probe limit must equal the manager check, expiry runs before admission, and a timed-out probe leaves a live session until the reaper (fresh server between arms).

Export the new accessor and raise on a declared but invalid
duplex_session.max_sessions instead of silently falling back to the
runtime default, which would test a limit the server never applies.

Signed-off-by: Deep Shah <deep@socratic.co>
@dshah1333
dshah1333 force-pushed the fix-6671-admission-limit branch from 686a3a8 to 43894b8 Compare August 27, 2026 09:33
Comment thread tests/helpers/stage_config.py Outdated
def get_deploy_duplex_max_sessions(rel_path: str, default: int = 1) -> int:
"""Return ``duplex_session.max_sessions`` from a deploy yaml.

``default`` mirrors ``DuplexSessionConfig.max_sessions`` so a deploy config

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe DuplexSessionRuntimeConfig rather than DuplexSessionConfig?

…loader

Review follow-up. get_deploy_duplex_max_sessions re-implemented defaulting on
top of a raw safe_load, which matches minicpmo_4_5.yaml but not a config that
inherits capacity from its base_config. Load through load_deploy_config so
base_config merging and DuplexSessionRuntimeConfig defaults stay in lockstep
with the server.

Add a CPU test over the shipped deploy configs covering a declared capacity, a
config that declares none, and an overlay inheriting one from its base, so a
capacity edit surfaces there instead of as another nightly admission timeout.

Signed-off-by: Deep Shah <deep@socratic.co>
@dshah1333
dshah1333 force-pushed the fix-6671-admission-limit branch from 43894b8 to ce23fca Compare August 27, 2026 09:37
@NumberWan

Copy link
Copy Markdown
Contributor

LGTM

@dshah1333

Copy link
Copy Markdown
Contributor Author

Self-review:

  • Root cause: the probe's expected limit has to equal what the session manager enforces. [Bugfix][MiniCPM-o] Serve native duplex from shipping YAMLs and fence… #6619 moved this test onto shipping minicpmo_4_5.yaml (max_sessions: 4) while the probe stayed hardcoded at 2, so session 3 is admitted and _receive_until(..., "error") blocks. Rejection only fires at duplex_session.py:404 once len(_sessions) >= max_sessions.
  • Ran the failing node itself on 1x H100 with vLLM 0.28.0: test_duplex_admission_and_expiry_reaper[three-stage-single-gpu] passes in 411s, and the server logs the overflow session rejected with duplex_session_capacity_exhausted: limit=4. Before the fix, the same probe at limit 2 reproduces the TimeoutError.
  • Switched the helper to load_deploy_config as you asked, so base_config merging and DuplexSessionRuntimeConfig defaults come from the same object the server uses. I dropped my own default and validation rather than keep a second copy. They weren't identical: mine also rejected bool and non-int, the dataclass only rejects <= 0. The helper now inherits exactly the server's rules.
  • CPU test covers three shipped configs: minicpmo_4_5.yaml -> 4, minicpmo_4_5_8x4090.yaml -> 1 (declares none), minicpmo_4_5_3gpu_stage1_replicas.yaml -> 4 (inherits via base_config). 15 passed in that file, 534 in tests/config/.
  • On your third case, minicpmo_4_5_duplex.yaml -> 2: that file was removed in [Bugfix][MiniCPM-o] Serve native duplex from shipping YAMLs and fence… #6619, so I used the 3-GPU overlay to cover inheritance instead. Happy to change it if you meant something else.
  • Two lifecycle notes from reproducing: expiry runs before admission in run_lifecycle_probes, and a timed-out probe leaves one accepted session alive until the reaper, so arms need a fresh server in between.

@amy-why-3459 amy-why-3459 added the ready label to trigger buildkite CI label Aug 27, 2026
@amy-why-3459
amy-why-3459 merged commit 069e6c5 into vllm-project:main Aug 27, 2026
6 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready label to trigger buildkite CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Nightly / CI failed - MiniCPM-o 4.5 duplex admission and expiry reaper - TimeoutError on CUDA and NPU

5 participants