Files
livekit--agents/tests/test_plugin_azure_stt.py
2026-07-13 13:39:38 +08:00

91 lines
3.2 KiB
Python

import asyncio
from types import SimpleNamespace
import pytest
from livekit.agents import stt
from livekit.plugins.azure import stt as azure_stt
pytestmark = pytest.mark.plugin("azure")
def test_azure_recognized_emits_final_transcript(monkeypatch):
events = []
monkeypatch.setattr(
azure_stt.speechsdk,
"AutoDetectSourceLanguageResult",
lambda result: SimpleNamespace(language="en-US"),
)
stream = azure_stt.SpeechStream.__new__(azure_stt.SpeechStream)
stream._opts = SimpleNamespace(language=["en-US"])
stream._loop = SimpleNamespace(call_soon_threadsafe=lambda callback, *args: callback(*args))
stream._event_ch = SimpleNamespace(send_nowait=events.append)
stream._start_time_offset = 0.0
stream._on_recognized(
SimpleNamespace(
result=SimpleNamespace(
text="hello",
offset=10_000_000,
duration=20_000_000,
result_id="azure-result-id",
)
)
)
assert [event.type for event in events] == [stt.SpeechEventType.FINAL_TRANSCRIPT]
def test_azure_stream_emits_usage_for_processed_audio():
events = []
stream = azure_stt.SpeechStream.__new__(azure_stt.SpeechStream)
stream._event_ch = SimpleNamespace(send_nowait=events.append)
stream._audio_duration = 3.5
stream._last_audio_duration_report_time = 0.0
stream._emit_recognition_usage()
assert [event.type for event in events] == [stt.SpeechEventType.RECOGNITION_USAGE]
assert events[0].recognition_usage is not None
assert events[0].recognition_usage.audio_duration == 3.5
assert stream._audio_duration == 0.0
def _canceled_event(reason, code=None, error_details=""):
return SimpleNamespace(
cancellation_details=SimpleNamespace(reason=reason, code=code, error_details=error_details)
)
def test_azure_canceled_error_unblocks_run():
# An Error cancellation (e.g. a service timeout) must wake _run via the
# stopped event and stash the details, so the base retry/fallback path runs
# instead of the stream hanging on a dead recognizer.
stream = azure_stt.SpeechStream.__new__(azure_stt.SpeechStream)
stream._loop = SimpleNamespace(call_soon_threadsafe=lambda callback, *args: callback(*args))
stream._session_stopped_event = asyncio.Event()
stream._cancellation_error = None
details = SimpleNamespace(
reason=azure_stt.speechsdk.CancellationReason.Error,
code=azure_stt.speechsdk.CancellationErrorCode.ServiceTimeout,
error_details="timeout",
)
stream._on_canceled(SimpleNamespace(cancellation_details=details))
assert stream._session_stopped_event.is_set()
assert stream._cancellation_error is details
def test_azure_canceled_without_error_is_ignored():
stream = azure_stt.SpeechStream.__new__(azure_stt.SpeechStream)
stream._loop = SimpleNamespace(call_soon_threadsafe=lambda callback, *args: callback(*args))
stream._session_stopped_event = asyncio.Event()
stream._cancellation_error = None
stream._on_canceled(_canceled_event(azure_stt.speechsdk.CancellationReason.EndOfStream))
assert not stream._session_stopped_event.is_set()
assert stream._cancellation_error is None