From 4b66f62e10f1b765dc6dc6baa5bbfe633234ec25 Mon Sep 17 00:00:00 2001 From: Yukim1 <121286183+zwhzzz0821@users.noreply.github.com> Date: Thu, 13 Aug 2026 21:29:03 +0800 Subject: [PATCH] [Refactor] Remove internal stage_configs_path plumbing (#5741) Signed-off-by: zwhzzz0821 <2831474076@qq.com> Signed-off-by: Hongsheng Liu Co-authored-by: Hongsheng Liu --- .../offline_inference/covo_audio/end2end.py | 6 +- .../offline_inference/dynin_omni/end2end.py | 6 +- .../hunyuan_image3/end2end.py | 19 +-- .../offline_inference/minicpmo/end2end.py | 6 - .../offline_inference/qwen3_omni/end2end.py | 4 +- .../offline_inference/step_audio2/end2end.py | 22 +-- .../text_to_image/text_to_image.py | 12 +- .../text_to_speech/fish_speech/end2end.py | 13 +- .../text_to_speech/glm_tts/end2end.py | 2 +- .../text_to_speech/qwen3_tts/end2end.py | 4 +- tests/config/test_config_factory.py | 52 +------ .../quantization/test_quantization_fp8.py | 2 +- .../test_bagel_mooncake_connector.py | 2 +- .../test_bagel_shared_memory_connector.py | 4 +- .../test_audiox_model_expansion.py | 2 +- .../offline_inference/test_bagel_expansion.py | 2 +- .../test_cosyvoice3_expansion.py | 2 +- .../test_ernie_image_expansion.py | 2 +- .../test_glm_tts_expansion.py | 2 +- .../test_hunyuanimage3_i2t_expansion.py | 2 +- .../test_longcat_video_avatar.py | 1 - .../test_mammoth_moda2_expansion.py | 2 +- .../offline_inference/test_minicpmo_4_5.py | 2 +- .../test_moss_tts_nano_expansion.py | 2 +- .../test_omnivoice_expansion.py | 6 +- .../test_sensenova_u1_expansion.py | 2 +- .../test_sensenova_u1_img2img_expansion.py | 6 +- .../test_sensenova_u1_text2img_expansion.py | 6 +- .../test_soulxsinger_expansion.py | 4 +- .../test_step_audio2_expansion.py | 2 +- .../test_voxtral_tts_expansion.py | 2 +- tests/engine/test_arg_utils.py | 139 ------------------ tests/engine/test_async_omni_engine_abort.py | 2 +- tests/engine/test_single_stage_mode.py | 4 +- tests/entrypoints/test_async_omni.py | 2 +- .../test_async_omni_diffusion_config.py | 6 +- tests/entrypoints/test_serve.py | 2 +- tests/entrypoints/test_utils.py | 16 +- tests/helpers/runtime.py | 13 +- tools/baselines/omni_hash_baseline.py | 2 +- vllm_omni/config/omni_config.py | 1 - vllm_omni/config/stage_config.py | 43 +----- vllm_omni/engine/arg_utils.py | 6 - vllm_omni/engine/async_omni_engine.py | 93 +----------- vllm_omni/entrypoints/async_omni.py | 5 +- vllm_omni/entrypoints/cli/serve.py | 2 - vllm_omni/entrypoints/openai/api_server.py | 4 +- vllm_omni/entrypoints/utils.py | 89 ++--------- 48 files changed, 93 insertions(+), 537 deletions(-) diff --git a/examples/offline_inference/covo_audio/end2end.py b/examples/offline_inference/covo_audio/end2end.py index 5b7ac5fef..b5050f62c 100644 --- a/examples/offline_inference/covo_audio/end2end.py +++ b/examples/offline_inference/covo_audio/end2end.py @@ -59,7 +59,7 @@ def main(args): omni = Omni( model=args.model_name, - stage_configs_path=args.stage_configs_path, + deploy_config=args.deploy_config, log_stats=args.log_stats, stage_init_timeout=args.stage_init_timeout, batch_timeout=args.batch_timeout, @@ -167,10 +167,10 @@ def parse_args(): help="Sampling rate for audio loading (default: 16000).", ) parser.add_argument( - "--stage-configs-path", + "--deploy-config", type=str, default=None, - help="Path to stage configs YAML file.", + help="Path to a deploy config YAML file.", ) parser.add_argument( "--log-stats", diff --git a/examples/offline_inference/dynin_omni/end2end.py b/examples/offline_inference/dynin_omni/end2end.py index 0cab46fee..74d33fe01 100644 --- a/examples/offline_inference/dynin_omni/end2end.py +++ b/examples/offline_inference/dynin_omni/end2end.py @@ -876,7 +876,7 @@ def parse_args(repo_root: Path) -> argparse.Namespace: parser.add_argument("--task", type=str, required=True, choices=TASK_CHOICES) parser.add_argument("--model", type=str, required=True, help="HF repo id or local model directory.") parser.add_argument( - "--stage-config-path", + "--deploy-config", type=str, default=str(repo_root / "vllm_omni/deploy/dynin_omni.yaml"), help="Path to deploy config yaml.", @@ -1394,8 +1394,8 @@ def main() -> None: from vllm_omni.entrypoints.omni import Omni - stage_config_path = str(Path(args.stage_config_path).expanduser()) - omni = Omni(model=model_source, stage_configs_path=stage_config_path, dtype=args.dtype) + deploy_config_path = str(Path(args.deploy_config).expanduser()) + omni = Omni(model=model_source, deploy_config=deploy_config_path, dtype=args.dtype) sampling_params_list = [ SamplingParams(max_tokens=int(args.max_tokens_per_stage), temperature=0.0, top_p=1.0, detokenize=False) for _ in range(omni.num_stages) diff --git a/examples/offline_inference/hunyuan_image3/end2end.py b/examples/offline_inference/hunyuan_image3/end2end.py index 9a2350156..fbe4289be 100644 --- a/examples/offline_inference/hunyuan_image3/end2end.py +++ b/examples/offline_inference/hunyuan_image3/end2end.py @@ -73,7 +73,6 @@ def parse_args(): ) parser.add_argument("--sys-type", type=str, default=None, help="Override system prompt type.") parser.add_argument("--deploy-config", type=str, default=None, help="Custom deploy YAML path.") - parser.add_argument("--stage-configs-path", type=str, default=None, help="Custom legacy stage config YAML path.") parser.add_argument( "--stream", action="store_true", @@ -146,13 +145,7 @@ def main(): else: bot_task = args.bot_task - if args.deploy_config is not None and args.stage_configs_path is not None: - raise ValueError("--deploy-config and --stage-configs-path are mutually exclusive.") - - deploy_config = args.deploy_config - stage_configs_path = args.stage_configs_path - if deploy_config is None and stage_configs_path is None: - deploy_config = _MODALITY_DEFAULT_DEPLOY_CONFIG[args.modality] + deploy_config = args.deploy_config or _MODALITY_DEFAULT_DEPLOY_CONFIG[args.modality] omni_kwargs = { "model": args.model, @@ -168,10 +161,7 @@ def main(): if additional_config is not None: omni_kwargs["additional_config"] = additional_config - if deploy_config is not None: - omni_kwargs["deploy_config"] = deploy_config - else: - omni_kwargs["stage_configs_path"] = stage_configs_path + omni_kwargs["deploy_config"] = deploy_config omni = Omni(**omni_kwargs) @@ -272,10 +262,7 @@ def main(): print(f" Modality: {args.modality}") print(f" Prompt task: {task}") print(f" Bot task: {bot_task}") - if deploy_config is not None: - print(f" Deploy config: {deploy_config}") - else: - print(f" Stage config: {stage_configs_path}") + print(f" Deploy config: {deploy_config}") print(f" Num stages: {omni.num_stages}") if args.modality in ("text2img", "img2img"): print(f" Inference steps: {args.steps}") diff --git a/examples/offline_inference/minicpmo/end2end.py b/examples/offline_inference/minicpmo/end2end.py index e9c9019c0..857f1f00f 100644 --- a/examples/offline_inference/minicpmo/end2end.py +++ b/examples/offline_inference/minicpmo/end2end.py @@ -452,12 +452,6 @@ def parse_args(): default=None, help="Path to a .txt file with one prompt per line (preferred).", ) - parser.add_argument( - "--stage-configs-path", - type=str, - default=None, - help="Path to a stage configs file (deprecated; prefer --deploy-config).", - ) parser.add_argument( "--deploy-config", type=str, diff --git a/examples/offline_inference/qwen3_omni/end2end.py b/examples/offline_inference/qwen3_omni/end2end.py index 9434b3058..faa200bc9 100644 --- a/examples/offline_inference/qwen3_omni/end2end.py +++ b/examples/offline_inference/qwen3_omni/end2end.py @@ -484,10 +484,10 @@ def parse_args(): help="Path to a .txt file with one prompt per line (preferred).", ) parser.add_argument( - "--stage-configs-path", + "--deploy-config", type=str, default=None, - help="Path to a stage configs file.", + help="Path to a deploy config YAML.", ) parser.add_argument( "--video-path", diff --git a/examples/offline_inference/step_audio2/end2end.py b/examples/offline_inference/step_audio2/end2end.py index f175db750..418a7f129 100644 --- a/examples/offline_inference/step_audio2/end2end.py +++ b/examples/offline_inference/step_audio2/end2end.py @@ -232,15 +232,8 @@ def main(args): raise ValueError(f"Unknown query type: {args.query_type}") # Initialize vLLM-Omni with Step-Audio2 - # Resolve deploy config path. Keep explicit stage_configs_path for legacy - # custom configs, but use bundled deploy configs by default. - deploy_config_path = None - stage_config_path = None - if args.stage_configs_path and args.deploy_config: - raise ValueError("--stage-configs-path and --deploy-config are mutually exclusive") - if args.stage_configs_path: - stage_config_path = args.stage_configs_path - elif args.deploy_config: + # Resolve an explicit deploy config or use the bundled task default. + if args.deploy_config: deploy_config_path = args.deploy_config else: configs_dir = Path(__file__).parent.parent.parent.parent / "vllm_omni/deploy" @@ -262,10 +255,7 @@ def main(args): "ray_address": args.ray_address, "trust_remote_code": True, } - if stage_config_path is not None: - omni_kwargs["stage_configs_path"] = stage_config_path - else: - omni_kwargs["deploy_config"] = deploy_config_path + omni_kwargs["deploy_config"] = deploy_config_path omni_llm = Omni(**omni_kwargs) @@ -418,12 +408,6 @@ def parse_args(): " - HuggingFace ID: stepfun-ai/Step-Audio-2-mini" ), ) - parser.add_argument( - "--stage-configs-path", - type=str, - default=None, - help="Path to a legacy stage config YAML file (default: use bundled deploy config)", - ) parser.add_argument( "--deploy-config", type=str, diff --git a/examples/offline_inference/text_to_image/text_to_image.py b/examples/offline_inference/text_to_image/text_to_image.py index 360c726fd..2d1f9823a 100644 --- a/examples/offline_inference/text_to_image/text_to_image.py +++ b/examples/offline_inference/text_to_image/text_to_image.py @@ -92,12 +92,6 @@ def parse_args() -> argparse.Namespace: "meituan-longcat/LongCat-Image, OvisAI/Ovis-Image, " "stabilityai/stable-diffusion-3.5-medium, Tongyi-MAI/Z-Image-Turbo and etc.", ) - parser.add_argument( - "--stage-configs-path", - type=str, - default=None, - help="[Deprecated] Path to a legacy stage_args-format YAML. Prefer --deploy-config.", - ) parser.add_argument( "--deploy-config", type=str, @@ -475,8 +469,6 @@ def main(): omni_kwargs["tensor_parallel_size"] = args.tensor_parallel_size if args.enforce_eager is not None: omni_kwargs["enforce_eager"] = args.enforce_eager - if args.stage_configs_path: - omni_kwargs["stage_configs_path"] = args.stage_configs_path if args.deploy_config: omni_kwargs["deploy_config"] = args.deploy_config if use_nextstep: @@ -515,8 +507,8 @@ def main(): print(f" Image size: {args.width}x{args.height}") if args.lora_path: print(f" LoRA: scale={args.lora_scale}") - if args.stage_configs_path: - print(f" stage-configs-path: {args.stage_configs_path}") + if args.deploy_config: + print(f" deploy-config: {args.deploy_config}") print(f"{'=' * 60}\n") # Build LoRA request when --lora-path is set diff --git a/examples/offline_inference/text_to_speech/fish_speech/end2end.py b/examples/offline_inference/text_to_speech/fish_speech/end2end.py index 18b4d79f8..8ce4664b9 100644 --- a/examples/offline_inference/text_to_speech/fish_speech/end2end.py +++ b/examples/offline_inference/text_to_speech/fish_speech/end2end.py @@ -38,7 +38,7 @@ from vllm_omni.utils.tracking_parser import TrackingArgumentParser logger = logging.getLogger(__name__) DEFAULT_MODEL = "fishaudio/s2-pro" -DEFAULT_STAGE_CONFIG = os.path.join( +DEFAULT_DEPLOY_CONFIG = os.path.join( os.path.dirname(__file__), "..", "..", @@ -152,10 +152,7 @@ def _build_omni_kwargs(args, model_name: str) -> dict: log_stats=args.log_stats, stage_init_timeout=args.stage_init_timeout, ) - if args.deploy_config: - omni_kwargs["deploy_config"] = args.deploy_config - else: - omni_kwargs["stage_configs_path"] = args.stage_configs_path or DEFAULT_STAGE_CONFIG + omni_kwargs["deploy_config"] = args.deploy_config or DEFAULT_DEPLOY_CONFIG return omni_kwargs @@ -278,12 +275,6 @@ def parse_args(): default=None, help="Deploy config YAML path (under vllm_omni/deploy/).", ) - parser.add_argument( - "--stage-configs-path", - type=str, - default=None, - help="Path to stage configs YAML (legacy, prefer --deploy-config).", - ) parser.add_argument( "--output-dir", default="output_audio", diff --git a/examples/offline_inference/text_to_speech/glm_tts/end2end.py b/examples/offline_inference/text_to_speech/glm_tts/end2end.py index 6624c2b35..3f66ed8c3 100644 --- a/examples/offline_inference/text_to_speech/glm_tts/end2end.py +++ b/examples/offline_inference/text_to_speech/glm_tts/end2end.py @@ -145,7 +145,7 @@ def main(args): omni = Omni( model=args.model, - stage_configs_path=deploy_config_path, + deploy_config=deploy_config_path, log_stats=args.log_stats, stage_init_timeout=args.stage_init_timeout, ) diff --git a/examples/offline_inference/text_to_speech/qwen3_tts/end2end.py b/examples/offline_inference/text_to_speech/qwen3_tts/end2end.py index 7c0ce125e..75f562af6 100644 --- a/examples/offline_inference/text_to_speech/qwen3_tts/end2end.py +++ b/examples/offline_inference/text_to_speech/qwen3_tts/end2end.py @@ -490,10 +490,10 @@ def parse_args(): help="Path to a .txt file with one prompt per line (preferred).", ) parser.add_argument( - "--stage-configs-path", + "--deploy-config", type=str, default=None, - help="Path to a stage configs file.", + help="Path to a deploy config YAML.", ) parser.add_argument( "--audio-path", diff --git a/tests/config/test_config_factory.py b/tests/config/test_config_factory.py index c3c3edbd1..9708c1c50 100644 --- a/tests/config/test_config_factory.py +++ b/tests/config/test_config_factory.py @@ -6,7 +6,7 @@ Unit tests for StageConfigFactory and related classes. import importlib import warnings -from dataclasses import dataclass, fields +from dataclasses import dataclass from pathlib import Path from unittest.mock import patch @@ -34,9 +34,8 @@ from vllm_omni.config.stage_config import ( load_deploy_config, merge_pipeline_deploy, pipeline_cfg_resolver, - strip_parent_engine_args, ) -from vllm_omni.engine.arg_utils import SHARED_FIELDS, EngineArgs, internal_blacklist_keys +from vllm_omni.engine.arg_utils import SHARED_FIELDS, internal_blacklist_keys pytestmark = [pytest.mark.core_model, pytest.mark.cpu] @@ -475,7 +474,7 @@ class TestStageConfigFactory: cli_overrides = { "gpu_memory_utilization": 0.9, "model": "some_model", # Internal - "stage_configs_path": "/path", # Internal + "deploy_config": "/path", # Internal "batch_timeout": 10, # Internal } @@ -483,7 +482,7 @@ class TestStageConfigFactory: assert overrides["gpu_memory_utilization"] == 0.9 assert "model" not in overrides - assert "stage_configs_path" not in overrides + assert "deploy_config" not in overrides assert "batch_timeout" not in overrides def test_per_stage_override_excludes_internal_keys(self): @@ -525,49 +524,6 @@ class TestStageResolutionHelpers: assert "model" not in overrides assert "parallel_config" not in overrides - def test_strip_parent_engine_args_reports_only_surprising_parent_overrides(self): - parent_fields = {f.name: f for f in fields(EngineArgs)} - filtered, overridden = strip_parent_engine_args( - { - "model": "some/model", - "stage_configs_path": "/tmp/stages.yaml", - "tensor_parallel_size": 4, - "worker_extension_cls": "some.Extension", - "custom_pipeline_args": {"pipeline_class": "demo.Pipeline"}, - }, - parent_fields=parent_fields, - keep_keys={"worker_extension_cls"}, - strip_keys={"stage_configs_path"}, - no_warn_keys={"model"}, - ) - - assert filtered == { - "worker_extension_cls": "some.Extension", - "custom_pipeline_args": {"pipeline_class": "demo.Pipeline"}, - } - assert overridden == ["tensor_parallel_size"] - - def test_strip_parent_engine_args_keeps_allowed_media_access_controls(self): - parent_fields = {f.name: f for f in fields(EngineArgs)} - filtered, overridden = strip_parent_engine_args( - { - "model": "some/model", - "stage_configs_path": "/tmp/stages.yaml", - "allowed_local_media_path": "/data/qwentts", - "allowed_media_domains": ["example.com"], - }, - parent_fields=parent_fields, - keep_keys={"allowed_local_media_path", "allowed_media_domains"}, - strip_keys={"stage_configs_path"}, - no_warn_keys={"model"}, - ) - - assert filtered == { - "allowed_local_media_path": "/data/qwentts", - "allowed_media_domains": ["example.com"], - } - assert overridden == [] - class TestPipelineDiscovery: """Tests for the central pipeline registry (``OMNI_PIPELINES``).""" diff --git a/tests/diffusion/quantization/test_quantization_fp8.py b/tests/diffusion/quantization/test_quantization_fp8.py index 617083398..0da5b6dd3 100644 --- a/tests/diffusion/quantization/test_quantization_fp8.py +++ b/tests/diffusion/quantization/test_quantization_fp8.py @@ -199,7 +199,7 @@ def _generate_bagel_image( config_path = get_deploy_config_path("ci/bagel.yaml") omni_kwargs: dict[str, Any] = { "model": "ByteDance-Seed/BAGEL-7B-MoT", - "stage_configs_path": config_path, + "deploy_config": config_path, "stage_init_timeout": 300, } if diffusion_quantization_config: diff --git a/tests/distributed/omni_connectors/test_bagel_mooncake_connector.py b/tests/distributed/omni_connectors/test_bagel_mooncake_connector.py index 57997905c..3b9fcbb64 100644 --- a/tests/distributed/omni_connectors/test_bagel_mooncake_connector.py +++ b/tests/distributed/omni_connectors/test_bagel_mooncake_connector.py @@ -314,7 +314,7 @@ def test_bagel_text2img_mooncake_connector(run_level): temp_config_file = _resolve_deploy_config(temp_config_file, run_level) with OmniRunner( "ByteDance-Seed/BAGEL-7B-MoT", - stage_configs_path=temp_config_file, + deploy_config=temp_config_file, stage_init_timeout=300, ) as runner: generated_image = _generate_bagel_image(runner.omni) diff --git a/tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py b/tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py index b97161335..72b56d3e9 100644 --- a/tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py +++ b/tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py @@ -251,7 +251,7 @@ def test_bagel_img2img_shared_memory_connector(run_level): config_path = _resolve_deploy_config(BAGEL_CI_DEPLOY, run_level) with OmniRunner( "ByteDance-Seed/BAGEL-7B-MoT", - stage_configs_path=config_path, + deploy_config=config_path, ) as runner: generated_image = _generate_bagel_img2img(runner.omni, input_image) if run_level == "advanced_model": @@ -266,7 +266,7 @@ def test_bagel_text2img_shared_memory_connector(run_level): config_path = _resolve_deploy_config(BAGEL_CI_DEPLOY, run_level) with OmniRunner( "ByteDance-Seed/BAGEL-7B-MoT", - stage_configs_path=config_path, + deploy_config=config_path, ) as runner: generated_image = _generate_bagel_text2img(runner.omni) if run_level == "advanced_model": diff --git a/tests/e2e/offline_inference/test_audiox_model_expansion.py b/tests/e2e/offline_inference/test_audiox_model_expansion.py index af944b144..5739cc463 100644 --- a/tests/e2e/offline_inference/test_audiox_model_expansion.py +++ b/tests/e2e/offline_inference/test_audiox_model_expansion.py @@ -17,7 +17,7 @@ from vllm_omni.platforms import current_omni_platform # Override in CI if needed: AUDIOX_TEST_MODEL= models = [os.environ.get("AUDIOX_TEST_MODEL", "zhangj1an/audiox_random")] -# (model, stage_configs_path, extra_omni_kwargs) for ``omni_runner`` indirect parametrize +# (model, deploy_config_path, extra_omni_kwargs) for ``omni_runner`` indirect parametrize _OMNI_RUNNER_PARAMS = [(m, None, {"model_class_name": "AudioXPipeline"}) for m in models] pytestmark = [ diff --git a/tests/e2e/offline_inference/test_bagel_expansion.py b/tests/e2e/offline_inference/test_bagel_expansion.py index 980fc633f..e8ac6c19f 100644 --- a/tests/e2e/offline_inference/test_bagel_expansion.py +++ b/tests/e2e/offline_inference/test_bagel_expansion.py @@ -43,7 +43,7 @@ MODEL = "ByteDance-Seed/BAGEL-7B-MoT" BAGEL_STAGE_CONFIG = get_deploy_config_path("ci/bagel.yaml") DEFAULT_PROMPT = "<|im_start|>A cute cat<|im_end|>" -# (model, stage_configs_path) for ``@pytest.mark.parametrize("omni_runner", ..., indirect=True)`` +# (model, deploy_config_path) for ``@pytest.mark.parametrize("omni_runner", ..., indirect=True)`` _OMNI_RUNNER_PARAM = (MODEL, BAGEL_STAGE_CONFIG) diff --git a/tests/e2e/offline_inference/test_cosyvoice3_expansion.py b/tests/e2e/offline_inference/test_cosyvoice3_expansion.py index fa1bdfead..7e57433ef 100644 --- a/tests/e2e/offline_inference/test_cosyvoice3_expansion.py +++ b/tests/e2e/offline_inference/test_cosyvoice3_expansion.py @@ -126,7 +126,7 @@ def _build_reference_inputs(prompt_audio: tuple[np.ndarray, int]) -> list[dict[s ] -# (model, stage_configs_path, extra_omni_kwargs) for ``@pytest.mark.parametrize("omni_runner", ..., indirect=True)`` +# (model, deploy_config_path, extra_omni_kwargs) for ``@pytest.mark.parametrize("omni_runner", ..., indirect=True)`` _cosy_deployment = get_deploy_config_path("cosyvoice3.yaml") _cosy_model_path = str(_resolve_model_dir()) _OMNI_RUNNER_PARAMS = [ diff --git a/tests/e2e/offline_inference/test_ernie_image_expansion.py b/tests/e2e/offline_inference/test_ernie_image_expansion.py index 19793ff00..a43db7d7e 100644 --- a/tests/e2e/offline_inference/test_ernie_image_expansion.py +++ b/tests/e2e/offline_inference/test_ernie_image_expansion.py @@ -17,7 +17,7 @@ from vllm_omni.inputs.data import OmniDiffusionSamplingParams MODEL = "baidu/ERNIE-Image" -# (model, stage_configs_path, extra_omni_kwargs) for ``omni_runner`` indirect parametrize +# (model, deploy_config_path, extra_omni_kwargs) for ``omni_runner`` indirect parametrize _OMNI_RUNNER_PARAM = ( MODEL, None, diff --git a/tests/e2e/offline_inference/test_glm_tts_expansion.py b/tests/e2e/offline_inference/test_glm_tts_expansion.py index e54dd5480..2bde38e6b 100644 --- a/tests/e2e/offline_inference/test_glm_tts_expansion.py +++ b/tests/e2e/offline_inference/test_glm_tts_expansion.py @@ -96,7 +96,7 @@ def test_offline_voice_clone_zh(async_chunk: bool) -> None: with OmniRunner( MODEL, - stage_configs_path=_get_deploy_config(async_chunk=async_chunk), + deploy_config=_get_deploy_config(async_chunk=async_chunk), stage_init_timeout=600, trust_remote_code=True, ) as omni_runner: diff --git a/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py b/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py index 4bdb42c34..84a9291b0 100644 --- a/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py +++ b/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py @@ -52,7 +52,7 @@ pytestmark = [pytest.mark.full_model, pytest.mark.diffusion] def omni() -> Generator[Omni, None, None]: with OmniRunner( MODEL_NAME, - stage_configs_path=str(STAGE_CONFIG_PATH), + deploy_config=str(STAGE_CONFIG_PATH), trust_remote_code=True, ) as runner: yield runner.omni diff --git a/tests/e2e/offline_inference/test_longcat_video_avatar.py b/tests/e2e/offline_inference/test_longcat_video_avatar.py index f0fa81eb3..404ad9485 100644 --- a/tests/e2e/offline_inference/test_longcat_video_avatar.py +++ b/tests/e2e/offline_inference/test_longcat_video_avatar.py @@ -84,7 +84,6 @@ def longcat_omni_runner() -> Generator[OmniRunner, None, None]: with OmniRunner( model, seed=42, - stage_configs_path=None, model_class_name="LongCatVideoAvatarPipeline", additional_config={ "model_type": "avatar-v1.5", diff --git a/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py b/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py index ec1545f6a..f570a6ea7 100644 --- a/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py +++ b/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py @@ -38,7 +38,7 @@ _AR_PATCH_SIZE = 16 MODEL_PATH = "bytedance-research/MammothModa2-Preview" T2I_DEPLOY_CONFIG = get_deploy_config_path("mammoth_moda2.yaml") -_OMNI_RUNNER_PARAM = (MODEL_PATH, None, {"deploy_config": T2I_DEPLOY_CONFIG}) +_OMNI_RUNNER_PARAM = (MODEL_PATH, T2I_DEPLOY_CONFIG) # Golden pixel reference file. Set UPDATE_GOLDEN=1 to regenerate. _GOLDEN_T2I_PATH = Path(__file__).parent / "fixtures" / "mammoth_moda2_t2i_golden.json" diff --git a/tests/e2e/offline_inference/test_minicpmo_4_5.py b/tests/e2e/offline_inference/test_minicpmo_4_5.py index 6725ce2aa..5a518a7f7 100644 --- a/tests/e2e/offline_inference/test_minicpmo_4_5.py +++ b/tests/e2e/offline_inference/test_minicpmo_4_5.py @@ -17,7 +17,7 @@ models = ["openbmb/MiniCPM-o-4_5"] _CI_DEPLOY = get_deploy_config_path("minicpmo_4_5.yaml") -test_params = [(model, None, {"deploy_config": _CI_DEPLOY, "trust_remote_code": True}) for model in models] +test_params = [(model, _CI_DEPLOY, {"trust_remote_code": True}) for model in models] def get_question(prompt_type: str = "text") -> str: diff --git a/tests/e2e/offline_inference/test_moss_tts_nano_expansion.py b/tests/e2e/offline_inference/test_moss_tts_nano_expansion.py index b894fe4ea..41e2229d3 100644 --- a/tests/e2e/offline_inference/test_moss_tts_nano_expansion.py +++ b/tests/e2e/offline_inference/test_moss_tts_nano_expansion.py @@ -25,7 +25,7 @@ from vllm_omni import Omni MODEL_NAME = "OpenMOSS-Team/MOSS-TTS-Nano" STAGE_CONFIG = get_deploy_config_path("moss_tts_nano.yaml") -# (model, stage_configs_path, extra_omni_kwargs) for ``omni_runner`` indirect parametrize +# (model, deploy_config_path, extra_omni_kwargs) for ``omni_runner`` indirect parametrize _OMNI_RUNNER_PARAM = ( MODEL_NAME, STAGE_CONFIG, diff --git a/tests/e2e/offline_inference/test_omnivoice_expansion.py b/tests/e2e/offline_inference/test_omnivoice_expansion.py index 5b4c7a935..dd65cbdcc 100644 --- a/tests/e2e/offline_inference/test_omnivoice_expansion.py +++ b/tests/e2e/offline_inference/test_omnivoice_expansion.py @@ -20,13 +20,11 @@ from tests.helpers.stage_config import get_deploy_config_path MODEL = "k2-fsa/OmniVoice" DEPLOY_CONFIG = get_deploy_config_path("omnivoice.yaml") -# OmniRunner tuple: model, legacy stage config path, extra Omni kwargs. -# The migrated test passes deploy_config through extra Omni kwargs. +# OmniRunner tuple: model, deploy config path, extra Omni kwargs. _OMNI_RUNNER_PARAM = ( MODEL, - None, + DEPLOY_CONFIG, { - "deploy_config": DEPLOY_CONFIG, "trust_remote_code": True, "log_stats": True, }, diff --git a/tests/e2e/offline_inference/test_sensenova_u1_expansion.py b/tests/e2e/offline_inference/test_sensenova_u1_expansion.py index 4f5c97d11..42510d7c6 100644 --- a/tests/e2e/offline_inference/test_sensenova_u1_expansion.py +++ b/tests/e2e/offline_inference/test_sensenova_u1_expansion.py @@ -131,7 +131,7 @@ def test_sensenova_u1_lora_scale_and_deactivation(tmp_path) -> None: """Validate LoRA effect, scale sensitivity, bounded perturbation, and clean deactivation.""" lora_request = _make_file_lora_request(tmp_path / "sensenova_lora") - with OmniRunner(MODEL, stage_configs_path=None) as runner: + with OmniRunner(MODEL) as runner: omni = runner.omni baseline = _generate(omni) img_1x = _generate(omni, lora_request, lora_scale=1.0) diff --git a/tests/e2e/offline_inference/test_sensenova_u1_img2img_expansion.py b/tests/e2e/offline_inference/test_sensenova_u1_img2img_expansion.py index 1ac76da28..c2739844c 100644 --- a/tests/e2e/offline_inference/test_sensenova_u1_img2img_expansion.py +++ b/tests/e2e/offline_inference/test_sensenova_u1_img2img_expansion.py @@ -164,10 +164,7 @@ def _generate_sensenova_u1_img2img( def test_sensenova_u1_img2img(run_level): """Test SenseNova-U1 img2img (single-stage diffusion, no deploy YAML).""" input_image = _load_input_image() - with OmniRunner( - "SenseNova/SenseNova-U1-8B-MoT", - stage_configs_path=None, - ) as runner: + with OmniRunner("SenseNova/SenseNova-U1-8B-MoT") as runner: generated_image = _generate_sensenova_u1_img2img(runner.omni, input_image) if run_level == "full_model" or run_level == "advanced_model": _validate_pixels(generated_image) @@ -182,7 +179,6 @@ def test_sensenova_u1_img2img_cache_dit(): input_image = _load_input_image() with OmniRunner( "SenseNova/SenseNova-U1-8B-MoT", - stage_configs_path=None, cache_backend="cache_dit", ) as runner: _generate_sensenova_u1_img2img(runner.omni, input_image) diff --git a/tests/e2e/offline_inference/test_sensenova_u1_text2img_expansion.py b/tests/e2e/offline_inference/test_sensenova_u1_text2img_expansion.py index 092da16b9..2e07e2076 100644 --- a/tests/e2e/offline_inference/test_sensenova_u1_text2img_expansion.py +++ b/tests/e2e/offline_inference/test_sensenova_u1_text2img_expansion.py @@ -151,10 +151,7 @@ def _generate_sensenova_u1_image( @hardware_test(res={"cuda": "H100"}) def test_sensenova_u1_text2img(run_level): """Test SenseNova-U1 text2img (single-stage diffusion, no deploy YAML).""" - with OmniRunner( - "SenseNova/SenseNova-U1-8B-MoT", - stage_configs_path=None, - ) as runner: + with OmniRunner("SenseNova/SenseNova-U1-8B-MoT") as runner: generated_image = _generate_sensenova_u1_image(runner.omni) if run_level == "full_model" or run_level == "advanced_model": _validate_pixels(generated_image) @@ -168,7 +165,6 @@ def test_sensenova_u1_text2img_cache_dit(): """Test SenseNova-U1 text2img with Cache-DiT enabled.""" with OmniRunner( "SenseNova/SenseNova-U1-8B-MoT", - stage_configs_path=None, cache_backend="cache_dit", ) as runner: _generate_sensenova_u1_image(runner.omni) diff --git a/tests/e2e/offline_inference/test_soulxsinger_expansion.py b/tests/e2e/offline_inference/test_soulxsinger_expansion.py index c3a6091d6..a008be526 100644 --- a/tests/e2e/offline_inference/test_soulxsinger_expansion.py +++ b/tests/e2e/offline_inference/test_soulxsinger_expansion.py @@ -159,7 +159,7 @@ def test_soulxsinger_multistage_from_audio( with OmniRunner( model, - stage_configs_path=get_deploy_config_path(deploy_yaml), + deploy_config=get_deploy_config_path(deploy_yaml), async_chunk=False, ) as runner: sampling = OmniDiffusionSamplingParams( @@ -212,7 +212,7 @@ def test_soulxsinger_svs_precomputed(soulx_weights: tuple[Path, Path, Path]) -> with OmniRunner( str(base_dir), - stage_configs_path=get_deploy_config_path("soulxsinger_svs.yaml"), + deploy_config=get_deploy_config_path("soulxsinger_svs.yaml"), async_chunk=False, ) as runner: sampling = OmniDiffusionSamplingParams( diff --git a/tests/e2e/offline_inference/test_step_audio2_expansion.py b/tests/e2e/offline_inference/test_step_audio2_expansion.py index 483396b23..3a1b60c94 100644 --- a/tests/e2e/offline_inference/test_step_audio2_expansion.py +++ b/tests/e2e/offline_inference/test_step_audio2_expansion.py @@ -19,7 +19,7 @@ from vllm_omni.outputs import OmniRequestOutput MODEL = "stepfun-ai/Step-Audio-2-mini" DEPLOY_CONFIG = get_deploy_config_path("step_audio2_ci.yaml") -TEST_PARAMS = [(MODEL, None, {"deploy_config": DEPLOY_CONFIG, "trust_remote_code": True})] +TEST_PARAMS = [(MODEL, DEPLOY_CONFIG, {"trust_remote_code": True})] pytestmark = [pytest.mark.slow, pytest.mark.tts] diff --git a/tests/e2e/offline_inference/test_voxtral_tts_expansion.py b/tests/e2e/offline_inference/test_voxtral_tts_expansion.py index 2441c46dc..dcff41cda 100644 --- a/tests/e2e/offline_inference/test_voxtral_tts_expansion.py +++ b/tests/e2e/offline_inference/test_voxtral_tts_expansion.py @@ -102,7 +102,7 @@ def test_voxtral_tts_offline_streaming(): async def _run(): async_omni = AsyncOmni( model=MODEL, - stage_configs_path=STAGE_CONFIG, + deploy_config=STAGE_CONFIG, stage_init_timeout=300, enforce_eager=True, ) diff --git a/tests/engine/test_arg_utils.py b/tests/engine/test_arg_utils.py index 055866bd5..ed1031b64 100644 --- a/tests/engine/test_arg_utils.py +++ b/tests/engine/test_arg_utils.py @@ -4,7 +4,6 @@ invariant to the specific attributes of vLLM config except in cases where we explicitly patch values that differ from vLLM. """ -import argparse import inspect import json import os @@ -20,7 +19,6 @@ from vllm.engine.arg_utils import EngineArgs from vllm_omni.config.model import OmniModelConfig from vllm_omni.engine.arg_utils import OmniEngineArgs -from vllm_omni.engine.async_omni_engine import AsyncOmniEngine from vllm_omni.engine.stage_init_utils import build_engine_args_dict pytestmark = [pytest.mark.core_model, pytest.mark.cpu] @@ -149,19 +147,6 @@ def test_qwen3_tts_startup_task_type_is_validated(): ) -def test_from_cli_args_picks_up_stage_configs_path(): - """from_cli_args should pick up stage_configs_path from namespace.""" - ns = argparse.Namespace( - model="facebook/opt-125m", - stage_configs_path="/some/path.yaml", - custom_pipeline_args=None, - ) - - args = OmniEngineArgs.from_cli_args(ns) - assert args.stage_configs_path == "/some/path.yaml" - assert args.custom_pipeline_args is None - - def test_qwen3_tts_code2wav_injects_max_position_embeddings(monkeypatch): """Ensure Code2Wav mirrors stage max_model_len into nested HF overrides. @@ -418,130 +403,6 @@ def test_non_override_ar_stage_inputs_embeds_size_matches_hidden_size(): assert omni_config.get_inputs_embeds_size() == omni_config.hf_text_config.hidden_size -def test_stage_configs_path_field(): - """OmniEngineArgs with stage_configs_path should construct without error.""" - args = OmniEngineArgs(stage_configs_path="/some/path.yaml") - assert args.stage_configs_path == "/some/path.yaml" - - -def test_strip_single_engine_args(): - """_strip_single_engine_args should remove EngineArgs fields but keep omni fields.""" - kwargs = { - # Parent EngineArgs fields — stripped unless explicitly allowlisted - "compilation_config": '{"cudagraph_mode": "FULL_AND_PIECEWISE"}', - "tensor_parallel_size": 4, - "gpu_memory_utilization": 0.9, - "model": "some/model", - # Parent field that should be kept (allowlisted) - "worker_extension_cls": "some.Extension", - # OmniEngineArgs-only / non-engine fields — should pass through - "stage_configs_path": "/path/to/yaml", - "custom_pipeline_args": {"pipeline_class": "my.Pipeline"}, - "mode": "text-to-image", - "lora_path": "/some/lora", - } - - filtered = AsyncOmniEngine._strip_single_engine_args(kwargs) - - # Stripped — parent EngineArgs fields - assert "compilation_config" not in filtered - assert filtered["tensor_parallel_size"] == 4 - assert "gpu_memory_utilization" not in filtered - assert "model" not in filtered - - # Stripped — orchestrator-level OmniEngineArgs field - assert "stage_configs_path" not in filtered - - # Kept - assert filtered["worker_extension_cls"] == "some.Extension" - assert filtered["custom_pipeline_args"] == {"pipeline_class": "my.Pipeline"} - assert filtered["mode"] == "text-to-image" - assert filtered["lora_path"] == "/some/lora" - - -def test_strip_single_engine_args_model_does_not_trigger_warning(mocker): - """model is always in kwargs (callers set it via from_cli_args/asdict), - so it should not cause the override warning by itself or appear in it.""" - mock_warn = mocker.patch("vllm_omni.engine.async_omni_engine.logger.warning") - - # Typical caller kwargs: model is always present, no other parent - # EngineArgs fields are explicitly overridden. - AsyncOmniEngine._strip_single_engine_args( - { - "model": "some/model", - "custom_pipeline_args": {"pipeline_class": "my.Pipeline"}, - } - ) - mock_warn.assert_not_called() - - # When there *are* genuinely surprising overrides alongside model, - # the warning should mention them but not model. Keep-listed fields such as - # tensor_parallel_size are intentionally passed through and should not warn. - AsyncOmniEngine._strip_single_engine_args( - { - "model": "some/model", - "compilation_config": '{"cudagraph_mode": "FULL_AND_PIECEWISE"}', - "tensor_parallel_size": 4, - "custom_pipeline_args": {"pipeline_class": "my.Pipeline"}, - } - ) - mock_warn.assert_called_once() - warned_args = mock_warn.call_args[0][-1] # the formatted arg list - assert "compilation_config" in warned_args - assert "tensor_parallel_size" not in warned_args - assert "model" not in warned_args - - -def test_new_format_deploy_yaml_does_not_strip_interleave_cli_overrides(tmp_path): - """New-format deploy YAMLs must not strip interleave_mm_strings / media_io_kwargs. - - Pytest historically passed ``--stage-configs-path`` for deploy YAMLs; stripping - those CLI flags dropped MiniCPM interleaved AV packing and cut Daily-Omni - accuracy from ~78% to ~70%. - """ - from vllm_omni.entrypoints.utils import is_new_format_deploy_config - - yaml_path = tmp_path / "minicpm_deploy.yaml" - yaml_path.write_text( - """ -pipeline: minicpmo_4_5 -async_chunk: true -stages: - - stage_id: 0 - devices: "0" -""".strip(), - encoding="utf-8", - ) - path = str(yaml_path) - assert is_new_format_deploy_config(path) - - kwargs = { - "stage_configs_path": path, - "trust_remote_code": True, - "interleave_mm_strings": True, - "media_io_kwargs": {"video": {"fps": 1, "num_frames": 128}}, - "gpu_memory_utilization": 0.55, - } - # Mirror AsyncOmniEngine._resolve_stage_configs strip decision. - if kwargs.get("stage_configs_path") is not None and not is_new_format_deploy_config(kwargs["stage_configs_path"]): - base = AsyncOmniEngine._strip_single_engine_args(dict(kwargs)) - else: - base = dict(kwargs) - assert base.get("interleave_mm_strings") is True - assert base.get("media_io_kwargs") == {"video": {"fps": 1, "num_frames": 128}} - # Parent EngineArgs fields also remain available for deploy merge. - assert base.get("gpu_memory_utilization") == 0.55 - - legacy = tmp_path / "legacy.yaml" - legacy.write_text("stage_args:\n - stage_id: 0\n", encoding="utf-8") - assert not is_new_format_deploy_config(str(legacy)) - legacy_kwargs = dict(kwargs, stage_configs_path=str(legacy)) - stripped = AsyncOmniEngine._strip_single_engine_args(legacy_kwargs) - assert "interleave_mm_strings" not in stripped - assert "media_io_kwargs" not in stripped - assert "gpu_memory_utilization" not in stripped - - # For https://github.com/vllm-project/vllm-omni/issues/3293 def test_tensor_parallel_size_none_is_handled(): """Ensure the tensor parallel size of None isn't forwarded.""" diff --git a/tests/engine/test_async_omni_engine_abort.py b/tests/engine/test_async_omni_engine_abort.py index 40f89df84..56b25704d 100644 --- a/tests/engine/test_async_omni_engine_abort.py +++ b/tests/engine/test_async_omni_engine_abort.py @@ -68,7 +68,7 @@ async def test_abort(): with ExitStack() as after: engine = AsyncOmni( model=model, - stage_configs_path=stage_config, + deploy_config=stage_config, ) after.callback(engine.shutdown) diff --git a/tests/engine/test_single_stage_mode.py b/tests/engine/test_single_stage_mode.py index bde8ba490..65ec024d5 100644 --- a/tests/engine/test_single_stage_mode.py +++ b/tests/engine/test_single_stage_mode.py @@ -428,7 +428,7 @@ class TestSingleStageModeDetection: assert engine.single_stage_mode is False assert engine._single_stage_id_filter is None - def test_stage_configs_path_loads_duplex_runtime_config(self, mocker: MockerFixture): + def test_deploy_config_loads_duplex_runtime_config(self, mocker: MockerFixture): duplex_session = DuplexSessionRuntimeConfig(max_sessions=2) get_pipeline_config = mocker.patch( "vllm_omni.engine.async_omni_engine.StageConfigFactory.get_pipeline_config", @@ -441,7 +441,7 @@ class TestSingleStageModeDetection: engine = self._make_engine_no_thread( mocker, - stage_configs_path="/fake/duplex.yaml", + deploy_config="/fake/duplex.yaml", ) get_pipeline_config.assert_called_once_with( diff --git a/tests/entrypoints/test_async_omni.py b/tests/entrypoints/test_async_omni.py index 6120d5ea4..a672c32e9 100644 --- a/tests/entrypoints/test_async_omni.py +++ b/tests/entrypoints/test_async_omni.py @@ -364,7 +364,7 @@ async def test_omni_generate_request_id(): Text modality only; asserts caller-visible ids are preserved across the multi-stage orchestrator path on H100. """ - engine = AsyncOmni(model=OMNI_MODEL, stage_configs_path=OMNI_STAGE_CONFIG) + engine = AsyncOmni(model=OMNI_MODEL, deploy_config=OMNI_STAGE_CONFIG) try: for req_id in _OMNI_REQ_IDS: async for output in engine.generate( diff --git a/tests/entrypoints/test_async_omni_diffusion_config.py b/tests/entrypoints/test_async_omni_diffusion_config.py index 2fa8c92bd..75a9de2a7 100644 --- a/tests/entrypoints/test_async_omni_diffusion_config.py +++ b/tests/entrypoints/test_async_omni_diffusion_config.py @@ -478,12 +478,11 @@ def test_resolve_stage_configs_injects_additional_config_into_diffusion_stage(mo ) engine = AsyncOmniEngine.__new__(AsyncOmniEngine) - engine._strip_single_engine_args = lambda kwargs: kwargs _, stage_configs = engine._resolve_stage_configs( "dummy-model", { - "stage_configs_path": "dummy.yaml", + "deploy_config": "dummy.yaml", "additional_config": {"torchair_graph_config": {"enabled": True}}, }, trust_remote_code=False, @@ -516,12 +515,11 @@ def test_resolve_stage_configs_injects_quantization_config_into_diffusion_stage( ) engine = AsyncOmniEngine.__new__(AsyncOmniEngine) - engine._strip_single_engine_args = lambda kwargs: kwargs _, stage_configs = engine._resolve_stage_configs( "dummy-model", { - "stage_configs_path": "dummy.yaml", + "deploy_config": "dummy.yaml", "quantization_config": {"method": "bitsandbytes"}, }, trust_remote_code=False, diff --git a/tests/entrypoints/test_serve.py b/tests/entrypoints/test_serve.py index 04a29edd1..6aa5d6855 100644 --- a/tests/entrypoints/test_serve.py +++ b/tests/entrypoints/test_serve.py @@ -193,7 +193,7 @@ def test_run_headless_parses_and_forwards_stage_overrides(mocker: MockerFixture) with pytest.raises(ValueError, match="No stage config found for stage_id=0"): run_headless(args) - assert captured["args"][1] is None + assert len(captured["args"]) == 2 assert captured["deploy_config_path"] == "/tmp/deploy.yaml" assert captured["stage_overrides"] == {"0": {"devices": "0,1"}, "1": {"devices": "2"}} assert captured["strategy_config_path"] == "/tmp/strategy.yaml" diff --git a/tests/entrypoints/test_utils.py b/tests/entrypoints/test_utils.py index c6d8e0354..a20c97a58 100644 --- a/tests/entrypoints/test_utils.py +++ b/tests/entrypoints/test_utils.py @@ -381,7 +381,6 @@ class TestLoadAndResolveStageConfigs: config_path, stage_configs, _ = load_and_resolve_stage_configs( model="black-forest-labs/FLUX.2-klein-4B", - stage_configs_path=None, kwargs=kwargs, trust_remote_code=False, default_stage_cfg_factory=lambda: AsyncOmniEngine._create_default_diffusion_stage_cfg(kwargs), @@ -390,9 +389,7 @@ class TestLoadAndResolveStageConfigs: assert len(stage_configs) == 1 assert "dtype" in stage_configs[0]["engine_args"] - def test_stage_configs_path_promotes_new_deploy_yaml_without_expanding_replicas( - self, tmp_path, mocker: MockerFixture - ): + def test_deploy_config_preserves_cli_overrides_and_replicas(self, tmp_path, mocker: MockerFixture): deploy_path = tmp_path / "qwen3_multi.yaml" deploy_path.write_text( 'stages:\n - stage_id: 0\n devices: "0"\n - stage_id: 1\n devices: "1,2,3"\n num_replicas: 3\n', @@ -413,18 +410,23 @@ class TestLoadAndResolveStageConfigs: "vllm_omni.entrypoints.utils.load_stage_configs_from_model", return_value=(returned_stage_configs, None), ) + cli_overrides = { + "interleave_mm_strings": True, + "media_io_kwargs": {"video": {"fps": 1, "num_frames": 128}}, + "gpu_memory_utilization": 0.55, + } config_path, stage_configs, _ = load_and_resolve_stage_configs( model="dummy-model", - stage_configs_path=str(deploy_path), - kwargs={}, + kwargs=cli_overrides, trust_remote_code=True, + deploy_config_path=str(deploy_path), ) load_stage_configs.assert_called_once_with( "dummy-model", trust_remote_code=True, - base_engine_args={}, + base_engine_args=cli_overrides, deploy_config_path=str(deploy_path), stage_overrides=None, strategy_config_path=None, diff --git a/tests/helpers/runtime.py b/tests/helpers/runtime.py index 82fcc308d..cfc8cc75d 100644 --- a/tests/helpers/runtime.py +++ b/tests/helpers/runtime.py @@ -2635,7 +2635,7 @@ class OmniRunner: # affects the test runner wrapper. init_timeout: int = 1800, log_stats: bool = False, - stage_configs_path: str | None = None, + deploy_config: str | None = None, **kwargs, ) -> None: startup_t0 = time.perf_counter() @@ -2653,7 +2653,7 @@ class OmniRunner: stage_init_timeout=stage_init_timeout, batch_timeout=batch_timeout, init_timeout=init_timeout, - stage_configs_path=stage_configs_path, + deploy_config=deploy_config, **kwargs, ) startup_s = time.perf_counter() - startup_t0 @@ -3179,12 +3179,7 @@ def iter_omni_server( print("OmniServer stopping...") else: if stage_config_path is not None: - from vllm_omni.entrypoints.utils import is_new_format_deploy_config - - if is_new_format_deploy_config(stage_config_path): - server_args += ["--deploy-config", stage_config_path] - else: - server_args += ["--stage-configs-path", stage_config_path] + server_args += ["--deploy-config", stage_config_path] with ( OmniServer( @@ -3237,7 +3232,7 @@ def iter_omni_runner( model = model_prefix + model if run_level == "core_model" and request.node.get_closest_marker("diffusion"): model = resolve_tiny_model_path(model) - with OmniRunner(model, seed=42, stage_configs_path=stage_config_path, **extra_omni_kwargs) as runner: + with OmniRunner(model, seed=42, deploy_config=stage_config_path, **extra_omni_kwargs) as runner: print("OmniRunner started successfully") yield runner print("OmniRunner stopping...") diff --git a/tools/baselines/omni_hash_baseline.py b/tools/baselines/omni_hash_baseline.py index 1bb93099b..8d8f5ff13 100644 --- a/tools/baselines/omni_hash_baseline.py +++ b/tools/baselines/omni_hash_baseline.py @@ -147,7 +147,7 @@ def _run_once(model: str, yaml_path: str, *, async_mode: bool) -> list[dict]: # (see tests/e2e/accuracy/qwen3_omni). Confirm the exact knob at bench time. omni = Omni( model, - stage_configs_path=yaml_path, + deploy_config=yaml_path, stage_init_timeout=1800, init_timeout=3600, async_chunk=async_mode, diff --git a/vllm_omni/config/omni_config.py b/vllm_omni/config/omni_config.py index 4b5c73a39..32ee2c826 100644 --- a/vllm_omni/config/omni_config.py +++ b/vllm_omni/config/omni_config.py @@ -53,7 +53,6 @@ _NON_STAGE_ENGINE_CLI_FIELDS = frozenset( "model", "omni", "output_modalities", - "stage_configs_path", "stage_id", "tokenizer", } diff --git a/vllm_omni/config/stage_config.py b/vllm_omni/config/stage_config.py index 496aea208..86a6fed1b 100644 --- a/vllm_omni/config/stage_config.py +++ b/vllm_omni/config/stage_config.py @@ -4,7 +4,6 @@ from __future__ import annotations -import dataclasses import functools import re import warnings @@ -56,7 +55,7 @@ def build_stage_runtime_overrides( ``internal_keys`` defaults to the union of ``arg_utils.internal_blacklist_keys()`` and ``arg_utils.SHARED_FIELDS`` so that neither orchestrator-only fields nor shared-pipeline fields - (``model`` / ``stage_configs_path`` / ``log_stats`` / ``stage_id``) leak + (``model`` / ``log_stats`` / ``stage_id``) leak into a stage's per-stage runtime overrides — the orchestrator sets those uniformly for every stage, they are not per-stage knobs. Callers can pass an explicit set for tests or specialized flows. @@ -90,46 +89,6 @@ def build_stage_runtime_overrides( return result -def strip_parent_engine_args( - kwargs: dict[str, Any], - *, - parent_fields: dict[str, dataclasses.Field], - keep_keys: set[str] | frozenset[str] = frozenset(), - strip_keys: set[str] | frozenset[str] = frozenset(), - no_warn_keys: set[str] | frozenset[str] = frozenset(), -) -> tuple[dict[str, Any], list[str]]: - """Strip parent ``EngineArgs`` fields before merging into stage YAML.""" - overridden: list[str] = [] - result: dict[str, Any] = {} - - for key, value in kwargs.items(): - if key in strip_keys: - continue - - if key not in parent_fields or key in keep_keys: - result[key] = value - continue - - field_def = parent_fields[key] - if field_def.default is not dataclasses.MISSING: - default = field_def.default - elif field_def.default_factory is not dataclasses.MISSING: - default = field_def.default_factory() - else: - default = dataclasses.MISSING - - if default is dataclasses.MISSING or value is None: - continue - - if dataclasses.is_dataclass(default) and not isinstance(default, type): - default = asdict(default) - - if value != default and key not in no_warn_keys: - overridden.append(key) - - return result, sorted(overridden) - - def _apply_diffusion_parallel_runtime_overrides( engine_args: dict[str, Any], runtime_overrides: dict[str, Any], diff --git a/vllm_omni/engine/arg_utils.py b/vllm_omni/engine/arg_utils.py index a3aa96f60..29c84a1c9 100644 --- a/vllm_omni/engine/arg_utils.py +++ b/vllm_omni/engine/arg_utils.py @@ -167,9 +167,6 @@ class OmniEngineArgs(EngineArgs): Required when single-stage mode is active. omni_master_port: TCP port for the OmniMasterServer registration socket. Required when single-stage mode is active. - stage_configs_path: Optional path to a JSON/YAML file containing - stage configurations for the multi-stage pipeline. If None, - stage configs are resolved from the model's default configuration. output_modalities: Optional list of output modality names to enable (e.g. ["text", "audio"]). If None, all modalities supported by the model are used. @@ -231,7 +228,6 @@ class OmniEngineArgs(EngineArgs): omni_dp_size_local: int = 1 omni_lb_policy: str = "random" omni_heartbeat_timeout: float = 30.0 - stage_configs_path: str | None = None output_modalities: list[str] | None = None log_stats: bool = False custom_pipeline_args: dict[str, Any] | None = None @@ -490,7 +486,6 @@ class OrchestratorArgs: ray_address: str | None = None # === Config Files === - stage_configs_path: str | None = None deploy_config: str | None = None stage_overrides: str | None = None # raw JSON string; parsed downstream # Optional composable-parallel strategy.yaml; orchestrator reads it, overlays @@ -582,7 +577,6 @@ SHARED_FIELDS: frozenset[str] = frozenset( "model", # orch: detect model_type; engine: load weights "stage_id", # orch: route (headless); engine: identity "log_stats", # both want the flag - "stage_configs_path", # orch: load legacy YAML; engine: may reference for validation "async_chunk", # orch: read from CLI, redistribute; engine: per-stage flag "tokenizer", # orch: detect model type; engine: tokenization } diff --git a/vllm_omni/engine/async_omni_engine.py b/vllm_omni/engine/async_omni_engine.py index aa284067b..a613ebc80 100644 --- a/vllm_omni/engine/async_omni_engine.py +++ b/vllm_omni/engine/async_omni_engine.py @@ -9,7 +9,6 @@ from __future__ import annotations import asyncio import concurrent.futures -import dataclasses import json import queue import threading @@ -24,7 +23,6 @@ import janus import torch from omegaconf import OmegaConf from vllm import envs as vllm_envs -from vllm.engine.arg_utils import EngineArgs from vllm.inputs import PromptType from vllm.logger import init_logger from vllm.v1.engine import EngineCoreRequest @@ -34,7 +32,6 @@ from vllm_omni.config.config_factory import StageConfigFactory, with_trust_remot from vllm_omni.config.stage_config import ( DuplexSessionRuntimeConfig, load_deploy_config, - strip_parent_engine_args, ) from vllm_omni.diffusion.data import DiffusionParallelConfig, parse_attention_config from vllm_omni.diffusion.diffusion_engine import supports_audio_output @@ -69,7 +66,6 @@ from vllm_omni.engine.stage_runtime import ( ) from vllm_omni.entrypoints.pd_utils import PDDisaggregationMixin from vllm_omni.entrypoints.utils import ( - is_new_format_deploy_config, load_and_resolve_stage_configs, parse_stage_overrides, ) @@ -85,39 +81,6 @@ if TYPE_CHECKING: _STARTUP_POLL_INTERVAL_S = 1.0 _REQUEST_QUEUE_MAXSIZE = 256 -# ============================================================================ -# Parent-EngineArgs field-routing contracts (consumed by -# AsyncOmniEngine._strip_parent_engine_args when ``stage_configs_path`` is set). -# ============================================================================ - -# Fields that must survive the "equal to default → strip" filter because -# diffusion stages need them even when equal to vllm's default value -# (e.g. colocate worker setup relies on worker_extension_cls being forwarded). -_PARENT_ARGS_KEEP: frozenset[str] = frozenset( - { - "worker_extension_cls", - "allowed_local_media_path", - "allowed_media_domains", - # Legacy stage-config YAMLs may intentionally leave parallel or - # distributed knobs unspecified at the stage level and rely on - # top-level CLI values to fill them in during the per-stage merge. - # Keep these fields so stages that omit them can inherit CLI values, - # while stages with explicit YAML values still win because the legacy - # stage-config loader prefers stage-local engine args. - "tensor_parallel_size", - } -) - -# Omni orchestrator-level fields consumed by ``_resolve_stage_configs`` that -# must never leak into per-stage EngineArgs (``stage_configs_path`` would -# trigger the ``create_model_config`` guard). -_PARENT_ARGS_STRIP: frozenset[str] = frozenset({"stage_configs_path"}) - - -# Fields always populated by callers (via ``from_cli_args`` / ``asdict``) so -# their presence as an override is never a surprise — suppress the -# "override ignored" warning for these. -_PARENT_ARGS_NO_WARN: frozenset[str] = frozenset({"model"}) class AsyncOmniEngine: @@ -210,10 +173,8 @@ class AsyncOmniEngine: self._omni_master_port, ) - # Stage resolution pops deploy_config, so get pipeline-wide settings - # beforehand. The stage CLI exposes the same deploy YAML through - # stage_configs_path. - deploy_config_path = kwargs.get("deploy_config") or kwargs.get("stage_configs_path") + # Stage resolution pops deploy_config, so get pipeline-wide settings beforehand. + deploy_config_path = kwargs.get("deploy_config") # ``trust_remote_code`` is tri-state (bool | None): ``None`` means "not # specified" so stage-config resolution can defer to the deploy yaml's # per-stage value (see ``with_trust_remote_code_override``). The @@ -1116,40 +1077,6 @@ class AsyncOmniEngine: default_stage_cfg[0]["engine_args"]["model_stage"] = "diffusion" return default_stage_cfg - @staticmethod - def _strip_single_engine_args(kwargs: dict[str, Any]) -> dict[str, Any]: - """Remove parent ``EngineArgs`` fields from *kwargs*. - - When ``stage_configs_path`` is set, per-stage engine args are defined - in the YAML. Top-level single-engine fields (``compilation_config``, - ``tensor_parallel_size``, …) must not leak into per-stage configs via - the ``base_engine_args`` merge in ``load_stage_configs_from_yaml`` — - they can cause type errors (e.g. ``compilation_config`` as a JSON - string rejected by ``VllmConfig``) or silently override YAML values. - - Logs a warning for any parent field whose value differs from the - dataclass default, so users know their explicit overrides are ignored. - See the module-level ``_PARENT_ARGS_*`` constants for the routing - contracts this method enforces. - """ - parent_fields: dict[str, dataclasses.Field] = {f.name: f for f in dataclasses.fields(EngineArgs)} - result, overridden = strip_parent_engine_args( - kwargs, - parent_fields=parent_fields, - keep_keys=_PARENT_ARGS_KEEP, - strip_keys=_PARENT_ARGS_STRIP, - no_warn_keys=_PARENT_ARGS_NO_WARN, - ) - - if overridden: - logger.warning( - "stage_configs_path is set — the following top-level engine " - "args are ignored (per-stage YAML takes precedence): %s", - ", ".join(sorted(overridden)), - ) - - return result - def _apply_strategy_lb_policy(self, derived: str | None, kwargs: dict[str, Any]) -> None: """Apply a strategy-derived ``omni_lb_policy`` to the engine. @@ -1188,7 +1115,6 @@ class AsyncOmniEngine: ) -> tuple[str, list[Any]]: """Resolve stage configs and inject defaults shared by orchestrator/headless.""" - stage_configs_path = kwargs.get("stage_configs_path", None) deploy_config_path = kwargs.pop("deploy_config", None) strategy_config_path = kwargs.pop("strategy_config", None) stage_overrides_json = kwargs.pop("stage_overrides", None) @@ -1196,26 +1122,15 @@ class AsyncOmniEngine: if explicit_stage_configs is not None: logger.warning( "`stage_configs` is not part of the public API. " - "Ignoring it and resolving stages from stage_configs_path/model factory." + "Ignoring it and resolving stages from deploy_config/model factory." ) - # Legacy ``stage_args`` YAMLs own per-stage EngineArgs completely, so - # strip parent CLI fields. New-format deploy YAMLs (also accepted via - # ``--stage-configs-path``) merge CLI overrides the same way as - # ``--deploy-config`` — do not strip, or flags like - # ``interleave_mm_strings`` / ``media_io_kwargs`` are silently dropped. - if stage_configs_path is not None and not is_new_format_deploy_config(stage_configs_path): - base_kwargs = self._strip_single_engine_args(kwargs) - else: - base_kwargs = kwargs - # Parse --stage-overrides JSON string if provided stage_overrides = parse_stage_overrides(stage_overrides_json) config_path, stage_configs, strategy_lb_policy = load_and_resolve_stage_configs( model, - stage_configs_path, - base_kwargs, + kwargs, trust_remote_code=trust_remote_code, default_stage_cfg_factory=lambda: self._create_default_diffusion_stage_cfg(kwargs), deploy_config_path=deploy_config_path, diff --git a/vllm_omni/entrypoints/async_omni.py b/vllm_omni/entrypoints/async_omni.py index f6899deaa..33905b254 100644 --- a/vllm_omni/entrypoints/async_omni.py +++ b/vllm_omni/entrypoints/async_omni.py @@ -123,9 +123,8 @@ class AsyncOmni(EngineClient, OmniBase): Args: model: Model name or path to load. **kwargs: Additional keyword arguments. - - stage_configs_path: Optional path to YAML file containing stage - configurations. If None, configurations are resolved from model - pipeline factory. + - deploy_config: Optional path to a deploy YAML. If None, + configurations are resolved from the model pipeline factory. - log_stats: Whether to enable statistics logging. - stage_init_timeout: Timeout for per-stage initialization. - init_timeout: Total timeout for orchestrator startup. diff --git a/vllm_omni/entrypoints/cli/serve.py b/vllm_omni/entrypoints/cli/serve.py index dfa222cd1..62ee10d94 100644 --- a/vllm_omni/entrypoints/cli/serve.py +++ b/vllm_omni/entrypoints/cli/serve.py @@ -945,8 +945,6 @@ def run_headless(args: TrackingNamespace) -> None: config_path, stage_configs, _ = load_and_resolve_stage_configs( model, - # The serve CLI no longer accepts legacy stage_args YAMLs. - None, args_dict, # store_true cannot express an explicit False: absent maps to None # ("not specified") so the deploy yaml's per-stage value applies. diff --git a/vllm_omni/entrypoints/openai/api_server.py b/vllm_omni/entrypoints/openai/api_server.py index 0a8daa055..16114bcb6 100755 --- a/vllm_omni/entrypoints/openai/api_server.py +++ b/vllm_omni/entrypoints/openai/api_server.py @@ -679,7 +679,7 @@ async def build_async_omni_from_stage_config( EngineClient instance (AsyncOmni) ready for use Note: - Stage configurations are loaded from args.stage_configs_path if provided, + Stage configurations are loaded from ``args.deploy_config`` when provided, otherwise from the model's default configuration. """ @@ -1156,7 +1156,7 @@ async def omni_init_app_state( state.openai_serving_duplex = None if state.openai_serving_chat is not None and should_enable_duplex_endpoint( state.stage_configs, - config_path=getattr(args, "stage_configs_path", None) or getattr(args, "deploy_config", None), + config_path=getattr(args, "deploy_config", None), ): from vllm_omni.experimental.fullduplex.openai.serving import OmniDuplexSessionHandler diff --git a/vllm_omni/entrypoints/utils.py b/vllm_omni/entrypoints/utils.py index ff0cd4f9e..07b45f374 100644 --- a/vllm_omni/entrypoints/utils.py +++ b/vllm_omni/entrypoints/utils.py @@ -6,7 +6,6 @@ from dataclasses import fields, is_dataclass from pathlib import Path from typing import Any, get_args, get_origin -import yaml from vllm.logger import init_logger from vllm.sampling_params import RequestOutputKind, SamplingParams from vllm.transformers_utils.config import get_config, get_hf_file_to_dict @@ -30,15 +29,6 @@ PROJECT_ROOT = Path(__file__).parent.parent.parent logger = init_logger(__name__) -def is_new_format_deploy_config(path: str | None) -> bool: - """Return True when *path* is a deploy YAML (``stages:`` without legacy ``stage_args:``).""" - if not path or not os.path.exists(path): - return False - with open(path, encoding="utf-8") as f: - peek = yaml.safe_load(f) or {} - return isinstance(peek, dict) and "stages" in peek and "stage_args" not in peek - - _DIFFUSERS_CLASS_TO_CONFIG: dict[str, str] = { "GlmImagePipeline": "glm_image", } @@ -606,7 +596,6 @@ def parse_stage_overrides(value: Any) -> dict[str, dict[str, Any]] | None: def load_and_resolve_stage_configs( model: str, - stage_configs_path: str | None, kwargs: dict | None, *, trust_remote_code: bool | None, @@ -615,17 +604,15 @@ def load_and_resolve_stage_configs( stage_overrides: dict[str, dict[str, Any]] | None = None, strategy_config_path: str | None = None, ) -> tuple[str, list, str | None]: - """Load stage configurations from model or YAML file with fallback to defaults. + """Load stage configurations from a deploy YAML or model defaults. Args: model: Model name or path - stage_configs_path: Optional path to legacy YAML (stage_args format) kwargs: Engine arguments to merge with stage configs trust_remote_code: Whether to trust remote code while resolving the model config. default_stage_cfg_factory: Optional callable that takes no args and returns default stage config list when no configs are found - deploy_config_path: Optional path to deploy YAML (new format). - Mutually exclusive with ``stage_configs_path``. + deploy_config_path: Optional path to a deploy YAML. stage_overrides: Per-stage overrides from ``--stage-overrides`` JSON. Keys are stage_id strings, values are dicts of overrides. strategy_config_path: Optional path to a composable-parallel @@ -636,65 +623,21 @@ def load_and_resolve_stage_configs( the strategy-derived pipeline-wide load-balancer policy (``None`` when no strategy set one), returned for the engine to apply. """ - if stage_configs_path is not None and deploy_config_path is not None: - raise ValueError( - "--stage-configs-path and --deploy-config are mutually exclusive: " - "they use different path resolution rules and loading paths. " - "Use --deploy-config for new-format YAMLs (preferred); " - "--stage-configs-path is kept only for the legacy `stage_args` format " - "and will be removed in a future release." - ) - if stage_configs_path is not None and deploy_config_path is None: - if not os.path.exists(stage_configs_path): - raise FileNotFoundError( - f"--stage-configs-path {stage_configs_path!r} does not exist. " - "Legacy `stage_configs/` yamls were replaced by `vllm_omni/deploy/.yaml`; " - "use --deploy-config. See docs/configuration/stage_configs.md." - ) - if is_new_format_deploy_config(stage_configs_path): - deploy_config_path = stage_configs_path - stage_configs_path = None + config_path = deploy_config_path if deploy_config_path is not None else resolve_model_config_path(model) + stage_configs, omni_lb_policy = load_stage_configs_from_model( + model, + trust_remote_code=trust_remote_code, + base_engine_args=kwargs, + deploy_config_path=deploy_config_path, + stage_overrides=stage_overrides, + strategy_config_path=strategy_config_path, + ) + if not stage_configs: + if default_stage_cfg_factory is not None: + default_stage_cfg = default_stage_cfg_factory() + stage_configs = create_config(_convert_dataclasses_to_dict(default_stage_cfg)) else: - logger.warning( - "--stage-configs-path is deprecated; migrate %r and use --deploy-config.", - stage_configs_path, - ) - - omni_lb_policy: str | None = None - if deploy_config_path is not None: - config_path = deploy_config_path - stage_configs, omni_lb_policy = load_stage_configs_from_model( - model, - trust_remote_code=trust_remote_code, - base_engine_args=kwargs, - deploy_config_path=deploy_config_path, - stage_overrides=stage_overrides, - strategy_config_path=strategy_config_path, - ) - if not stage_configs: - if default_stage_cfg_factory is not None: - default_stage_cfg = default_stage_cfg_factory() - stage_configs = create_config(_convert_dataclasses_to_dict(default_stage_cfg)) - else: - stage_configs = [] - elif stage_configs_path is None: - config_path = resolve_model_config_path(model) - stage_configs, omni_lb_policy = load_stage_configs_from_model( - model, - trust_remote_code=trust_remote_code, - base_engine_args=kwargs, - stage_overrides=stage_overrides, - strategy_config_path=strategy_config_path, - ) - if not stage_configs: - if default_stage_cfg_factory is not None: - default_stage_cfg = default_stage_cfg_factory() - stage_configs = create_config(_convert_dataclasses_to_dict(default_stage_cfg)) - else: - stage_configs = [] - else: - config_path = stage_configs_path - stage_configs = load_stage_configs_from_yaml(stage_configs_path, base_engine_args=kwargs) + stage_configs = [] stage_configs = filter_stages(config_path, stage_configs, kwargs) logger.debug(f"stage_configs: {stage_configs}")