-
Notifications
You must be signed in to change notification settings - Fork 317
feat(voice): stream tool call status events #1929
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@livekit/agents': patch | ||
| --- | ||
|
|
||
| Stream voice tool call status lifecycle events. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -108,6 +108,7 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createMetricsCollectedEvent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createSessionUsageUpdatedEvent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createSpeechCreatedEvent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createToolExecutionUpdatedEvent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createUserInputTranscribedEvent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from './events.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { ToolExecutionOutput, ToolOutput, _TTSGenerationData } from './generation.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3069,6 +3070,11 @@ export class AgentActivity implements RecognitionHooks { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const respondToolChoice = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schedulingPaused || modelSettings.toolChoice === 'none' ? 'none' : 'auto'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updateIds = functionToolsExecutedEvent.functionCallOutputs.map( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (output) => output.callId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const initialChatItemCount = speechHandle.chatItems.length; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Reuse the same speechHandle for the tool response. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const toolResponseTask = this.createSpeechTask({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| taskFn: () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3087,7 +3093,33 @@ export class AgentActivity implements RecognitionHooks { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'AgentActivity.pipelineReply', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolResponseTask.result.finally(() => this.onPipelineReplyDone()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.agentSession.emit( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentSessionEventTypes.ToolExecutionUpdated, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createToolExecutionUpdatedEvent({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'tool_reply_updated', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateIds, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: 'scheduled', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| speechId: speechHandle.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolResponseTask.result.finally(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const status = speechHandle.interrupted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? 'interrupted' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : speechHandle.chatItems.length > initialChatItemCount | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? 'completed' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : 'skipped'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.agentSession.emit( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AgentSessionEventTypes.ToolExecutionUpdated, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createToolExecutionUpdatedEvent({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'tool_reply_updated', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateIds, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| speechId: speechHandle.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.onPipelineReplyDone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3106
to
+3122
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Agent cleanup step skipped when an event listener throws, leaving the agent stuck A critical post-reply cleanup call is placed after an event emission ( Impact: The agent can get stuck in a non-listening state, preventing it from responding to further user input. Mechanism: emit() can prevent onPipelineReplyDone() from executingPreviously, the
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.scheduleSpeech(speechHandle, SpeechHandle.SPEECH_PRIORITY_NORMAL, true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (functionToolsExecutedEvent.functionCallOutputs.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚩 Realtime path does not emit tool_reply_updated events
The pipeline path in
_pipelineReplyTaskImplemitstool_reply_updatedevents (lines 3096–3122 inagent_activity.ts), but the realtime path in_realtimeGenerationTaskImpl(around line 3617) only emitsFunctionToolsExecutedwithout correspondingtool_reply_updatedevents. This means consumers listening forToolExecutionUpdatedwithtool_reply_updatedwill not receive events for realtime model tool replies. This may be intentional since the realtime model handles tool replies differently (auto-generation), but it creates an asymmetry between the two paths that consumers should be aware of.(Refers to lines 3616-3628)
Was this helpful? React with 👍 or 👎 to provide feedback.