mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
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:
parent
7a24168080
commit
eb34c0b09a
8 changed files with 317 additions and 10 deletions
|
|
@ -1194,3 +1194,40 @@ class TestVoiceStopAndTranscribeReal:
|
|||
cli = _make_voice_cli(_voice_recording=True, _voice_recorder=recorder)
|
||||
cli._voice_stop_and_transcribe()
|
||||
mock_tr.assert_called_once_with("/tmp/test.wav", model="whisper-large-v3")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bugfix: _refresh_level must read _voice_recording under lock
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestRefreshLevelLock:
|
||||
"""Bug: _refresh_level thread read _voice_recording without lock."""
|
||||
|
||||
def test_refresh_stops_when_recording_false(self):
|
||||
import threading, time
|
||||
|
||||
lock = threading.Lock()
|
||||
recording = True
|
||||
iterations = 0
|
||||
|
||||
def refresh_level():
|
||||
nonlocal iterations
|
||||
while True:
|
||||
with lock:
|
||||
still = recording
|
||||
if not still:
|
||||
break
|
||||
iterations += 1
|
||||
time.sleep(0.01)
|
||||
|
||||
t = threading.Thread(target=refresh_level, daemon=True)
|
||||
t.start()
|
||||
|
||||
time.sleep(0.05)
|
||||
with lock:
|
||||
recording = False
|
||||
|
||||
t.join(timeout=1)
|
||||
assert not t.is_alive(), "Refresh thread did not stop"
|
||||
assert iterations > 0, "Refresh thread never ran"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue