[Bugfix][CI] Vendor Qwen3-TTS and CosyVoice3 reference audio fixtures (#3267)

Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
This commit is contained in:
Yueqian Lin authored and GitHub committed 2026-04-30 08:44:05 +08:00
1 parent b9789091e9
commit b1ff695029
10 files changed
+53 -11

No files matched your search

+1
View File
@@ -27,6 +27,7 @@
*.mov binary *.mov binary
*.mp4 binary *.mp4 binary
*.mp3 binary *.mp3 binary
*.wav binary
*.flv binary *.flv binary
*.fla binary *.fla binary
*.swf binary *.swf binary
+2
View File
@@ -264,6 +264,8 @@ tmp_test
vllm_omni/_version.py vllm_omni/_version.py
# output files # output files
*.wav *.wav
# Vendored test reference audio (small, intentionally checked in)
!tests/assets/**/*.wav
.worktrees/ .worktrees/
# CI overlay yamls materialized from tests/utils.py:_CI_OVERLAYS at test time # CI overlay yamls materialized from tests/utils.py:_CI_OVERLAYS at test time
tests/.ci_generated/ tests/.ci_generated/
Binary file not shown.
Binary file not shown.
@@ -13,10 +13,8 @@ verifies a stable reference recipe:
from __future__ import annotations from __future__ import annotations
import functools import functools
import io
import os import os
from pathlib import Path from pathlib import Path
from urllib.request import urlopen
import numpy as np import numpy as np
import pytest import pytest
@@ -25,6 +23,7 @@ from huggingface_hub import snapshot_download
from vllm.sampling_params import SamplingParams from vllm.sampling_params import SamplingParams
from tests.helpers.mark import hardware_test from tests.helpers.mark import hardware_test
from tests.helpers.media import test_asset_path
from tests.helpers.runtime import OmniRunner from tests.helpers.runtime import OmniRunner
from tests.helpers.stage_config import get_deploy_config_path from tests.helpers.stage_config import get_deploy_config_path
from vllm_omni.model_executor.models.cosyvoice3.config import CosyVoice3Config from vllm_omni.model_executor.models.cosyvoice3.config import CosyVoice3Config
@@ -33,7 +32,9 @@ from vllm_omni.model_executor.models.cosyvoice3.tokenizer import get_qwen_tokeni
MODEL = "FunAudioLLM/Fun-CosyVoice3-0.5B-2512" MODEL = "FunAudioLLM/Fun-CosyVoice3-0.5B-2512"
MODEL_DIR_ENV = "VLLM_OMNI_COSYVOICE3_MODEL_DIR" MODEL_DIR_ENV = "VLLM_OMNI_COSYVOICE3_MODEL_DIR"
REFERENCE_PROMPT_WAV_URL = "https://raw.githubusercontent.com/FunAudioLLM/CosyVoice/main/asset/zero_shot_prompt.wav" # Vendored under tests/assets/cosyvoice3/ so the test does not depend on
# raw.githubusercontent.com being reachable from CI runners.
REFERENCE_PROMPT_WAV_PATH = test_asset_path("cosyvoice3/zero_shot_prompt.wav")
REFERENCE_PROMPT_TEXT = "You are a helpful assistant.<|endofprompt|>希望你以后能够做的比我还好呦。" REFERENCE_PROMPT_TEXT = "You are a helpful assistant.<|endofprompt|>希望你以后能够做的比我还好呦。"
REFERENCE_SYNTH_TEXT = ( REFERENCE_SYNTH_TEXT = (
"CosyVoice is undergoing a comprehensive upgrade, providing more accurate, " "CosyVoice is undergoing a comprehensive upgrade, providing more accurate, "
@@ -51,9 +52,7 @@ ASYNC_CHUNK_MODES = [
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
def _load_reference_prompt_wav() -> tuple[np.ndarray, int]: def _load_reference_prompt_wav() -> tuple[np.ndarray, int]:
with urlopen(REFERENCE_PROMPT_WAV_URL, timeout=30) as resp: audio, sr = sf.read(str(REFERENCE_PROMPT_WAV_PATH), dtype="float32", always_2d=False)
data = resp.read()
audio, sr = sf.read(io.BytesIO(data), dtype="float32", always_2d=False)
if isinstance(audio, np.ndarray) and audio.ndim > 1: if isinstance(audio, np.ndarray) and audio.ndim > 1:
audio = np.mean(audio, axis=-1) audio = np.mean(audio, axis=-1)
return np.asarray(audio, dtype=np.float32), int(sr) return np.asarray(audio, dtype=np.float32), int(sr)
@@ -16,10 +16,12 @@ os.environ["VLLM_TEST_CLEAN_GPU_MEMORY"] = "0"
import pytest import pytest
from tests.helpers.mark import hardware_test from tests.helpers.mark import hardware_test
from tests.helpers.media import load_test_audio_data_url
from tests.helpers.stage_config import get_deploy_config_path, modify_stage_config from tests.helpers.stage_config import get_deploy_config_path, modify_stage_config
MODEL = "Qwen/Qwen3-TTS-12Hz-0.6B-Base" MODEL = "Qwen/Qwen3-TTS-12Hz-0.6B-Base"
REF_AUDIO_URL = "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone_2.wav" # See tests/e2e/online_serving/test_qwen3_tts_base.py for the vendored-asset rationale.
REF_AUDIO_URL = load_test_audio_data_url("qwen3_tts/clone_2.wav")
REF_TEXT = "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it! And thanks to you." REF_TEXT = "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it! And thanks to you."
@@ -15,13 +15,16 @@ os.environ["VLLM_TEST_CLEAN_GPU_MEMORY"] = "0"
import pytest import pytest
from tests.helpers.mark import hardware_test from tests.helpers.mark import hardware_test
from tests.helpers.media import load_test_audio_data_url
from tests.helpers.runtime import OmniServerParams from tests.helpers.runtime import OmniServerParams
from tests.helpers.stage_config import get_deploy_config_path from tests.helpers.stage_config import get_deploy_config_path
MODEL = "FunAudioLLM/Fun-CosyVoice3-0.5B-2512" MODEL = "FunAudioLLM/Fun-CosyVoice3-0.5B-2512"
# Official CosyVoice zero-shot prompt audio and its transcript # Official CosyVoice zero-shot prompt audio and its transcript. Vendored under
REF_AUDIO_URL = "https://raw.githubusercontent.com/FunAudioLLM/CosyVoice/main/asset/zero_shot_prompt.wav" # tests/assets/ so the server does not depend on raw.githubusercontent.com being
# reachable at request time (same rationale as issue #3263 for Qwen3-TTS).
REF_AUDIO_URL = load_test_audio_data_url("cosyvoice3/zero_shot_prompt.wav")
REF_TEXT = "希望你以后能够做的比我还好呦。" REF_TEXT = "希望你以后能够做的比我还好呦。"
@@ -15,12 +15,16 @@ os.environ["VLLM_TEST_CLEAN_GPU_MEMORY"] = "0"
import pytest import pytest
from tests.helpers.mark import hardware_test from tests.helpers.mark import hardware_test
from tests.helpers.media import load_test_audio_data_url
from tests.helpers.runtime import OmniServerParams from tests.helpers.runtime import OmniServerParams
from tests.helpers.stage_config import get_deploy_config_path from tests.helpers.stage_config import get_deploy_config_path
MODEL = "Qwen/Qwen3-TTS-12Hz-0.6B-Base" MODEL = "Qwen/Qwen3-TTS-12Hz-0.6B-Base"
REF_AUDIO_URL = "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone_2.wav" # Vendored under tests/assets/qwen3_tts/clone_2.wav so the server does not need
# to fetch the reference audio over HTTPS at request time (CI runners in some
# regions cannot reach the original Aliyun OSS host; see issue #3263).
REF_AUDIO_URL = load_test_audio_data_url("qwen3_tts/clone_2.wav")
REF_TEXT = "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it! And thanks to you." REF_TEXT = "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it! And thanks to you."
@@ -12,6 +12,7 @@ import os
import pytest import pytest
from tests.helpers.mark import hardware_test from tests.helpers.mark import hardware_test
from tests.helpers.media import load_test_audio_data_url
from tests.helpers.runtime import OmniServerParams from tests.helpers.runtime import OmniServerParams
from tests.helpers.stage_config import get_deploy_config_path from tests.helpers.stage_config import get_deploy_config_path
@@ -22,7 +23,8 @@ os.environ["VLLM_TEST_CLEAN_GPU_MEMORY"] = "0"
MODEL = "Qwen/Qwen3-TTS-12Hz-0.6B-Base" MODEL = "Qwen/Qwen3-TTS-12Hz-0.6B-Base"
REF_AUDIO_URL = "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen3-TTS-Repo/clone_2.wav" # See tests/e2e/online_serving/test_qwen3_tts_base.py for the vendored-asset rationale.
REF_AUDIO_URL = load_test_audio_data_url("qwen3_tts/clone_2.wav")
REF_TEXT = "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it! And thanks to you." REF_TEXT = "Okay. Yeah. I resent you. I love you. I respect you. But you know what? You blew it! And thanks to you."
+29
View File
@@ -496,6 +496,33 @@ def generate_synthetic_image(
} }
_TEST_ASSETS_ROOT = Path(__file__).resolve().parents[1] / "assets"
_AUDIO_MIME_BY_SUFFIX = {
".wav": "audio/wav",
".mp3": "audio/mpeg",
".flac": "audio/flac",
".ogg": "audio/ogg",
}
def test_asset_path(relative_path: str | os.PathLike) -> Path:
"""Resolve a path under ``tests/assets/`` to its absolute on-disk location."""
return _TEST_ASSETS_ROOT / Path(relative_path)
def load_test_audio_data_url(relative_path: str | os.PathLike) -> str:
"""Load a vendored test audio file under ``tests/assets/`` as a base64 data URL.
Used by tests that need real reference audio (e.g. voice cloning) without
relying on the server's ability to fetch external URLs at request time.
"""
path = test_asset_path(relative_path)
mime = _AUDIO_MIME_BY_SUFFIX.get(path.suffix.lower(), "application/octet-stream")
encoded = base64.b64encode(path.read_bytes()).decode("ascii")
return f"data:{mime};base64,{encoded}"
def decode_b64_image(b64: str): def decode_b64_image(b64: str):
img = Image.open(io.BytesIO(base64.b64decode(b64))) img = Image.open(io.BytesIO(base64.b64decode(b64)))
img.load() img.load()
@@ -653,5 +680,7 @@ __all__ = [
"generate_synthetic_audio", "generate_synthetic_audio",
"generate_synthetic_image", "generate_synthetic_image",
"generate_synthetic_video", "generate_synthetic_video",
"load_test_audio_data_url",
"preprocess_text", "preprocess_text",
"test_asset_path",
] ]