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:
javis-bot
2026-06-12 21:59:50 +09:00
parent ccbaed9030
commit 932aacef6e
2 changed files with 57 additions and 0 deletions

View File

@@ -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(),
}
)