diff --git a/tests/engine/test_single_stage_mode.py b/tests/engine/test_single_stage_mode.py index 675a0650b..fcce14350 100644 --- a/tests/engine/test_single_stage_mode.py +++ b/tests/engine/test_single_stage_mode.py @@ -16,7 +16,7 @@ from pytest_mock import MockerFixture from vllm.v1.engine.utils import EngineZmqAddresses from vllm_omni.config.config_factory import StageConfigFactory -from vllm_omni.config.stage_config import DuplexSessionRuntimeConfig +from vllm_omni.config.stage_config import DuplexSessionRuntimeConfig, PipelineConfig, StagePipelineConfig from vllm_omni.engine.async_omni_engine import AsyncOmniEngine from vllm_omni.engine.stage_engine_core_client import StageEngineCoreClientBase from vllm_omni.engine.stage_engine_startup import ( @@ -378,6 +378,7 @@ class TestSingleStageModeDetection: stage_cfgs: list[Any] | None = None, resolved_config_path: str = "/fake/path", patch_deploy_config: bool = True, + resolved_pipeline: PipelineConfig | None = None, **kwargs: Any, ) -> AsyncOmniEngine: mock_stage_configs = stage_cfgs or [_make_stage_cfg(0)] @@ -392,10 +393,19 @@ class TestSingleStageModeDetection: "vllm_omni.engine.async_omni_engine.load_deploy_config", return_value=SimpleNamespace(duplex_session=DuplexSessionRuntimeConfig()), ) + + def resolve_stage_configs(engine, *args, **kwargs): + if resolved_pipeline is not None: + engine._config_resolution = SimpleNamespace( + pipeline_config=resolved_pipeline, config_path=resolved_config_path + ) + return resolved_config_path, mock_stage_configs + mocker.patch.object( AsyncOmniEngine, "_resolve_stage_configs", - return_value=(resolved_config_path, mock_stage_configs), + autospec=True, + side_effect=resolve_stage_configs, ) mocker.patch.object(AsyncOmniEngine, "_bootstrap_orchestrator") mock_thread_cls = mocker.patch("threading.Thread") @@ -420,6 +430,17 @@ class TestSingleStageModeDetection: ) assert engine.single_stage_mode is True + @pytest.mark.parametrize("model_type, expected", [("qwen3_tts", True), ("qwen3_omni_moe", False)]) + def test_event_driven_default_uses_resolved_pipeline(self, mocker, model_type, expected): + pipeline = PipelineConfig( + model_type=model_type, + stages=(StagePipelineConfig(stage_id=0, model_stage="a", final_output=True),), + ) + engine = self._make_engine_no_thread(mocker, resolved_pipeline=pipeline) + + StageConfigFactory.get_pipeline_config.assert_not_called() + assert engine._event_driven_orch_default is expected + def test_stage_id_kwarg_promotes_to_single_stage_mode(self, mocker: MockerFixture): engine = self._make_engine_no_thread( mocker, diff --git a/vllm_omni/engine/async_omni_engine.py b/vllm_omni/engine/async_omni_engine.py index 2abcfed0c..f0e5ab72d 100644 --- a/vllm_omni/engine/async_omni_engine.py +++ b/vllm_omni/engine/async_omni_engine.py @@ -215,32 +215,6 @@ class AsyncOmniEngine: # retaining the richer resolver result for pipeline-wide settings. # Overrides of that private seam fall back to the factory below. deploy_config_path = kwargs.get("deploy_config") - # ``trust_remote_code`` is tri-state (bool | None): ``None`` means "not - # specified" so stage-config resolution can defer to the deploy yaml's - # per-stage value (see ``with_trust_remote_code_override``). The - # restriction path below loads the top-level HF config via vLLM's - # ``get_config``, which needs a real bool, so collapse ``None`` to the - # default ``False`` here (#5495). - pipeline_config = StageConfigFactory.get_pipeline_config( - model=model, - trust_remote_code=bool(trust_remote_code), - deploy_config_path=deploy_config_path, - ) - self.endpoint_restrictions = pipeline_config.endpoint_restrictions if pipeline_config is not None else () - self._duplex_runtime_extension_path = ( - pipeline_config.duplex_runtime_extension if pipeline_config is not None else None - ) - self.duplex_serving_adapter_path = ( - pipeline_config.duplex_serving_adapter if pipeline_config is not None else None - ) - self._duplex_control_enabled = bool(pipeline_config and pipeline_config.duplex_control_enabled) - self._event_driven_orch_default = _event_driven_orch_default_for_pipeline( - pipeline_config.model_type if pipeline_config is not None else None - ) - self.duplex_session_config = DuplexSessionRuntimeConfig() - if deploy_config_path is not None: - self.duplex_session_config = load_deploy_config(deploy_config_path).duplex_session - # Tri-state: None means "not specified" — the deploy yaml's per-stage # trust_remote_code stays in effect. An explicit True/False here is a # global override (precedence: caller > deploy yaml > default False); @@ -1043,6 +1017,9 @@ class AsyncOmniEngine: pipeline_config.duplex_serving_adapter if pipeline_config is not None else None ) self._duplex_control_enabled = bool(pipeline_config and pipeline_config.duplex_control_enabled) + self._event_driven_orch_default = _event_driven_orch_default_for_pipeline( + pipeline_config.model_type if pipeline_config is not None else None + ) self.duplex_session_config = DuplexSessionRuntimeConfig() if config_path is not None: self.duplex_session_config = load_deploy_config(config_path).duplex_session