From fe8598e09f575beef5ad158fc00964de30462d8a Mon Sep 17 00:00:00 2001 From: arys Date: Sun, 30 Aug 2026 11:39:03 +0500 Subject: [PATCH] fix: TTS ack-bank served stale voice after voice-config changes; add website/English pronunciation rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RuntimeConfiguredTTSProvider (the live 'dynamic' TTS provider, resolves voice from DB-backed config) never overrode cache_fingerprint(), so it fell back to the base class's empty string. PrebakedAckBank keys its on-disk cache on that fingerprint, so short filler phrases like 'Секунду' kept serving audio baked with the previous ElevenLabs voice even after a voice change, while full LLM replies (cached inside the resolved provider itself, keyed on its own voice id) already used the new voice — explaining why callers heard two different voices in the same call. Fix: delegate cache_fingerprint() to the resolved provider. Also extend the voice delivery_hint so the model transliterates website addresses and English words/abbreviations into spoken Cyrillic instead of leaving raw Latin text for the TTS engine to mangle (egov.kz was coming out as 'эговкз'). --- services/ai_orchestrator_service/operator_persona.py | 7 ++++++- services/ai_voice_runtime_service/runtime_tts_provider.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/services/ai_orchestrator_service/operator_persona.py b/services/ai_orchestrator_service/operator_persona.py index 6f2d0e3..2d92647 100644 --- a/services/ai_orchestrator_service/operator_persona.py +++ b/services/ai_orchestrator_service/operator_persona.py @@ -143,7 +143,12 @@ def operator_system_prompt(*, language: str, channel_label: str, is_voice: bool, "Short hotline or service numbers (e.g. 1414, 109) must be spelled out the way people say them as a code, " "grouped and read naturally (\"1414\" as \"четырнадцать четырнадцать\", not \"тысяча четыреста четырнадцать\"). " "Calendar dates must use the correct spoken grammatical case (\"25 числа\" as \"двадцать пятого числа\", " - "not \"двадцать пять число\"; \"14 марта\" as \"четырнадцатого марта\")." + "not \"двадцать пять число\"; \"14 марта\" as \"четырнадцатого марта\"). " + "Never leave a website address, domain, or English word/abbreviation in raw Latin script — the TTS engine " + "slurs it into gibberish (e.g. \"egov.kz\" comes out as \"эговкз\"). Transliterate it into how a person " + "actually pronounces it aloud, with an explicit pause word for punctuation: write \"egov.kz\" as " + "\"игов точка кэ-зэт\", write \".kz\"/\".com\" as \"точка кэ-зэт\"/\"точка ком\", spell out an acronym or " + "English word phonetically in Cyrillic (\"IT\" as \"ай-ти\", \"email\" as \"имейл\")." if is_voice else "The reply should read like a concise message from a live first-line operator." ) diff --git a/services/ai_voice_runtime_service/runtime_tts_provider.py b/services/ai_voice_runtime_service/runtime_tts_provider.py index 8f2e7a2..34651de 100644 --- a/services/ai_voice_runtime_service/runtime_tts_provider.py +++ b/services/ai_voice_runtime_service/runtime_tts_provider.py @@ -114,6 +114,10 @@ class RuntimeConfiguredTTSProvider(TTSProvider): self._provider_cache[cache_key] = provider return provider + def cache_fingerprint(self, language: str | None, *, style_hints: dict[str, object] | None = None) -> str: + provider = self._provider_for_language(language) + return f"{provider.name}:{provider.cache_fingerprint(language, style_hints=style_hints)}" + def synthesize(self, text: str, *, language: str | None = None, style_hints: dict[str, object] | None = None): provider = self._provider_for_language(language) return provider.synthesize(text, language=language, style_hints=style_hints) -- 2.54.0