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

76 lines
2.2 KiB
Python

"""
Tests for SpeechHandle error reporting.
When a generation fails (e.g. a realtime ``generate_reply`` timeout, see
https://github.com/livekit/agents/issues/6224), the error is recorded on the
SpeechHandle instead of being set on its done future: awaiting a handle never
raises (most handles are never awaited, so a stored exception would trigger
"Future exception was never retrieved" warnings). Users inspect failures with
``SpeechHandle.exception()`` after the handle is done, and ``session.run()``
still raises through RunResult.
"""
from __future__ import annotations
import asyncio
import pytest
from livekit.agents.llm import RealtimeError
from livekit.agents.voice.run_result import RunResult
from livekit.agents.voice.speech_handle import SpeechHandle
pytestmark = pytest.mark.unit
async def test_await_does_not_raise_on_error() -> None:
handle = SpeechHandle.create()
handle._mark_done(error=RealtimeError("generate_reply timed out."))
result = await handle
assert result is handle
await handle.wait_for_playout()
exc = handle.exception()
assert isinstance(exc, RealtimeError)
assert str(exc) == "generate_reply timed out."
async def test_exception_is_none_without_error() -> None:
handle = SpeechHandle.create()
handle._mark_done()
await handle
assert handle.exception() is None
async def test_exception_raises_if_not_done() -> None:
handle = SpeechHandle.create()
with pytest.raises(asyncio.InvalidStateError):
handle.exception()
async def test_error_ignored_after_done() -> None:
handle = SpeechHandle.create()
handle._mark_done(error=RealtimeError("first"))
# e.g. the task-level done callback marking the handle again
handle._mark_done()
handle._mark_done(error=RealtimeError("second"))
exc = handle.exception()
assert isinstance(exc, RealtimeError)
assert str(exc) == "first"
async def test_run_result_propagates_speech_handle_error() -> None:
run_result = RunResult[None](output_type=None)
handle = SpeechHandle.create()
run_result._watch_handle(handle)
handle._mark_done(error=RealtimeError("generate_reply timed out."))
with pytest.raises(RealtimeError, match="generate_reply timed out"):
await run_result