Files
vllm-omni/tests/e2e/offline_inference/test_indextts2_expansion.py
T
wangyuandamy-why-3459 4af0ddc1c2 [Test] Cleanup for helpers (#6523)
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: [Your Name] <your.email@example.com>
Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-08-25 09:10:17 +08:00

98 lines
2.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""E2E offline inference tests for IndexTTS2 two-stage pipeline.
Stage 0 (GPT AR) → Stage 1 (S2Mel + BigVGAN). Output is 22050 Hz mono WAV.
Covers: basic TTS.
"""
from __future__ import annotations
import pytest
import torch
from tests.helpers.mark import hardware_test
from tests.helpers.media import get_asset_path
from tests.helpers.runtime import OmniRunner
from tests.helpers.stage_config import get_deploy_config_path
from vllm_omni import Omni
from vllm_omni.model_executor.models.indextts2.prompt_utils import (
build_indextts2_prefill_prompt_ids,
)
MODEL_NAME = "IndexTeam/IndexTTS-2"
STAGE_CONFIG = get_deploy_config_path("indextts2.yaml")
_OMNI_RUNNER_PARAM = (
MODEL_NAME,
STAGE_CONFIG,
)
REF_AUDIO = str(get_asset_path("indextts2/ref_audio.wav"))
pytestmark = [
pytest.mark.slow,
pytest.mark.tts,
pytest.mark.parametrize("omni_runner", [_OMNI_RUNNER_PARAM], indirect=True),
]
SAMPLE_RATE = 22050
def _audio_from_mm(mm: dict) -> torch.Tensor | None:
audio = mm.get("audio")
if audio is None:
audio = mm.get("model_outputs")
if isinstance(audio, list):
chunks = [chunk.reshape(-1) for chunk in audio if isinstance(chunk, torch.Tensor) and chunk.numel() > 0]
return torch.cat(chunks, dim=0) if chunks else None
return audio if isinstance(audio, torch.Tensor) else None
def _sample_rate_from_mm(mm: dict) -> int:
sr = mm.get("sr")
if isinstance(sr, list):
sr = sr[-1] if sr else None
if sr is None:
return SAMPLE_RATE
item = getattr(sr, "item", None)
if callable(item):
return int(item())
return int(sr)
def _build_request(text: str, ref_audio: str, **extra) -> dict:
info = {
"text": [text],
"voice": [ref_audio],
}
info.update(extra)
return {
"prompt_token_ids": build_indextts2_prefill_prompt_ids(MODEL_NAME, text),
"additional_information": info,
}
def _collect_audio(omni: Omni, request: dict) -> tuple[torch.Tensor, int]:
for omni_out in omni.generate(request):
mm = omni_out.multimodal_output
assert mm is not None, "Expected multimodal_output"
audio = _audio_from_mm(mm)
assert audio is not None, "Expected audio output"
return audio.cpu(), _sample_rate_from_mm(mm)
raise AssertionError("No outputs received")
def _assert_valid_audio(audio: torch.Tensor, sr: int) -> None:
assert sr == SAMPLE_RATE
assert audio.numel() > 0
assert torch.any(audio != 0).item()
@hardware_test(res={"cuda": "L4"})
def test_basic_english(omni_runner: OmniRunner) -> None:
"""English TTS: text + ref_audio, no emotion conditioning."""
req = _build_request("Hello, this is a voice synthesis test.", REF_AUDIO)
audio, sr = _collect_audio(omni_runner.omni, req)
_assert_valid_audio(audio, sr)