Merge pull request #4514 from pipecat-ai/mb/websocket-stt-service-exception-handling
Align websocket STT connection failures
This commit is contained in:
45
tests/test_cartesia_stt.py
Normal file
45
tests/test_cartesia_stt.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# Copyright (c) 2024-2026, Daily
|
||||
#
|
||||
# SPDX-License-Identifier: BSD 2-Clause License
|
||||
#
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from websockets.protocol import State
|
||||
|
||||
from pipecat.services.cartesia.stt import CartesiaSTTService
|
||||
|
||||
|
||||
class _FakeWebsocket:
|
||||
def __init__(self, *, state=State.OPEN, send_side_effect=None):
|
||||
self.state = state
|
||||
self.send = AsyncMock(side_effect=send_side_effect)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cartesia_connect_failure_clears_stale_websocket(monkeypatch):
|
||||
async def fake_websocket_connect(*args, **kwargs):
|
||||
raise RuntimeError("connection failed")
|
||||
|
||||
monkeypatch.setattr("pipecat.services.cartesia.stt.websocket_connect", fake_websocket_connect)
|
||||
|
||||
service = CartesiaSTTService(api_key="test-key", sample_rate=16000)
|
||||
service._websocket = _FakeWebsocket(state=State.CLOSED)
|
||||
|
||||
await service._connect_websocket()
|
||||
|
||||
assert service._websocket is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cartesia_run_stt_logs_send_failure_without_clearing_websocket():
|
||||
service = CartesiaSTTService(api_key="test-key", sample_rate=16000)
|
||||
websocket = _FakeWebsocket(send_side_effect=RuntimeError("websocket closed"))
|
||||
service._websocket = websocket
|
||||
|
||||
async for _ in service.run_stt(b"\x00" * 160):
|
||||
pass
|
||||
|
||||
assert service._websocket is websocket
|
||||
@@ -5,8 +5,10 @@
|
||||
#
|
||||
|
||||
import json
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from websockets.protocol import State
|
||||
|
||||
from pipecat.frames.frames import TranscriptionFrame
|
||||
from pipecat.services.soniox.stt import END_TOKEN, SonioxSTTService, _language_from_tokens
|
||||
@@ -14,8 +16,10 @@ from pipecat.transcriptions.language import Language
|
||||
|
||||
|
||||
class _FakeWebsocket:
|
||||
def __init__(self, messages):
|
||||
def __init__(self, messages, *, state=State.OPEN, send_side_effect=None):
|
||||
self._messages = messages
|
||||
self.state = state
|
||||
self.send = AsyncMock(side_effect=send_side_effect)
|
||||
|
||||
def __aiter__(self):
|
||||
return self._iter_messages()
|
||||
@@ -25,6 +29,21 @@ class _FakeWebsocket:
|
||||
yield message
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect_failure_clears_stale_websocket_without_raising(monkeypatch):
|
||||
async def fake_websocket_connect(*args, **kwargs):
|
||||
raise RuntimeError("connection failed")
|
||||
|
||||
monkeypatch.setattr("pipecat.services.soniox.stt.websocket_connect", fake_websocket_connect)
|
||||
|
||||
service = SonioxSTTService(api_key="test-key")
|
||||
service._websocket = _FakeWebsocket([], state=State.CLOSED)
|
||||
|
||||
await service._connect_websocket()
|
||||
|
||||
assert service._websocket is None
|
||||
|
||||
|
||||
def test_language_from_tokens_uses_single_recognized_language():
|
||||
tokens = [
|
||||
{"text": "Hello", "language": "en"},
|
||||
|
||||
@@ -165,6 +165,19 @@ async def test_reconnect_exhausted_emits_non_fatal_error(service, report_error):
|
||||
assert "Connection refused" in final_error.error
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reconnect_exhausted_when_connect_does_not_raise(service, report_error):
|
||||
"""A non-raising failed connect is treated as a failed reconnect attempt."""
|
||||
result = await service._try_reconnect(report_error=report_error)
|
||||
|
||||
assert result is False
|
||||
assert report_error.call_count == 4
|
||||
final_error = report_error.call_args_list[-1][0][0]
|
||||
assert isinstance(final_error, ErrorFrame)
|
||||
assert final_error.fatal is False
|
||||
assert "websocket reconnection failed verification" in final_error.error
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Quick failure detection — accept then immediately close
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user