Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1e687f0
docs(epp): stop pinning vLLM on-ramp version
JulienDarve Sep 8, 2026
10c7e68
build(vllm): prepare core v0.29.0 compatibility
JulienDarve Sep 9, 2026
3ea9c55
docs(epp): require an explicit vLLM image for on-ramp
JulienDarve Sep 9, 2026
2e4e11c
revert(epp): separate on-ramp changes from vLLM bump
JulienDarve Sep 9, 2026
3992009
test(vllm): remove redundant contract test docstring
JulienDarve Sep 9, 2026
5e777fc
chore: merge main into vLLM 0.29 bump
JulienDarve Sep 10, 2026
19d7a6e
build(vllm): align Omni 0.29 release
JulienDarve Sep 10, 2026
9e1883b
fix(vllm): resolve dependency license metadata
JulienDarve Sep 10, 2026
9dbb498
chore: merge main into vLLM 0.29 bump
JulienDarve Sep 11, 2026
be94e48
test(vllm): bound deployment context for CI GPUs
JulienDarve Sep 11, 2026
00adb6b
chore(vllm): merge main into v0.29 bump
JulienDarve Sep 11, 2026
e600851
test(vllm): exclude reserved block from KVBM context limits
JulienDarve Sep 11, 2026
e5c3af3
chore(vllm): sync main before CI rerun
JulienDarve Sep 11, 2026
a351a6e
fix(vllm): preserve XPU pins during core upgrades
JulienDarve Sep 11, 2026
0993c92
test(vllm): remove obsolete 0.28 output variants
JulienDarve Sep 11, 2026
f33676c
fix(vllm): handle absent Omni stage config paths
JulienDarve Sep 11, 2026
4b51a63
test(vllm): isolate forward-pass tracing environment
JulienDarve Sep 14, 2026
c254f9b
chore(vllm): merge main into v0.29.0 bump
JulienDarve Sep 14, 2026
42dc5a3
fix(vllm): validate CPU runtime and preserve deferred Omni options
JulienDarve Sep 15, 2026
830c713
ci(vllm): fit CPU smoke test within runner shared memory
JulienDarve Sep 15, 2026
a281810
ci(vllm): defer dedicated CPU image validation
JulienDarve Sep 15, 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
2 changes: 1 addition & 1 deletion components/src/dynamo/frontend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ def parse_args() -> tuple[FrontendConfig, Optional[Namespace], Optional[Namespac

try:
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.entrypoints.openai.cli_args import FrontendArgs
from vllm.entrypoints.launchers.cli_args import FrontendArgs
except ModuleNotFoundError:
logger.exception("Flag '--chat-processor vllm' requires vllm be installed.")
sys.exit(1)
Expand Down
10 changes: 5 additions & 5 deletions components/src/dynamo/frontend/prepost.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
from typing import TYPE_CHECKING, Any, Protocol, TypeGuard, cast

from vllm.entrypoints.chat_utils import make_tool_call_id
from vllm.entrypoints.generate.base.protocol import (
DeltaFunctionCall,
DeltaMessage,
DeltaToolCall,
)
from vllm.entrypoints.openai.chat_completion.protocol import (
ChatCompletionNamedFunction,
ChatCompletionNamedToolChoiceParam,
ChatCompletionRequest,
)
from vllm.entrypoints.openai.engine.protocol import (
DeltaFunctionCall,
DeltaMessage,
DeltaToolCall,
)
from vllm.reasoning import ReasoningParser
from vllm.renderers import ChatParams, merge_kwargs
from vllm.sampling_params import SamplingParams
Expand Down
14 changes: 13 additions & 1 deletion components/src/dynamo/vllm/omni/base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import Any, AsyncGenerator, Dict

from vllm import SamplingParams
from vllm_omni.config import resolve_omni_config
from vllm_omni.entrypoints import AsyncOmni

try:
Expand Down Expand Up @@ -126,8 +127,19 @@ def _build_omni_kwargs(self, config) -> Dict[str, Any]:
if config.stage_configs_path:
omni_kwargs["deploy_config"] = config.stage_configs_path

resolved_config = resolve_omni_config(
config.model,
trust_remote_code=config.engine_args.trust_remote_code,
deploy_config_path=config.stage_configs_path,
cli_overrides={},
stage_overrides=None,
strategy_config_path=None,
)
has_diffusion_stage = any(
stage.stage_type == "diffusion" for stage in resolved_config.stage_configs
)
for field, value in dataclasses.asdict(config.diffusion).items():
if value is not None:
if value is not None and (has_diffusion_stage or field == "enforce_eager"):
omni_kwargs[field] = value

# These three fields are shared vLLM engine settings. Keep their CLI
Expand Down
15 changes: 7 additions & 8 deletions components/src/dynamo/vllm/omni/stage_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import uuid
from typing import Any, AsyncGenerator, Dict, List

from vllm_omni.config import resolve_omni_config
from vllm_omni.distributed.omni_connectors import initialize_orchestrator_connectors
from vllm_omni.entrypoints.utils import load_and_resolve_stage_configs

from dynamo import prometheus_names
from dynamo.common.model_taints import register_model_taint_route
Expand Down Expand Up @@ -53,26 +53,25 @@ def __init__(
) -> None:
self.config = config
self.connectors: dict[tuple[str, str], Any] = {}
(
resolved_stage_configs_path,
self.stage_configs,
_omni_lb_policy,
) = load_and_resolve_stage_configs(
resolved_config = resolve_omni_config(
config.model,
kwargs={},
cli_overrides={},
trust_remote_code=bool(
getattr(
getattr(config, "engine_args", None), "trust_remote_code", False
)
),
deploy_config_path=stage_configs_path,
stage_overrides=None,
strategy_config_path=None,
)
self.stage_configs = list(resolved_config.stage_configs)
self.stage_clients: Dict[str, Any] = {}

# Initialize connectors so the router can fetch final-stage output
# via connector.get() instead of SHM -- enabling multi-node deployments.
connector_configs_path = _ensure_stage_connectors(
resolved_stage_configs_path,
resolved_config.config_path,
self.stage_configs,
)
# Only register NixlConnector if it's actually used in stage configs
Expand Down
16 changes: 7 additions & 9 deletions components/src/dynamo/vllm/omni/stage_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@

import torch
import yaml
from vllm_omni.config import register_pipeline
from vllm_omni.config import register_pipeline, resolve_omni_config
from vllm_omni.config.config_factory import StageConfigFactory
from vllm_omni.config.pipeline_registry import OMNI_PIPELINES
from vllm_omni.distributed.omni_connectors import initialize_orchestrator_connectors
from vllm_omni.engine.orchestrator import build_engine_core_request_from_tokens
from vllm_omni.entrypoints.async_omni import AsyncOmni
from vllm_omni.entrypoints.stage_utils import serialize_obj, shm_write_bytes
from vllm_omni.entrypoints.utils import load_and_resolve_stage_configs
from vllm_omni.inputs.data import OmniTokensPrompt

from dynamo import prometheus_names
Expand Down Expand Up @@ -478,18 +477,17 @@ async def init_omni_stage(
getattr(getattr(config, "engine_args", None), "trust_remote_code", False)
)

(
resolved_stage_configs_path,
stage_configs,
_omni_lb_policy,
) = load_and_resolve_stage_configs(
resolved_config = resolve_omni_config(
config.model,
kwargs={},
cli_overrides={},
trust_remote_code=trust_remote_code,
deploy_config_path=config.stage_configs_path,
stage_overrides=None,
strategy_config_path=None,
)
stage_configs = list(resolved_config.stage_configs)
connector_configs_path = _ensure_stage_connectors(
resolved_stage_configs_path,
resolved_config.config_path,
stage_configs,
)
# Only register NixlConnector if it's actually used in stage configs
Expand Down
31 changes: 27 additions & 4 deletions components/src/dynamo/vllm/tests/omni/test_omni_base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
"""Tests that every DiffusionParallelConfig field is either exposed in Dynamo or intentionally skipped."""

import dataclasses
from unittest.mock import MagicMock
from types import SimpleNamespace
from unittest.mock import MagicMock, patch

import pytest

try:
from vllm_omni.config import DeployConfig, VllmOmniConfig
from vllm_omni.diffusion.data import DiffusionParallelConfig
from vllm_omni.model_executor.models.qwen3_tts.pipeline import QWEN3_TTS_PIPELINE

from dynamo.vllm.omni.args import OmniDiffusionKwargs, OmniParallelKwargs
from dynamo.vllm.omni.base_handler import BaseOmniHandler
Expand All @@ -23,11 +26,12 @@
pytest.mark.pre_merge,
]

# These fields are not exposed in OmniParallelKwargs, because they are derived from other fields.
# Keep upstream defaults for unexposed fields and let it derive the sequence size.
_SKIP_FIELDS = {
"sequence_parallel_size",
"enable_expert_parallel",
"ulysses_mode",
"ulysses_a2a_permute",
"mask_sp_padding",
}

Expand Down Expand Up @@ -61,9 +65,15 @@ def _make_config(**parallel_overrides):
return cfg


def _build_kwargs(config):
def _build_kwargs(config, stage_type="diffusion"):
handler = BaseOmniHandler.__new__(BaseOmniHandler)
return handler._build_omni_kwargs(config)
with patch(
"dynamo.vllm.omni.base_handler.resolve_omni_config",
return_value=SimpleNamespace(
stage_configs=(SimpleNamespace(stage_type=stage_type),)
),
):
return handler._build_omni_kwargs(config)


class TestDiffusionParallelConfigCoverage:
Expand Down Expand Up @@ -108,6 +118,19 @@ def test_output_modalities_forwarded_to_async_omni(self):

assert kwargs["output_modalities"] == ["image"]

def test_tts_kwargs_accepted_by_upstream_pipeline(self):
config = _make_config()
config.diffusion.enforce_eager = True

kwargs = _build_kwargs(config, stage_type="llm")

VllmOmniConfig.from_pipeline_config(
QWEN3_TTS_PIPELINE,
user_deploy_config=DeployConfig(),
cli_overrides=kwargs,
)
assert kwargs["enforce_eager"] is True

def test_lora_disabled_resolves_no_capacity(self):
config = _make_config()
handler = BaseOmniHandler.__new__(BaseOmniHandler)
Expand Down
17 changes: 12 additions & 5 deletions components/src/dynamo/vllm/tests/omni/test_omni_stage_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from dynamo.common.utils.output_modalities import RequestType

try:
from vllm_omni.config import OmniConfigResolution

from dynamo.vllm.omni import stage_router
except ImportError:
pytest.skip("vLLM omni dependencies not available", allow_module_level=True)
Expand Down Expand Up @@ -88,18 +90,23 @@ def test_router_loads_stage_configs_from_model_deploy_config():

with (
patch(
"dynamo.vllm.omni.stage_router.load_and_resolve_stage_configs",
return_value=("/deploy/glm_image.yaml", stage_configs, None),
) as load_and_resolve_stage_configs,
"dynamo.vllm.omni.stage_router.resolve_omni_config",
return_value=OmniConfigResolution(
config_path="/deploy/glm_image.yaml",
stage_configs=tuple(stage_configs),
),
) as resolve_omni_config,
patch("dynamo.vllm.omni.stage_router.OutputFormatter") as output_formatter,
):
router = stage_router.OmniStageRouter(config, "/deploy/glm_image.yaml")

load_and_resolve_stage_configs.assert_called_once_with(
resolve_omni_config.assert_called_once_with(
config.model,
kwargs={},
cli_overrides={},
trust_remote_code=False,
deploy_config_path="/deploy/glm_image.yaml",
stage_overrides=None,
strategy_config_path=None,
)
output_formatter.assert_called_once()
assert router.stage_configs == stage_configs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,10 @@ def test_new_request_data_has_fields_instrumented_scheduler_sets():


def test_scheduler_output_new_connector_fields_remain_optional():
"""Dynamo constructs SchedulerOutput directly without v0.27's new fields."""
from vllm.v1.core.sched.output import SchedulerOutput

fields = {field.name: field for field in dataclasses.fields(SchedulerOutput)}
for field_name in ("ec_manager_metadata", "partial_tail_offloads"):
for field_name in ("ec_manager_metadata", "kv_connector_block_state"):
assert field_name in fields, (
f"vLLM SchedulerOutput.{field_name} is gone — re-audit Dynamo's "
"direct SchedulerOutput construction."
Expand Down
3 changes: 3 additions & 0 deletions components/src/dynamo/vllm/tests/test_vllm_kv_events_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def test_block_stored_fields(self):
10. kv_cache_spec_kind (semantic cache type; optional for older vLLM)
11. kv_cache_spec_sliding_window (semantic cache window; optional for older vLLM)
12. locality (per-tier storage locality; optional for older vLLM)
13. ownership (secondary offloading tier; added in vLLM 0.29)

If vLLM adds/removes/reorders fields, this test will fail.
"""
Expand All @@ -104,6 +105,7 @@ def test_block_stored_fields(self):
expected_fields.append("kv_cache_spec_sliding_window")
if _has_locality(BlockStored):
expected_fields.append("locality")
expected_fields.append("ownership")
expected_fields = tuple(expected_fields)

actual_fields = BlockStored.__struct_fields__
Expand Down Expand Up @@ -131,6 +133,7 @@ def test_block_removed_fields(self):
expected_fields.append("kv_cache_spec_sliding_window")
if _has_locality(BlockRemoved):
expected_fields.append("locality")
expected_fields.append("ownership")
expected_fields = tuple(expected_fields)

actual_fields = BlockRemoved.__struct_fields__
Expand Down
19 changes: 9 additions & 10 deletions components/src/dynamo/vllm/tests/test_vllm_renderer_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
_chat_protocol = importlib.import_module(
"vllm.entrypoints.openai.chat_completion.protocol"
)
_engine_protocol = importlib.import_module("vllm.entrypoints.openai.engine.protocol")
_engine_protocol = importlib.import_module("vllm.entrypoints.generate.base.protocol")
_inputs_data = importlib.import_module("vllm.inputs")
_reasoning = importlib.import_module("vllm.reasoning")
_sampling_params = importlib.import_module("vllm.sampling_params")
Expand Down Expand Up @@ -469,16 +469,13 @@ def test_engine_core_struct_contract(self):
base_output_fields,
cached_token_output_fields,
)
# vLLM 0.28 appends optional multimodal cache-miss and sampling-mask
# payloads. Dynamo does not enable either feature, so both retain their
# None defaults, but their declaration order remains part of vLLM's
# array-like wire contract.
vllm_028_output_extra_fields = (
"mm_cache_miss_hashes",
"new_sampling_mask",
)
core_output_fields = core_output_fields + tuple(
fields + vllm_028_output_extra_fields for fields in core_output_fields
fields + ("mm_cache_miss_hashes", "new_sampling_mask")
for fields in core_output_fields
)
core_output_fields += (
base_output_fields
+ ("mm_cache_miss_hashes", "new_sampling_mask", "spec_decode_metrics"),
Comment thread
JulienDarve marked this conversation as resolved.
Resolved
)
valid_output_fields = core_output_fields + tuple(
fields + omni_output_extra_fields for fields in core_output_fields
Expand Down Expand Up @@ -518,6 +515,8 @@ def test_engine_core_output_construction(self):
assert output.mm_cache_miss_hashes is None
if "new_sampling_mask" in EngineCoreOutput.__struct_fields__:
assert output.new_sampling_mask is None
if "spec_decode_metrics" in EngineCoreOutput.__struct_fields__:
assert output.spec_decode_metrics is None
assert output.finish_reason is FinishReason.STOP
assert output.stop_reason == "eos"

Expand Down
4 changes: 4 additions & 0 deletions container/compliance/license_overrides.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1036,3 +1036,7 @@ overrides:
- { ecosystem: python, name: kaleido, license: "MIT", source: "Plotly Kaleido — https://github.com/plotly/kaleido/blob/main/LICENSE.txt (MIT)" }
- { ecosystem: python, name: choreographer, license: "MIT", source: "Plotly Choreographer — https://github.com/plotly/choreographer/blob/main/LICENSE.md (MIT)" }
- { ecosystem: python, name: logistro, license: "MIT", source: "GeoPozo logistro — https://github.com/geopozo/logistro/blob/main/LICENSE (MIT)" }

- { ecosystem: python, name: instanttensor, license: "Apache-2.0", source: "https://github.com/scitix/InstantTensor/blob/v0.1.9/LICENSE" }
- { ecosystem: python, name: sigstore-models, license: "MIT", source: "https://github.com/astral-sh/sigstore-models/blob/v0.0.6/LICENSE" }
- { ecosystem: rust, name: tsp-asn1, license: "Apache-2.0", source: "https://github.com/trailofbits/rfc3161-client/blob/v1.0.8/LICENSE" }
20 changes: 7 additions & 13 deletions container/context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ vllm:
base_image: nvcr.io/nvidia/cuda-dl-base
runtime_image: vllm/vllm-openai
base_image_tag: 25.11-cuda13.0-devel-ubuntu24.04
runtime_image_tag: v0.28.0-ubuntu2404
runtime_image_tag: v0.29.0-ubuntu2404
# Baseline is vllm-openai's TRUE base, nvidia/cuda:13.0.2-base-ubuntu24.04
# (Docker Hub), NOT a self-baseline of the third-party vllm/vllm-openai image.
# vllm-openai is built FROM nvidia/cuda, so this attributes everything vLLM
Expand All @@ -89,25 +89,19 @@ vllm:
xpu:
base_image: vllm/vllm-openai-xpu
runtime_image: vllm/vllm-openai-xpu
base_image_tag: v0.27.1
runtime_image_tag: v0.27.1
# Match the vLLM 0.27.1 XPU image. vLLM-Omni 0.28 imports APIs only
# available in vLLM 0.28 and fails during vLLM plugin discovery.
vllm_omni_ref: "v0.27.0rc1"
base_image_tag: v0.29.0
Comment thread
JulienDarve marked this conversation as resolved.
Resolved
runtime_image_tag: v0.29.0
baseline_sbom: vllm-openai-xpu@3309c2e1
cpu:
base_image: ubuntu
runtime_image: vllm/vllm-openai-cpu
base_image_tag: 22.04
runtime_image_tag: v0.28.0
runtime_image_tag: v0.29.0
Comment thread
JulienDarve marked this conversation as resolved.
Resolved
Comment thread
JulienDarve marked this conversation as resolved.
Resolved
# No baseline_sbom: cpu images are unshipped and skip the compliance stages.
vllm_omni_ref: "v0.28.0rc1"
# vLLM-Omni 0.28.0rc1 requires transformers>=5.10.1,<5.15, while vLLM's own
# unbounded transformers>=5.5.3 lets the release images resolve to 5.15.1.
# Omni is layered against the installed Transformers version, so without
# this pin its dependency solve is unsatisfiable. Remove the pin once
# vLLM-Omni lifts its <5.15 upper bound.
vllm_omni_ref: "v0.29.0rc1"
# Omni requires Transformers <5.15, which requires tokenizers <=0.23.0.
transformers_version: "5.14.1"
tokenizers_version: "0.22.2"
nixl_ref: v1.3.2
max_jobs: "10"
enable_media_ffmpeg: "true"
Expand Down
3 changes: 1 addition & 2 deletions container/deps/vllm/protected_packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ triton
vllm
transformers
tokenizers
# vLLM-Omni (v0.28.0rc1) may require a newer safetensors than the upstream
# vLLM 0.28.0 image ships, so safetensors is intentionally left unfrozen here.
# Omni may require a newer safetensors than the upstream image; leave it unfrozen.
# safetensors
msgspec
flashinfer-python
Expand Down
1 change: 1 addition & 0 deletions container/templates/args.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ ARG PLANNER_RUNTIME_IMAGE_TAG={{ context.dynamo.planner_runtime_image_tag }}
{% if framework == "vllm" -%}
ARG MAX_JOBS={{ context.vllm.max_jobs }}
ARG TRANSFORMERS_VERSION={{ context.vllm.transformers_version }}
ARG TOKENIZERS_VERSION={{ context.vllm.tokenizers_version }}
ARG VLLM_OMNI_REF={{ context.vllm[device_key].get("vllm_omni_ref", context.vllm.vllm_omni_ref) }}

{% if device == "cuda" -%}
Expand Down
Loading
Loading