[Bugfix][TTS] Fix Audio8 voice clone reference resolution (#7815)

Signed-off-by: liuyihua <i@liuyihua.com>
This commit is contained in:
liuyihua95 authored and GitHub committed 2026-09-21 19:13:03 -07:00
1 parent 5152b9bf4c
commit 36cdeb8fda
2 files changed
+39 -2

No files matched your search

@@ -13,15 +13,21 @@ model (falling through to "unknown TTS model") instead of failing at startup.
These tests pin the contract from both ends.
"""
import asyncio
from dataclasses import dataclass
import pytest
from vllm_omni.entrypoints.openai.protocol.audio import OpenAICreateSpeechRequest
from vllm_omni.entrypoints.openai.tts_adapters import (
SpeechServingContext,
all_tts_model_types,
all_tts_stage_keys,
detect_tts_model_type,
resolve_adapter,
)
from vllm_omni.entrypoints.openai.tts_adapters.audio8_tts import Audio8TTSAdapter
from vllm_omni.entrypoints.openai.tts_adapters.base import conditioning_cache_salt
from vllm_omni.model_executor.models.audio8_tts.pipeline import AUDIO8_TTS_PIPELINE
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
@@ -29,6 +35,15 @@ pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
SLOW_AR_STAGE_KEY = "audio8_tts_slow_ar"
@dataclass
class _FakeSpeechServer:
_tts_executor: object | None = None
async def _resolve_ref_audio(self, ref_audio: str) -> tuple[list[float], int, str]:
assert ref_audio == "data:audio/wav;base64,UklGRg=="
return [0.0, 0.1], 44_100, "cache-key"
def _pipeline_stage_keys() -> list[str]:
return [stage.model_stage for stage in AUDIO8_TTS_PIPELINE.stages]
@@ -83,3 +98,24 @@ def test_apply_sampling_overrides_defaults_tts_local_seed_for_fast_ar():
# No seed at all -> nothing injected (non-deterministic by request contract).
out_unseeded = adapter.apply_sampling_overrides([SamplingParams(seed=None)], request)
assert (out_unseeded[0].extra_args or {}).get("tts_local_seed") is None
def test_build_accepts_ref_audio_resolver_cache_key(monkeypatch):
"""The shared resolver returns a cache key in addition to audio and rate."""
adapter = Audio8TTSAdapter(SpeechServingContext(server=_FakeSpeechServer()))
request = OpenAICreateSpeechRequest(
input="Clone this voice.",
ref_audio="data:audio/wav;base64,UklGRg==",
)
monkeypatch.setattr(
adapter,
"_build_prompt",
lambda request, ref_audio_data, has_inline_ref_audio: {"ref_audio_data": ref_audio_data},
)
prepared = asyncio.run(adapter.build(request, [], has_inline_ref_audio=True))
assert prepared.prompt["ref_audio_data"] == ([0.0, 0.1], 44_100)
assert prepared.tts_params == {"ref_audio_cache_key": "cache-key"}
assert prepared.prompt["cache_salt"] == conditioning_cache_salt(request, prepared.tts_params)
assert prepared.prompt["cache_salt"] != conditioning_cache_salt(request, {})
@@ -97,9 +97,11 @@ class Audio8TTSAdapter(ARTTSAdapter):
del sampling_params_list
server = self.ctx.server
ref_audio_data = None
tts_params: dict = {}
if request.ref_audio is not None:
wav_list, sample_rate = await server._resolve_ref_audio(request.ref_audio)
wav_list, sample_rate, cache_key = await server._resolve_ref_audio(request.ref_audio)
ref_audio_data = (wav_list, sample_rate)
tts_params["ref_audio_cache_key"] = cache_key
# Prompt building tokenizes and, for voice clone, allocates tensors; keep
# it off the event loop via the server's single-worker TTS executor.
build_prompt = make_async(self._build_prompt, executor=server._tts_executor)
@@ -108,7 +110,6 @@ class Audio8TTSAdapter(ARTTSAdapter):
ref_audio_data=ref_audio_data,
has_inline_ref_audio=has_inline_ref_audio,
)
tts_params: dict = {}
prompt["cache_salt"] = conditioning_cache_salt(request, tts_params)
return PreparedRequest(prompt=prompt, tts_params=tts_params, model_type=self.name)