Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a0cf8ea
docker: install vLLM from the precompiled wheel at the rebase baseline
tzhouam Aug 8, 2026
f80609c
ci: sync Dockerfile.ci wheel pin/deps to vLLM 4b25076c8186
tzhouam Aug 8, 2026
bae7df6
rebase: adapt omni modules to vLLM 0.27 upstream API changes
tzhouam Aug 9, 2026
a9da981
rebase: Phase 3 repairs verified green by their own tests
tzhouam Aug 9, 2026
59b9e71
sched: track discarded async tokens via num_stale_output_tokens
tzhouam Aug 9, 2026
efa11ca
tests: mock FusedMoEFactory, the name vLLM 0.27 actually exports
tzhouam Aug 9, 2026
b06e58c
tests: defer the scenarios import so --help does not load the engine
tzhouam Aug 9, 2026
d43ca5a
tests: give _HashableRequest the 0.27 stale-output counter
tzhouam Aug 9, 2026
319f457
tests: point the comfyui api_server at a writable storage dir
tzhouam Aug 9, 2026
4c9b8f3
tests: mark mimo-audio and voxtral-tts expansion tests for their CI jobs
tzhouam Aug 9, 2026
029a839
engine: register omni archs unconditionally so omni classes win the r…
tzhouam Aug 10, 2026
a4a0497
tests: escalate voxtral-tts transcript grading to whisper large-v3
tzhouam Aug 10, 2026
2bb5574
Revert test edits that were made to satisfy drifted harness config
tzhouam Aug 10, 2026
8b2e581
docker: pin torch before flashinfer, not after
tzhouam Aug 10, 2026
ae32ef1
Merge branch 'main' into dev/vllm-align
tzhouam Aug 10, 2026
0dbcbbb
ci: give the Custom Pipeline step 8gb of /dev/shm
tzhouam Aug 10, 2026
e64c054
fix: tolerate absent model_config.model_stage in non-staged runs
tzhouam Aug 10, 2026
0518f05
fix: unwrap MediaWithBytes before handing videos to the HF processor
tzhouam Aug 10, 2026
78028e1
ci: target the released vLLM 0.27.0 base image
tzhouam Aug 11, 2026
d109df4
Revert "synthetic media cache dir write-probe fallback"
tzhouam Aug 11, 2026
701bcc6
fix: accept randomize_inputs in the omni _dummy_run overrides
tzhouam Aug 11, 2026
e3efeaa
fix: return a bare tensor from the qwen3-omni thinker when no capture…
tzhouam Aug 11, 2026
2ebda68
fix: honor size/width/height from extra_args in lingbot dimension res…
tzhouam Aug 11, 2026
06e36eb
fix: coerce numpy arrays to PIL in the text-to-image example output path
tzhouam Aug 11, 2026
213c149
fix: fall back to matched torch SDPA when the FA3 hub kernel has no b…
tzhouam Aug 11, 2026
dedda28
fix: never randomize generation-stage dummy ids during warmup
tzhouam Aug 11, 2026
c382e80
fix: create the diffusion EP group with use_all2all so vLLM 0.27 MoE …
tzhouam Aug 11, 2026
b6956a9
fix: match the diffusers reference backend to the adapter's fallback …
tzhouam Aug 11, 2026
64e8cd3
test: report all bagel SHM probe pixels on mismatch, not just the first
tzhouam Aug 11, 2026
e3b4285
fix: fall back to eager when a regionally compiled block hits a compi…
tzhouam Aug 11, 2026
30603e4
fix: keep the eager-fallback wrapper signature-transparent for cache-dit
tzhouam Aug 11, 2026
5c1fe35
test: re-golden bagel SHM img2img reference pixels to the 0.27-aligne…
tzhouam Aug 11, 2026
6638b5d
test: looser SSIM gate for the SDPA-fallback accuracy path only
tzhouam Aug 11, 2026
9c31928
fix: isolate the compile cache per additional stage replica at spawn
tzhouam Aug 11, 2026
e39f260
test: run both quantization-quality arms eager so LPIPS measures quan…
tzhouam Aug 11, 2026
acf245b
fix: prove factorable symbolic divisibility that torch 2.13's inducto…
tzhouam Aug 11, 2026
de3502c
revert: drop the compiled-block eager fallback; the inductor bug is f…
tzhouam Aug 11, 2026
c59d1fc
Revert "fix: coerce numpy arrays to PIL in the text-to-image example …
tzhouam Aug 11, 2026
6965a5a
Merge branch 'main' into dev/vllm-align
tzhouam Aug 11, 2026
e21bffa
test: pin the stale-drain defects behind the seed-TTS WER regression
tzhouam Aug 11, 2026
c2b6523
fix: seed the stale-output drain in scheduled-token units
tzhouam Aug 11, 2026
2dc5e51
fix: read duplex metrics tolerantly for plain-vLLM benchmark backends
tzhouam Aug 11, 2026
9b4eccc
test: PSNR fallback gate for the SDPA-matched accuracy path
tzhouam Aug 11, 2026
b26b908
Revert "test: PSNR fallback gate for the SDPA-matched accuracy path"
tzhouam Aug 11, 2026
86bee6d
ci: restore h100_2 for the MiniCPM-o 4.5 merge job
tzhouam Aug 11, 2026
a1e8834
refactor: address PR 5976 review comments from Gaohan123
tzhouam Aug 11, 2026
81aecaa
chore: DCO remediation for prior dev/vllm-align commits
tzhouam Aug 11, 2026
25b80bb
style: apply ruff format to gpu_model_runner
tzhouam Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .buildkite/cuda/test-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ steps:
timeout_in_minutes: 50
commands:
- pytest -s -v tests/e2e/offline_inference/test_minicpmo_4_5.py tests/e2e/online_serving/test_minicpmo_4_5.py -m 'advanced_model and cuda' --run-level 'advanced_model'
mirror_hardwares: h100_1
# h100_2, not h100_1: advanced_model asserts transcribe every audio
# response with Whisper, and tests/helpers/media.py only uses an
# accelerator for Whisper when a spare GPU exists. On a single card it
# grades on CPU: ~8-10 min per clip vs ~30 s, which alone overruns the
# 50-minute job timeout (build 2956).
mirror_hardwares: h100_2

- label: "Omni · MiniCPM-o 4.5 Duplex Test"
source_file_dependencies:
Expand Down
15 changes: 15 additions & 0 deletions .buildkite/cuda/test-ready.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,21 @@ steps:
image: public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
always-pull: true
propagate-environment: true
# vLLM 0.27 added a pre-flight capacity check in
# distributed/device_communicators/shm_broadcast.py. Without this
# the container gets Docker's 64 MiB default and every test in
# this step dies at engine start with
# RuntimeError: Insufficient space in /dev/shm:
# 240 MiB required, 64 MiB available
# (9 failed in 8.7s on build #2949). vLLM 0.26 had no such check
# and allocated regardless, so this step passed on main while
# being under-provisioned the whole time — 0.27 turned a silent
# risk into an explicit error.
#
# This is the only inline docker plugin block in .buildkite/cuda/;
# every other step inherits common/ci_mirror_hardwares.yml, which
# already sets 8gb. Matching that rather than inventing a value.
shm-size: "8gb"
environment:
- "HF_HOME=/fsx/hf_cache"
volumes:
Expand Down
21 changes: 19 additions & 2 deletions docker/Dockerfile.ci
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
# Target vllm version: v0.26.0 (releases/v0.26.0 branch).
# Target vllm version: v0.27.0 (releases/v0.27.0 branch).
# Keep VLLM_BASE_TAG aligned with the target vLLM release on every rebase.
#
# Release-boundary form: there is no precompiled-wheel pin here, because the
# base image already ships exactly the target vLLM. Everything the runtime
# needs — torch, flashinfer, nvJitLink, numpy — comes from that image as one
# self-consistent set.
#
# This block reappears only when the rebase targets a vLLM commit that is NOT a
# released tag. During the 0.27 rebase, targeting v0.27.0rc1 on top of the
# v0.26.0 base image required reinstalling vLLM from wheels.vllm.ai and then
# hand-repairing the fallout: pinning torch ahead of flashinfer (installing it
# after bound flashinfer's TVM FFI runtime to the old torch and aborted engine
# core init), registering libnvJitLink.so.13 with ldconfig, and re-pinning numpy
# under numba's ceiling. All of it was compensation for a base image and a vLLM
# that disagreed. At a real release boundary they agree, so none of it is
# needed. If you pin an untagged commit again, restore the reinstall block —
# agent/tasks/20_update_wheel_url.sh fails the run rather than letting the
# Dockerfile silently ship the wrong vLLM.
ARG VLLM_BASE_IMAGE=vllm/vllm-openai
ARG VLLM_BASE_TAG=v0.26.0
ARG VLLM_BASE_TAG=v0.27.0

# We use registry-based layer caching for the dependencies,
# where the tag is determined based on the hash of the contents
Expand Down
3 changes: 3 additions & 0 deletions tests/core/sched/test_omni_ar_scheduler_logprobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ def __init__(self, request_id: str) -> None:
self.num_computed_tokens = 0
self.num_in_flight_tokens = 0
self.num_output_placeholders = 0
# vLLM 0.27 (a0c092ee72): Request gained num_stale_output_tokens to
# track in-flight outputs discarded at preemption/streaming-stop.
self.num_stale_output_tokens = 0
self.has_encoder_inputs = False
self.pooling_params = None
self.resumable = True
Expand Down
223 changes: 223 additions & 0 deletions tests/core/sched/test_omni_ar_scheduler_stale_drain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project

"""Investigation tests for the num_stale_output_tokens drain (59b9e719).

The seed-TTS WER regression appeared with the vLLM 0.27 rebase (nightly
2946 -> 2949: mean WER 0.2695 -> 0.3936, median 0.0 in both), whose prime
suspect is the reimplemented async-token discard: vLLM 0.26 drained
``async_tokens_to_discard`` one arriving frame at a time and suppressed
only the token append; the 0.27 adaptation seeds
``num_stale_output_tokens`` with the discarded *placeholder count* and
drains it by each arriving frame's ``num_tokens_scheduled``, dropping the
whole per-request update. Whenever seed and arrivals disagree, the counter
outlives the old segment and the drain consumes *valid* frames of the new
segment — a hole in the talker's codec stream (garbled audio, text/audio
mismatch) — or underflows its own assert on the new segment's prefill
frame (engine-core death mid-step).

The fix seeds the counter from ``num_in_flight_tokens`` — the same
scheduled-token units the drain subtracts — so every pre-discard frame
drains exactly its own contribution and new-segment frames can never be
swallowed. These tests pin that contract one frame at a time; the
delivered / swallowed observable is whether ``_update_request_with_output``
runs for the frame.
"""

from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import MagicMock

import pytest

# Imports must run in this order: vllm_omni applies patches to vllm.v1.request
# before Request / StreamingUpdate are bound in this module.
# isort: off
import vllm_omni # noqa: F401 - import for side effects (patch vLLM)
from vllm.sampling_params import SamplingParams
from vllm.v1.core.sched.output import SchedulerOutput
from vllm.v1.outputs import ModelRunnerOutput
from vllm.v1.request import Request, RequestStatus, StreamingUpdate
from vllm_omni.core.sched.omni_ar_scheduler import OmniARScheduler

# isort: on

pytestmark = [pytest.mark.core_model, pytest.mark.cpu]


def _make_session() -> Request:
session = Request(
request_id="req-stale-drain-test",
prompt_token_ids=[1, 2, 3],
sampling_params=SamplingParams(max_tokens=32),
pooling_params=None,
arrival_time=100.0,
block_hasher=None,
)
session.status = RequestStatus.RUNNING
if not hasattr(session, "num_stale_output_tokens"):
# vLLM 0.27 defines this Request field; 0.26 (local dev) does not.
session.num_stale_output_tokens = 0
if not hasattr(session, "num_in_flight_tokens"):
session.num_in_flight_tokens = 0
return session


def _replace_streaming_session(session: Request) -> None:
"""Run the real stage-0 streaming replacement (the discard/seed site)."""
sched = OmniARScheduler.__new__(OmniARScheduler)
sched._new_prompt_len_snapshot = {}
sched.vllm_config = SimpleNamespace(model_config=SimpleNamespace(stage_id=0))
sched.num_waiting_for_streaming_input = 0
sched.log_stats = False
sched.chunk_transfer_adapter = None
sched.skipped_waiting = set()
session.status = RequestStatus.WAITING_FOR_STREAMING_REQ
update = StreamingUpdate(
mm_features=None,
prompt_token_ids=[10, 20],
max_tokens=32,
arrival_time=200.0,
sampling_params=SamplingParams(max_tokens=16),
)
sched._update_request_as_session(session, update)
session.status = RequestStatus.RUNNING


def _make_drain_sched(session: Request) -> MagicMock:
sched = MagicMock()
sched.requests = {session.request_id: session}
sched.perf_metrics = None
sched.structured_output_manager.should_advance.return_value = False
sched._update_request_with_output.return_value = ([42], False)
sched._process_kv_transfer_trigger.return_value = False
sched.chunk_transfer_adapter = MagicMock()
sched.running = [session]
sched.waiting_for_transfer_free = set()
sched.transfer_triggered_requests = set()
sched.active_kv_transfers = set()
sched.pending_stop_after_extraction = set()
sched.connector = None
sched.kv_cache_manager.take_events.return_value = None
sched.finished_req_ids_dict = {}
sched.make_stats.return_value = None
return sched


def _run_step(sched: MagicMock, session: Request, *, num_scheduled: int, token: int) -> bool:
"""Feed one model-runner frame through update_from_output.

Returns True when the frame's tokens were delivered (the append ran),
False when the frame was swallowed by the stale drain.
"""
scheduler_output = MagicMock(spec=SchedulerOutput)
scheduler_output.num_scheduled_tokens = {session.request_id: num_scheduled}
scheduler_output.scheduled_spec_decode_tokens = {}
scheduler_output.num_invalid_spec_tokens = 0

model_runner_output = MagicMock(spec=ModelRunnerOutput)
model_runner_output.sampled_token_ids = [[token]]
model_runner_output.logprobs = None
model_runner_output.prompt_logprobs_dict = {}
model_runner_output.pooler_output = None
model_runner_output.num_nans_in_logits = None
model_runner_output.kv_connector_output = None
model_runner_output.cudagraph_stats = None
model_runner_output.req_id_to_index = {session.request_id: 0}
model_runner_output.routed_experts = None
model_runner_output.inter_stage_outputs = None

sched._update_request_with_output.reset_mock()
OmniARScheduler.update_from_output(sched, scheduler_output, model_runner_output)
return sched._update_request_with_output.called


def test_exact_drain_delivers_new_segment_frame() -> None:
"""One in-flight decode frame at replacement: its late output drains the
counter exactly and the new segment's first frame is delivered."""
session = _make_session()
session.append_output_token_ids([7, 8, 9])
session.num_computed_tokens = 6
session.num_output_placeholders = 1
session.num_in_flight_tokens = 1

_replace_streaming_session(session)
assert session.num_stale_output_tokens == 1

sched = _make_drain_sched(session)
assert _run_step(sched, session, num_scheduled=1, token=42) is False # late frame dropped
assert session.num_stale_output_tokens == 0
assert _run_step(sched, session, num_scheduled=1, token=43) is True # new segment survives


def test_two_in_flight_frames_drain_without_swallowing() -> None:
"""Two in-flight decode frames: both late frames drop, the new segment's
first valid frame is delivered. A placeholder-based seed swallowed it
whenever fewer frames arrived than placeholders counted (the driver of
the seed-TTS WER regression, nightly 2946 -> 2949: 0.2695 -> 0.3936)."""
session = _make_session()
session.append_output_token_ids([7, 8, 9])
session.num_computed_tokens = 7
session.num_output_placeholders = 2
session.num_in_flight_tokens = 2

_replace_streaming_session(session)
assert session.num_stale_output_tokens == 2

sched = _make_drain_sched(session)
assert _run_step(sched, session, num_scheduled=1, token=42) is False # late frame 1
assert _run_step(sched, session, num_scheduled=1, token=44) is False # late frame 2
assert session.num_stale_output_tokens == 0

delivered = _run_step(sched, session, num_scheduled=1, token=43) # new segment, frame 1
assert delivered is True, (
"valid new-segment frame was swallowed by leftover stale count "
f"(num_stale_output_tokens left at {session.num_stale_output_tokens})"
)


def test_spec_placeholder_asymmetry_does_not_swallow_valid_frame() -> None:
"""Placeholder counts can exceed scheduled counts (e.g. a draft
invalidated before execution): 1 in-flight MTP frame scheduled 2 tokens
but contributed 3 placeholders. Seeding from num_in_flight_tokens keeps
seed == drain, so the new segment's valid frame is delivered."""
session = _make_session()
session.append_output_token_ids([7])
session.num_computed_tokens = 7
session.num_output_placeholders = 3 # 1 sampled + 2 spec placeholders
session.num_in_flight_tokens = 2 # the frame actually scheduled 1 + 1 tokens

_replace_streaming_session(session)
assert session.num_stale_output_tokens == 2

sched = _make_drain_sched(session)
assert _run_step(sched, session, num_scheduled=2, token=42) is False # late MTP frame
assert session.num_stale_output_tokens == 0
delivered = _run_step(sched, session, num_scheduled=1, token=43) # new segment, frame 1
assert delivered is True, (
"valid new-segment frame swallowed after spec placeholder asymmetry "
f"(num_stale_output_tokens left at {session.num_stale_output_tokens})"
)


def test_in_flight_prefill_chunk_drains_exactly_without_underflow() -> None:
"""An in-flight prefill chunk carries no placeholders but 5 scheduled
tokens. The placeholder-based seed missed it entirely (its late output
was appended to the rolled-back session) and any leftover counter
underflowed 'assert num_stale_output_tokens >= 0' on the chunk's arrival,
killing the engine-core step. Seeding from num_in_flight_tokens drops the
late chunk and drains to exactly zero."""
session = _make_session()
session.append_output_token_ids([7, 8, 9])
session.num_computed_tokens = 7
session.num_output_placeholders = 0 # prefill chunk: no placeholders
session.num_in_flight_tokens = 5 # the chunk's scheduled tokens

_replace_streaming_session(session)
assert session.num_stale_output_tokens == 5

sched = _make_drain_sched(session)
assert _run_step(sched, session, num_scheduled=5, token=42) is False # late prefill chunk dropped
assert session.num_stale_output_tokens == 0
assert _run_step(sched, session, num_scheduled=1, token=43) is True # new segment survives
37 changes: 37 additions & 0 deletions tests/diffusion/distributed/test_expert_parallel_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def fake_init_vllm_model_parallel_group(
local_rank,
backend,
group_name,
use_all2all=False,
):
del backend
group = _FakeGroup(
Expand All @@ -73,6 +74,7 @@ def fake_init_vllm_model_parallel_group(
device_communicator=object(),
reduce_scatter=lambda tensor, **kwargs: tensor,
)
group.use_all2all = use_all2all
created_groups.append(group)
return group

Expand Down Expand Up @@ -121,6 +123,9 @@ def fake_init_vllm_model_parallel_group(
assert parallel_state.vllm_parallel_state._PCP.device_communicator is not None
assert parallel_state.vllm_parallel_state._DP.device_communicator is not None
assert parallel_state.vllm_parallel_state._EP.device_communicator is not None
# vLLM 0.27's MoE oracle asserts the EP communicator has an all2all
# manager, which vLLM only builds for groups created with use_all2all.
assert parallel_state.vllm_parallel_state._EP.use_all2all is True
assert hasattr(parallel_state.vllm_parallel_state._PCP, "reduce_scatter")
assert hasattr(parallel_state.vllm_parallel_state._DP, "reduce_scatter")
assert hasattr(parallel_state.vllm_parallel_state._EP, "reduce_scatter")
Expand Down Expand Up @@ -313,3 +318,35 @@ def fake_init_model_parallel_group(
assert parallel_state.vllm_parallel_state._PCP is None
assert parallel_state.vllm_parallel_state._EP is None
assert parallel_state._EXPERT_PARALLEL_GROUP_RANKS is None


@pytest.mark.cpu
@pytest.mark.core_model
def test_init_vllm_group_forwards_use_all2all_only_when_supported(monkeypatch):
"""use_all2all reaches vLLM only on versions whose API accepts it.

vLLM 0.27 builds the EP all2all manager only for groups created with
use_all2all=True (its MoE oracle then asserts the manager exists —
build 2953, HunyuanImage3-DIT accuracy). Older vLLM has no such kwarg,
so forwarding it unconditionally would TypeError at init.
"""
received = {}

def modern(group_ranks, local_rank, backend, group_name=None, use_device_communicator=True, use_all2all=False):
received["modern"] = use_all2all
return object()

monkeypatch.setattr(parallel_state.vllm_parallel_state, "init_model_parallel_group", modern)
parallel_state.init_vllm_model_parallel_group([[0]], 0, "gloo", "ep", use_all2all=True)
assert received["modern"] is True

parallel_state.init_vllm_model_parallel_group([[0]], 0, "gloo", "tp")
assert received["modern"] is False

def legacy(group_ranks, local_rank, backend, group_name=None, use_device_communicator=True):
received["legacy"] = True
return object()

monkeypatch.setattr(parallel_state.vllm_parallel_state, "init_model_parallel_group", legacy)
parallel_state.init_vllm_model_parallel_group([[0]], 0, "gloo", "ep", use_all2all=True)
assert received["legacy"] is True
6 changes: 3 additions & 3 deletions tests/diffusion/layers/test_fused_moe.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def test_new_returns_vllm_runner_and_registers_hooks(self, mocker, monkeypatch):
monkeypatch.setitem(
sys.modules,
"vllm.model_executor.layers.fused_moe",
SimpleNamespace(FusedMoE=mock_vllm_fused_moe),
SimpleNamespace(FusedMoEFactory=mock_vllm_fused_moe),
)

result = fused_moe.FusedMoE(prefix="test", a=1, reduce_results=True)
Expand Down Expand Up @@ -139,7 +139,7 @@ def register_forward_pre_hook(self, hook, *, with_kwargs):
monkeypatch.setitem(
sys.modules,
"vllm.model_executor.layers.fused_moe",
SimpleNamespace(FusedMoE=mocker.MagicMock(return_value=mock_runner)),
SimpleNamespace(FusedMoEFactory=mocker.MagicMock(return_value=mock_runner)),
)
mocker.patch.object(fused_moe, "current_omni_platform", mocker.MagicMock())
mock_set_num_tokens = mocker.patch.object(fused_moe, "_set_forward_context_num_tokens")
Expand Down Expand Up @@ -169,7 +169,7 @@ def register_forward_pre_hook(self, hook, *, with_kwargs):
monkeypatch.setitem(
sys.modules,
"vllm.model_executor.layers.fused_moe",
SimpleNamespace(FusedMoE=mocker.MagicMock(return_value=mock_runner)),
SimpleNamespace(FusedMoEFactory=mocker.MagicMock(return_value=mock_runner)),
)
mocker.patch.object(fused_moe, "current_omni_platform", mocker.MagicMock())
mock_set_num_tokens = mocker.patch.object(fused_moe, "_set_forward_context_num_tokens")
Expand Down
Loading
Loading