[CI][ROCm] Shorten the blocking CosyVoice test path (#7748)

Signed-off-by: andyluo7 <andy.luo@amd.com>
This commit is contained in:
andyluo7 authored and GitHub committed 2026-09-21 18:34:40 -07:00
1 parent cb31e0774f
commit 1586c41ae4
6 files changed
+111 -12

No files matched your search

+18
View File
@@ -64,3 +64,21 @@ steps:
commands:
- export VLLM_ROCM_USE_AITER=1
- pytest -s -v tests/e2e/online_serving/test_qwen3_omni_expansion.py::test_text_to_text_audio_001 -m "full_model and rocm and MI325 and cards_2" --run-level "full_model"
- group: ":speaker: CosyVoice3 MI300 Nightly"
steps:
# Exercise all sync/async and Chinese/English cases with the platform's
# default attention backend. On ROCm this retains end-to-end AITER
# coverage without putting its cold JIT build on the READY critical path.
- label: "CosyVoice3-TTS E2E Test"
agent_pool: mi300_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: NonBlocking
timeout_in_minutes: 90
retry:
automatic:
- exit_status: 134
limit: 1
commands:
- pytest -s -v tests/e2e/online_serving/test_cosyvoice3_tts_expansion.py -m "slow" --run-level "core_model"
+6 -2
View File
@@ -328,7 +328,10 @@ steps:
- export VLLM_ALLOW_LONG_MAX_MODEL_LEN="1"
- timeout 30m pytest -s -v tests/e2e/online_serving/test_qwen3_tts_customvoice.py -m "core_model" --run-level "core_model"
- label: "CosyVoice3-TTS E2E Test"
# Keep a fast model/server integration check in the blocking lane. The
# full default-backend suite remains in the nightly pipeline because a
# cold ROCm AITER MHA build can add more than 25 minutes to this job.
- label: "CosyVoice3-TTS E2E Smoke (SDPA)"
agent_pool: mi300_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
@@ -340,7 +343,8 @@ steps:
- exit_status: 134
limit: 1
commands:
- pytest -s -v tests/e2e/online_serving/test_cosyvoice3_tts_expansion.py -m "slow" --run-level "core_model"
- export DIFFUSION_ATTENTION_BACKEND=TORCH_SDPA
- pytest -s -v tests/e2e/online_serving/test_cosyvoice3_tts_expansion.py::test_voice_clone_zh_002 -m "slow" --run-level "core_model"
# TODO: Bagel test on ROCm is very unstable. @tjtanaa
# Need to debug before reneable numerical changes across large PRs
+22 -2
View File
@@ -67,11 +67,31 @@ def test_ready_diffusion_cpu_suite_is_sharded() -> None:
assert "--shard-id=$$BUILDKITE_PARALLEL_JOB" in pytest_command
def test_cosyvoice_gpu_abort_gets_one_fresh_job_retry() -> None:
step = _find_step("CosyVoice3-TTS E2E Test", AMD_READY_PIPELINE)
def test_cosyvoice_ready_smoke_uses_sdpa() -> None:
step = _find_step("CosyVoice3-TTS E2E Smoke (SDPA)", AMD_READY_PIPELINE)
assert step["grade"] == "Blocking"
assert step["retry"] == {"automatic": [{"exit_status": 134, "limit": 1}]}
assert "export DIFFUSION_ATTENTION_BACKEND=TORCH_SDPA" in step["commands"]
pytest_command = next(command for command in step["commands"] if "pytest" in command)
assert "tests/e2e/online_serving/test_cosyvoice3_tts_expansion.py::test_voice_clone_zh_002" in pytest_command
def test_cosyvoice_full_default_backend_suite_runs_nightly() -> None:
step = _find_step("CosyVoice3-TTS E2E Test", AMD_NIGHTLY_PIPELINE)
assert step["grade"] == "NonBlocking"
assert step["timeout_in_minutes"] == 90
assert step["retry"] == {"automatic": [{"exit_status": 134, "limit": 1}]}
assert all("DIFFUSION_ATTENTION_BACKEND" not in command for command in step["commands"])
pytest_command = next(command for command in step["commands"] if "pytest" in command)
assert "tests/e2e/online_serving/test_cosyvoice3_tts_expansion.py" in pytest_command
assert "::" not in pytest_command
def test_amd_template_preserves_step_retry_policy() -> None:
template = AMD_TEMPLATE.read_text(encoding="utf-8")
# Both grouped and top-level AMD steps must preserve an explicit retry
# policy when the source suite is rendered into the uploaded pipeline.
@@ -260,6 +260,37 @@ class TestDiTAttention:
assert attention.to_k.out_features == 512
assert attention.to_v.out_features == 512
def test_embedded_dit_honors_diffusion_attention_backend(self, monkeypatch):
"""The LLM-hosted code2wav DiT must consume the diffusion env override."""
from vllm_omni.diffusion.config import get_current_diffusion_config_or_none
from vllm_omni.model_executor.models.cosyvoice3.cosyvoice3_code2wav import _build_dit_estimator
monkeypatch.setenv("DIFFUSION_ATTENTION_BACKEND", "TORCH_SDPA")
estimator = _build_dit_estimator(
{
"dim": 32,
"depth": 1,
"heads": 2,
"dim_head": 16,
"dropout": 0.0,
"ff_mult": 2,
"mel_dim": 8,
"mu_dim": 8,
"spk_dim": 4,
"out_channels": 8,
"static_chunk_size": 4,
"num_decoding_left_chunks": 1,
}
)
attention = estimator.transformer_blocks[0].attn.attn
assert attention.backend_pref == "TORCH_SDPA"
assert attention.backend_explicit is True
assert attention.attn_backend is not None
assert attention.attn_backend.get_name() == "SDPA"
assert get_current_diffusion_config_or_none() is None
@pytest.mark.core_model
@pytest.mark.cpu
def test_forward_passes_2d_mask_without_casting_qkv(self):
@@ -39,7 +39,10 @@ CHUNK_LEN = 24
TOTAL_MEL = 96
SPM = 480 # samples per mel frame for the small test HiFT (8*5*3*4), matching the real model
LONG_TOTAL_MEL = 400 # long enough that the 64-frame window actually truncates
WINDOW_LEN = 64
# Six chunks exercise three updates after history exceeds the bounded window,
# without spending minutes recomputing an unnecessarily long synthetic signal.
LONG_TOTAL_MEL = 144
PCM16_LSB = 1.0 / 32767.0 # smallest representable PCM16 difference
# Worst observed deviation is 1.97e-7 (~2x float32 eps); atol=1e-6 keeps 5x headroom.
@@ -189,16 +192,19 @@ def test_incremental_hift_bounded_window_is_close(config):
def test_incremental_hift_matches_streaming_reference_within_tolerance(config):
"""Windowed output matches the full-cumulative reference within float32 rounding.
The load-bearing correctness test: 400 mel frames far exceeds the 64-frame
window, so truncation genuinely exercises the phase/noise carry. Float64
agrees to ~1e-16, confirming the residual here is rounding, not truncation.
The load-bearing correctness test crosses the 64-frame window repeatedly,
so truncation genuinely exercises the phase/noise carry. Float64 agrees to
~1e-16, confirming the residual here is rounding, not truncation.
"""
hift = _make_hift(config)
model = _make_model(hift, window_len=64) # the real _hift_window_len
model = _make_model(hift, window_len=WINDOW_LEN) # the real _hift_window_len
chunks = _chunks(total_mel=LONG_TOTAL_MEL)
trim = int(hift.f0_predictor.condnet[0].causal_padding)
assert LONG_TOTAL_MEL > 64 + trim + CHUNK_LEN # window must actually truncate
truncating_steps = sum(
history_len > WINDOW_LEN + trim for history_len in range(CHUNK_LEN, LONG_TOTAL_MEL, CHUNK_LEN)
)
assert truncating_steps >= 3 # exercise repeated truncation, not only the boundary
full = _full_reference(model, chunks)
incr = _incremental(model, chunks)
@@ -10,7 +10,8 @@ This module contains the code2wav (token-to-waveform) stage which uses:
"""
from collections import Counter
from typing import TypedDict, cast
from collections.abc import Mapping
from typing import Any, TypedDict, cast
import numpy as np
import torch
@@ -18,6 +19,8 @@ import torch.nn as nn
from omegaconf import DictConfig
from vllm.logger import init_logger
from vllm_omni.diffusion.config import get_current_diffusion_config_or_none, set_current_diffusion_config
from vllm_omni.diffusion.data import OmniDiffusionConfig
from vllm_omni.diffusion.models.cosyvoice3_audio.cosyvoice3_dit import DiT
from vllm_omni.model_executor.models.cosyvoice3.code2wav_core.cfm import (
CausalConditionalCFM,
@@ -37,6 +40,23 @@ from vllm_omni.transformers_utils.configs.cosyvoice3 import CosyVoice3Config
logger = init_logger(__name__)
def _build_dit_estimator(estimator_config: Mapping[str, Any]) -> DiT:
"""Build CosyVoice's embedded DiT with diffusion backend configuration.
The code2wav model runs inside an LLM-generation worker, so it does not go
through the diffusion model loader that normally installs an
``OmniDiffusionConfig`` during model construction. Install a local default
config when needed so ``DIFFUSION_ATTENTION_BACKEND`` and the platform
default are resolved by the same path as standalone diffusion models.
"""
if get_current_diffusion_config_or_none() is not None:
return DiT(**estimator_config)
diffusion_config = OmniDiffusionConfig()
with set_current_diffusion_config(diffusion_config):
return DiT(**estimator_config)
class StreamingFlowItem(TypedDict, total=False):
"""One entry of the batched-streaming item list passed to forward_streaming_batch."""
@@ -71,7 +91,7 @@ class CosyVoice3Code2Wav(nn.Module):
cfm_params = DictConfig(decoder_cfg["cfm_params"])
# DiT estimator using diffusion attention (Flash/Sage/SDPA backends)
estimator = DiT(**decoder_cfg["estimator"])
estimator = _build_dit_estimator(decoder_cfg["estimator"])
decoder = CausalConditionalCFM(
in_channels=decoder_cfg["in_channels"],