76 lines
2.2 KiB
Python
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
|