Skip to content

fix(phonic): reconnect after abnormal disconnects - #6828

Closed
qionghuang6 wants to merge 1 commit into
livekit:mainfrom
Phonic-Co:q/phonic-reconnect-abnormal-disconnect
Closed

fix(phonic): reconnect after abnormal disconnects#6828
qionghuang6 wants to merge 1 commit into
livekit:mainfrom
Phonic-Co:q/phonic-reconnect-abnormal-disconnect

Conversation

@qionghuang6

Copy link
Copy Markdown
Contributor

Summary

  • opt Phonic realtime sessions into automatic conversation reconnection after abnormal WebSocket disconnects

Test plan

  • Ruff lint and format checks for the modified module
  • Compile the modified module
  • Type-check the Phonic package

Made with Cursor

Co-authored-by: Cursor <cursoragent@cursor.com>
@qionghuang6
qionghuang6 marked this pull request as ready for review August 12, 2026 22:31
@qionghuang6
qionghuang6 requested a review from a team as a code owner August 12, 2026 22:31

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review


self._client = AsyncPhonic(
api_key=self._opts.api_key,
reconnect_conversation_on_abnormal_disconnect=True,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ”΄ Voice sessions can go silent with no error when the provider connection drops for good

Automatic reconnection is turned on (reconnect_conversation_on_abnormal_disconnect=True at livekit-plugins/livekit-plugins-phonic/livekit/plugins/phonic/realtime/realtime_model.py:389) without handling the case where reconnecting ultimately fails, so a dropped call ends quietly instead of reporting a failure.
Impact: When the connection cannot be restored, the agent simply stops talking and listening and nothing is notified, so the call hangs instead of erroring out or being recovered.

Silent break in the reconnectable socket iterator swallows abnormal-close errors

With the flag enabled, client.conversations.connect() yields a ReconnectableAsyncConversationsSocketClient instead of AsyncConversationsSocketClient (phonic SDK phonic/conversations/client.py:1258-1286). Its __aiter__ catches ConnectionClosed and only re-raises when the close code is neither normal (1000) nor "reconnectable" (1006/1012/1001-restarting); for a 1006 abnormal close where reconnect is impossible (no conversation_id yet) or exhausted (10 attempts), it just breaks without raising (phonic/conversations/reconnectable_socket_client.py:193-206, 155-191).

The plugin's _recv_task (livekit-plugins/livekit-plugins-phonic/livekit/plugins/phonic/realtime/realtime_model.py:956-995) therefore returns normally. In _main_task (...:926-938) recv_task is done with no exception, so no _emit_error(..., recoverable=True) is raised; the code cancels the peers, closes the socket and the current generation, and the session ends without surfacing any error to AgentSession.

Before this change, the plain socket iterator propagated ConnectionClosedError on abnormal closes, so the plugin logged and emitted a recoverable error.

Prompt for agents
Enabling reconnect_conversation_on_abnormal_disconnect makes phonic's connect() return a ReconnectableAsyncConversationsSocketClient whose __aiter__ swallows ConnectionClosed for abnormal/reconnectable close codes (1006/1012/1001-restarting) once reconnection is impossible or exhausted: it breaks out of the loop instead of raising. As a result RealtimeSession._recv_task in livekit-plugins/livekit-plugins-phonic/livekit/plugins/phonic/realtime/realtime_model.py finishes normally, _main_task sees no exception, and the session tears down without emitting any error, leaving the agent silently dead. Consider detecting an unexpected end of the receive loop (e.g. loop exited while _session_should_close is not set) and emitting a recoverable error / raising so the agent session can react.
Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.


self._client = AsyncPhonic(
api_key=self._opts.api_key,
reconnect_conversation_on_abnormal_disconnect=True,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟑 Tool results and audio sent while the connection is being restored are dropped without notice

Messages such as tool results are handed to the connection while it is being re-established (via the client created at livekit-plugins/livekit-plugins-phonic/livekit/plugins/phonic/realtime/realtime_model.py:389), where they are quietly discarded, so the assistant can wait forever for a result that was never delivered.
Impact: After a brief network hiccup the conversation can stall mid tool call, with the assistant never responding.

Reconnectable client no-ops sends while reconnecting, but the plugin already marked the tool call as answered

All send_* methods of ReconnectableAsyncConversationsSocketClient are guarded by _is_send_safe() and silently return when _reconnecting is True (phonic/conversations/reconnectable_socket_client.py:106-107, 221-263). The reconnect window includes an exponential backoff sleep of up to 5s per attempt (...:178-191).

In update_chat_ctx the plugin removes the call id from self._pending_tool_call_ids before calling send_tool_call_output (livekit-plugins/livekit-plugins-phonic/livekit/plugins/phonic/realtime/realtime_model.py:475-488), so a dropped send is never retried and no error is raised; Phonic resumes the conversation still waiting for that tool output. The same silent drop applies to send_say, send_generate_reply, send_reset and buffered audio chunks in _send_task.

Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

@qionghuang6
qionghuang6 marked this pull request as draft August 12, 2026 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant