fix(voice): preserve transcript flush during session close - #6843
fix(voice): preserve transcript flush during session close#6843IanCollection wants to merge 3 commits into
Conversation
| if flush_error is not None: | ||
| raise flush_error |
There was a problem hiding this comment.
π‘ A failure while saving the last spoken words aborts the rest of session shutdown
An error raised while finishing the last transcript is re-thrown out of audio-recognition teardown (raise flush_error at livekit-agents/livekit/agents/voice/audio_recognition.py:860) instead of being logged, so the remaining shutdown steps never run and the session is left half-closed.
Impact: If saving the final user utterance fails, the room connection is never released and applications never receive the session-closed notification.
How the error escapes and which teardown steps get skipped
Before this PR the close-owned commit task was always cancelled, so any exception it raised was discarded. Now _finish_close_flush records it into flush_error and _cleanup re-raises it, which propagates out of AudioRecognition._aclose() β AgentActivity._close_session() (livekit-agents/livekit/agents/voice/agent_activity.py:1320-1321) β AgentActivity.aclose() β AgentSession._aclose_impl() at livekit-agents/livekit/agents/voice/agent_session.py:1253.
Everything after that await activity.aclose() is skipped: self._activity = None, span ends, self._started = False, self.emit("close", CloseEvent(...)), self._session_host.aclose() and self._room_io.aclose(). _aclose_impl is wrapped in @utils.log_exceptions, so the failure is only logged and the session stays in a started-but-broken state with room I/O still open.
The commit coroutine is not wrapped in log_exceptions (livekit-agents/livekit/agents/voice/audio_recognition.py:1054) and calls user-visible hooks (_hooks.on_final_transcript, _hooks.retrieve_chat_ctx, _run_eou_detection), any of which can raise.
Logging the flush failure (as the surrounding teardown does for other best-effort work) instead of re-raising would keep the rest of the session shutdown intact.
| if flush_error is not None: | |
| raise flush_error | |
| if flush_error is not None: | |
| logger.error( | |
| "transcript flush failed during session close", exc_info=flush_error | |
| ) |
Was this helpful? React with π or π to provide feedback.
What changed
AgentSessionclose from ordinary commit/EOU tasksWhy
Normal session close starts
_commit_user_turn(...)withsession_close_transcript_timeoutand immediately closes the activity.AudioRecognition._aclose()then cancelled the same commit task (and its EOU task), so an interim/final transcript still being flushed could never reach conversation history.Only the exact tasks owned by normal session close are preserved. Public commits, handoff teardown, and error-close tasks retain the existing cancellation behavior. If outer cancellation arrives, cleanup is reaped before
CancelledErroris propagated.Impact
The last user utterance can finish committing during normal session shutdown, so
session_close_transcript_timeoutagain provides its intended transcript-capture behavior without broadening waits on unrelated close paths.Validation
AgentSessionclose emitted no final transcript/history item for a pending interim transcriptpytest tests/test_audio_recognition_aclose.py tests/test_agent_session.py --unit -qβ 92 passedlivekit.agents) β 206 files passed