feat(bot): delay login until MeloTTS voice is warm
The bot, bridge and melo worker boot together, but the MeloTTS model takes
tens of seconds to load. If the bot logged in and auto-joined the voice channel
before the voice was warm, the first reply synthesised to nothing and was
silently dropped.
- bridge /health now reports `tts_ready`. For MeloTTS this pings the worker,
which only binds its HTTP port AFTER the model is loaded (main() warms before
serve_forever()), so a successful ping is a precise "voice is warm" signal.
- The bot polls /health and waits for `tts_ready` before logging in. It does
not wait on brain_ready (the reply engine / Whisper load lazily on the first
turn — a slow first turn is fine, a silent one is the bug). After a 180s cap
it proceeds anyway so a TTS load failure degrades to text-only.
Live-verified: startup logs show "⏳ MeloTTS 준비 대기 중" then
"✓ 음성(MeloTTS) 준비 완료 — 로그인 진행" then "✓ 유저봇 로그인", in that order.
This commit is contained in:
@@ -255,6 +255,29 @@ def _piper_synthesize(text: str) -> Optional[bytes]:
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def _tts_ready() -> bool:
|
||||
"""Whether the configured TTS voice can synthesise right now.
|
||||
|
||||
The bot polls this before logging in so the very first spoken reply is not
|
||||
silently dropped while the voice is still warming up. For MeloTTS the worker
|
||||
only binds its HTTP port AFTER the model is loaded (``main()`` warms the
|
||||
model before ``serve_forever()``), so a successful /health ping is a precise
|
||||
"voice is warm" signal. Piper loads on first synth and was never gated, so
|
||||
it reports ready. TTS disabled means there is nothing to wait for.
|
||||
"""
|
||||
if not TTS_ENABLED:
|
||||
return True
|
||||
if TTS_ENGINE == "melo":
|
||||
import urllib.request
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(f"{MELO_WORKER_URL}/health", timeout=2) as resp:
|
||||
return resp.status == 200
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def synthesize(text: str) -> Optional[bytes]:
|
||||
"""Synthesize text to a 16-bit PCM WAV. The primary voice is MeloTTS
|
||||
(Korean speaker, speed 1.5) served by the warm melo worker; Piper is a
|
||||
@@ -286,6 +309,7 @@ def health():
|
||||
"brain_error": _brain_error,
|
||||
"tts_enabled": TTS_ENABLED,
|
||||
"tts_engine": TTS_ENGINE,
|
||||
"tts_ready": _tts_ready(),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user