From f46c958b67a1f8440414ea3abfdb6137d5f855cd Mon Sep 17 00:00:00 2001 From: Zhou Taichang Date: Fri, 25 Sep 2026 08:41:03 +0800 Subject: [PATCH] [Bugfix][Qwen3-Omni] Restore the talker sampling seed (#7474) Signed-off-by: tzhouam Signed-off-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com> --- tests/config/test_config_factory.py | 22 ++++++++++++++++++++++ vllm_omni/deploy/qwen3_omni_moe.yaml | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/tests/config/test_config_factory.py b/tests/config/test_config_factory.py index 7f9f51eaa..6c4b5c7ab 100644 --- a/tests/config/test_config_factory.py +++ b/tests/config/test_config_factory.py @@ -2857,6 +2857,28 @@ class TestPlatformOverrides: config = deploy.stages[0].compilation_config or {} assert "+rotary_embedding" not in config.get("custom_ops", []) + def test_qwen3_omni_talker_sampling_is_seeded(self): + """The only stochastic Qwen3-Omni stage must stay reproducible. + + Dropping this seed (as #4986 did) leaves the talker sampling codec + tokens unseeded at temperature 0.9, which reopened the audio-vs-text + nightly failures in #6090. Pin it so a cleanup cannot remove it again + without failing here. + """ + deploy_path = Path(get_deploy_config_path("qwen3_omni_moe.yaml")) + pipeline = resolve_pipeline_config( + "qwen3_omni_moe", + Q3_OMNI_ALL_STAGES_HF_CONFIG, + ) + assert isinstance(pipeline, PipelineConfig) + + for platform in ("cpu", "cuda", "musa", "npu", "rocm", "xpu"): + deploy = _apply_platform_overrides(load_deploy_config(deploy_path), platform=platform) + stages = merge_pipeline_deploy(pipeline, deploy) + talker_sampling = stages[1].yaml_extras["default_sampling_params"] + assert talker_sampling["temperature"] > 0.0, "talker is expected to sample, not decode greedily" + assert talker_sampling.get("seed") == 42, f"talker sampling lost its seed on {platform}" + def test_minicpmo_4_5_cuda_caps_talker_kv_cache(self): pipeline = resolve_pipeline_config("minicpmo_4_5") assert isinstance(pipeline, PipelineConfig) diff --git a/vllm_omni/deploy/qwen3_omni_moe.yaml b/vllm_omni/deploy/qwen3_omni_moe.yaml index 151e90ad6..1ed8b8cd6 100644 --- a/vllm_omni/deploy/qwen3_omni_moe.yaml +++ b/vllm_omni/deploy/qwen3_omni_moe.yaml @@ -46,6 +46,16 @@ stages: input_connectors: from_stage_0: connector_of_shared_memory default_sampling_params: + # The talker is the only stochastic stage (the thinker and code2wav both + # decode greedily at temperature 0). Without a seed, every request draws + # fresh codec tokens, and a small fraction of draws derail into fluent + # nonsense part-way through an utterance while the thinker text stays + # correct -- the audio-vs-text nightly failures in #6090. This seed was + # present from the original deploy schema and was dropped as collateral + # in #4986; keep it so Qwen3-Omni speech is reproducible for a given + # request. It bounds sampling only: it does not make the talker immune + # to the underlying degeneration, which is tracked separately. + seed: 42 temperature: 0.9 top_k: 50 max_tokens: 4096