mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-26 01:01:40 +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
|
|
@ -1928,3 +1928,38 @@ class TestVoiceChannelAwareness:
|
|||
def test_context_empty_when_not_connected(self):
|
||||
adapter = self._make_adapter()
|
||||
assert adapter.get_voice_channel_context(111) == ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bugfix: disconnect() must clean up voice state
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestDisconnectVoiceCleanup:
|
||||
"""Bug: disconnect() left voice dicts populated after closing client."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disconnect_clears_voice_state(self):
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
adapter = MagicMock()
|
||||
adapter._voice_clients = {111: MagicMock(), 222: MagicMock()}
|
||||
adapter._voice_receivers = {111: MagicMock(), 222: MagicMock()}
|
||||
adapter._voice_listen_tasks = {111: MagicMock(), 222: MagicMock()}
|
||||
adapter._voice_timeout_tasks = {111: MagicMock(), 222: MagicMock()}
|
||||
adapter._voice_text_channels = {111: 999, 222: 888}
|
||||
|
||||
async def mock_leave(guild_id):
|
||||
adapter._voice_receivers.pop(guild_id, None)
|
||||
adapter._voice_listen_tasks.pop(guild_id, None)
|
||||
adapter._voice_clients.pop(guild_id, None)
|
||||
adapter._voice_timeout_tasks.pop(guild_id, None)
|
||||
adapter._voice_text_channels.pop(guild_id, None)
|
||||
|
||||
for gid in list(adapter._voice_clients.keys()):
|
||||
await mock_leave(gid)
|
||||
|
||||
assert len(adapter._voice_clients) == 0
|
||||
assert len(adapter._voice_receivers) == 0
|
||||
assert len(adapter._voice_listen_tasks) == 0
|
||||
assert len(adapter._voice_timeout_tasks) == 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue