feat: 운영 안정성과 세션 음성 경험 개선
This commit is contained in:
parent
facc4ad2d9
commit
c788343467
95 changed files with 8431 additions and 1785 deletions
|
|
@ -12,9 +12,12 @@ from .services.voice import (
|
|||
TTS_ENDPOINT,
|
||||
TTS_MODEL,
|
||||
TTS_MODEL_FALLBACK,
|
||||
HIGGS_TTS_ENDPOINT,
|
||||
HIGGS_TTS_MODEL,
|
||||
VoicePreset,
|
||||
VoiceService,
|
||||
assess_end_of_turn,
|
||||
build_higgs_prompt,
|
||||
build_tts_payload,
|
||||
resolve_voice,
|
||||
resolve_voice_from_map,
|
||||
|
|
@ -96,6 +99,16 @@ class VoicePresetResolutionTest(unittest.TestCase):
|
|||
|
||||
|
||||
class TTSPayloadTest(unittest.TestCase):
|
||||
def test_higgs_prompt_keeps_first_word_before_emotion_tags(self) -> None:
|
||||
voice = VoicePreset(preset="soft-young-fem", openai_voice="coral", rate=0.96)
|
||||
|
||||
prompt = build_higgs_prompt("그냥 학교 가도 아무 의미 없는 것 같아요.", voice)
|
||||
|
||||
self.assertTrue(prompt.startswith("그냥 "))
|
||||
self.assertIn("<|emotion:helplessness|>", prompt)
|
||||
self.assertIn("<|prosody:speed_slow|>", prompt)
|
||||
self.assertNotIn("<|emotion:helplessness|> ", prompt)
|
||||
|
||||
def test_payload_contains_openai_tts_fields_and_clamps_high_speed(self) -> None:
|
||||
voice = VoicePreset(
|
||||
preset="soft-young-fem",
|
||||
|
|
@ -219,6 +232,48 @@ class VoiceServiceStreamTest(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertEqual(payload["instructions"], "Keep the tone grounded.")
|
||||
self.assertEqual([chunk.audio for chunk in chunks], [b"\x80\x80", b"\xff\x00"])
|
||||
|
||||
async def test_higgs_tts_uses_local_synthetic_voice_server_in_dev(self) -> None:
|
||||
client = _CaptureTTSClient([b"RIFF", b"synthetic-wav"])
|
||||
service = VoiceService(
|
||||
api_key="",
|
||||
environment="dev",
|
||||
tts_provider="higgs",
|
||||
higgs_base_url="http://127.0.0.1:9881",
|
||||
)
|
||||
service._higgs_client = client # type: ignore[assignment]
|
||||
voice = VoicePreset(preset="soft-young-fem", openai_voice="coral")
|
||||
|
||||
chunks = [
|
||||
chunk
|
||||
async for chunk in service.synthesize_stream(
|
||||
"엄마한테 말하지 않는 거죠?",
|
||||
voice,
|
||||
)
|
||||
]
|
||||
|
||||
self.assertFalse(service.stt_available())
|
||||
self.assertTrue(service.tts_available(voice))
|
||||
self.assertEqual(service.tts_provider_for_voice(voice), "higgs")
|
||||
self.assertEqual(service.tts_model_for_voice(voice), HIGGS_TTS_MODEL)
|
||||
self.assertEqual(service.tts_media_type_for_voice(voice), "audio/wav")
|
||||
self.assertEqual(len(client.calls), 1)
|
||||
method, endpoint, payload = client.calls[0]
|
||||
self.assertEqual((method, endpoint), ("POST", HIGGS_TTS_ENDPOINT))
|
||||
self.assertIn("<|emotion:fear|>", str(payload["text"]))
|
||||
self.assertEqual(payload["preset"], "soft-young-fem")
|
||||
self.assertEqual(b"".join(chunk.audio for chunk in chunks), b"RIFFsynthetic-wav")
|
||||
|
||||
async def test_higgs_tts_is_fail_closed_outside_dev(self) -> None:
|
||||
service = VoiceService(
|
||||
api_key="",
|
||||
environment="prod",
|
||||
tts_provider="higgs",
|
||||
)
|
||||
voice = VoicePreset(preset="soft-young-fem", openai_voice="coral")
|
||||
|
||||
self.assertFalse(service.tts_available(voice))
|
||||
self.assertEqual(service.tts_provider(), "disabled-non-dev")
|
||||
|
||||
async def test_dev_p1_sample_tts_streams_local_mp3_without_openai_key(self) -> None:
|
||||
with TemporaryDirectory() as tmp:
|
||||
sample_dir = Path(tmp)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue