from pathlib import Path import numpy as np from backend.omnivoice_adapter import OmniVoiceAdapter from backend.types import VoiceConfig class FakeModel: def __init__(self) -> None: self.generate_calls = [] self.clone_prompt_calls = [] def create_voice_clone_prompt(self, *, ref_audio, ref_text=None): self.clone_prompt_calls.append({"ref_audio": ref_audio, "ref_text": ref_text}) return {"prompt": "cached"} def generate(self, **kwargs): self.generate_calls.append(kwargs) return [np.zeros(24000, dtype=np.float32)] def test_omnivoice_adapter_uses_stable_generation_config_for_designed_voice(tmp_path: Path) -> None: adapter = OmniVoiceAdapter() fake_model = FakeModel() adapter._model = fake_model adapter._backend = "omnivoice" adapter.synthesize( text="Chapter one text.", output_path=tmp_path / "chapter.wav", voice_config=VoiceConfig(mode="design", design_prompt="male, elderly, low pitch, british accent"), diffusion_steps=32, speed=1.0, ) generation_config = fake_model.generate_calls[0]["generation_config"] assert generation_config.position_temperature == 0.0 assert generation_config.class_temperature == 0.0 def test_omnivoice_adapter_reuses_cached_clone_prompt_across_chapters(tmp_path: Path) -> None: adapter = OmniVoiceAdapter() fake_model = FakeModel() adapter._model = fake_model adapter._backend = "omnivoice" voice = VoiceConfig(mode="clone", sample_path="/tmp/sample.wav", reference_text="Reference speech") adapter.synthesize( text="Chapter one text.", output_path=tmp_path / "chapter-1.wav", voice_config=voice, diffusion_steps=32, speed=1.0, ) adapter.synthesize( text="Chapter two text.", output_path=tmp_path / "chapter-2.wav", voice_config=voice, diffusion_steps=32, speed=1.0, ) assert len(fake_model.clone_prompt_calls) == 1 assert fake_model.generate_calls[0]["voice_clone_prompt"] == {"prompt": "cached"} assert fake_model.generate_calls[1]["voice_clone_prompt"] == {"prompt": "cached"} assert "ref_audio" not in fake_model.generate_calls[0] assert "ref_audio" not in fake_model.generate_calls[1]