Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/expose-interrupted-speech-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@livekit/agents": patch
---

feat(agents): expose `generatedText` and `spokenText` on `SpeechHandle` for interrupted speeches, allowing apps to recover undelivered content
10 changes: 9 additions & 1 deletion agents/src/voice/agent_activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2249,6 +2249,8 @@ export class AgentActivity implements RecognitionHooks {
}
}

speechHandle._setInterruptionData(llmGenData.generatedText, forwardedText);

if (forwardedText) {
hasSpeechMessage = true;
const message = ChatMessage.create({
Expand Down Expand Up @@ -2716,10 +2718,14 @@ export class AgentActivity implements RecognitionHooks {
replyAbortController.abort();
await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);

let generatedText = '';
let forwardedText = '';

if (messageOutputs.length > 0) {
// there should be only one message
const [msgId, textOut, audioOut, msgModalities] = messageOutputs[0]!;
let forwardedText = textOut?.text || '';
generatedText = textOut?.text || '';
forwardedText = generatedText;

if (audioOutput) {
audioOutput.clearBuffer();
Expand Down Expand Up @@ -2768,6 +2774,8 @@ export class AgentActivity implements RecognitionHooks {
'playout completed with interrupt',
);
}

speechHandle._setInterruptionData(generatedText, forwardedText);
speechHandle._markGenerationDone();
await executeToolsTask.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);

Expand Down
18 changes: 18 additions & 0 deletions agents/src/voice/speech_handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class SpeechHandle {
private doneFut = new Future<void>();
private generations: Future<void>[] = [];
private _chatItems: ChatItem[] = [];
private _generatedText: string | null = null;
private _spokenText: string | null = null;

/** @internal */
_tasks: Task<void>[] = [];
Expand Down Expand Up @@ -159,6 +161,22 @@ export class SpeechHandle {
return this._chatItems;
}

/** Full text the model generated before the speech was interrupted. Null if not interrupted or no text was generated. */
get generatedText(): string | null {
return this._generatedText;
}

/** Text that was actually spoken (heard by the listener) before interruption. Null if not interrupted or nothing was spoken. */
get spokenText(): string | null {
return this._spokenText;
}

/** @internal */
_setInterruptionData(generatedText: string, spokenText: string): void {
this._generatedText = generatedText || null;
this._spokenText = spokenText || null;
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
}
Comment thread
pouya-commos marked this conversation as resolved.

/**
* Interrupt the current speech generation.
*
Expand Down