fix(tts): repli sur la voix par défaut si le .cvps configuré est absent

Incident device : profil actif PatientTest a voice_id=zelda, mais seuls
damien.cvps + elodie.cvps sont déployés → voiceHandleOrThrow() levait
« aucune voix chargée ('zelda') » → speakText catch → TTS MUET (aucun son),
alors que STT + LLM fonctionnaient.

voiceHandleOrThrow bascule désormais sur defaultVoiceId (damien, toujours livré)
quand la voix demandée n'a pas de .cvps, au lieu de couper toute parole. Une
voix manquante dégrade gracieusement au lieu de rendre Kazeia muet.

Note (hors scope) : l'endpoint /voices reflète l'inventaire LEGACY Qwen3
(WAV+.bin), pas les .cvps CosyVoice → l'admin peut sélectionner une voix
non déployée. À réconcilier séparément.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kazeia Team 2026-06-18 23:40:42 +02:00
parent ca530f6d85
commit 613c7efc66
1 changed files with 12 additions and 2 deletions

View File

@ -104,8 +104,18 @@ class CosyVoiceTtsEngine(
private fun voiceHandleOrThrow(): Long { private fun voiceHandleOrThrow(): Long {
require(handle != 0L) { "CosyVoice: not loaded" } require(handle != 0L) { "CosyVoice: not loaded" }
val v = voices[currentVoiceId] ?: ensureVoice(currentVoiceId) var v = voices[currentVoiceId] ?: ensureVoice(currentVoiceId)
require(v != 0L) { "CosyVoice: aucune voix chargée ('$currentVoiceId')" } // Repli gracieux : une voix configurée mais non déployée (.cvps absent) ne
// doit JAMAIS rendre le TTS muet — on bascule sur la voix par défaut
// (toujours livrée). Sinon un profil pointant une voix manquante coupe
// toute parole en silence (cf. incident "zelda" non déployé).
if (v == 0L && currentVoiceId != defaultVoiceId) {
log("voix '$currentVoiceId' indisponible → repli sur défaut '$defaultVoiceId'")
v = voices[defaultVoiceId] ?: ensureVoice(defaultVoiceId)
}
require(v != 0L) {
"CosyVoice: aucune voix chargée ('$currentVoiceId' ni défaut '$defaultVoiceId')"
}
return v return v
} }