fix: voice pipeline hardening — 7 bug fixes with tests

1. Anthropic + ElevenLabs TTS silence: forward full response to TTS
   callback for non-streaming providers (choices first, then native
   content blocks fallback).

2. Subprocess timeout kill: play_audio_file now kills the process on
   TimeoutExpired instead of leaving zombie processes.

3. Discord disconnect cleanup: leave all voice channels before closing
   the client to prevent leaked state.

4. Audio stream leak: close InputStream if stream.start() fails.

5. Race condition: read/write _on_silence_stop under lock in audio
   callback thread.

6. _vprint force=True: show API error, retry, and truncation messages
   even during streaming TTS.

7. _refresh_level lock: read _voice_recording under _voice_lock.
This commit is contained in:
0xbyt4 2026-03-14 13:06:49 +03:00
parent 7a24168080
commit eb34c0b09a
8 changed files with 317 additions and 10 deletions

View file

@ -310,8 +310,9 @@ class AudioRecorder:
should_fire = True
if should_fire:
cb = self._on_silence_stop
self._on_silence_stop = None # fire only once
with self._lock:
cb = self._on_silence_stop
self._on_silence_stop = None # fire only once
if cb:
def _safe_cb():
try:
@ -321,6 +322,7 @@ class AudioRecorder:
threading.Thread(target=_safe_cb, daemon=True).start()
# Create stream — may block on CoreAudio (first call only).
stream = None
try:
stream = sd.InputStream(
samplerate=SAMPLE_RATE,
@ -330,6 +332,11 @@ class AudioRecorder:
)
stream.start()
except Exception as e:
if stream is not None:
try:
stream.close()
except Exception:
pass
raise RuntimeError(
f"Failed to open audio input stream: {e}. "
"Check that a microphone is connected and accessible."
@ -670,6 +677,12 @@ def play_audio_file(file_path: str) -> bool:
with _playback_lock:
_active_playback = None
return True
except subprocess.TimeoutExpired:
logger.warning("System player %s timed out, killing process", cmd[0])
proc.kill()
proc.wait()
with _playback_lock:
_active_playback = None
except Exception as e:
logger.debug("System player %s failed: %s", cmd[0], e)
with _playback_lock: