From e52a73c868ba9a19915bc2952d9421ecf3d02e0c Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Sun, 11 Oct 2026 05:18:52 +0200 Subject: [PATCH] fix(tts): custom engine availability follows health, not the library voice list Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 2 ++ routes/tts.py | 3 ++- tests/test_tts_health_probe.py | 11 +++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35c4cf5..3561f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ยท versioned wi --- ## [Unreleased] +### Fixed +- Custom TTS engines no longer count as available just because the app's own voice library is non-empty: availability follows the engine's health, so a stopped VoiceDesign/CustomVoice container is no longer listed. --- diff --git a/routes/tts.py b/routes/tts.py index 539393e..7a58e46 100644 --- a/routes/tts.py +++ b/routes/tts.py @@ -457,7 +457,8 @@ def _backend_configured(settings: dict, backend: str, voices: list) -> bool: def _backend_available(backend: str, voices: list, health: bool) -> bool: - if _clean_preview_backend(backend) in {"nvidia_zeroshot", "nvidia_flow", "kokoro", "vibevoice", "fishspeech"}: + # A custom engine's voice list is the app's own library, so it proves nothing about the engine. + if backend.startswith("custom:") or _clean_preview_backend(backend) in {"nvidia_zeroshot", "nvidia_flow", "kokoro", "vibevoice", "fishspeech"}: return health return bool(voices) or health diff --git a/tests/test_tts_health_probe.py b/tests/test_tts_health_probe.py index a1b56d9..1a4bc25 100644 --- a/tests/test_tts_health_probe.py +++ b/tests/test_tts_health_probe.py @@ -95,5 +95,16 @@ class GatewayUpstreamProbeTests(unittest.TestCase): self.assertEqual(get.call_count, 1) # second lookup served from the cache +class CustomAvailabilityTests(unittest.TestCase): + def test_custom_engine_availability_follows_health_not_the_library_voice_list(self): + library = ["EN_F_a", "DE_M_b"] + self.assertFalse(tts._backend_available("custom:abc", library, False)) + self.assertTrue(tts._backend_available("custom:abc", [], True)) + + def test_builtin_backends_keep_their_rules(self): + self.assertTrue(tts._backend_available("voice_clone", ["x"], False)) + self.assertFalse(tts._backend_available("kokoro", ["x"], False)) + + if __name__ == "__main__": unittest.main()