Skip to content

(phonic): add logic for maintaining a single ws conn and remove say()#1220

Merged
tinalenguyen merged 7 commits intobrian/reuse-realtimefrom
tina/rm-phonic-say
Apr 10, 2026
Merged

(phonic): add logic for maintaining a single ws conn and remove say()#1220
tinalenguyen merged 7 commits intobrian/reuse-realtimefrom
tina/rm-phonic-say

Conversation

@tinalenguyen
Copy link
Copy Markdown
Member

No description provided.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 9, 2026

🦋 Changeset detected

Latest commit: ded311a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
Name Type
@livekit/agents-plugin-phonic Patch
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread agents/src/voice/agent_activity.ts Outdated
// this means the content is the same as the previous session
const capabilities = this.llm.capabilities;
if (!rtReused || capabilities.midSessionInstructionsUpdate) {
if (!rtReused && this.realtimeSession?.realtimeModel.provider == 'phonic') {
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.

Should this be if (this.realtimeSession?.realtimeModel.provider == 'phonic'?
In the Python implementation, it looks like update_session is called whether or not rt_reused is true.
Image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ah, i think it should be if rtReused is true then we call _updateSession, but it's also dependent on whether or not your _updateSession can support both the initial and reset config. in that case we can call _updateSession unconditionally

Comment thread agents/src/voice/agent_activity.ts
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 8 additional findings in Devin Review.

Open in Devin Review

return this.startNewAssistantTurn({ userInitiated: true });
}
this.pendingGenerateReplyFut = new Future<llm.GenerationCreatedEvent>();
this.sendGenerateReply(instructions);
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.

🔴 Unhandled promise rejection from fire-and-forget sendGenerateReply can crash Node.js

In generateReply(), this.sendGenerateReply(instructions) at line 477 is called without being awaited or having .catch() attached. Inside sendGenerateReply, if this.socket.sendGenerateReply(...) at line 489 throws (e.g., the underlying WebSocket is in a closing/errored state that passes the !this.socket guard at line 484), the resulting promise rejection is completely unhandled. In modern Node.js, unhandled promise rejections terminate the process by default. Additionally, since pendingGenerateReplyFut is never rejected through this path, the caller of generateReply() may hang until the socket close handler fires rejectPendingGenerateReply().

Suggested change
this.sendGenerateReply(instructions);
this.sendGenerateReply(instructions).catch((err) => {
if (this.pendingGenerateReplyFut && !this.pendingGenerateReplyFut.done) {
this.pendingGenerateReplyFut.reject(err instanceof Error ? err : new Error(String(err)));
this.pendingGenerateReplyFut = undefined;
}
});
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 9 additional findings in Devin Review.

Open in Devin Review

Comment on lines 624 to 625
case 'assistant_chose_not_to_respond':
case 'input_cancelled':
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.

🔴 pendingGenerateReplyFut hangs forever when Phonic server sends assistant_chose_not_to_respond or input_cancelled

The new generateReply implementation creates a pendingGenerateReplyFut Future (line 476) and returns its .await promise (line 479). This future is only resolved inside startNewAssistantTurn (line 763-766) or rejected in rejectPendingGenerateReply (only called on socket/session close). However, the server events assistant_chose_not_to_respond and input_cancelled (lines 624-625) fall through to the default break without resolving or rejecting the pending future. When the Phonic server decides not to respond after a generate_reply request, the caller (realtimeReplyTask at agents/src/voice/agent_activity.ts:2925) blocks forever on await this.realtimeSession.generateReply(instructions). This also blocks the speech pipeline drain, which can cause handoff deadlocks since _pauseSchedulingTask waits for _mainTask.result.

(Refers to lines 624-629)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@tinalenguyen tinalenguyen merged commit ab5c528 into brian/reuse-realtime Apr 10, 2026
2 checks passed
@tinalenguyen tinalenguyen deleted the tina/rm-phonic-say branch April 10, 2026 00:37
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.

3 participants