Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Empty file added tests/config/__init__.py
Empty file.
111 changes: 111 additions & 0 deletions tests/config/test_pipeline_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Tests for the central pipeline registry (2.5/N)."""

from __future__ import annotations

import pytest

from vllm_omni.config.pipeline_registry import (
_DIFFUSION_PIPELINES,
_OMNI_PIPELINES,
_VLLM_OMNI_PIPELINES,
)
from vllm_omni.config.stage_config import (
_PIPELINE_REGISTRY,
PipelineConfig,
StageExecutionType,
StagePipelineConfig,
register_pipeline,
)


class TestCentralRegistryDeclarations:
"""Every in-tree pipeline must be declared exactly once in the central registry."""

def test_union_contains_all_omni(self):
for key in _OMNI_PIPELINES:
assert key in _VLLM_OMNI_PIPELINES

def test_union_contains_all_diffusion(self):
for key in _DIFFUSION_PIPELINES:
assert key in _VLLM_OMNI_PIPELINES

def test_no_duplicate_model_type_between_omni_and_diffusion(self):
overlap = set(_OMNI_PIPELINES) & set(_DIFFUSION_PIPELINES)
assert not overlap, f"Duplicate model_types across omni/diffusion: {overlap}"

def test_expected_omni_pipelines_present(self):
# Guard against accidental removal during future refactors.
assert "qwen2_5_omni" in _OMNI_PIPELINES
assert "qwen2_5_omni_thinker_only" in _OMNI_PIPELINES
assert "qwen3_omni_moe" in _OMNI_PIPELINES
assert "qwen3_tts" in _OMNI_PIPELINES


class TestLazyLoading:
"""Pipelines are imported only on first access."""

def test_contains_without_import(self):
# ``in`` hits the lazy map, not the loaded cache.
assert "qwen3_omni_moe" in _PIPELINE_REGISTRY

def test_getitem_loads_correct_pipeline(self):
pipeline = _PIPELINE_REGISTRY["qwen3_omni_moe"]
assert pipeline.model_type == "qwen3_omni_moe"
assert pipeline.model_arch == "Qwen3OmniMoeForConditionalGeneration"

def test_unknown_model_type_returns_none_via_get(self):
assert _PIPELINE_REGISTRY.get("not_a_real_pipeline") is None

def test_unknown_model_type_raises_keyerror_via_getitem(self):
with pytest.raises(KeyError):
_PIPELINE_REGISTRY["not_a_real_pipeline"]

def test_iteration_yields_registered_pipelines(self):
keys = set(_PIPELINE_REGISTRY)
assert "qwen2_5_omni" in keys
assert "qwen3_omni_moe" in keys


class TestDynamicRegistration:
"""``register_pipeline()`` still works for plugins and tests."""

def test_register_adds_to_registry(self):
custom = PipelineConfig(
model_type="_test_dynamic_registration",
model_arch="DynamicTestModel",
stages=(
StagePipelineConfig(
stage_id=0,
model_stage="test",
execution_type=StageExecutionType.LLM_AR,
input_sources=(),
final_output=True,
),
),
)
register_pipeline(custom)
try:
assert "_test_dynamic_registration" in _PIPELINE_REGISTRY
assert _PIPELINE_REGISTRY["_test_dynamic_registration"] is custom
finally:
# Don't leak the test registration into other tests.
if "_test_dynamic_registration" in _PIPELINE_REGISTRY:
del _PIPELINE_REGISTRY["_test_dynamic_registration"]

def test_dynamic_registration_overrides_lazy_entry(self):
# Build a substitute for qwen3_omni_moe that we can distinguish.
original = _PIPELINE_REGISTRY["qwen3_omni_moe"]
override = PipelineConfig(
model_type="qwen3_omni_moe",
model_arch="OverriddenArch",
stages=original.stages,
)
register_pipeline(override)
try:
assert _PIPELINE_REGISTRY["qwen3_omni_moe"].model_arch == "OverriddenArch"
finally:
# Remove the dynamic override so later tests see the original.
if "qwen3_omni_moe" in _PIPELINE_REGISTRY._loaded:
del _PIPELINE_REGISTRY["qwen3_omni_moe"]
36 changes: 25 additions & 11 deletions tests/test_config_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
register_pipeline,
strip_parent_engine_args,
)
from vllm_omni.engine.arg_utils import internal_blacklist_keys
from vllm_omni.engine.arg_utils import SHARED_FIELDS, internal_blacklist_keys


class TestStageType:
Expand Down Expand Up @@ -330,6 +330,9 @@ class TestStageResolutionHelpers:
"""Tests for shared stage override / filtering helpers."""

def test_build_stage_runtime_overrides_ignores_other_stage_and_internal_keys(self):
# Pass the same filter set the function uses by default
# (orchestrator-only fields plus SHARED_FIELDS so ``model`` is
# treated as not-per-stage-overridable).
overrides = build_stage_runtime_overrides(
0,
{
Expand All @@ -339,7 +342,7 @@ def test_build_stage_runtime_overrides_ignores_other_stage_and_internal_keys(sel
"stage_0_model": "should_be_ignored",
"parallel_config": {"world_size": 2},
},
internal_keys=internal_blacklist_keys(),
internal_keys=internal_blacklist_keys() | SHARED_FIELDS,
)

assert overrides["gpu_memory_utilization"] == 0.9
Expand Down Expand Up @@ -672,19 +675,27 @@ def test_parse_missing_async_chunk_defaults_false(self, tmp_path):


class TestPipelineDiscovery:
"""Tests for auto-discovery of pipelines from models/*/pipeline.py."""
"""Tests for the central pipeline registry (``pipeline_registry._VLLM_OMNI_PIPELINES``)."""

def test_discover_populates_registry_with_known_models(self):
"""``_discover_all_pipelines`` imports every pipeline.py so the
registry is populated with the built-in models after one call."""
from vllm_omni.config.stage_config import _discover_all_pipelines

_discover_all_pipelines()
# These models have a pipeline.py in-tree and must be registered.
def test_registry_has_known_models(self):
"""Built-in pipelines are lazy-loaded from the central declaration
on first access; no eager import or discovery walk needed."""
# ``in`` triggers the lazy-map lookup without forcing a load.
assert "qwen2_5_omni" in _PIPELINE_REGISTRY
assert "qwen3_omni_moe" in _PIPELINE_REGISTRY
assert "qwen3_tts" in _PIPELINE_REGISTRY

def test_registry_loads_pipeline_on_getitem(self):
"""Looking up a registered model_type returns the matching PipelineConfig."""
pipeline = _PIPELINE_REGISTRY["qwen3_omni_moe"]
assert pipeline.model_type == "qwen3_omni_moe"
assert len(pipeline.stages) == 3 # thinker + talker + code2wav

def test_registry_returns_none_for_unknown(self):
"""Unknown model_types aren't found; ``get()`` returns None."""
assert "definitely_not_a_real_model" not in _PIPELINE_REGISTRY
assert _PIPELINE_REGISTRY.get("definitely_not_a_real_model") is None

def test_pipeline_config_supports_hf_architectures(self):
"""PipelineConfig accepts hf_architectures for HF-arch fallback
(replaces the old _ARCHITECTURE_MODELS dict)."""
Expand Down Expand Up @@ -950,7 +961,10 @@ def test_ci_inherits_from_main(self):
assert deploy.stages[0].gpu_memory_utilization == 0.9
assert deploy.connectors is not None
assert "connector_of_shared_memory" in deploy.connectors
assert deploy.async_chunk is True
# CI overlay explicitly sets async_chunk: False (see
# tests/utils.py::_CI_OVERLAYS and PR #2383 discussion). Overlay
# bool overrides base even when the base yaml has async_chunk: true.
assert deploy.async_chunk is False

def test_ci_sampling_merge(self):
from tests.utils import get_deploy_config_path
Expand Down
55 changes: 55 additions & 0 deletions vllm_omni/config/pipeline_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Central declarative registry of all vllm-omni pipelines.

Mirrors the pattern in ``vllm/model_executor/models/registry.py``: each entry
is ``model_type -> (module_path, variable_name)``, and the module is imported
lazily on first lookup (see ``_LazyPipelineRegistry`` in
``vllm_omni/config/stage_config.py``). Keeping every pipeline declared in one
file makes it easy to spot a missing registration, which was the original
motivation in https://github.com/vllm-project/vllm-omni/issues/2887 (item 4).

Per-model ``pipeline.py`` modules still define the ``PipelineConfig`` instance;
they just no longer need to self-register via ``register_pipeline(...)``.

Adding a new pipeline:
1. Define the ``PipelineConfig`` instance as a module-level variable in
``vllm_omni/.../pipeline.py``.
2. Add one line to ``_OMNI_PIPELINES`` or ``_DIFFUSION_PIPELINES`` below.

``register_pipeline(config)`` in ``stage_config`` is still supported for
out-of-tree plugins and tests that create pipelines at runtime; those override
the entries declared here.
"""

from __future__ import annotations

# --- Multi-stage omni pipelines (LLM-centric; audio / video I/O) ---
_OMNI_PIPELINES: dict[str, tuple[str, str]] = {
# model_type -> (module_path, variable_name)
"qwen2_5_omni": (
"vllm_omni.model_executor.models.qwen2_5_omni.pipeline",
"QWEN2_5_OMNI_PIPELINE",
),
"qwen2_5_omni_thinker_only": (
"vllm_omni.model_executor.models.qwen2_5_omni.pipeline",
"QWEN2_5_OMNI_THINKER_ONLY_PIPELINE",
),
"qwen3_omni_moe": (
"vllm_omni.model_executor.models.qwen3_omni.pipeline",
"QWEN3_OMNI_PIPELINE",
),
"qwen3_tts": (
"vllm_omni.model_executor.models.qwen3_tts.pipeline",
"QWEN3_TTS_PIPELINE",
),
}

# --- Single-stage diffusion pipelines (populated in PR 3/N) ---
_DIFFUSION_PIPELINES: dict[str, tuple[str, str]] = {}

# Union view used by ``_LazyPipelineRegistry``; don't mutate at runtime.
_VLLM_OMNI_PIPELINES: dict[str, tuple[str, str]] = {
**_OMNI_PIPELINES,
**_DIFFUSION_PIPELINES,
}
Loading
Loading