rebase: target the official vLLM 0.31.0 release

Use upstream release images at db9527a46873, preserve custom processor conditioning, and include weekly jobs in full Buildkite release validation.

Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
This commit is contained in:
tzhouam committed 2026-10-03 21:11:54 +08:00
1 parent 008fa25263
commit ed876de879
15 files changed
+142 -45

No files matched your search

+10
View File
@@ -33,3 +33,13 @@ steps:
- python3 .buildkite/common/scripts/upload_pipeline.py --upload --all .buildkite/cuda/test-nightly.yml
agents:
queue: "cpu_queue_premerge"
# Include the slow model cases moved from nightly into weekly for release validation.
- label: "Upload Weekly Pipeline"
depends_on: image-build
key: upload-weekly-pipeline
if: build.env("WEEKLY") == "1"
commands:
- python3 .buildkite/common/scripts/upload_pipeline.py --upload --all .buildkite/cuda/test-weekly.yml
agents:
queue: "cpu_queue_premerge"
@@ -12,24 +12,24 @@ from pathlib import Path
def main() -> None:
dockerfile = Path(__file__).resolve().parents[3] / "docker" / "Dockerfile.ci"
match = re.search(
r"^ARG VLLM_PRECOMPILED_WHEEL_COMMIT=([0-9a-f]{40})$",
r"^ARG VLLM_VERSION=([0-9]+\.[0-9]+\.[0-9]+)$",
dockerfile.read_text(),
re.MULTILINE,
)
if match is None:
raise RuntimeError(f"Missing vLLM commit pin in {dockerfile}")
expected_commit = match.group(1)
raise RuntimeError(f"Missing vLLM release version in {dockerfile}")
expected_version = match.group(1)
import torch
import vllm
assert expected_commit[:8] in vllm.__version__, vllm.__version__
assert vllm.__version__ == expected_version, vllm.__version__
assert torch.cuda.is_available(), "vLLM runtime check requires a GPU worker"
for module in ("flashinfer", "vllm._C_stable_libtorch"):
importlib.import_module(module)
print(
f"Verified vLLM CUDA runtime: vllm={vllm.__version__} "
f"commit={expected_commit} torch={torch.__version__} "
f"expected={expected_version} torch={torch.__version__} "
f"cuda={torch.version.cuda} device={torch.cuda.get_device_name()}"
)
+8 -10
View File
@@ -1,11 +1,9 @@
# Target an unreleased vLLM main commit. The official commit image carries
# the matching CUDA/PyTorch/FlashInfer stack; do not overlay a nightly wheel
# on the previous release image. Copilot updates this commit pin, which also
# selects the image used by CI. The selected commit must have a published
# nightly image as well as a cu130 wheel.
ARG VLLM_PRECOMPILED_WHEEL_COMMIT=ac9126e58aa7bbab1856ba6593ba4d5003fea516
ARG VLLM_BASE_IMAGE=vllm/vllm-openai
ARG VLLM_BASE_TAG=nightly-${VLLM_PRECOMPILED_WHEEL_COMMIT}
# Use the official release image with its matching CUDA/PyTorch stack.
# The release images are available in upstream ECR before Docker Hub promotion.
ARG VLLM_VERSION=0.31.0
ARG VLLM_PRECOMPILED_WHEEL_COMMIT=db9527a46873454610df6dbedf79a36d6bf1a7f6
ARG VLLM_BASE_IMAGE=public.ecr.aws/q9t5s3a7/vllm-release-repo
ARG VLLM_BASE_TAG=${VLLM_PRECOMPILED_WHEEL_COMMIT}
# We use registry-based layer caching for the dependencies,
# where the tag is determined based on the hash of the contents
@@ -13,7 +11,7 @@ ARG VLLM_BASE_TAG=nightly-${VLLM_PRECOMPILED_WHEEL_COMMIT}
# everything except the actual project dir, since that should not
# change much across branches.
FROM ${VLLM_BASE_IMAGE}:${VLLM_BASE_TAG} AS deps
ARG VLLM_PRECOMPILED_WHEEL_COMMIT
ARG VLLM_VERSION
# Keep APP_DIR outside /workspace so k8s Buildkite workspace mounts cannot
# shadow the editable install target (#5364). Docker WORKDIR creates /opt if
# needed.
@@ -47,7 +45,7 @@ RUN uv pip install --system -e ".[dev]"
# Fail the image build if dependency resolution replaced the selected vLLM.
# The CPU builder has no NVIDIA driver; CUDA imports are checked at the start
# of each GPU test job by .buildkite/cuda/scripts/check_vllm_runtime.py.
RUN python3 -c "import vllm; assert '${VLLM_PRECOMPILED_WHEEL_COMMIT}'[:8] in vllm.__version__, vllm.__version__"
RUN python3 -c "import vllm; assert vllm.__version__ == '${VLLM_VERSION}', vllm.__version__"
# The final stage just copies the working dir on top of the
# dependencies image. This uses --link so that we can skip
+2 -1
View File
@@ -1,4 +1,5 @@
ARG BASE_IMAGE=vllm/vllm-openai:v0.30.0
# Official vLLM v0.31.0 release image, before Docker Hub tag promotion.
ARG BASE_IMAGE=public.ecr.aws/q9t5s3a7/vllm-release-repo:db9527a46873454610df6dbedf79a36d6bf1a7f6
FROM ${BASE_IMAGE}
ARG COMMON_WORKDIR=/app
+4 -3
View File
@@ -1,7 +1,8 @@
# Keep the ROCm runtime aligned with the vLLM release targeted by
# docker/Dockerfile.ci. A stale base image can build successfully while failing
# at runtime when vLLM-Omni imports APIs added by the newer vLLM release.
ARG BASE_IMAGE=vllm/vllm-openai-rocm:v0.30.0
# Official vLLM v0.31.0 release image, before Docker Hub tag promotion.
ARG BASE_IMAGE=public.ecr.aws/q9t5s3a7/vllm-release-repo:db9527a46873454610df6dbedf79a36d6bf1a7f6-rocm
# Keep the dependency-heavy portion independent from the source tree. The
# registry cache is keyed on these inputs, while the final source copy can be
@@ -12,11 +13,11 @@ FROM ${BASE_IMAGE} AS deps
# BASE_IMAGE pins the default prebuilt runtime when USE_NIGHTLY_BUILD=0.
# VLLM_VERSION_OR_COMMIT_HASH is consumed only by the opt-in source rebuild when
# USE_NIGHTLY_BUILD=1. Keep both aligned with docker/Dockerfile.ci's
# VLLM_BASE_TAG. This is enforced by tests/buildkite/test_rocm_dockerfile.py. An
# release version. This is enforced by tests/buildkite/test_rocm_dockerfile.py. An
# intentional custom source pin must update that regression in the same change,
# and should return to the stable release as soon as one is available.
ARG USE_NIGHTLY_BUILD=0
ARG VLLM_VERSION_OR_COMMIT_HASH=v0.30.0
ARG VLLM_VERSION_OR_COMMIT_HASH=v0.31.0
ARG ARG_PYTORCH_ROCM_ARCH
ENV PYTORCH_ROCM_ARCH=${ARG_PYTORCH_ROCM_ARCH:-${PYTORCH_ROCM_ARCH}}
+1 -1
View File
@@ -2,7 +2,7 @@
# kernels==0.16.1 requires the LayerRepository version=/revision= integration
# introduced in transformers 5.13. Keep the tested upper bound: 5.15 has
# a reported model-construction regression; later versions need validation.
# This range also satisfies vLLM 0.30.0's transformers>=5.10.4 requirement.
# This range also satisfies vLLM 0.31.0's transformers>=5.10.4,<5.18.0 requirement.
# MOSS-TTS-Nano's list/set weight-loading compatibility is handled by
# transformers_keys_to_ignore_compat in model_executor/models/utils.py.
transformers >= 5.13.0, < 5.15
+7 -8
View File
@@ -11,6 +11,7 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
AMD_TEMPLATE = REPO_ROOT / ".buildkite/amd/test-template-amd-omni.j2"
AMD_BUILD_SCRIPT = REPO_ROOT / ".buildkite/amd/scripts/build-ci-image.sh"
CUDA_RELEASE_DOCKERFILE = REPO_ROOT / "docker/Dockerfile.cuda"
CUDA_CI_DOCKERFILE = REPO_ROOT / "docker/Dockerfile.ci"
ROCM_DOCKERFILE = REPO_ROOT / "docker/Dockerfile.rocm"
ROCM_DOCKERIGNORE = REPO_ROOT / "docker/Dockerfile.rocm.dockerignore"
@@ -31,16 +32,15 @@ def _line_index(lines: list[str], prefix: str) -> int:
def test_rocm_base_tracks_cuda_vllm_release() -> None:
# CUDA CI can target a main-branch nightly independently of the stable
# release images used by the CUDA and ROCm distribution Dockerfiles.
cuda_base = _docker_arg(CUDA_RELEASE_DOCKERFILE, "BASE_IMAGE")
_, _, cuda_release = cuda_base.rpartition(":")
cuda_image, _, cuda_release = cuda_base.rpartition(":")
rocm_base = _docker_arg(ROCM_DOCKERFILE, "BASE_IMAGE")
image_ref, separator, image_tag = rocm_base.rpartition(":")
assert separator, f"expected a tagged ROCm base image, got {rocm_base}"
assert image_ref.rsplit("/", 1)[-1] == "vllm-openai-rocm", image_ref
assert image_tag == cuda_release
assert image_ref == cuda_image
assert image_tag == f"{cuda_release}-rocm"
assert cuda_release == _docker_arg(CUDA_CI_DOCKERFILE, "VLLM_PRECOMPILED_WHEEL_COMMIT")
def test_rocm_defaults_to_prebuilt_base_image() -> None:
@@ -107,11 +107,10 @@ def test_rocm_build_context_excludes_git_history() -> None:
def test_rocm_source_ref_tracks_cuda_vllm_release() -> None:
cuda_base = _docker_arg(CUDA_RELEASE_DOCKERFILE, "BASE_IMAGE")
_, _, cuda_release = cuda_base.rpartition(":")
cuda_release = _docker_arg(CUDA_CI_DOCKERFILE, "VLLM_VERSION")
rocm_source_ref = _docker_arg(ROCM_DOCKERFILE, "VLLM_VERSION_OR_COMMIT_HASH")
assert rocm_source_ref == cuda_release
assert rocm_source_ref == f"v{cuda_release}"
def test_rocm_dockerfile_contains_vllm_api_canary() -> None:
+78 -1
View File
@@ -1,14 +1,17 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
from importlib import import_module
from types import SimpleNamespace
from unittest.mock import Mock
import pytest
from transformers import BatchFeature
from vllm.config.multimodal import MultiModalDummyOptions
from vllm.multimodal.parse import AudioProcessorItems, ImageProcessorItems, MultiModalDataItems
from vllm.multimodal.processing import BaseMultiModalProcessor
from vllm_omni.inputs.mm_processor import OmniMultiModalProcessor
from vllm_omni.inputs.mm_processor import OmniDummyInputsBuilder, OmniMultiModalProcessor
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
@@ -47,3 +50,77 @@ def test_custom_processor_bridge_preserves_prompt_kwargs_and_passthrough(counts,
)
assert dict(result) == ({"features": "processed", "embedding": "preserve"} if counts else {"features": "processed"})
assert kwargs == before
@pytest.mark.parametrize(
"module_name,builder_name,processor_name,expected_kwargs",
[
(
"cosyvoice3",
"CosyVoice3DummyInputsBuilder",
"CosyVoice3MultiModalProcessor",
{"prompt_text": "Testing my voices. Why should I not?"},
),
(
"glm_tts",
"GLMTTSDummyInputsBuilder",
"GLMTTSMultiModalProcessor",
{"prompt_text": "This is the reference voice."},
),
(
"omnivoice",
"OmniVoiceDummyInputsBuilder",
"OmniVoiceMultiModalProcessor",
{"ref_text": "Testing voice cloning."},
),
("mimo_audio", "MiMoAudioLLMDummyInputsBuilder", "MiMoAudioLLMMultiModalProcessor", {}),
],
)
def test_dummy_budget_preserves_model_conditioning(module_name, builder_name, processor_name, expected_kwargs):
"""The v0.31 budget path must reach each model's custom builder hook."""
module = import_module(f"vllm_omni.model_executor.models.{module_name}.{module_name}")
builder = object.__new__(getattr(module, builder_name))
assert isinstance(builder, OmniDummyInputsBuilder)
items = MultiModalDataItems({})
builder.info = SimpleNamespace(ctx=SimpleNamespace(tokenizer=None), parse_mm_data=Mock(return_value=items))
builder.get_dummy_mm_data = Mock(return_value={})
processor = object.__new__(getattr(module, processor_name))
processor.dummy_inputs = builder
inputs = processor.get_dummy_inputs(128, {"audio": 1}, MultiModalDummyOptions())
assert inputs.mm_data_items is items
assert inputs.hf_processor_mm_kwargs == expected_kwargs
assert inputs.prompt == ("<|empty|>" if module_name == "mimo_audio" else [])
def test_dummy_budget_keeps_upstream_path_for_standard_builders(monkeypatch):
class Processor(OmniMultiModalProcessor):
def _get_mm_fields_config(self, *args):
return {}
def _get_prompt_updates(self, *args):
return []
processor = object.__new__(Processor)
processor.dummy_inputs = SimpleNamespace()
expected = object()
upstream = Mock(return_value=expected)
monkeypatch.setattr(BaseMultiModalProcessor, "get_dummy_inputs", upstream)
options = MultiModalDummyOptions()
assert processor.get_dummy_inputs(128, {"audio": 1}, options) is expected
upstream.assert_called_once_with(128, {"audio": 1}, options)
def test_voxtral_dummy_budget_uses_audio_token_prompt():
from vllm_omni.model_executor.models.voxtral_tts.voxtral_tts_audio_generation import VoxtralTTSMultiModalProcessor
processor = object.__new__(VoxtralTTSMultiModalProcessor)
expected = SimpleNamespace(prompt=[1, 25, 24, 35])
builder = SimpleNamespace(get_dummy_processor_inputs=Mock(return_value=expected))
processor.dummy_inputs = builder
options = MultiModalDummyOptions()
assert processor.get_dummy_inputs(128, {"audio": 1}, options) is expected
builder.get_dummy_processor_inputs.assert_called_once_with(128, {"audio": 1}, options)
@@ -986,6 +986,27 @@ def test_cosyvoice3_full_payload_replace_keys_present() -> None:
)
def test_cosyvoice3_conditioning_replaces_snapshot_while_audio_accumulates() -> None:
from vllm_omni.model_executor.stage_input_processors.cosyvoice3 import _FULL_PAYLOAD_REPLACE_KEYS
from vllm_omni.outputs.mm_outputs import MultimodalPayload
from vllm_omni.outputs.output_modality import OutputModality
previous = {key: torch.ones(1, 174) for key in _FULL_PAYLOAD_REPLACE_KEYS}
padded = {key: torch.zeros(1, 1500) for key in _FULL_PAYLOAD_REPLACE_KEYS}
previous["embed.speech_token_len"] = torch.tensor([174])
padded["embed.speech_token_len"] = torch.tensor([174])
payload = MultimodalPayload.from_dict({**previous, "audio": torch.ones(2)})
incoming = MultimodalPayload.from_dict({**padded, "audio": torch.ones(3)})
assert payload is not None and incoming is not None
payload = payload.merged_with(incoming)
payload.consolidate_tensors(OutputModality.AUDIO)
for key in _FULL_PAYLOAD_REPLACE_KEYS:
assert torch.equal(payload[key], padded[key])
assert torch.equal(payload["audio"], torch.ones(5))
def test_ming_flash_omni_thinker2talker_token_only_smoke() -> None:
"""Smoke: ming_flash_omni token-only carries voice metadata."""
from vllm_omni.model_executor.stage_input_processors.ming_flash_omni import (
@@ -46,7 +46,6 @@ def _check_prefill():
state = OmniModelState.__new__(OmniModelState)
state.model_config = SimpleNamespace(max_model_len=256)
state.vllm_config = c
state.max_model_len = 256
state.supports_mm_inputs = False
with set_current_vllm_config(c):
group = AttentionGroup(FlashAttentionBackend, ["attn"], spec, 0)
+6 -11
View File
@@ -7,6 +7,7 @@ from collections.abc import Mapping
from typing import TypeVar
from transformers import BatchFeature
from vllm.config.multimodal import BaseDummyOptions, MultiModalDummyOptions
from vllm.multimodal.parse import MultiModalDataItems
from vllm.multimodal.processing import (
BaseDummyInputsBuilder,
@@ -26,18 +27,14 @@ class OmniDummyInputsBuilder(BaseDummyInputsBuilder[_I]):
self,
seq_len: int,
mm_counts: Mapping[str, int],
mm_options: Mapping[str, object],
mm_options: Mapping[str, BaseDummyOptions] | None = None,
) -> ProcessorInputs:
dummy_text = self.get_dummy_text(mm_counts)
dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts, mm_options)
dummy_mm_data = self.get_dummy_mm_data(seq_len, mm_counts, MultiModalDummyOptions(mm_options or {}))
dummy_mm_items = self.info.parse_mm_data(dummy_mm_data, validate=False)
tokenizer = self.info.ctx.tokenizer
dummy_prompt = (
[]
if tokenizer is None
else cached_encode(tokenizer, dummy_text, truncation=False)
)
dummy_prompt = [] if tokenizer is None else cached_encode(tokenizer, dummy_text, truncation=False)
return ProcessorInputs(prompt=dummy_prompt, mm_data_items=dummy_mm_items)
@@ -55,12 +52,10 @@ class OmniMultiModalProcessor(BaseMultiModalProcessor[_I]):
self,
seq_len: int,
mm_counts: Mapping[str, int],
mm_options: Mapping[str, object],
mm_options: MultiModalDummyOptions,
) -> ProcessorInputs:
if isinstance(self.dummy_inputs, OmniDummyInputsBuilder):
return self.dummy_inputs.get_dummy_processor_inputs(
seq_len, mm_counts, mm_options
)
return self.dummy_inputs.get_dummy_processor_inputs(seq_len, mm_counts, mm_options)
return super().get_dummy_inputs(seq_len, mm_counts, mm_options)
def _apply_hf_processor_main(
@@ -28,7 +28,6 @@ from vllm.multimodal import MULTIMODAL_REGISTRY
from vllm.multimodal.inputs import MultiModalFieldConfig, MultiModalKwargsItems
from vllm.multimodal.parse import MultiModalDataItems, MultiModalDataParser
from vllm.multimodal.processing import (
BaseDummyInputsBuilder,
BaseProcessingInfo,
ProcessorInputs,
PromptIndexTargets,
@@ -38,7 +38,6 @@ from vllm.multimodal import MULTIMODAL_REGISTRY
from vllm.multimodal.inputs import MultiModalFieldConfig, MultiModalKwargsItems
from vllm.multimodal.parse import MultiModalDataItems, MultiModalDataParser
from vllm.multimodal.processing import (
BaseDummyInputsBuilder,
BaseProcessingInfo,
ProcessorInputs,
PromptIndexTargets,
@@ -29,7 +29,6 @@ from vllm.multimodal.inputs import (
)
from vllm.multimodal.parse import AudioProcessorItems, MultiModalDataItems, MultiModalDataParser
from vllm.multimodal.processing import (
BaseDummyInputsBuilder,
BaseProcessingInfo,
ProcessorInputs,
PromptReplacement,
@@ -28,7 +28,6 @@ from vllm.multimodal.inputs import (
)
from vllm.multimodal.parse import MultiModalDataItems, MultiModalDataParser
from vllm.multimodal.processing import (
BaseDummyInputsBuilder,
BaseProcessingInfo,
ProcessorInputs,
PromptIndexTargets,