diff --git a/tests/config/composable_parallel/test_factory_integration.py b/tests/config/composable_parallel/test_factory_integration.py index ea685541c..468b3db2c 100644 --- a/tests/config/composable_parallel/test_factory_integration.py +++ b/tests/config/composable_parallel/test_factory_integration.py @@ -100,8 +100,7 @@ def test_device_check_survives_cli_override(): # guard: effective world=1, replicas=2 admits only 1 or 2 device ids. pipeline_cfg = OMNI_PIPELINES["qwen2_5_omni"] with pytest.raises(StrategyApplyError): - StageConfigFactory._create_from_registry( - "qwen2_5_omni", + StageConfigFactory._create_legacy_from_registry( pipeline_cfg, cli_overrides={"stage_1_devices": "0,1,2"}, strategy_specs={"talker": [_stage_replica(2, "round_robin")]}, @@ -126,8 +125,7 @@ def test_cli_overrides_strategy_with_warning(): log.addHandler(handler) try: pipeline_cfg = OMNI_PIPELINES["qwen2_5_omni"] - stages, _ = StageConfigFactory._create_from_registry( - "qwen2_5_omni", + stages, _ = StageConfigFactory._create_legacy_from_registry( pipeline_cfg, cli_overrides={"stage_1_num_replicas": 3}, strategy_specs={"talker": [_stage_replica(2, "round_robin")]}, diff --git a/tests/config/test_omni_config.py b/tests/config/test_omni_config.py index bf2dba701..23e53ea3d 100644 --- a/tests/config/test_omni_config.py +++ b/tests/config/test_omni_config.py @@ -9,7 +9,9 @@ from pathlib import Path import pytest from pydantic import ValidationError +from transformers import Qwen3OmniMoeConfig +from tests.helpers.stage_config import get_deploy_config_path from vllm_omni.config import omni_config as omni_config_module from vllm_omni.config.omni_config import ( BaseVllmOmniStageConfig, @@ -26,7 +28,7 @@ from vllm_omni.config.omni_config import ( VllmOmniDiffusionStageConfig, VllmOmniGenerationStageConfig, ) -from vllm_omni.config.pipeline_registry import OMNI_PIPELINES +from vllm_omni.config.pipeline_registry import OMNI_PIPELINES, resolve_pipeline_config from vllm_omni.config.stage_config import ( _STAGE_DEPLOY_FIELDS, PIPELINE_WIDE_ENGINE_FIELDS, @@ -53,28 +55,39 @@ def _stable_test_platform(monkeypatch): monkeypatch.setattr(platform, "device_type", "cpu", raising=False) -def _load_default_deploy(model_type: str) -> DeployConfig: - deploy_path = _DEPLOY_DIR / f"{model_type}.yaml" - if deploy_path.exists(): - return load_deploy_config(deploy_path) +def _load_default_deploy(pipeline: PipelineConfig) -> DeployConfig: + if pipeline.default_deploy_config_name is not None: + return load_deploy_config(_DEPLOY_DIR / pipeline.default_deploy_config_name) return DeployConfig() -def _resolve_pipeline_or_skip(model_type: str) -> PipelineConfig: - registered = OMNI_PIPELINES[model_type] - pipeline = registered(None) if callable(registered) else registered +def _resolve_pipeline_or_skip(model_type: str, hf_config=None) -> PipelineConfig: + pipeline = resolve_pipeline_config(model_type, hf_config) if pipeline is None: pytest.skip(f"Pipeline {model_type!r} requires an HF config to resolve") return pipeline +def _from_pipeline_key( + model_type: str, + hf_config=None, + deploy_config_path: str | None = None, + cli_overrides: dict | None = None, +) -> VllmOmniConfig: + return VllmOmniConfig.from_pipeline_config( + _resolve_pipeline_or_skip(model_type, hf_config), + deploy_config_path=deploy_config_path, + cli_overrides=cli_overrides, + ) + + @pytest.mark.parametrize("model_type", sorted(OMNI_PIPELINES)) -def test_vllm_omni_config_from_registry_matches_merge_pipeline_deploy(model_type: str): +def test_vllm_omni_config_from_pipeline_config_matches_merge_pipeline_deploy(model_type: str): pipeline = _resolve_pipeline_or_skip(model_type) - legacy_deploy = _load_default_deploy(model_type) + legacy_deploy = _load_default_deploy(pipeline) legacy_stages = merge_pipeline_deploy(pipeline, legacy_deploy) - omni_config = VllmOmniConfig.from_registry(model_type) + omni_config = VllmOmniConfig.from_pipeline_config(pipeline) assert omni_config.pipeline_config is pipeline assert len(omni_config.stage_configs) == len(legacy_stages) @@ -119,7 +132,7 @@ def test_vllm_omni_config_from_registry_matches_merge_pipeline_deploy(model_type def test_stage_by_id_raises_for_unknown_stage(): - omni_config = VllmOmniConfig.from_registry("qwen3_tts") + omni_config = _from_pipeline_key("qwen3_tts") with pytest.raises(KeyError, match="no stage 99"): omni_config.stage_by_id(99) @@ -130,8 +143,8 @@ def test_resolve_execution_mode_rejects_unknown_execution_type(): omni_config_module._resolve_execution_mode("unknown_execution_type") -def test_from_registry_preserves_current_pipeline_config_object(): - omni_config = VllmOmniConfig.from_registry("minicpmo_4_5") +def test_from_pipeline_config_preserves_current_pipeline_config_object(): + omni_config = _from_pipeline_key("minicpmo_4_5") pipeline = _resolve_pipeline_or_skip("minicpmo_4_5") assert omni_config.pipeline_config is pipeline @@ -140,11 +153,11 @@ def test_from_registry_preserves_current_pipeline_config_object(): assert omni_config.pipeline_config.hf_config_predicate is pipeline.hf_config_predicate -def test_from_registry_normalizes_stage_engine_extras_without_expanding_stage_deploy_config(): +def test_from_pipeline_config_normalizes_stage_engine_extras_without_expanding_stage_deploy_config(): assert not hasattr(StageDeployConfig, "model_config") assert not hasattr(StageDeployConfig, "parallel_config") - stage = VllmOmniConfig.from_registry("dreamzero", deploy_config_path="dreamzero_tp1_cfg2").stage_by_id(0) + stage = _from_pipeline_key("dreamzero", deploy_config_path="dreamzero_tp1_cfg2").stage_by_id(0) assert isinstance(stage, VllmOmniDiffusionStageConfig) assert stage.parallel_config.tensor_parallel_size == 1 @@ -152,8 +165,8 @@ def test_from_registry_normalizes_stage_engine_extras_without_expanding_stage_de assert stage.diffusion_config.model_config["default_robot_embodiment"] == "roboarena" -def test_from_registry_applies_cli_overrides_without_stage_config_runtime_bridge(): - omni_config = VllmOmniConfig.from_registry( +def test_from_pipeline_config_applies_cli_overrides_without_stage_config_runtime_bridge(): + omni_config = _from_pipeline_key( "qwen3_tts", cli_overrides={ "stage_0_max_num_seqs": 7, @@ -170,7 +183,7 @@ def test_from_registry_applies_cli_overrides_without_stage_config_runtime_bridge def test_runtime_num_gpus_is_derived_from_parallel_world_size(): - omni_config = VllmOmniConfig.from_registry("hunyuan_image3_dit") + omni_config = _from_pipeline_key("hunyuan_image3_dit") stage = omni_config.stage_by_id(0) assert stage.parallel_config.tensor_parallel_size == 4 @@ -179,7 +192,7 @@ def test_runtime_num_gpus_is_derived_from_parallel_world_size(): def test_runtime_num_gpus_ignores_stale_runtime_override(): - omni_config = VllmOmniConfig.from_registry( + omni_config = _from_pipeline_key( "hunyuan_image3_dit", cli_overrides={ "stage_0_num_gpus": 1, @@ -191,8 +204,8 @@ def test_runtime_num_gpus_ignores_stale_runtime_override(): assert stage.runtime_config.num_gpus == 4 -def test_from_registry_does_not_route_server_cli_keys_to_diffusion_stage(): - omni_config = VllmOmniConfig.from_registry( +def test_from_pipeline_config_does_not_route_server_cli_keys_to_diffusion_stage(): + omni_config = _from_pipeline_key( "dreamzero", deploy_config_path="dreamzero_tp1_cfg2", cli_overrides={ @@ -220,7 +233,7 @@ def test_pipeline_deploy_cli_fields_reuse_legacy_pipeline_wide_engine_fields(): def test_pipeline_wide_model_fields_are_retained_on_structured_stage_configs(tmp_path): custom_voice_dir = tmp_path / "voices" - omni_config = VllmOmniConfig.from_registry( + omni_config = _from_pipeline_key( "qwen3_tts", cli_overrides={ "active_stream_window": 2, @@ -267,8 +280,8 @@ def test_public_config_exports_use_stage_specific_sub_config_names(): }.issubset(config_pkg.__all__) -def test_from_registry_keeps_worker_backend_separate_from_distributed_executor_backend(): - omni_config = VllmOmniConfig.from_registry("dreamzero", deploy_config_path="dreamzero_tp1_cfg2") +def test_from_pipeline_config_keeps_worker_backend_separate_from_distributed_executor_backend(): + omni_config = _from_pipeline_key("dreamzero", deploy_config_path="dreamzero_tp1_cfg2") stage = omni_config.stage_by_id(0) assert isinstance(stage, VllmOmniDiffusionStageConfig) @@ -276,8 +289,8 @@ def test_from_registry_keeps_worker_backend_separate_from_distributed_executor_b assert omni_config.orchestrator_config.worker_backend == "multi_process" -def test_from_registry_maps_orchestrator_cli_overrides(): - omni_config = VllmOmniConfig.from_registry( +def test_from_pipeline_config_maps_orchestrator_cli_overrides(): + omni_config = _from_pipeline_key( "qwen3_tts", cli_overrides={ "stage_init_timeout": 1200, @@ -308,21 +321,21 @@ def test_from_registry_maps_orchestrator_cli_overrides(): assert orchestrator_config.batch_timeout == 3 -def test_from_registry_records_loaded_deploy_path_on_orchestrator_config(): - omni_config = VllmOmniConfig.from_registry("dreamzero", deploy_config_path="dreamzero_tp1_cfg2") +def test_from_pipeline_config_records_loaded_deploy_path_on_orchestrator_config(): + omni_config = _from_pipeline_key("dreamzero", deploy_config_path="dreamzero_tp1_cfg2") assert omni_config.pipeline_config.model_type == "dreamzero" assert omni_config.orchestrator_config.deploy_config_path == str(_DEPLOY_DIR / "dreamzero_tp1_cfg2.yaml") -def test_from_registry_dispatches_async_chunk_processors_without_mutating_topology(): +def test_from_pipeline_config_dispatches_async_chunk_processors_without_mutating_topology(): pipeline = _resolve_pipeline_or_skip("qwen3_tts") - async_config = VllmOmniConfig.from_registry("qwen3_tts") + async_config = _from_pipeline_key("qwen3_tts") assert async_config.stage_by_id(0).custom_process_next_stage_input_func.endswith("talker2code2wav_async_chunk") assert async_config.stage_by_id(1).custom_process_input_func is None - sync_config = VllmOmniConfig.from_registry("qwen3_tts", cli_overrides={"async_chunk": False}) + sync_config = _from_pipeline_key("qwen3_tts", cli_overrides={"async_chunk": False}) assert sync_config.stage_by_id(0).custom_process_next_stage_input_func.endswith("talker2code2wav_full_payload") assert sync_config.stage_by_id(1).custom_process_input_func.endswith("talker2code2wav_token_only") @@ -503,8 +516,8 @@ def test_diffusion_parallel_config_rejects_hsdp_with_tp_or_dp(): OmniStageDiffusionParallelConfig(data_parallel_size=2, use_hsdp=True, hsdp_shard_size=2) -def test_from_registry_preserves_legacy_pp_dp_for_world_size(): - cfg = VllmOmniConfig.from_registry("hunyuan_image3_dit").stage_by_id(0).parallel_config +def test_from_pipeline_config_preserves_legacy_pp_dp_for_world_size(): + cfg = _from_pipeline_key("hunyuan_image3_dit").stage_by_id(0).parallel_config assert cfg.pipeline_parallel_size == 1 assert cfg.data_parallel_size == 1 @@ -512,7 +525,7 @@ def test_from_registry_preserves_legacy_pp_dp_for_world_size(): assert cfg.world_size == 4 -def test_from_registry_derives_sequence_parallel_size_from_degrees(tmp_path): +def test_from_pipeline_config_derives_sequence_parallel_size_from_degrees(tmp_path): deploy_path = tmp_path / "dreamzero_derived_parallel.yaml" deploy_path.write_text( "\n".join( @@ -529,7 +542,7 @@ def test_from_registry_derives_sequence_parallel_size_from_degrees(tmp_path): ) ) - stage = VllmOmniConfig.from_registry("dreamzero", deploy_config_path=str(deploy_path)).stage_by_id(0) + stage = _from_pipeline_key("dreamzero", deploy_config_path=str(deploy_path)).stage_by_id(0) assert isinstance(stage, VllmOmniDiffusionStageConfig) assert stage.parallel_config.sequence_parallel_size == 6 @@ -542,10 +555,10 @@ def test_diffusion_parallel_config_rejects_cfg_parallel_size_outside_current_bou def test_stage_realizations_use_stage_specific_parallel_config_types(): - qwen_config = VllmOmniConfig.from_registry("qwen3_tts") + qwen_config = _from_pipeline_key("qwen3_tts") ar_stage = qwen_config.stage_by_id(0) generation_stage = qwen_config.stage_by_id(1) - diffusion_stage = VllmOmniConfig.from_registry("hunyuan_image3_dit").stage_by_id(0) + diffusion_stage = _from_pipeline_key("hunyuan_image3_dit").stage_by_id(0) assert isinstance(ar_stage, VllmOmniARStageConfig) assert type(ar_stage.parallel_config) is OmniStageParallelConfig @@ -566,7 +579,7 @@ def test_stage_realizations_use_stage_specific_parallel_config_types(): assert diffusion_stage.parallel_config.ulysses_degree == 1 -def test_from_registry_preserves_diffusion_parallel_mask_sp_padding(tmp_path): +def test_from_pipeline_config_preserves_diffusion_parallel_mask_sp_padding(tmp_path): deploy_path = tmp_path / "dreamzero_mask_sp_padding.yaml" deploy_path.write_text( "\n".join( @@ -581,17 +594,17 @@ def test_from_registry_preserves_diffusion_parallel_mask_sp_padding(tmp_path): ) ) - stage = VllmOmniConfig.from_registry("dreamzero", deploy_config_path=str(deploy_path)).stage_by_id(0) + stage = _from_pipeline_key("dreamzero", deploy_config_path=str(deploy_path)).stage_by_id(0) assert isinstance(stage, VllmOmniDiffusionStageConfig) assert stage.parallel_config.mask_sp_padding is True -def test_from_registry_matches_stage_config_to_omegaconf_behavior_for_representative_stage(): +def test_from_pipeline_config_matches_stage_config_to_omegaconf_behavior_for_representative_stage(): pipeline = _resolve_pipeline_or_skip("qwen3_tts") - legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy("qwen3_tts"))[0] + legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy(pipeline))[0] omega_stage = legacy_stage.to_omegaconf() - omni_stage = VllmOmniConfig.from_registry("qwen3_tts").stage_by_id(legacy_stage.stage_id) + omni_stage = _from_pipeline_key("qwen3_tts").stage_by_id(legacy_stage.stage_id) assert omega_stage.stage_id == omni_stage.stage_id assert omega_stage.stage_type == omni_stage.stage_type.value @@ -606,11 +619,93 @@ def test_from_registry_matches_stage_config_to_omegaconf_behavior_for_representa assert omega_stage.runtime.requires_multimodal_data == omni_stage.requires_multimodal_data -def test_from_registry_matches_to_omegaconf_diffusion_parallel_config(): +def test_from_pipeline_config_uses_hf_config_for_callable_resolver(): + hf_config = Qwen3OmniMoeConfig() + hf_config.enable_audio_output = False + + omni_config = _from_pipeline_key("qwen3_omni_moe", hf_config=hf_config) + + assert omni_config.pipeline_config.model_type == "qwen3_omni_moe_thinker_only" + assert len(omni_config.stage_configs) == 1 + assert omni_config.orchestrator_config.deploy_config_path is None + + thinker = omni_config.stage_configs[0] + assert thinker.model_stage == "thinker" + assert thinker.model_config.default_sampling_params == {"detokenize": True} + + +def test_from_pipeline_config_accepts_pre_resolved_pipeline(): + resolved_pipeline = PipelineConfig(model_type="callable_resolved_variant") + + omni_config = VllmOmniConfig.from_pipeline_config(resolved_pipeline) + + assert omni_config.pipeline_config is resolved_pipeline + + +def test_from_pipeline_config_prefers_loaded_user_deploy_config(monkeypatch): + pipeline = _resolve_pipeline_or_skip("qwen3_tts") + user_deploy_config = DeployConfig( + stages=[StageDeployConfig(stage_id=0, max_num_seqs=7)], + ) + monkeypatch.setattr( + omni_config_module, + "load_deploy_config", + lambda _path: pytest.fail("default deploy config should not be loaded"), + ) + + omni_config = VllmOmniConfig.from_pipeline_config( + pipeline, + user_deploy_config=user_deploy_config, + ) + + assert omni_config.stage_by_id(0).scheduler_config.max_num_seqs == 7 + + +def test_from_pipeline_config_default_deploy_name_ignores_cwd(monkeypatch, tmp_path): + default_name = "pipeline_default.yaml" + (tmp_path / default_name).write_text("stages: []\n", encoding="utf-8") + monkeypatch.chdir(tmp_path) + pipeline = PipelineConfig( + model_type="pipeline_with_default", + default_deploy_config_name=default_name, + ) + loaded_paths = [] + + def _load_deploy_config(path): + loaded_paths.append(Path(path)) + return DeployConfig() + + monkeypatch.setattr(omni_config_module, "load_deploy_config", _load_deploy_config) + + omni_config = VllmOmniConfig.from_pipeline_config(pipeline) + + assert omni_config.orchestrator_config.deploy_config_path == str(_DEPLOY_DIR / default_name) + assert loaded_paths == [_DEPLOY_DIR / default_name] + + +def test_from_pipeline_config_uses_resolved_deploy_pipeline(): + deploy_path = get_deploy_config_path("aura_omni.yaml") + pipeline = _resolve_pipeline_or_skip("aura_omni") + + omni_config = VllmOmniConfig.from_pipeline_config( + pipeline, + deploy_config_path=str(deploy_path), + ) + + assert omni_config.pipeline_config.model_type == "aura_omni" + assert [stage.model_stage for stage in omni_config.stage_configs] == [ + "asr", + "aura", + "qwen3_tts", + "code2wav", + ] + + +def test_from_pipeline_config_matches_to_omegaconf_diffusion_parallel_config(): pipeline = _resolve_pipeline_or_skip("hunyuan_image3_dit") - legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy("hunyuan_image3_dit"))[0] + legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy(pipeline))[0] omega_stage = legacy_stage.to_omegaconf() - omni_stage = VllmOmniConfig.from_registry("hunyuan_image3_dit").stage_by_id(legacy_stage.stage_id) + omni_stage = _from_pipeline_key("hunyuan_image3_dit").stage_by_id(legacy_stage.stage_id) assert ( omega_stage.engine_args.parallel_config.pipeline_parallel_size @@ -631,19 +726,19 @@ def test_from_registry_matches_to_omegaconf_diffusion_parallel_config(): ) -def test_from_registry_matches_build_engine_args_dict_behavior_for_representative_stage(monkeypatch): +def test_from_pipeline_config_matches_build_engine_args_dict_behavior_for_representative_stage(monkeypatch): from vllm_omni.engine import stage_init_utils monkeypatch.setattr(stage_init_utils, "resolve_worker_cls", lambda engine_args: None) pipeline = _resolve_pipeline_or_skip("qwen3_tts") - legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy("qwen3_tts"))[0] + legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy(pipeline))[0] omega_stage = legacy_stage.to_omegaconf() legacy_engine_args = build_engine_args_dict( omega_stage, model="/tmp/qwen3-tts", stage_connector_spec={"name": "SharedMemoryConnector", "extra": {}}, ) - omni_stage = VllmOmniConfig.from_registry("qwen3_tts").stage_by_id(legacy_stage.stage_id) + omni_stage = _from_pipeline_key("qwen3_tts").stage_by_id(legacy_stage.stage_id) assert legacy_engine_args["model"] == "/tmp/qwen3-tts" assert legacy_engine_args["stage_id"] == omni_stage.stage_id @@ -657,8 +752,8 @@ def test_from_registry_matches_build_engine_args_dict_behavior_for_representativ assert omni_stage.model_config.has_sampling_extra_args == legacy_engine_args["has_sampling_extra_args"] -def test_from_registry_derives_has_sampling_extra_args_from_stage_defaults(): - stage = VllmOmniConfig.from_registry("voxtral_tts").stage_by_id(0) +def test_from_pipeline_config_derives_has_sampling_extra_args_from_stage_defaults(): + stage = _from_pipeline_key("voxtral_tts").stage_by_id(0) assert (stage.model_config.default_sampling_params or {}).get("extra_args") assert stage.model_config.has_sampling_extra_args is True @@ -709,7 +804,7 @@ def test_diffusion_config_from_kwargs_reuses_legacy_normalization(monkeypatch): assert cfg.diffusers_call_kwargs == {} -def test_from_registry_normalizes_diffusion_config_aliases_from_engine_args(tmp_path): +def test_from_pipeline_config_normalizes_diffusion_config_aliases_from_engine_args(tmp_path): deploy_path = tmp_path / "dreamzero_diffusion_aliases.yaml" deploy_path.write_text( "\n".join( @@ -723,7 +818,7 @@ def test_from_registry_normalizes_diffusion_config_aliases_from_engine_args(tmp_ ) ) - stage = VllmOmniConfig.from_registry( + stage = _from_pipeline_key( "dreamzero", deploy_config_path=str(deploy_path), ).stage_by_id(0) diff --git a/tests/e2e/accuracy/test_hunyuan_image3.py b/tests/e2e/accuracy/test_hunyuan_image3.py index 00e77fc8f..88c7e255d 100644 --- a/tests/e2e/accuracy/test_hunyuan_image3.py +++ b/tests/e2e/accuracy/test_hunyuan_image3.py @@ -298,7 +298,7 @@ def _run_offline(deploy_config_path: str, output_path: Path) -> tuple[Image.Imag system_prompt_type = result.system_prompt_type ar_stop_token_ids = resolve_stop_token_ids(task="it2i", bot_task="think_recaption", tokenizer=tokenizer) - with OmniRunner(MODEL_PATH, deploy_config=deploy_config_path) as runner: + with OmniRunner(MODEL_PATH, deploy_config=deploy_config_path, trust_remote_code=True) as runner: params_list = list(runner.omni.default_sampling_params_list) for sp in params_list: if isinstance(sp, OmniDiffusionSamplingParams): @@ -491,7 +491,12 @@ def _run_dit_model( os.environ["VLLM_NVFP4_GEMM_BACKEND"] = nvfp4_backend try: - with OmniRunner(model, deploy_config=deploy_config_path, mode="text-to-image") as runner: + with OmniRunner( + model, + deploy_config=deploy_config_path, + mode="text-to-image", + trust_remote_code=True, + ) as runner: generator = torch.Generator(device=current_omni_platform.device_type or "cuda").manual_seed(SEED) params = OmniDiffusionSamplingParams( height=QUANT_HEIGHT, diff --git a/tests/e2e/offline_inference/test_covo_audio_expansion.py b/tests/e2e/offline_inference/test_covo_audio_expansion.py index ca05932ef..11672686e 100644 --- a/tests/e2e/offline_inference/test_covo_audio_expansion.py +++ b/tests/e2e/offline_inference/test_covo_audio_expansion.py @@ -14,7 +14,7 @@ from vllm_omni.model_executor.models.covo_audio.prompt_utils import ( models = ["tencent/Covo-Audio-Chat"] stage_config = get_deploy_config_path("covo_audio.yaml") -test_params = [(model, stage_config) for model in models] +test_params = [(model, stage_config, {"trust_remote_code": True}) for model in models] def get_question(): diff --git a/tests/e2e/offline_inference/test_dynin_omni_expansion.py b/tests/e2e/offline_inference/test_dynin_omni_expansion.py index 13b9e5cb3..19a847332 100644 --- a/tests/e2e/offline_inference/test_dynin_omni_expansion.py +++ b/tests/e2e/offline_inference/test_dynin_omni_expansion.py @@ -29,7 +29,7 @@ _DEFAULT_STAGE_CONFIG_PATH = Path(get_deploy_config_path("dynin_omni_ci.yaml")) models = ["snu-aidas/Dynin-Omni"] stage_configs = [str(_DEFAULT_STAGE_CONFIG_PATH)] -test_params = [(model, stage_config) for model in models for stage_config in stage_configs] +test_params = [(model, stage_config, {"trust_remote_code": True}) for model in models for stage_config in stage_configs] DYNIN_CONFIG_PATH = str(_DEFAULT_DYNIN_CONFIG_PATH) if _DEFAULT_DYNIN_CONFIG_PATH is not None else None diff --git a/tests/e2e/offline_inference/test_glm_tts_expansion.py b/tests/e2e/offline_inference/test_glm_tts_expansion.py index d6a8729f0..e54dd5480 100644 --- a/tests/e2e/offline_inference/test_glm_tts_expansion.py +++ b/tests/e2e/offline_inference/test_glm_tts_expansion.py @@ -98,6 +98,7 @@ def test_offline_voice_clone_zh(async_chunk: bool) -> None: MODEL, stage_configs_path=_get_deploy_config(async_chunk=async_chunk), stage_init_timeout=600, + trust_remote_code=True, ) as omni_runner: outputs = omni_runner.omni.generate( [ diff --git a/tests/e2e/offline_inference/test_hunyuanimage3.py b/tests/e2e/offline_inference/test_hunyuanimage3.py index ac1cb13cb..ebd338fe8 100644 --- a/tests/e2e/offline_inference/test_hunyuanimage3.py +++ b/tests/e2e/offline_inference/test_hunyuanimage3.py @@ -275,6 +275,7 @@ def omni() -> Generator[Omni, None, None]: MODEL_NAME, deploy_config=str(DEPLOY_CONFIG_PATH), mode="text-to-image", + trust_remote_code=True, ) as runner: yield runner.omni diff --git a/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py b/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py index 5178a40a6..008f0d2a9 100644 --- a/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py +++ b/tests/e2e/offline_inference/test_hunyuanimage3_i2t_expansion.py @@ -53,6 +53,7 @@ def omni() -> Generator[Omni, None, None]: with OmniRunner( MODEL_NAME, stage_configs_path=str(STAGE_CONFIG_PATH), + trust_remote_code=True, ) as runner: yield runner.omni diff --git a/tests/e2e/offline_inference/test_moss_tts_expansion.py b/tests/e2e/offline_inference/test_moss_tts_expansion.py index 73e919d1c..81a64fcd9 100644 --- a/tests/e2e/offline_inference/test_moss_tts_expansion.py +++ b/tests/e2e/offline_inference/test_moss_tts_expansion.py @@ -76,7 +76,11 @@ pytestmark = [ pytest.mark.slow, pytest.mark.skip(reason="https://github.com/vllm-project/vllm-omni/issues/4700"), pytest.mark.tts, - pytest.mark.parametrize("omni_runner", [(MODEL, _get_test_config())], indirect=True), + pytest.mark.parametrize( + "omni_runner", + [(MODEL, _get_test_config(), {"trust_remote_code": True})], + indirect=True, + ), ] 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 2d247f691..d3e7fcb0f 100644 --- a/tests/e2e/offline_inference/test_moss_tts_nano_expansion.py +++ b/tests/e2e/offline_inference/test_moss_tts_nano_expansion.py @@ -29,6 +29,7 @@ STAGE_CONFIG = get_deploy_config_path("moss_tts_nano.yaml") _OMNI_RUNNER_PARAM = ( MODEL_NAME, STAGE_CONFIG, + {"trust_remote_code": True}, ) pytestmark = [ diff --git a/tests/e2e/offline_inference/test_moss_tts_realtime.py b/tests/e2e/offline_inference/test_moss_tts_realtime.py index 5034f0f1d..892a395ba 100644 --- a/tests/e2e/offline_inference/test_moss_tts_realtime.py +++ b/tests/e2e/offline_inference/test_moss_tts_realtime.py @@ -98,7 +98,11 @@ pytestmark = [ pytest.mark.skip(reason="https://github.com/vllm-project/vllm-omni/issues/4700"), pytest.mark.full_model, pytest.mark.tts, - pytest.mark.parametrize("omni_runner", [(MODEL, _get_test_config())], indirect=True), + pytest.mark.parametrize( + "omni_runner", + [(MODEL, _get_test_config(), {"trust_remote_code": True})], + indirect=True, + ), ] # --------------------------------------------------------------------------- diff --git a/tests/e2e/offline_inference/test_moss_tts_realtime_expansion.py b/tests/e2e/offline_inference/test_moss_tts_realtime_expansion.py index 80b12e2e4..b84e5a0ff 100644 --- a/tests/e2e/offline_inference/test_moss_tts_realtime_expansion.py +++ b/tests/e2e/offline_inference/test_moss_tts_realtime_expansion.py @@ -24,7 +24,11 @@ from tests.helpers.stage_config import get_deploy_config_path MODEL = "OpenMOSS-Team/MOSS-TTS-Realtime" DEPLOY_CONFIG = get_deploy_config_path("moss_tts_realtime.yaml") -_OMNI_RUNNER_PARAM = (MODEL, DEPLOY_CONFIG, {"stage_init_timeout": 300}) +_OMNI_RUNNER_PARAM = ( + MODEL, + DEPLOY_CONFIG, + {"stage_init_timeout": 300, "trust_remote_code": True}, +) pytestmark = [ pytest.mark.slow, diff --git a/tests/e2e/offline_inference/test_moss_tts_v1_5_expansion.py b/tests/e2e/offline_inference/test_moss_tts_v1_5_expansion.py index 43bf80415..25c671e05 100644 --- a/tests/e2e/offline_inference/test_moss_tts_v1_5_expansion.py +++ b/tests/e2e/offline_inference/test_moss_tts_v1_5_expansion.py @@ -22,7 +22,11 @@ from tests.helpers.stage_config import get_deploy_config_path MODEL = "OpenMOSS-Team/MOSS-TTS-v1.5" DEPLOY_CONFIG = get_deploy_config_path("moss_tts.yaml") -_OMNI_RUNNER_PARAM = (MODEL, DEPLOY_CONFIG, {"stage_init_timeout": 600}) +_OMNI_RUNNER_PARAM = ( + MODEL, + DEPLOY_CONFIG, + {"stage_init_timeout": 600, "trust_remote_code": True}, +) pytestmark = [ pytest.mark.skip(reason="https://github.com/vllm-project/vllm-omni/issues/4643"), diff --git a/tests/e2e/offline_inference/test_voxcpm2_tts.py b/tests/e2e/offline_inference/test_voxcpm2_tts.py index 5c91db45a..c10694a40 100644 --- a/tests/e2e/offline_inference/test_voxcpm2_tts.py +++ b/tests/e2e/offline_inference/test_voxcpm2_tts.py @@ -14,8 +14,8 @@ VOXCPM2_MODEL = "openbmb/VoxCPM2" DEPLOY_CONFIG = get_deploy_config_path("voxcpm2.yaml") SAMPLE_RATE = 48000 -# (model, stage_config_path) — see ``omni_runner`` in tests.helpers.fixtures.runtime -_OMNI_RUNNER_PARAM = (VOXCPM2_MODEL, DEPLOY_CONFIG) +# VoxCPM2 ships a custom tokenizer, so remote code must be explicitly enabled. +_OMNI_RUNNER_PARAM = (VOXCPM2_MODEL, DEPLOY_CONFIG, {"trust_remote_code": True}) pytestmark = pytest.mark.parametrize("omni_runner", [_OMNI_RUNNER_PARAM], indirect=True) diff --git a/tests/engine/test_async_omni_engine_stage_init.py b/tests/engine/test_async_omni_engine_stage_init.py index d5a50cf29..e98c58d74 100644 --- a/tests/engine/test_async_omni_engine_stage_init.py +++ b/tests/engine/test_async_omni_engine_stage_init.py @@ -745,6 +745,7 @@ def test_resolve_stage_configs_injects_global_diffusion_attention_when_missing(m _config_path, stage_configs = engine._resolve_stage_configs( model="dummy-model", kwargs={"diffusion_attention_backend": "FLASH_ATTN"}, + trust_remote_code=False, ) diffusion_attention_config = stage_configs[0].engine_args.diffusion_attention_config @@ -778,6 +779,7 @@ def test_resolve_stage_configs_preserves_stage_diffusion_attention(monkeypatch): _config_path, stage_configs = engine._resolve_stage_configs( model="dummy-model", kwargs={"diffusion_attention_backend": "FLASH_ATTN"}, + trust_remote_code=False, ) assert stage_configs[0].engine_args.diffusion_attention_config is existing_attention @@ -804,6 +806,7 @@ def test_resolve_stage_configs_does_not_inject_diffusion_attention_into_llm_stag _config_path, stage_configs = engine._resolve_stage_configs( model="dummy-model", kwargs={"diffusion_attention_backend": "TORCH_SDPA"}, + trust_remote_code=False, ) assert stage_configs[0].engine_args.attention_config == {"backend": "FLASH_ATTN"} diff --git a/tests/entrypoints/test_async_omni_diffusion_config.py b/tests/entrypoints/test_async_omni_diffusion_config.py index ba132dbd2..d6b33dc89 100644 --- a/tests/entrypoints/test_async_omni_diffusion_config.py +++ b/tests/entrypoints/test_async_omni_diffusion_config.py @@ -355,6 +355,7 @@ def test_resolve_stage_configs_injects_additional_config_into_diffusion_stage(mo "stage_configs_path": "dummy.yaml", "additional_config": {"torchair_graph_config": {"enabled": True}}, }, + trust_remote_code=False, ) assert not hasattr(stage_configs[0].engine_args, "additional_config") @@ -392,6 +393,7 @@ def test_resolve_stage_configs_injects_quantization_config_into_diffusion_stage( "stage_configs_path": "dummy.yaml", "quantization_config": {"method": "bitsandbytes"}, }, + trust_remote_code=False, ) assert stage_configs[0].engine_args.quantization_config == {"method": "bitsandbytes"} diff --git a/tests/entrypoints/test_resolve_dreamzero_config.py b/tests/entrypoints/test_resolve_dreamzero_config.py index 4dcaddd63..cc459c269 100644 --- a/tests/entrypoints/test_resolve_dreamzero_config.py +++ b/tests/entrypoints/test_resolve_dreamzero_config.py @@ -28,11 +28,18 @@ def test_dreamzero_config_sets_model_class_and_policy_config(monkeypatch): classmethod(lambda _cls, model, trust_remote_code=True: "vla"), ) monkeypatch.setattr( - "vllm_omni.diffusion.utils.hf_utils._looks_like_dreamzero", + "vllm_omni.config.config_factory.StageConfigFactory.get_hf_config", + classmethod(lambda _cls, model, trust_remote_code=True: None), + ) + monkeypatch.setattr( + "vllm_omni.config.config_factory._looks_like_dreamzero", lambda _model: True, ) - stage_configs, _ = load_stage_configs_from_model("GEAR-Dreams/DreamZero-DROID") + stage_configs, _ = load_stage_configs_from_model( + "GEAR-Dreams/DreamZero-DROID", + trust_remote_code=False, + ) engine_args = stage_configs[0].engine_args assert engine_args.model_class_name == "DreamZeroPipeline" diff --git a/tests/entrypoints/test_utils.py b/tests/entrypoints/test_utils.py index b48ed2d91..4c8522ae0 100644 --- a/tests/entrypoints/test_utils.py +++ b/tests/entrypoints/test_utils.py @@ -315,13 +315,17 @@ class TestResolveModelConfigPath: class TestLoadAndResolveStageConfigs: - def test_load_and_resolve_with_kwargs(self): + def test_load_and_resolve_with_kwargs(self, mocker: MockerFixture): """Ensure that dtype survives default stage creation.""" kwargs = {"dtype": torch.float32} + mocker.patch("vllm_omni.entrypoints.utils.resolve_model_config_path", return_value=None) + mocker.patch("vllm_omni.entrypoints.utils.load_stage_configs_from_model", return_value=([], None)) + 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), ) assert config_path is None @@ -356,10 +360,12 @@ class TestLoadAndResolveStageConfigs: model="dummy-model", stage_configs_path=str(deploy_path), kwargs={}, + trust_remote_code=True, ) load_stage_configs.assert_called_once_with( "dummy-model", + trust_remote_code=True, base_engine_args={}, deploy_config_path=str(deploy_path), stage_overrides=None, diff --git a/tests/test_config_factory.py b/tests/test_config_factory.py index 54683e67c..e160a9b10 100644 --- a/tests/test_config_factory.py +++ b/tests/test_config_factory.py @@ -5,7 +5,6 @@ Unit tests for StageConfigFactory and related classes. """ import importlib -import inspect import warnings from dataclasses import dataclass, fields from pathlib import Path @@ -17,8 +16,10 @@ from transformers import PretrainedConfig, Qwen3OmniMoeConfig from tests.helpers.stage_config import get_deploy_config_path, get_deploy_config_stage from vllm_omni.config.config_factory import StageConfigFactory from vllm_omni.config.endpoint_policy import EndpointRestriction, OmniServingCapability -from vllm_omni.config.pipeline_registry import OMNI_PIPELINES, register_pipeline +from vllm_omni.config.omni_config import VllmOmniConfig +from vllm_omni.config.pipeline_registry import OMNI_PIPELINES, register_pipeline, resolve_pipeline_config from vllm_omni.config.stage_config import ( + _DEPLOY_DIR, DeployConfig, PipelineConfig, StageConfig, @@ -40,13 +41,21 @@ from vllm_omni.engine.arg_utils import SHARED_FIELDS, EngineArgs, internal_black pytestmark = [pytest.mark.core_model, pytest.mark.cpu] +@pytest.fixture(autouse=True) +def _stable_test_platform(monkeypatch): + from vllm_omni import platforms + + platform = platforms.current_omni_platform + monkeypatch.setattr(platform, "device_name", "cpu", raising=False) + monkeypatch.setattr(platform, "device_type", "cpu", raising=False) + + @pytest.fixture(autouse=True) def clear_config_factory_caches(): """Clear cached classmethods from the StageConfigFactory to prevent test pollution.""" yield StageConfigFactory.get_hf_config.cache_clear() StageConfigFactory.try_infer_model_type.cache_clear() - StageConfigFactory.get_pipeline_config.cache_clear() Q3_OMNI_ALL_STAGES_HF_CONFIG = Qwen3OmniMoeConfig(enable_audio_output=True) @@ -534,29 +543,41 @@ class TestPipelineDiscovery: def test_registry_resolver_qwen3_omni_all_stages(self): """Test that providing the HF config for qwen3 omni with audio enabled uses all stages.""" - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) assert isinstance(pipeline, PipelineConfig) assert pipeline.model_type == "qwen3_omni_moe" + assert pipeline.default_deploy_config_name == "qwen3_omni_moe.yaml" assert len(pipeline.stages) == 3 # thinker + talker + code2wav def test_registry_resolver_qwen3_omni_thinker_only(self): """Test that providing the HF config for qwen3 omni without audio is thinker only.""" - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_THINKER_HF_CONFIG, ) assert isinstance(pipeline, PipelineConfig) assert pipeline.model_type == "qwen3_omni_moe_thinker_only" + assert pipeline.default_deploy_config_name is None assert len(pipeline.stages) == 1 # thinker only + @pytest.mark.parametrize( + "pipeline", + [registered for registered in OMNI_PIPELINES.values() if isinstance(registered, PipelineConfig)], + ids=lambda pipeline: pipeline.model_type, + ) + def test_registered_default_deploy_config_exists(self, pipeline): + if pipeline.default_deploy_config_name is None: + return + assert Path(get_deploy_config_path(pipeline.default_deploy_config_name)).is_file() + def test_registry_returns_none_for_unknown(self): """Unknown model_types aren't found and resolve to `None`.""" assert "definitely_not_a_real_model" not in OMNI_PIPELINES assert OMNI_PIPELINES.get("definitely_not_a_real_model") is None - assert StageConfigFactory.resolve_pipeline_config("definitely_not_a_real_model") is None + assert resolve_pipeline_config("definitely_not_a_real_model") is None def test_pipeline_config_supports_hf_architectures(self): """PipelineConfig accepts hf_architectures for HF-arch fallback @@ -607,6 +628,236 @@ class TestPipelineConfigNew: class TestPipelineRegistration: + def test_resolve_pipeline_prefers_deploy_pipeline_key(self, clean_pipeline_registry, tmp_path): + deploy_key = "deploy_selected_pipeline" + model_type_key = "hf_model_type_pipeline" + deploy_pipe = PipelineConfig(model_type=deploy_key) + model_type_pipe = PipelineConfig(model_type=model_type_key) + register_pipeline(deploy_pipe) + register_pipeline(model_type_pipe) + + deploy_path = tmp_path / "deploy.yaml" + deploy_path.write_text(f"pipeline: {deploy_key}\n", encoding="utf-8") + + class FakeConfig(PretrainedConfig): + model_type = model_type_key + + fake_config = FakeConfig() + with patch("vllm_omni.config.config_factory.get_config", return_value=fake_config): + pipeline_cfg = StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=str(deploy_path), + ) + + assert pipeline_cfg is deploy_pipe + + def test_resolve_pipeline_rejects_invalid_deploy_pipeline_key( + self, + clean_pipeline_registry, + tmp_path, + ): + model_type_key = "registered_model_type_pipeline" + register_pipeline(PipelineConfig(model_type=model_type_key)) + + deploy_path = tmp_path / "deploy.yaml" + deploy_path.write_text("pipeline: missing_pipeline\n", encoding="utf-8") + + class FakeConfig(PretrainedConfig): + model_type = model_type_key + + with patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()): + with pytest.raises(KeyError, match="missing_pipeline"): + StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=str(deploy_path), + ) + with pytest.raises(KeyError, match="missing_pipeline"): + StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=str(deploy_path), + ) + with pytest.raises(KeyError, match="missing_pipeline"): + StageConfigFactory.create_legacy_stage_configs_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=str(deploy_path), + ) + + def test_resolve_pipeline_matches_hf_architecture_fallback(self, clean_pipeline_registry): + pipeline_key = "architecture_fallback_pipeline" + pipe_cfg = PipelineConfig( + model_type=pipeline_key, + hf_architectures=("ArchitectureFallbackForTest",), + ) + register_pipeline(pipe_cfg) + + class FakeConfig(PretrainedConfig): + model_type = "unregistered_model_type" + + fake_config = FakeConfig() + fake_config.architectures = ["ArchitectureFallbackForTest"] + with patch("vllm_omni.config.config_factory.get_config", return_value=fake_config): + pipeline_cfg = StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=None, + ) + + assert pipeline_cfg is pipe_cfg + + def test_resolve_pipeline_architecture_fallback_respects_predicates(self, clean_pipeline_registry): + shared_arch = "SharedPredicateArchitectureForTest" + + def rejecting_predicate(_hf_config): + return False + + def raising_predicate(_hf_config): + raise ValueError("predicate failure") + + reject_cfg = PipelineConfig( + model_type="rejected_by_predicate", + hf_architectures=(shared_arch,), + hf_config_predicate=rejecting_predicate, + ) + raises_cfg = PipelineConfig( + model_type="raises_in_predicate", + hf_architectures=(shared_arch,), + hf_config_predicate=raising_predicate, + ) + accept_cfg = PipelineConfig( + model_type="accepted_by_predicate", + hf_architectures=(shared_arch,), + ) + register_pipeline(reject_cfg) + register_pipeline(raises_cfg) + register_pipeline(accept_cfg) + + class FakeConfig(PretrainedConfig): + model_type = "unregistered_model_type" + + fake_config = FakeConfig() + fake_config.architectures = [shared_arch] + with patch("vllm_omni.config.config_factory.get_config", return_value=fake_config): + pipeline_cfg = StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=None, + ) + + assert pipeline_cfg is accept_cfg + + def test_resolve_pipeline_architecture_fallback_rejects_non_matching_architecture( + self, + clean_pipeline_registry, + ): + predicate_calls = [] + + def predicate(_hf_config): + predicate_calls.append(_hf_config) + return True + + pipe_cfg = PipelineConfig( + model_type="predicate_without_arch_match", + hf_architectures=("DifferentArchitectureForTest",), + hf_config_predicate=predicate, + ) + register_pipeline(pipe_cfg) + + class FakeConfig(PretrainedConfig): + model_type = "unregistered_model_type" + + fake_config = FakeConfig() + fake_config.architectures = ["RequestedArchitectureForTest"] + with patch("vllm_omni.config.config_factory.get_config", return_value=fake_config): + pipeline_cfg = StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=None, + ) + + assert pipeline_cfg is None + assert predicate_calls == [fake_config] + + def test_resolve_pipeline_architecture_fallback_resolves_callable_pipeline(self, clean_pipeline_registry): + pipeline_key = "callable_architecture_fallback" + resolved_cfg = PipelineConfig( + model_type="callable_resolved_pipeline", + hf_architectures=("CallableArchitectureForTest",), + ) + seen_hf_configs = [] + + def resolver(hf_config): + seen_hf_configs.append(hf_config) + return resolved_cfg + + register_pipeline(resolver, model_type=pipeline_key) + + class FakeConfig(PretrainedConfig): + model_type = "unregistered_model_type" + + fake_config = FakeConfig() + fake_config.architectures = ["CallableArchitectureForTest"] + with patch("vllm_omni.config.config_factory.get_config", return_value=fake_config): + pipeline_cfg = StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=None, + ) + + assert pipeline_cfg is resolved_cfg + assert seen_hf_configs == [fake_config] + + def test_resolve_pipeline_returns_none_without_registry_or_architecture_match(self, clean_pipeline_registry): + class FakeConfig(PretrainedConfig): + model_type = "unregistered_model_type" + + fake_config = FakeConfig() + fake_config.architectures = ["UnmatchedArchitectureForTest"] + with patch("vllm_omni.config.config_factory.get_config", return_value=fake_config): + pipeline_cfg = StageConfigFactory.get_pipeline_config( + "fake/model", + trust_remote_code=True, + deploy_config_path=None, + ) + + assert pipeline_cfg is None + + def test_create_from_model_returns_structured_omni_config(self): + class FakeConfig(PretrainedConfig): + model_type = "qwen3_tts" + + with patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()): + omni_config = StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=None, + ) + + assert isinstance(omni_config, VllmOmniConfig) + assert omni_config.pipeline_config is OMNI_PIPELINES["qwen3_tts"] + assert len(omni_config.stage_configs) == 2 + + def test_create_from_model_preserves_model_on_structured_diffusion_stage(self): + class FakeConfig(PretrainedConfig): + model_type = "dreamzero" + + with patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()): + omni_config = StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=None, + ) + + assert isinstance(omni_config, VllmOmniConfig) + assert omni_config.stage_by_id(0).diffusion_config.model == "fake/model" + def test_pipeline_registration(self, clean_pipeline_registry): """Ensure that we can register and create a custom pipeline config.""" new_model_type = "new_model_type" @@ -622,15 +873,23 @@ class TestPipelineRegistration: model_type = new_model_type # Create the model + fake_config = FakeConfig() with ( - patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()), - patch.object(StageConfigFactory, "_create_from_registry") as mock_create, + patch("vllm_omni.config.config_factory.get_config", return_value=fake_config), + patch.object(VllmOmniConfig, "from_pipeline_config") as mock_create, ): - StageConfigFactory.create_from_model("fake/model") + StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=None, + ) mock_create.assert_called_once() - # Ensure that the passed pipeline_config is the right type - pipeline_cfg = mock_create.call_args[0][1] - assert isinstance(pipeline_cfg, PipelineConfig) + assert mock_create.call_args.args == (pipe_cfg,) + assert mock_create.call_args.kwargs["cli_overrides"] == { + "trust_remote_code": False, + "model": "fake/model", + } assert pipe_cfg.model_type == new_model_type def test_resolver_registration(self, clean_pipeline_registry): @@ -655,26 +914,125 @@ class TestPipelineRegistration: assert OMNI_PIPELINES[new_model_type] is custom_resolver # Create the model + fake_config = FakeConfig() with ( - patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()), - patch.object(StageConfigFactory, "_create_from_registry") as mock_create, + patch("vllm_omni.config.config_factory.get_config", return_value=fake_config) as mock_get_config, + patch.object(VllmOmniConfig, "from_pipeline_config") as mock_create, ): - StageConfigFactory.create_from_model("fake/model") + StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=True, + cli_overrides={}, + deploy_config_path=None, + ) mock_create.assert_called_once() - # Ensure that the passed pipeline_config is the right type - pipeline_cfg = mock_create.call_args[0][1] - assert isinstance(pipeline_cfg, PipelineConfig) - assert pipeline_cfg.model_type == resolved_type + assert mock_create.call_args.args[0].model_type == resolved_type + assert mock_create.call_args.kwargs["cli_overrides"] == { + "trust_remote_code": True, + "model": "fake/model", + } + mock_get_config.assert_called_once_with("fake/model", trust_remote_code=True) + + with patch.object(StageConfigFactory, "_create_legacy_from_registry", return_value=([], None)) as mock_legacy: + StageConfigFactory.create_legacy_stage_configs_from_model( + "fake/model", + trust_remote_code=True, + cli_overrides={}, + deploy_config_path=None, + ) + + assert mock_legacy.call_args.args[0].model_type == resolved_type + assert mock_legacy.call_args.args[1]["trust_remote_code"] is True def test_resolve_when_autodetect_resolves_none(self): """Regression test for: https://github.com/vllm-project/vllm-omni/issues/4726""" deploy_path = get_deploy_config_path("ming_tts.yaml") resolved_config = StageConfigFactory.create_from_model( model="inclusionAI/Ming-omni-tts-0.5B", + trust_remote_code=False, + cli_overrides={}, deploy_config_path=deploy_path, ) assert resolved_config is not None - assert len(resolved_config) > 0 + assert len(resolved_config.stage_configs) > 0 + + def test_legacy_and_structured_paths_share_deploy_pipeline_override(self, clean_pipeline_registry, tmp_path): + pipeline_key = "deploy_only_pipeline" + pipe_cfg = PipelineConfig( + model_type=pipeline_key, + model_arch="DeployOnlyArch", + stages=(StagePipelineConfig(stage_id=0, model_stage="diffusion"),), + ) + register_pipeline(pipe_cfg) + + deploy_path = tmp_path / "deploy_only.yaml" + deploy_path.write_text(f"pipeline: {pipeline_key}\n", encoding="utf-8") + + class FakeConfig(PretrainedConfig): + model_type = "dense" + architectures = ("UnregisteredArch",) + + with patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()): + omni_config = StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=str(deploy_path), + ) + legacy_configs, _ = StageConfigFactory.create_legacy_stage_configs_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=str(deploy_path), + ) + + assert omni_config is not None + assert omni_config.pipeline_config is pipe_cfg + assert legacy_configs is not None + assert len(legacy_configs) == 1 + assert legacy_configs[0].yaml_engine_args["model_arch"] == "DeployOnlyArch" + + def test_structured_path_loads_explicit_deploy_config_once(self, clean_pipeline_registry, tmp_path): + pipeline_key = "single_load_pipeline" + register_pipeline(PipelineConfig(model_type=pipeline_key)) + deploy_path = tmp_path / "single_load.yaml" + deploy_path.write_text(f"pipeline: {pipeline_key}\n", encoding="utf-8") + + class FakeConfig(PretrainedConfig): + model_type = "unregistered" + + with ( + patch("vllm_omni.config.config_factory.get_config", return_value=FakeConfig()), + patch( + "vllm_omni.config.config_factory.load_deploy_config", + wraps=load_deploy_config, + ) as mock_load, + ): + StageConfigFactory.create_from_model( + "fake/model", + trust_remote_code=False, + cli_overrides={}, + deploy_config_path=str(deploy_path), + ) + + mock_load.assert_called_once_with(deploy_path) + + def test_legacy_default_deploy_name_ignores_cwd(self, monkeypatch, tmp_path): + default_name = "pipeline_default.yaml" + (tmp_path / default_name).write_text("stages: []\n", encoding="utf-8") + monkeypatch.chdir(tmp_path) + pipeline = PipelineConfig( + model_type="pipeline_with_default", + default_deploy_config_name=default_name, + ) + + with patch( + "vllm_omni.config.config_factory.load_deploy_config", + return_value=DeployConfig(), + ) as mock_load: + StageConfigFactory._create_legacy_from_registry(pipeline, {}) + + mock_load.assert_called_once_with(_DEPLOY_DIR / default_name) def test_deploy_override_uses_correct_endpoint_restrictions(self, clean_pipeline_registry, tmp_path): """Ensure endpoint restrictions must come from the final pipeline @@ -756,7 +1114,7 @@ stages: ) deploy = load_deploy_config(deploy_path) - pipeline = StageConfigFactory.resolve_pipeline_config("qwen3_tts") + pipeline = resolve_pipeline_config("qwen3_tts") stages = merge_pipeline_deploy(pipeline, deploy) assert isinstance(pipeline, PipelineConfig) @@ -827,7 +1185,7 @@ stages: if not deploy_path.exists(): pytest.skip("Deploy config not found") - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -844,7 +1202,7 @@ stages: assert s0.yaml_extras["default_sampling_params"]["detokenize"] is True def test_merge_pipeline_deploy_preserves_num_replicas(self, tmp_path): - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1131,7 +1489,7 @@ stages: class TestQwen3OmniPipeline: def test_registered(self): - p = StageConfigFactory.resolve_pipeline_config( + p = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1141,7 +1499,7 @@ class TestQwen3OmniPipeline: assert p.validate() == [] def test_thinker(self): - p = StageConfigFactory.resolve_pipeline_config( + p = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1156,7 +1514,7 @@ class TestQwen3OmniPipeline: assert s.sampling_constraints["detokenize"] is True def test_talker(self): - p = StageConfigFactory.resolve_pipeline_config( + p = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1173,7 +1531,7 @@ class TestQwen3OmniPipeline: assert s.custom_process_next_stage_input_func is not None def test_code2wav(self): - p = StageConfigFactory.resolve_pipeline_config( + p = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1188,14 +1546,14 @@ class TestQwen3OmniPipeline: class TestQwen2_5OmniPipeline: def test_registered(self): - p = StageConfigFactory.resolve_pipeline_config("qwen2_5_omni") + p = resolve_pipeline_config("qwen2_5_omni") assert isinstance(p, PipelineConfig) assert p.model_arch == "Qwen2_5OmniForConditionalGeneration" assert len(p.stages) == 3 assert p.validate() == [] def test_thinker(self): - p = StageConfigFactory.resolve_pipeline_config("qwen2_5_omni") + p = resolve_pipeline_config("qwen2_5_omni") assert isinstance(p, PipelineConfig) s = p.get_stage(0) @@ -1207,7 +1565,7 @@ class TestQwen2_5OmniPipeline: assert s.requires_multimodal_data is True def test_talker(self): - p = StageConfigFactory.resolve_pipeline_config("qwen2_5_omni") + p = resolve_pipeline_config("qwen2_5_omni") assert isinstance(p, PipelineConfig) s = p.get_stage(1) @@ -1221,7 +1579,7 @@ class TestQwen2_5OmniPipeline: assert s.sync_process_input_func is not None def test_code2wav(self): - p = StageConfigFactory.resolve_pipeline_config("qwen2_5_omni") + p = resolve_pipeline_config("qwen2_5_omni") assert isinstance(p, PipelineConfig) s = p.get_stage(2) @@ -1233,7 +1591,7 @@ class TestQwen2_5OmniPipeline: class TestQwen3TTSPipeline: def test_registered(self): - p = StageConfigFactory.resolve_pipeline_config("qwen3_tts") + p = resolve_pipeline_config("qwen3_tts") assert isinstance(p, PipelineConfig) assert p is not None assert p.model_arch == "Qwen3TTSTalkerForConditionalGeneration" @@ -1241,7 +1599,7 @@ class TestQwen3TTSPipeline: assert p.validate() == [] def test_talker_stage(self): - p = StageConfigFactory.resolve_pipeline_config("qwen3_tts") + p = resolve_pipeline_config("qwen3_tts") assert isinstance(p, PipelineConfig) s = p.get_stage(0) @@ -1255,7 +1613,7 @@ class TestQwen3TTSPipeline: assert s.model_arch is None def test_code2wav_stage_has_per_stage_model_arch(self): - p = StageConfigFactory.resolve_pipeline_config("qwen3_tts") + p = resolve_pipeline_config("qwen3_tts") assert isinstance(p, PipelineConfig) s = p.get_stage(1) @@ -1314,14 +1672,14 @@ class TestQwen3TTSPipeline: class TestMingFlashOmniPipeline: def test_registered(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni") + p = resolve_pipeline_config("ming_flash_omni") assert isinstance(p, PipelineConfig) assert p.model_arch == "MingFlashOmniForConditionalGeneration" assert len(p.stages) == 2 assert p.validate() == [] def test_thinker_stage(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni") + p = resolve_pipeline_config("ming_flash_omni") assert isinstance(p, PipelineConfig) s = p.get_stage(0) @@ -1335,7 +1693,7 @@ class TestMingFlashOmniPipeline: assert s.sampling_constraints["detokenize"] is True def test_talker_stage(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni") + p = resolve_pipeline_config("ming_flash_omni") assert isinstance(p, PipelineConfig) s = p.get_stage(1) @@ -1361,7 +1719,7 @@ class TestMingFlashOmniPipeline: Lazy string references only fail at first inference otherwise — this catches typos in the pipeline declaration at import / registration time. """ - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni") + p = resolve_pipeline_config("ming_flash_omni") assert isinstance(p, PipelineConfig) s = p.get_stage(1) @@ -1371,14 +1729,14 @@ class TestMingFlashOmniPipeline: assert callable(getattr(module, attr)) def test_tts_pipeline_registered(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_tts") + p = resolve_pipeline_config("ming_flash_omni_tts") assert isinstance(p, PipelineConfig) assert p.model_arch == "MingFlashOmniTalkerForConditionalGeneration" assert len(p.stages) == 1 assert p.validate() == [] def test_tts_stage(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_tts") + p = resolve_pipeline_config("ming_flash_omni_tts") assert isinstance(p, PipelineConfig) s = p.get_stage(0) @@ -1405,7 +1763,7 @@ class TestMingFlashOmniPipeline: # We won't test stage 0/1 colocation contract here, # as there could exist more variant of custom device setup - pipeline = StageConfigFactory.resolve_pipeline_config("ming_flash_omni") + pipeline = resolve_pipeline_config("ming_flash_omni") assert isinstance(pipeline, PipelineConfig) stages = merge_pipeline_deploy(pipeline, deploy) assert len(stages) == 2 @@ -1428,14 +1786,14 @@ class TestMingFlashOmniPipeline: assert stages[0].yaml_engine_args["model_arch"] == "MingFlashOmniTalkerForConditionalGeneration" def test_thinker_only_pipeline_registered(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_thinker_only") + p = resolve_pipeline_config("ming_flash_omni_thinker_only") assert isinstance(p, PipelineConfig) assert p.model_arch == "MingFlashOmniForConditionalGeneration" assert len(p.stages) == 1 assert p.validate() == [] def test_thinker_only_stage(self): - p = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_thinker_only") + p = resolve_pipeline_config("ming_flash_omni_thinker_only") assert isinstance(p, PipelineConfig) s = p.get_stage(0) @@ -1460,7 +1818,7 @@ class TestMingFlashOmniPipeline: assert len(deploy.stages) == 1 assert deploy.pipeline == "ming_flash_omni_thinker_only" - pipeline = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_thinker_only") + pipeline = resolve_pipeline_config("ming_flash_omni_thinker_only") assert isinstance(pipeline, PipelineConfig) stages = merge_pipeline_deploy(pipeline, deploy) assert len(stages) == 1 @@ -1474,7 +1832,7 @@ class TestMingFlashOmniPipeline: assert p.validate() == [] def test_image_thinker_stage(self): - s = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_image").get_stage(0) + s = resolve_pipeline_config("ming_flash_omni_image").get_stage(0) assert s.model_stage == "thinker" assert s.execution_type == StageExecutionType.LLM_AR assert s.input_sources == () @@ -1488,7 +1846,7 @@ class TestMingFlashOmniPipeline: assert s.prompt_expand_func is not None def test_image_dit_stage(self): - s = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_image").get_stage(1) + s = resolve_pipeline_config("ming_flash_omni_image").get_stage(1) assert s.model_stage == "dit" assert s.execution_type == StageExecutionType.DIFFUSION assert s.input_sources == (0,) @@ -1500,7 +1858,7 @@ class TestMingFlashOmniPipeline: def test_image_processor_wiring_resolves(self): """The prompt_expand_func and custom_process_input_func strings must point to real callables.""" - pipeline = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_image") + pipeline = resolve_pipeline_config("ming_flash_omni_image") assert isinstance(pipeline, PipelineConfig) thinker = pipeline.get_stage(0) @@ -1523,7 +1881,7 @@ class TestMingFlashOmniPipeline: assert deploy.connectors is not None assert "shared_memory_connector" in deploy.connectors - pipeline = StageConfigFactory.resolve_pipeline_config("ming_flash_omni_image") + pipeline = resolve_pipeline_config("ming_flash_omni_image") assert isinstance(pipeline, PipelineConfig) stages = merge_pipeline_deploy(pipeline, deploy) assert len(stages) == 2 @@ -1680,7 +2038,7 @@ class TestCLIOverrideFlow: pytest.skip("Deploy config not found") deploy = load_deploy_config(deploy_path) - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1698,7 +2056,7 @@ class TestCLIOverrideFlow: pytest.skip("Deploy config not found") deploy = load_deploy_config(deploy_path) - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) @@ -1719,10 +2077,9 @@ class TestAuraOmniDeploy: assert deploy.pipeline == "aura_omni" def test_aura_omni_deploy_resolves_four_native_stages(self): - pipeline_cfg = StageConfigFactory.resolve_pipeline_config("aura_omni") + pipeline_cfg = resolve_pipeline_config("aura_omni") - stages, _ = StageConfigFactory._create_from_registry( - "qwen3_tts", + stages, _ = StageConfigFactory._create_legacy_from_registry( pipeline_cfg, cli_overrides={}, deploy_config_path=str(Path(__file__).parent.parent / "vllm_omni" / "deploy" / "aura_omni.yaml"), @@ -1939,12 +2296,11 @@ class TestSentinelDefaultPrecedence: def _stages(self, cli_overrides): model_type = "qwen3_omni_moe" - pipeline_cfg = StageConfigFactory.resolve_pipeline_config( + pipeline_cfg = resolve_pipeline_config( model_type, Q3_OMNI_ALL_STAGES_HF_CONFIG, ) - stages, _ = StageConfigFactory._create_from_registry( - "qwen3_omni_moe", + stages, _ = StageConfigFactory._create_legacy_from_registry( pipeline_cfg, cli_overrides=cli_overrides, ) @@ -2002,16 +2358,11 @@ class TestSentinelDefaultPrecedence: for stage in stages: assert stage.runtime_overrides == {} - def test_create_from_registry_no_cli_explicit_keys_param(self): - sig = inspect.signature(StageConfigFactory._create_from_registry) - named = [p for p in sig.parameters.values() if p.kind != p.VAR_KEYWORD] - assert "cli_explicit_keys" not in {p.name for p in named} - def test_async_chunk_dispatches_processors(self): """A single ``qwen3_tts`` pipeline picks per-chunk vs end-to-end processors based on ``deploy.async_chunk``, without needing a separate variant pipeline registration.""" - pipeline = StageConfigFactory.resolve_pipeline_config("qwen3_tts") + pipeline = resolve_pipeline_config("qwen3_tts") assert isinstance(pipeline, PipelineConfig) # async_chunk=True → stage 0's per-chunk processor wires up, stage 1 @@ -2082,7 +2433,7 @@ class TestSentinelDefaultPrecedence: called ``_build_talker_inputs`` identically. Merge under either async_chunk mode must not re-introduce a stage-0 full-payload hook.""" - pipeline = StageConfigFactory.resolve_pipeline_config("ming_flash_omni") + pipeline = resolve_pipeline_config("ming_flash_omni") assert isinstance(pipeline, PipelineConfig) stage0, stage1 = pipeline.stages @@ -2120,7 +2471,7 @@ class TestSamplingConstraintsPrecedence: pytest.skip("Deploy config not found") deploy = load_deploy_config(deploy_path) - pipeline = StageConfigFactory.resolve_pipeline_config( + pipeline = resolve_pipeline_config( "qwen3_omni_moe", Q3_OMNI_ALL_STAGES_HF_CONFIG, ) diff --git a/tests/utils/test_tracking_parser.py b/tests/utils/test_tracking_parser.py index 5bee3e3b0..00882744c 100644 --- a/tests/utils/test_tracking_parser.py +++ b/tests/utils/test_tracking_parser.py @@ -49,6 +49,11 @@ _TEST_DEPLOY = DeployConfig( @pytest.fixture() def mock_stages(monkeypatch): """Register a fake pipeline and mock deploy YAML loading.""" + from vllm_omni import platforms + + platform = platforms.current_omni_platform + monkeypatch.setattr(platform, "device_name", "cpu", raising=False) + monkeypatch.setattr(platform, "device_type", "cpu", raising=False) monkeypatch.setitem(OMNI_PIPELINES, _TEST_MODEL, _TEST_PIPELINE) monkeypatch.setattr( "vllm_omni.config.config_factory.load_deploy_config", @@ -584,6 +589,12 @@ def test_cli_overrides_config(tmp_path): ### Integration tests for arg resolution through StageConfigFactory +# +# These tests still target the transitional legacy StageConfig path because the +# current runtime reads explicit CLI values from ``stage.runtime_overrides``. +# After RFC #4021 migrates engine startup to consume ``VllmOmniConfig`` directly, +# these assertions should move to the structured config/runtime boundary and the +# ``_create_legacy_from_registry`` calls should be removed. def test_explicit_cli_arg_reaches_runtime_overrides(mock_stages): """Explicitly passed CLI values reach runtime_overrides on all stages.""" p = TrackingArgumentParser() @@ -591,8 +602,7 @@ def test_explicit_cli_arg_reaches_runtime_overrides(mock_stages): ns = p.parse_args(["--max-num-seqs", "999"]) explicit_kwargs = ns.get_explicit_kwargs_dict() - stages, _ = StageConfigFactory._create_from_registry( - _TEST_MODEL, + stages, _ = StageConfigFactory._create_legacy_from_registry( _TEST_PIPELINE, explicit_kwargs, deploy_config_path=mock_stages, @@ -608,8 +618,7 @@ def test_omitted_default_not_in_runtime_overrides(mock_stages): ns = p.parse_args([]) explicit_kwargs = ns.get_explicit_kwargs_dict() - stages, _ = StageConfigFactory._create_from_registry( - _TEST_MODEL, + stages, _ = StageConfigFactory._create_legacy_from_registry( _TEST_PIPELINE, explicit_kwargs, deploy_config_path=mock_stages, @@ -630,8 +639,7 @@ def test_config_file_args_reach_runtime_overrides(mock_stages): ns = p.parse_args(["--config", "fake.yaml"]) explicit_kwargs = ns.get_explicit_kwargs_dict() - stages, _ = StageConfigFactory._create_from_registry( - _TEST_MODEL, + stages, _ = StageConfigFactory._create_legacy_from_registry( _TEST_PIPELINE, explicit_kwargs, deploy_config_path=mock_stages, @@ -648,8 +656,7 @@ def test_per_stage_override_routes_correctly(mock_stages): ns = p.parse_args(["--stage-0-gpu-memory-utilization", "0.42"]) explicit_kwargs = ns.get_explicit_kwargs_dict() - stages, _ = StageConfigFactory._create_from_registry( - _TEST_MODEL, + stages, _ = StageConfigFactory._create_legacy_from_registry( _TEST_PIPELINE, explicit_kwargs, deploy_config_path=mock_stages, @@ -670,8 +677,7 @@ def test_explicit_args_omitted_from_yaml(mock_stages): ns = p.parse_args(["--enforce-eager"]) explicit_kwargs = ns.get_explicit_kwargs_dict() - stages, _ = StageConfigFactory._create_from_registry( - _TEST_MODEL, + stages, _ = StageConfigFactory._create_legacy_from_registry( _TEST_PIPELINE, explicit_kwargs, deploy_config_path=mock_stages, diff --git a/vllm_omni/config/config_factory.py b/vllm_omni/config/config_factory.py index 1b38a3d34..d43a7a48f 100644 --- a/vllm_omni/config/config_factory.py +++ b/vllm_omni/config/config_factory.py @@ -17,7 +17,8 @@ from vllm.transformers_utils.config import get_config from vllm.transformers_utils.repo_utils import get_hf_file_to_dict from vllm_omni.config.endpoint_policy import EndpointRestriction -from vllm_omni.config.pipeline_registry import OMNI_PIPELINES +from vllm_omni.config.omni_config import VllmOmniConfig +from vllm_omni.config.pipeline_registry import OMNI_PIPELINES, resolve_pipeline_config from vllm_omni.config.stage_config import ( _DEPLOY_DIR, DeployConfig, @@ -197,12 +198,12 @@ class StageConfigFactory: return None @classmethod - @functools.cache def get_pipeline_config( cls, model: str, trust_remote_code: bool, deploy_config_path: str | None = None, + user_deploy_config: DeployConfig | None = None, ) -> PipelineConfig | None: """Resolve the PipelineConfig for a model path/name.""" model_type = cls.try_infer_model_type(model=model, trust_remote_code=trust_remote_code) @@ -210,13 +211,15 @@ class StageConfigFactory: # Resolve the deploy config & check if the user set the pipeline; # If the pipeline is explicitly set, it takes highest priority - deploy_config_pipe = cls._get_deploy_override_pipe_config(hf_config, deploy_config_path) + if user_deploy_config is None: + user_deploy_config = cls._load_user_deploy_config(deploy_config_path) + deploy_config_pipe = cls._get_deploy_override_pipe_config(hf_config, user_deploy_config) if deploy_config_pipe is not None: return deploy_config_pipe # Pipeline isn't set in the yaml spec, so we need infer it ourselves. if model_type and model_type in OMNI_PIPELINES: - pipeline_cfg = cls.resolve_pipeline_config(model_type, hf_config) + pipeline_cfg = resolve_pipeline_config(model_type, hf_config) if pipeline_cfg is not None: return pipeline_cfg @@ -257,78 +260,114 @@ class StageConfigFactory: def _get_deploy_override_pipe_config( cls, hf_config: PretrainedConfig | None, - deploy_config_path: str | None, + deploy_config: DeployConfig | None, ) -> PipelineConfig | None: - """Load the deploy config and extract + resolve its pipeline field.""" - if deploy_config_path is None: + """Resolve an explicit pipeline override from a loaded deploy config.""" + if deploy_config is None or deploy_config.pipeline is None: return None + pipeline_cfg = resolve_pipeline_config(deploy_config.pipeline, hf_config) + if pipeline_cfg is None: + raise KeyError( + f"Pipeline {deploy_config.pipeline!r} from deploy config is not registered " + f"to OMNI_PIPELINES. Available: {sorted(OMNI_PIPELINES)}" + ) + return pipeline_cfg + + @staticmethod + def _load_user_deploy_config(deploy_config_path: str | None) -> DeployConfig | None: + """Load an explicit deploy YAML once for resolution and construction.""" + if deploy_config_path is None: + return None deploy_path = Path(deploy_config_path) - if deploy_path.exists(): - deploy_cfg = load_deploy_config(deploy_path) - if deploy_cfg.pipeline is not None: - return cls.resolve_pipeline_config(deploy_cfg.pipeline, hf_config) - return None + if not deploy_path.exists() and deploy_path.parent == Path("."): + candidate = _DEPLOY_DIR / deploy_path + if candidate.exists(): + deploy_path = candidate + if not deploy_path.exists(): + raise FileNotFoundError(f"Deploy config not found: {deploy_path}") + return load_deploy_config(deploy_path) @classmethod def create_from_model( cls, model: str, *, - trust_remote_code: bool = False, - cli_overrides: dict[str, Any] | None = None, - deploy_config_path: str | None = None, - strategy_specs: Mapping[Any, Any] | None = None, - **deprecated_kwargs: Any, - ) -> tuple[list[StageConfig] | None, str | None]: - """Load pipeline + deploy config, merge with CLI overrides. - - Checks OMNI_PIPELINES first, since supported models should be explicitly - registered. If a model is not registered in OMNI_PIPELINES, tries to fall - back to using the Transformers config & finding pipelines that have overlapping - supported architectures. - - When ``strategy_specs`` is provided (a mapping of role -> list of - ``StrategySpec``), the derived parallel sizing is overlaid onto the - merged stages (see ``vllm_omni.config.composable_parallel``). This is - opt-in: omitting it leaves the existing merge path untouched. - - Returns ``(stages, omni_lb_policy)``: the merged stages (``None`` when the - model is not in the pipeline registry and the caller should fall back to - the legacy YAML path) and the strategy-derived, pipeline-wide - ``omni_lb_policy`` (``None`` when no stage_replica axis set one). The - policy is returned rather than threaded through a mutable out-param so the - engine can apply it without every intermediate call carrying the dict. - """ - if cli_overrides is None: - cli_overrides = {} - + trust_remote_code: bool, + cli_overrides: dict[str, Any], + deploy_config_path: str | None, + ) -> VllmOmniConfig | None: + """Build the structured Omni config for a model/deploy pair.""" + user_deploy_config = cls._load_user_deploy_config(deploy_config_path) pipeline_cfg = cls.get_pipeline_config( model=model, trust_remote_code=trust_remote_code, deploy_config_path=deploy_config_path, + user_deploy_config=user_deploy_config, + ) + if pipeline_cfg is None: + return None + + registry_cli_overrides = { + **cli_overrides, + "trust_remote_code": trust_remote_code, + "model": model, + } + return VllmOmniConfig.from_pipeline_config( + pipeline_cfg, + user_deploy_config=user_deploy_config, + deploy_config_path=deploy_config_path, + cli_overrides=registry_cli_overrides, ) - if pipeline_cfg is not None: - return cls._create_from_registry( - pipeline_cfg.model_type, - pipeline_cfg, - cli_overrides, - deploy_config_path, - strategy_specs=strategy_specs, - ) - return None, None @classmethod - def _create_from_registry( + def create_legacy_stage_configs_from_model( + cls, + model: str, + *, + trust_remote_code: bool, + cli_overrides: dict[str, Any], + deploy_config_path: str | None, + strategy_specs: Mapping[Any, Any] | None = None, + ) -> tuple[list[StageConfig] | None, str | None]: + """Build current runtime stage configs from the shared resolution. + + The engine still consumes the legacy StageConfig/OmegaConf shape. + RFC #4021 will replace this transitional path as runtime consumers move + to VllmOmniConfig. + """ + user_deploy_config = cls._load_user_deploy_config(deploy_config_path) + pipeline_cfg = cls.get_pipeline_config( + model=model, + trust_remote_code=trust_remote_code, + deploy_config_path=deploy_config_path, + user_deploy_config=user_deploy_config, + ) + if pipeline_cfg is None: + return None, None + + legacy_cli_overrides = { + **cli_overrides, + "trust_remote_code": trust_remote_code, + } + return cls._create_legacy_from_registry( + pipeline_cfg, + legacy_cli_overrides, + deploy_config_path, + user_deploy_config=user_deploy_config, + strategy_specs=strategy_specs, + ) + + @classmethod + def _create_legacy_from_registry( cls, - model_type: str, pipeline_cfg: PipelineConfig, cli_overrides: dict[str, Any], deploy_config_path: str | None = None, + user_deploy_config: DeployConfig | None = None, strategy_specs: Mapping[Any, Any] | None = None, - **deprecated_kwargs: Any, ) -> tuple[list[StageConfig], str | None]: - """Create StageConfigs from pipeline registry + deploy YAML. + """Create current runtime StageConfigs from registry + deploy YAML. Precedence: caller-typed (non-None) value > deploy YAML > StageDeployConfig dataclass default. @@ -337,30 +376,15 @@ class StageConfigFactory: load-balancer policy (``None`` when no strategy set one) travels with the stages instead of through a mutable out-param. """ - # Resolve deploy config path - if deploy_config_path is None: - deploy_path = _DEPLOY_DIR / f"{model_type}.yaml" + if user_deploy_config is not None: + deploy_cfg = user_deploy_config + elif deploy_config_path is not None: + deploy_cfg = cls._load_user_deploy_config(deploy_config_path) + assert deploy_cfg is not None + elif pipeline_cfg.default_deploy_config_name is not None: + deploy_cfg = load_deploy_config(_DEPLOY_DIR / pipeline_cfg.default_deploy_config_name) else: - deploy_path = Path(deploy_config_path) - - if not deploy_path.exists(): - logger.warning( - "Deploy config not found: %s — using pipeline defaults only", - deploy_path, - ) deploy_cfg = DeployConfig() - else: - deploy_cfg = load_deploy_config(deploy_path) - # Fallback to using the deploy config pipeline class if it's a mismatch - if deploy_cfg.pipeline and deploy_cfg.pipeline != model_type: - resolved = cls.resolve_pipeline_config(deploy_cfg.pipeline) - if resolved is None: - raise KeyError( - f"Pipeline {deploy_cfg.pipeline!r} from {deploy_path.name!r} " - f"not found in OMNI_PIPELINES. Available: " - f"{sorted(OMNI_PIPELINES.keys())}" - ) - pipeline_cfg = resolved cli_async_chunk = cli_overrides.get("async_chunk") if cli_async_chunk is not None: @@ -565,13 +589,3 @@ class StageConfigFactory: ``filter_dataclass_kwargs(OmniEngineArgs, ...)``. """ return build_stage_runtime_overrides(stage.stage_id, cli_overrides) - - @staticmethod - def resolve_pipeline_config(model_type: str, hf_config: PretrainedConfig | None = None) -> PipelineConfig | None: - """Given a model type, resolve to the pipeline to be used. If the pipeline - maps to a callable we resolve based on the HF config.""" - if model_type not in OMNI_PIPELINES: - logger.warning("Model type %s is not registered to OMNI_PIPELINES", model_type) - return None - obj = OMNI_PIPELINES[model_type] - return obj(hf_config) if callable(obj) else obj diff --git a/vllm_omni/config/omni_config.py b/vllm_omni/config/omni_config.py index 256dcd5c7..4c2154401 100644 --- a/vllm_omni/config/omni_config.py +++ b/vllm_omni/config/omni_config.py @@ -2,9 +2,10 @@ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project """Structured vLLM-Omni configuration classes. -This module is additive for Phase 2 of RFC #4021. ``VllmOmniConfig.from_registry`` -builds the structured view directly from the pipeline registry and deploy config -so parity can be proven before later PRs cut consumers over to these classes. +This module is additive for Phase 2 of RFC #4021. +``VllmOmniConfig.from_pipeline_config`` builds the structured view from an +already-resolved pipeline and deploy config so parity can be proven before +later PRs cut consumers over to these classes. """ from __future__ import annotations @@ -215,10 +216,7 @@ def _stage_cli_overrides(stage_id: int, cli_overrides: Mapping[str, Any]) -> dic return result -def _resolve_deploy_path(model_type: str, deploy_config_path: str | None = None) -> Path: - if deploy_config_path is None: - return _DEPLOY_DIR / f"{model_type}.yaml" - +def _resolve_deploy_path(deploy_config_path: str) -> Path: deploy_path = Path(deploy_config_path) if not deploy_path.exists() and deploy_path.parent == Path("."): bare_name = deploy_path.name @@ -230,17 +228,27 @@ def _resolve_deploy_path(model_type: str, deploy_config_path: str | None = None) return deploy_path -def _registered_pipeline_keys() -> list[str]: - from vllm_omni.config.pipeline_registry import OMNI_PIPELINES +def _get_deploy_config( + pipeline_cfg: PipelineConfig, + user_deploy_config: DeployConfig | None, + deploy_config_path: str | None, +) -> tuple[DeployConfig, str | None]: + """Select user-provided, pipeline-default, or empty deploy settings.""" + if user_deploy_config is not None: + loaded_path = str(_resolve_deploy_path(deploy_config_path)) if deploy_config_path is not None else None + return copy.deepcopy(user_deploy_config), loaded_path - return sorted(OMNI_PIPELINES) + if deploy_config_path is not None: + resolved_path = _resolve_deploy_path(deploy_config_path) + if not resolved_path.exists(): + raise FileNotFoundError(f"Deploy config not found: {resolved_path}") + return load_deploy_config(resolved_path), str(resolved_path) + if pipeline_cfg.default_deploy_config_name is not None: + default_path = _DEPLOY_DIR / pipeline_cfg.default_deploy_config_name + return load_deploy_config(default_path), str(default_path) -def _resolve_registered_pipeline(model_type: str) -> PipelineConfig | None: - from vllm_omni.config.pipeline_registry import OMNI_PIPELINES - - registered = OMNI_PIPELINES[model_type] - return registered(None) if callable(registered) else registered + return DeployConfig(), None @config @@ -1225,52 +1233,41 @@ class VllmOmniConfig: raise KeyError(f"no stage {stage_id}") @classmethod - def from_registry( + def from_pipeline_config( cls, - model_type: str, + pipeline_cfg: PipelineConfig, + *, + user_deploy_config: DeployConfig | None = None, deploy_config_path: str | None = None, cli_overrides: dict[str, Any] | None = None, ) -> VllmOmniConfig: - """Create a structured config from a registered pipeline and deploy YAML.""" + """Create a structured config from a resolved pipeline and deploy YAML.""" if cli_overrides is None: cli_overrides = {} - deploy_path = _resolve_deploy_path(model_type, deploy_config_path) - loaded_deploy_config_path = str(deploy_path) if deploy_path.exists() else None - if loaded_deploy_config_path is not None: - deploy = load_deploy_config(deploy_path) - else: - deploy = DeployConfig() + deploy, loaded_deploy_config_path = _get_deploy_config( + pipeline_cfg, + user_deploy_config, + deploy_config_path, + ) - pipeline_key = deploy.pipeline or model_type - if pipeline_key not in _registered_pipeline_keys(): - raise KeyError( - f"Pipeline {pipeline_key!r} not in registry " - f"(resolved from {deploy_path.name!r}). Available: " - f"{_registered_pipeline_keys()}" - ) - pipeline = _resolve_registered_pipeline(pipeline_key) - if pipeline is None: - raise ValueError( - f"Pipeline {pipeline_key!r} did not resolve to a concrete PipelineConfig without an HF config" - ) - - deploy_for_registry = copy.deepcopy(deploy) if cli_overrides.get("async_chunk") is not None: - deploy_for_registry.async_chunk = bool(cli_overrides["async_chunk"]) + deploy.async_chunk = bool(cli_overrides["async_chunk"]) for name in _PIPELINE_DEPLOY_CLI_FIELDS: if cli_overrides.get(name) is not None: - setattr(deploy_for_registry, name, _copy_value(cli_overrides[name])) + setattr(deploy, name, _copy_value(cli_overrides[name])) - deploy_for_registry = _apply_platform_overrides(deploy_for_registry) - _validate_async_chunk_support(pipeline, deploy_for_registry) - deploy_by_id = {stage.stage_id: stage for stage in deploy_for_registry.stages} + deploy = _apply_platform_overrides(deploy) + if len(pipeline_cfg.stages) <= 1: + deploy.async_chunk = False + _validate_async_chunk_support(pipeline_cfg, deploy) + deploy_by_id = {stage.stage_id: stage for stage in deploy.stages} model = cli_overrides.get("model") stage_configs = tuple( _build_stage_config( - pipeline, - deploy_for_registry, + pipeline_cfg, + deploy, topology, deploy_by_id.get(topology.stage_id), _stage_engine_values( @@ -1279,7 +1276,7 @@ class VllmOmniConfig: ), model=model, ) - for topology in pipeline.stages + for topology in pipeline_cfg.stages ) orchestrator_config = VllmOmniOrchestratorConfig( @@ -1287,7 +1284,7 @@ class VllmOmniConfig: **_orchestrator_cli_overrides(cli_overrides), ) return cls( - pipeline_config=pipeline, + pipeline_config=pipeline_cfg, stage_configs=stage_configs, orchestrator_config=orchestrator_config, ) diff --git a/vllm_omni/config/pipeline_registry.py b/vllm_omni/config/pipeline_registry.py index 934d15d18..98468ff81 100644 --- a/vllm_omni/config/pipeline_registry.py +++ b/vllm_omni/config/pipeline_registry.py @@ -157,3 +157,15 @@ def register_pipeline(pipeline: PipelineConfig | PipelineResolverFunc, model_typ if errors: logger.warning("Registration for pipeline of type %s produced the following issues: %s", model_type, errors) OMNI_PIPELINES[model_type] = pipeline + + +def resolve_pipeline_config( + model_type: str, + hf_config: PretrainedConfig | None = None, +) -> PipelineConfig | None: + """Resolve a registry key to a concrete pipeline config.""" + if model_type not in OMNI_PIPELINES: + logger.warning("Model type %s is not registered to OMNI_PIPELINES", model_type) + return None + pipeline = OMNI_PIPELINES[model_type] + return pipeline(hf_config) if callable(pipeline) else pipeline diff --git a/vllm_omni/config/stage_config.py b/vllm_omni/config/stage_config.py index 6b8a09423..367e1b317 100644 --- a/vllm_omni/config/stage_config.py +++ b/vllm_omni/config/stage_config.py @@ -270,6 +270,9 @@ class PipelineConfig: # multi-component repos (e.g. GLM-Image). ``None`` = not a diffusers model. diffusers_class_name: str | None = None endpoint_restrictions: tuple[EndpointRestriction, ...] = () + # Bundled deploy defaults for this concrete pipeline topology. The file is + # loaded from vllm_omni/deploy; None uses DeployConfig defaults. + default_deploy_config_name: str | None = None def get_stage(self, stage_id: int) -> StagePipelineConfig | None: """Look up a stage by its ID.""" diff --git a/vllm_omni/engine/async_omni_engine.py b/vllm_omni/engine/async_omni_engine.py index ce9662884..fd3d63d7e 100644 --- a/vllm_omni/engine/async_omni_engine.py +++ b/vllm_omni/engine/async_omni_engine.py @@ -289,7 +289,11 @@ class AsyncOmniEngine: ) kwargs["trust_remote_code"] = trust_remote_code - self.config_path, self.stage_configs = self._resolve_stage_configs(model, kwargs) + self.config_path, self.stage_configs = self._resolve_stage_configs( + model, + kwargs, + trust_remote_code=trust_remote_code, + ) self.num_stages = len(self.stage_configs) stage0_args = getattr(self.stage_configs[0], "engine_args", None) if self.num_stages > 0 else None @@ -1156,7 +1160,13 @@ class AsyncOmniEngine: ) self._omni_lb_policy = str(derived) - def _resolve_stage_configs(self, model: str, kwargs: dict[str, Any]) -> tuple[str, list[Any]]: + def _resolve_stage_configs( + self, + model: str, + kwargs: dict[str, Any], + *, + trust_remote_code: bool, + ) -> tuple[str, list[Any]]: """Resolve stage configs and inject defaults shared by orchestrator/headless.""" stage_configs_path = kwargs.get("stage_configs_path", None) @@ -1182,6 +1192,7 @@ class AsyncOmniEngine: model, stage_configs_path, base_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, stage_overrides=stage_overrides, diff --git a/vllm_omni/entrypoints/cli/serve.py b/vllm_omni/entrypoints/cli/serve.py index c1ca73e44..71483445a 100644 --- a/vllm_omni/entrypoints/cli/serve.py +++ b/vllm_omni/entrypoints/cli/serve.py @@ -824,6 +824,7 @@ def run_headless(args: TrackingNamespace) -> None: model, stage_configs_path, args_dict, + trust_remote_code=bool(getattr(args, "trust_remote_code", False)), deploy_config_path=args_dict.get("deploy_config"), stage_overrides=stage_overrides, strategy_config_path=args_dict.get("strategy_config"), diff --git a/vllm_omni/entrypoints/omni_base.py b/vllm_omni/entrypoints/omni_base.py index 304070dfa..d8b2c0291 100644 --- a/vllm_omni/entrypoints/omni_base.py +++ b/vllm_omni/entrypoints/omni_base.py @@ -144,7 +144,7 @@ class OmniBase(PDDisaggregationMixin): log_stats = kwargs.pop("log_stats", False) self._enable_ar_profiler = kwargs.pop("enable_ar_profiler", False) # NOTE: read-only lookup — must NOT pop. Popping here drops the key - # before it reaches ``StageConfigFactory._create_from_registry``, so + # before it reaches ``StageConfigFactory._create_legacy_from_registry``, so # ``--no-async-chunk`` (``async_chunk=False``) silently fails to # override the deploy YAML's ``async_chunk: true`` default. async_chunk = kwargs.get("async_chunk") diff --git a/vllm_omni/entrypoints/utils.py b/vllm_omni/entrypoints/utils.py index a2c598b31..6d32f4ab1 100644 --- a/vllm_omni/entrypoints/utils.py +++ b/vllm_omni/entrypoints/utils.py @@ -314,6 +314,8 @@ def resolve_model_config_path(model: str) -> str: def load_stage_configs_from_model( model: str, + *, + trust_remote_code: bool, base_engine_args: dict | None = None, deploy_config_path: str | None = None, stage_overrides: dict[str, dict[str, Any]] | None = None, @@ -322,13 +324,14 @@ def load_stage_configs_from_model( """Load stage configurations from model's default config file. For models registered in the pipeline registry (new path), uses - ``StageConfigFactory.create_from_model()`` which merges + ``StageConfigFactory.create_legacy_stage_configs_from_model()`` which merges PipelineConfig + DeployConfig + CLI overrides. For other models (legacy path), loads stage configs from YAML. Args: model: Model name or path (used to determine model_type) + trust_remote_code: Whether to trust remote code while resolving the model config. base_engine_args: Base engine args to merge as CLI overrides. deploy_config_path: Optional explicit deploy config path. stage_overrides: Per-stage overrides from --stage-overrides. @@ -346,20 +349,25 @@ def load_stage_configs_from_model( base_engine_args = {} cli_overrides = _convert_dataclasses_to_dict(dict(base_engine_args)) + cli_overrides["trust_remote_code"] = trust_remote_code if stage_overrides: for stage_id_str, overrides in stage_overrides.items(): for key, val in overrides.items(): cli_overrides[f"stage_{stage_id_str}_{key}"] = val + # Current runtime initialization still consumes legacy OmegaConf stage + # configs. ``StageConfigFactory.create_from_model`` now produces the + # structured ``VllmOmniConfig`` object; future RFC #4021 changes will + # migrate the engine/runtime consumers and replace this legacy resolver. strategy_specs = None if strategy_config_path is not None: from vllm_omni.config.composable_parallel.strategy_loader import load_strategy_specs strategy_specs = load_strategy_specs(strategy_config_path) - stages, omni_lb_policy = StageConfigFactory.create_from_model( + stages, omni_lb_policy = StageConfigFactory.create_legacy_stage_configs_from_model( model, - trust_remote_code=cli_overrides.get("trust_remote_code", False), + trust_remote_code=trust_remote_code, cli_overrides=cli_overrides, deploy_config_path=deploy_config_path, strategy_specs=strategy_specs, @@ -550,6 +558,8 @@ def load_and_resolve_stage_configs( model: str, stage_configs_path: str | None, kwargs: dict | None, + *, + trust_remote_code: bool, default_stage_cfg_factory: Any = None, deploy_config_path: str | None = None, stage_overrides: dict[str, dict[str, Any]] | None = None, @@ -561,6 +571,7 @@ def load_and_resolve_stage_configs( 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). @@ -606,6 +617,7 @@ def load_and_resolve_stage_configs( 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, @@ -621,6 +633,7 @@ def load_and_resolve_stage_configs( 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, diff --git a/vllm_omni/model_executor/models/aura_omni/pipeline.py b/vllm_omni/model_executor/models/aura_omni/pipeline.py index 273589e1b..14aa5ff30 100644 --- a/vllm_omni/model_executor/models/aura_omni/pipeline.py +++ b/vllm_omni/model_executor/models/aura_omni/pipeline.py @@ -24,6 +24,7 @@ _QWEN3_TTS_PROC = "vllm_omni.model_executor.stage_input_processors.qwen3_tts" AURA_OMNI_PIPELINE = PipelineConfig( model_type="aura_omni", + default_deploy_config_name="aura_omni.yaml", model_arch="Qwen3ASRForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/bagel/pipeline.py b/vllm_omni/model_executor/models/bagel/pipeline.py index c68a531c2..b0544222f 100644 --- a/vllm_omni/model_executor/models/bagel/pipeline.py +++ b/vllm_omni/model_executor/models/bagel/pipeline.py @@ -27,6 +27,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.bagel" BAGEL_PIPELINE = PipelineConfig( model_type="bagel", + default_deploy_config_name="bagel.yaml", model_arch="OmniBagelForConditionalGeneration", hf_architectures=("BagelForConditionalGeneration",), stages=( @@ -63,6 +64,7 @@ BAGEL_PIPELINE = PipelineConfig( BAGEL_THINK_PIPELINE = PipelineConfig( model_type="bagel_think", + default_deploy_config_name="bagel_think.yaml", model_arch="OmniBagelForConditionalGeneration", hf_architectures=(), stages=( @@ -96,6 +98,7 @@ BAGEL_THINK_PIPELINE = PipelineConfig( BAGEL_SINGLE_STAGE_PIPELINE = PipelineConfig( model_type="bagel_single_stage", + default_deploy_config_name="bagel_single_stage.yaml", model_arch="BagelForConditionalGeneration", hf_architectures=(), stages=( diff --git a/vllm_omni/model_executor/models/cosyvoice3/pipeline.py b/vllm_omni/model_executor/models/cosyvoice3/pipeline.py index a8d51fdfe..413dc1dc3 100644 --- a/vllm_omni/model_executor/models/cosyvoice3/pipeline.py +++ b/vllm_omni/model_executor/models/cosyvoice3/pipeline.py @@ -22,6 +22,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.cosyvoice3" COSYVOICE3_PIPELINE = PipelineConfig( model_type="cosyvoice3", + default_deploy_config_name="cosyvoice3.yaml", model_arch="CosyVoice3Model", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/covo_audio/pipeline.py b/vllm_omni/model_executor/models/covo_audio/pipeline.py index f604bc57d..c2e1911ef 100644 --- a/vllm_omni/model_executor/models/covo_audio/pipeline.py +++ b/vllm_omni/model_executor/models/covo_audio/pipeline.py @@ -17,6 +17,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.covo_audio" COVO_AUDIO_PIPELINE = PipelineConfig( model_type="covo_audio", + default_deploy_config_name="covo_audio.yaml", model_arch="CovoAudioForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/dreamzero/pipeline.py b/vllm_omni/model_executor/models/dreamzero/pipeline.py index bc815dedc..58ba5b24d 100644 --- a/vllm_omni/model_executor/models/dreamzero/pipeline.py +++ b/vllm_omni/model_executor/models/dreamzero/pipeline.py @@ -10,6 +10,7 @@ from vllm_omni.config.stage_config import ( DREAMZERO_PIPELINE = PipelineConfig( model_type="dreamzero", + default_deploy_config_name="dreamzero.yaml", model_arch="DreamZeroPipeline", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/dynin_omni/pipeline.py b/vllm_omni/model_executor/models/dynin_omni/pipeline.py index db0c51e35..584b6ab5c 100644 --- a/vllm_omni/model_executor/models/dynin_omni/pipeline.py +++ b/vllm_omni/model_executor/models/dynin_omni/pipeline.py @@ -23,6 +23,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.dynin_omni" DYNIN_OMNI_PIPELINE = PipelineConfig( model_type="dynin_omni", + default_deploy_config_name="dynin_omni.yaml", model_arch="DyninOmniForConditionalGeneration", # Arch-fallback safety net: route by hf_config.architectures when the # auto-detected model_type does not match the registry key exactly. diff --git a/vllm_omni/model_executor/models/fish_speech/pipeline.py b/vllm_omni/model_executor/models/fish_speech/pipeline.py index baee1b5e7..1cb113094 100644 --- a/vllm_omni/model_executor/models/fish_speech/pipeline.py +++ b/vllm_omni/model_executor/models/fish_speech/pipeline.py @@ -20,6 +20,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.fish_speech" FISH_SPEECH_PIPELINE = PipelineConfig( model_type="fish_qwen3_omni", + default_deploy_config_name="fish_qwen3_omni.yaml", model_arch="FishSpeechSlowARForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/glm_image/pipeline.py b/vllm_omni/model_executor/models/glm_image/pipeline.py index bd041292c..fd74bfad8 100644 --- a/vllm_omni/model_executor/models/glm_image/pipeline.py +++ b/vllm_omni/model_executor/models/glm_image/pipeline.py @@ -14,6 +14,7 @@ from vllm_omni.config.stage_config import ( GLM_IMAGE_PIPELINE = PipelineConfig( model_type="glm_image", + default_deploy_config_name="glm_image.yaml", model_arch="GlmImageForConditionalGeneration", hf_architectures=("GlmImageForConditionalGeneration",), diffusers_class_name="GlmImagePipeline", diff --git a/vllm_omni/model_executor/models/glm_tts/pipeline.py b/vllm_omni/model_executor/models/glm_tts/pipeline.py index f016a993e..2a1fedd3f 100644 --- a/vllm_omni/model_executor/models/glm_tts/pipeline.py +++ b/vllm_omni/model_executor/models/glm_tts/pipeline.py @@ -12,6 +12,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.glm_tts" GLM_TTS_PIPELINE = PipelineConfig( model_type="glm_tts", + default_deploy_config_name="glm_tts.yaml", model_arch="GLMTTSForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/gr00t/pipeline.py b/vllm_omni/model_executor/models/gr00t/pipeline.py index 38a76f297..a2e4c8133 100644 --- a/vllm_omni/model_executor/models/gr00t/pipeline.py +++ b/vllm_omni/model_executor/models/gr00t/pipeline.py @@ -10,6 +10,7 @@ from vllm_omni.config.stage_config import ( GR00T_N1D7_PIPELINE = PipelineConfig( model_type="Gr00tN1d7", + default_deploy_config_name="Gr00tN1d7.yaml", model_arch="Gr00tN1d7Pipeline", hf_architectures=("Gr00tN1d7",), stages=( diff --git a/vllm_omni/model_executor/models/higgs_audio_v2/pipeline.py b/vllm_omni/model_executor/models/higgs_audio_v2/pipeline.py index fb6832569..1d3db52d6 100644 --- a/vllm_omni/model_executor/models/higgs_audio_v2/pipeline.py +++ b/vllm_omni/model_executor/models/higgs_audio_v2/pipeline.py @@ -12,6 +12,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.higgs_audio_v2" HIGGS_AUDIO_V2_PIPELINE = PipelineConfig( model_type="higgs_audio_v2", + default_deploy_config_name="higgs_audio_v2.yaml", model_arch="HiggsAudioV2ForConditionalGeneration", hf_architectures=("HiggsAudioV2ForConditionalGeneration",), stages=( diff --git a/vllm_omni/model_executor/models/higgs_audio_v3/pipeline.py b/vllm_omni/model_executor/models/higgs_audio_v3/pipeline.py index 2f35c497e..9bfc411df 100644 --- a/vllm_omni/model_executor/models/higgs_audio_v3/pipeline.py +++ b/vllm_omni/model_executor/models/higgs_audio_v3/pipeline.py @@ -24,6 +24,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.higgs_audio_v3" HIGGS_AUDIO_V3_PIPELINE = PipelineConfig( model_type="higgs_multimodal_qwen3", + default_deploy_config_name="higgs_multimodal_qwen3.yaml", model_arch="HiggsAudioV3TalkerForConditionalGeneration", hf_architectures=("HiggsMultimodalQwen3ForConditionalGeneration",), stages=( diff --git a/vllm_omni/model_executor/models/hunyuan_image3/pipeline.py b/vllm_omni/model_executor/models/hunyuan_image3/pipeline.py index 06ebff9c7..fd53043bd 100644 --- a/vllm_omni/model_executor/models/hunyuan_image3/pipeline.py +++ b/vllm_omni/model_executor/models/hunyuan_image3/pipeline.py @@ -18,6 +18,7 @@ _HUNYUAN_IMAGE3_INPUT_PROCESSOR = "vllm_omni.model_executor.stage_input_processo HUNYUAN_IMAGE3_PIPELINE = PipelineConfig( model_type="hunyuan_image_3_moe", + default_deploy_config_name="hunyuan_image_3_moe.yaml", model_arch=_HUNYUAN_IMAGE3_MODEL_ARCH, hf_architectures=_HUNYUAN_IMAGE3_HF_ARCHS, stages=( @@ -50,6 +51,7 @@ HUNYUAN_IMAGE3_PIPELINE = PipelineConfig( HUNYUAN_IMAGE3_AR_PIPELINE = PipelineConfig( model_type="hunyuan_image3_ar", + default_deploy_config_name="hunyuan_image3_ar.yaml", model_arch=_HUNYUAN_IMAGE3_MODEL_ARCH, hf_architectures=(), stages=( @@ -71,6 +73,7 @@ HUNYUAN_IMAGE3_AR_PIPELINE = PipelineConfig( HUNYUAN_IMAGE3_DIT_PIPELINE = PipelineConfig( model_type="hunyuan_image3_dit", + default_deploy_config_name="hunyuan_image3_dit.yaml", model_arch=_HUNYUAN_IMAGE3_MODEL_ARCH, hf_architectures=(), stages=( diff --git a/vllm_omni/model_executor/models/indextts2/pipeline.py b/vllm_omni/model_executor/models/indextts2/pipeline.py index 2ce02cee8..50f866ffd 100644 --- a/vllm_omni/model_executor/models/indextts2/pipeline.py +++ b/vllm_omni/model_executor/models/indextts2/pipeline.py @@ -16,6 +16,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.indextts2" INDEXTTS2_PIPELINE = PipelineConfig( model_type="indextts2", + default_deploy_config_name="indextts2.yaml", model_arch="IndexTTS2TalkerForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/lance/pipeline.py b/vllm_omni/model_executor/models/lance/pipeline.py index 777f8942d..126a23acb 100644 --- a/vllm_omni/model_executor/models/lance/pipeline.py +++ b/vllm_omni/model_executor/models/lance/pipeline.py @@ -15,6 +15,7 @@ from vllm_omni.config.stage_config import ( LANCE_PIPELINE = PipelineConfig( model_type="lance", + default_deploy_config_name="lance.yaml", model_arch="LancePipeline", hf_architectures=(), stages=( diff --git a/vllm_omni/model_executor/models/mammoth_moda2/pipeline.py b/vllm_omni/model_executor/models/mammoth_moda2/pipeline.py index be35eb562..830c63aff 100644 --- a/vllm_omni/model_executor/models/mammoth_moda2/pipeline.py +++ b/vllm_omni/model_executor/models/mammoth_moda2/pipeline.py @@ -18,6 +18,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.mammoth_moda2" MAMMOTH_MODA2_PIPELINE = PipelineConfig( model_type="mammoth_moda2", + default_deploy_config_name="mammoth_moda2.yaml", model_arch="MammothModa2ForConditionalGeneration", hf_architectures=("Mammothmoda2Model", "MammothModa2ForConditionalGeneration"), stages=( @@ -49,6 +50,7 @@ MAMMOTH_MODA2_PIPELINE = PipelineConfig( # Single-stage AR variant for understanding / summarization tasks MAMMOTH_MODA2_AR_PIPELINE = PipelineConfig( model_type="mammoth_moda2_ar", + default_deploy_config_name="mammoth_moda2_ar.yaml", model_arch="MammothModa2ForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/mimo_audio/pipeline.py b/vllm_omni/model_executor/models/mimo_audio/pipeline.py index 9de6575d9..9e54979de 100644 --- a/vllm_omni/model_executor/models/mimo_audio/pipeline.py +++ b/vllm_omni/model_executor/models/mimo_audio/pipeline.py @@ -23,6 +23,7 @@ _IM_END_TOKEN_ID = 151645 MIMO_AUDIO_PIPELINE = PipelineConfig( model_type="mimo_audio", + default_deploy_config_name="mimo_audio.yaml", # HF ``architectures: ["MiMoAudioModel"]`` is also the registry key in # ``model_executor/models/registry.py``; it resolves to the internal # class ``MiMoAudioForConditionalGeneration`` in ``mimo_audio.py``. diff --git a/vllm_omni/model_executor/models/ming_flash_omni/pipeline.py b/vllm_omni/model_executor/models/ming_flash_omni/pipeline.py index e416a117e..da8c24843 100644 --- a/vllm_omni/model_executor/models/ming_flash_omni/pipeline.py +++ b/vllm_omni/model_executor/models/ming_flash_omni/pipeline.py @@ -21,6 +21,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.ming_flash_omni" MING_FLASH_OMNI_PIPELINE = PipelineConfig( model_type="ming_flash_omni", + default_deploy_config_name="ming_flash_omni.yaml", model_arch="MingFlashOmniForConditionalGeneration", # Upstream HF config applies model_type="bailingmm_moe_v2_lite" # (the thinker sub-config name) rather than "ming_flash_omni". @@ -66,6 +67,7 @@ MING_FLASH_OMNI_PIPELINE = PipelineConfig( # Standalone TTS variant: talker only. MING_FLASH_OMNI_TTS_PIPELINE = PipelineConfig( model_type="ming_flash_omni_tts", + default_deploy_config_name="ming_flash_omni_tts.yaml", model_arch="MingFlashOmniTalkerForConditionalGeneration", stages=( StagePipelineConfig( @@ -87,6 +89,7 @@ MING_FLASH_OMNI_TTS_PIPELINE = PipelineConfig( # Thinker-only variant: multimodal understanding with text output MING_FLASH_OMNI_THINKER_ONLY_PIPELINE = PipelineConfig( model_type="ming_flash_omni_thinker_only", + default_deploy_config_name="ming_flash_omni_thinker_only.yaml", model_arch="MingFlashOmniForConditionalGeneration", stages=( StagePipelineConfig( @@ -109,6 +112,7 @@ MING_FLASH_OMNI_THINKER_ONLY_PIPELINE = PipelineConfig( # Thinker + image-generation (diffusion) variant: text-to-image / img2img. MING_FLASH_OMNI_IMAGE_PIPELINE = PipelineConfig( model_type="ming_flash_omni_image", + default_deploy_config_name="ming_flash_omni_image.yaml", model_arch="MingFlashOmniForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/ming_tts/pipeline.py b/vllm_omni/model_executor/models/ming_tts/pipeline.py index 762dcf6fd..86eecd41e 100644 --- a/vllm_omni/model_executor/models/ming_tts/pipeline.py +++ b/vllm_omni/model_executor/models/ming_tts/pipeline.py @@ -12,6 +12,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.ming_tts" MING_TTS_PIPELINE = PipelineConfig( model_type="ming_tts", + default_deploy_config_name="ming_tts.yaml", model_arch="MingTTSForConditionalGeneration", hf_architectures=("MingTTSForConditionalGeneration", "BailingMMNativeForConditionalGeneration"), # The dense (0.5B) and MoE (16.8B) share architectures=["BailingMMNativeForConditionalGeneration"] @@ -63,6 +64,7 @@ MING_TTS_PIPELINE = PipelineConfig( # Keep this for auto-detection of deploy config yaml. MING_TTS_MOE_PIPELINE = PipelineConfig( model_type="ming_tts_moe", + default_deploy_config_name="ming_tts_moe.yaml", model_arch="MingTTSForConditionalGeneration", hf_architectures=("MingTTSForConditionalGeneration", "BailingMMNativeForConditionalGeneration"), # Disambiguate from the dense-0.5B ming_tts pipeline diff --git a/vllm_omni/model_executor/models/minicpmo_4_5/pipeline.py b/vllm_omni/model_executor/models/minicpmo_4_5/pipeline.py index 701e014e6..f966555d0 100644 --- a/vllm_omni/model_executor/models/minicpmo_4_5/pipeline.py +++ b/vllm_omni/model_executor/models/minicpmo_4_5/pipeline.py @@ -22,6 +22,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.minicpmo_4_5_omni" MINICPMO_4_5_PIPELINE = PipelineConfig( model_type="minicpmo_4_5", + default_deploy_config_name="minicpmo_4_5.yaml", model_arch="MiniCPMO45OmniForConditionalGeneration", # MiniCPM-o 4.5's HF config.json reports `model_type="minicpmo"` and # `architectures=["MiniCPMO"]` — both shared verbatim with older MiniCPM-o diff --git a/vllm_omni/model_executor/models/moss_tts/pipeline.py b/vllm_omni/model_executor/models/moss_tts/pipeline.py index c6cf5c86c..254bc18fe 100644 --- a/vllm_omni/model_executor/models/moss_tts/pipeline.py +++ b/vllm_omni/model_executor/models/moss_tts/pipeline.py @@ -22,6 +22,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.moss_tts" MOSS_TTS_PIPELINE = PipelineConfig( model_type="moss_tts", + default_deploy_config_name="moss_tts.yaml", model_arch="MossTTSDelayModel", # HF architectures string stages=( StagePipelineConfig( @@ -53,6 +54,7 @@ MOSS_TTS_PIPELINE = PipelineConfig( MOSS_TTS_REALTIME_PIPELINE = PipelineConfig( model_type="moss_tts_realtime", + default_deploy_config_name="moss_tts_realtime.yaml", model_arch="MossTTSRealtime", # different talker class from the delay variant stages=( StagePipelineConfig( @@ -82,6 +84,7 @@ MOSS_TTS_REALTIME_PIPELINE = PipelineConfig( MOSS_TTS_LOCAL_PIPELINE = PipelineConfig( model_type="moss_tts_local", + default_deploy_config_name="moss_tts_local.yaml", model_arch="MossTTSLocalModel", # different talker class: GPT2-style local depth transformer stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/moss_tts_nano/pipeline.py b/vllm_omni/model_executor/models/moss_tts_nano/pipeline.py index 749c4f980..c089ea90e 100644 --- a/vllm_omni/model_executor/models/moss_tts_nano/pipeline.py +++ b/vllm_omni/model_executor/models/moss_tts_nano/pipeline.py @@ -18,6 +18,7 @@ from vllm_omni.config.stage_config import ( MOSS_TTS_NANO_PIPELINE = PipelineConfig( model_type="moss_tts_nano", + default_deploy_config_name="moss_tts_nano.yaml", model_arch="MossTTSNanoForCausalLM", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/omnivoice/pipeline.py b/vllm_omni/model_executor/models/omnivoice/pipeline.py index 88d479459..760d5314f 100644 --- a/vllm_omni/model_executor/models/omnivoice/pipeline.py +++ b/vllm_omni/model_executor/models/omnivoice/pipeline.py @@ -14,6 +14,7 @@ from vllm_omni.config.stage_config import ( OMNIVOICE_PIPELINE = PipelineConfig( model_type="omnivoice", + default_deploy_config_name="omnivoice.yaml", model_arch="OmniVoicePipeline", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/qwen2_5_omni/pipeline.py b/vllm_omni/model_executor/models/qwen2_5_omni/pipeline.py index b4ed8efee..4ce5ef207 100644 --- a/vllm_omni/model_executor/models/qwen2_5_omni/pipeline.py +++ b/vllm_omni/model_executor/models/qwen2_5_omni/pipeline.py @@ -17,6 +17,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.qwen2_5_omni" QWEN2_5_OMNI_PIPELINE = PipelineConfig( model_type="qwen2_5_omni", + default_deploy_config_name="qwen2_5_omni.yaml", model_arch="Qwen2_5OmniForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/qwen3_omni/pipeline.py b/vllm_omni/model_executor/models/qwen3_omni/pipeline.py index ca3a09f89..9baecdd3e 100644 --- a/vllm_omni/model_executor/models/qwen3_omni/pipeline.py +++ b/vllm_omni/model_executor/models/qwen3_omni/pipeline.py @@ -21,6 +21,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.qwen3_omni" QWEN3_OMNI_PIPELINE = PipelineConfig( model_type="qwen3_omni_moe", + default_deploy_config_name="qwen3_omni_moe.yaml", model_arch="Qwen3OmniMoeForConditionalGeneration", endpoint_restrictions=( EndpointRestriction( diff --git a/vllm_omni/model_executor/models/qwen3_tts/pipeline.py b/vllm_omni/model_executor/models/qwen3_tts/pipeline.py index 7f50931dd..8a68e63cd 100644 --- a/vllm_omni/model_executor/models/qwen3_tts/pipeline.py +++ b/vllm_omni/model_executor/models/qwen3_tts/pipeline.py @@ -15,6 +15,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.qwen3_tts" QWEN3_TTS_PIPELINE = PipelineConfig( model_type="qwen3_tts", + default_deploy_config_name="qwen3_tts.yaml", # Pipeline-level default; the code2wav stage overrides per-stage below. model_arch="Qwen3TTSTalkerForConditionalGeneration", stages=( diff --git a/vllm_omni/model_executor/models/voxcpm2/pipeline.py b/vllm_omni/model_executor/models/voxcpm2/pipeline.py index 0579ebc8c..c92cf61ba 100644 --- a/vllm_omni/model_executor/models/voxcpm2/pipeline.py +++ b/vllm_omni/model_executor/models/voxcpm2/pipeline.py @@ -15,6 +15,7 @@ from vllm_omni.config.stage_config import ( VOXCPM2_PIPELINE = PipelineConfig( model_type="voxcpm2", + default_deploy_config_name="voxcpm2.yaml", model_arch="VoxCPM2TalkerForConditionalGeneration", stages=( StagePipelineConfig( diff --git a/vllm_omni/model_executor/models/voxtral_tts/pipeline.py b/vllm_omni/model_executor/models/voxtral_tts/pipeline.py index 3bc2743c6..e6bc712fe 100644 --- a/vllm_omni/model_executor/models/voxtral_tts/pipeline.py +++ b/vllm_omni/model_executor/models/voxtral_tts/pipeline.py @@ -16,6 +16,7 @@ _PROC = "vllm_omni.model_executor.stage_input_processors.voxtral_tts" VOXTRAL_TTS_PIPELINE = PipelineConfig( model_type="voxtral_tts", + default_deploy_config_name="voxtral_tts.yaml", model_arch="VoxtralTTSForConditionalGeneration", stages=( StagePipelineConfig(