Conversation
simple_tts_failover remapped a local voice to an OpenAI voice when it fell through to the OpenAI endpoint (af_sky -> nova, anything unmapped -> alloy). A Kokoro or Piper hiccup therefore did not surface as an error -- it surfaced as the assistant suddenly speaking in a different, and billed, voice. Four reasons that is worth changing: - it is silent: nothing in the return value or the logs said the voice changed, so the first signal was auditory and mid-conversation - it converts a free local failure into a billed cloud call nobody asked for - it collapses distinct voices onto one (af_sky and af_sarah both became nova), which matters when several agents share a channel and the voice is how you tell them apart - it masks the real fault: the outage that caused the fallback goes unreported The requested voice is now passed through unchanged, so an endpoint that does not own it fails loudly. The old behaviour stays available behind VOICEMODE_TTS_VOICE_SUBSTITUTION (default false) and logs at WARNING rather than INFO when it fires. Users with no OpenAI key see no change -- they never reached this path. Updated the existing test_voice_mapping to assert the new default and added a parallel case covering the opt-in path, so both behaviours stay covered.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #533.
The problem
When TTS failover reaches the OpenAI endpoint, the requested voice is remapped
(
af_sky→nova, anything unmapped →alloy). So a Kokoro or Piper hiccup doesn'tsurface as an error — it surfaces as the assistant suddenly speaking in a different
voice, and a billed one.
Why it's worth changing
The first signal is auditory, mid-conversation.
wasn't told about.
af_skyandaf_sarahboth becomenova.With several agents sharing the channel, the voice is how you tell them apart.
the user debugs "why did the voice change" instead of "why is Kokoro down".
The change
The requested voice is passed through unchanged. An endpoint that doesn't own it now fails
loudly, surfacing the underlying outage.
The old behaviour stays reachable behind
VOICEMODE_TTS_VOICE_SUBSTITUTION(defaultfalse), and when it fires it logs at WARNING rather than INFO — so even opted in, it'sno longer silent.
Blast radius is small: one function, no config migration, and users with no OpenAI key
see no change at all since they never reached this path. Callers asking for a real OpenAI
voice are unaffected either way.
Tests
tests/test_tts_voice_substitution.py(6 cases) covers the default-off flag, thepass-through, the mapping table sitting behind the guard rather than in front of it, and
the WARNING-level log.
I also updated the existing
test_tts_error_handling.py::test_voice_mapping, which assertedthe old default — it now asserts pass-through, and a parallel
test_voice_mapping_when_substitution_is_enabledcovers the opt-in path, so both behavioursstay covered.
If you'd rather have the flag default the other way, or drop it entirely and always pass
through, both are easy — say which and I'll adjust.