@livekit/agents 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +12 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +3 -3
- package/dist/cli.d.ts +3 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -13
- package/dist/cli.js.map +1 -1
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +1 -1
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +2 -1
- package/dist/inference/tts.d.ts +2 -1
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +1 -5
- package/dist/inference/tts.js.map +1 -1
- package/dist/llm/chat_context.cjs +78 -0
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.cts +16 -0
- package/dist/llm/chat_context.d.ts +16 -0
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +78 -0
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/chat_context.test.cjs +531 -0
- package/dist/llm/chat_context.test.cjs.map +1 -1
- package/dist/llm/chat_context.test.js +531 -0
- package/dist/llm/chat_context.test.js.map +1 -1
- package/dist/llm/tool_context.cjs +43 -2
- package/dist/llm/tool_context.cjs.map +1 -1
- package/dist/llm/tool_context.d.cts +39 -11
- package/dist/llm/tool_context.d.ts +39 -11
- package/dist/llm/tool_context.d.ts.map +1 -1
- package/dist/llm/tool_context.js +42 -3
- package/dist/llm/tool_context.js.map +1 -1
- package/dist/llm/tool_context.test.cjs +197 -0
- package/dist/llm/tool_context.test.cjs.map +1 -1
- package/dist/llm/tool_context.test.js +175 -0
- package/dist/llm/tool_context.test.js.map +1 -1
- package/dist/llm/utils.cjs +17 -11
- package/dist/llm/utils.cjs.map +1 -1
- package/dist/llm/utils.d.cts +1 -2
- package/dist/llm/utils.d.ts +1 -2
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +17 -11
- package/dist/llm/utils.js.map +1 -1
- package/dist/llm/zod-utils.cjs +99 -0
- package/dist/llm/zod-utils.cjs.map +1 -0
- package/dist/llm/zod-utils.d.cts +65 -0
- package/dist/llm/zod-utils.d.ts +65 -0
- package/dist/llm/zod-utils.d.ts.map +1 -0
- package/dist/llm/zod-utils.js +61 -0
- package/dist/llm/zod-utils.js.map +1 -0
- package/dist/llm/zod-utils.test.cjs +389 -0
- package/dist/llm/zod-utils.test.cjs.map +1 -0
- package/dist/llm/zod-utils.test.js +372 -0
- package/dist/llm/zod-utils.test.js.map +1 -0
- package/dist/metrics/base.cjs.map +1 -1
- package/dist/metrics/base.d.cts +7 -0
- package/dist/metrics/base.d.ts +7 -0
- package/dist/metrics/base.d.ts.map +1 -1
- package/dist/stt/stt.cjs +1 -0
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +7 -1
- package/dist/stt/stt.d.ts +7 -1
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +1 -0
- package/dist/stt/stt.js.map +1 -1
- package/dist/vad.cjs +16 -0
- package/dist/vad.cjs.map +1 -1
- package/dist/vad.d.cts +6 -0
- package/dist/vad.d.ts +6 -0
- package/dist/vad.d.ts.map +1 -1
- package/dist/vad.js +16 -0
- package/dist/vad.js.map +1 -1
- package/dist/voice/agent_activity.cjs +83 -8
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +6 -2
- package/dist/voice/agent_activity.d.ts +6 -2
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +83 -8
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +3 -2
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +2 -1
- package/dist/voice/agent_session.d.ts +2 -1
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +3 -2
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +138 -16
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +11 -0
- package/dist/voice/audio_recognition.d.ts +11 -0
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +138 -16
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/generation.cjs +8 -3
- package/dist/voice/generation.cjs.map +1 -1
- package/dist/voice/generation.d.ts.map +1 -1
- package/dist/voice/generation.js +8 -3
- package/dist/voice/generation.js.map +1 -1
- package/dist/voice/room_io/_input.cjs.map +1 -1
- package/dist/voice/room_io/_input.d.ts.map +1 -1
- package/dist/voice/room_io/_input.js +0 -1
- package/dist/voice/room_io/_input.js.map +1 -1
- package/dist/worker.cjs +17 -11
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +16 -9
- package/dist/worker.d.ts +16 -9
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +16 -12
- package/dist/worker.js.map +1 -1
- package/package.json +5 -4
- package/src/cli.ts +17 -17
- package/src/inference/stt.ts +2 -1
- package/src/inference/tts.ts +2 -5
- package/src/llm/__snapshots__/zod-utils.test.ts.snap +341 -0
- package/src/llm/chat_context.test.ts +607 -0
- package/src/llm/chat_context.ts +106 -0
- package/src/llm/tool_context.test.ts +210 -1
- package/src/llm/tool_context.ts +101 -17
- package/src/llm/utils.ts +18 -15
- package/src/llm/zod-utils.test.ts +476 -0
- package/src/llm/zod-utils.ts +144 -0
- package/src/metrics/base.ts +7 -0
- package/src/stt/stt.ts +6 -0
- package/src/vad.ts +18 -0
- package/src/voice/agent_activity.ts +119 -9
- package/src/voice/agent_session.ts +3 -1
- package/src/voice/audio_recognition.ts +235 -57
- package/src/voice/generation.ts +8 -3
- package/src/voice/room_io/_input.ts +1 -1
- package/src/worker.ts +29 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/voice/agent_activity.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { Heap } from 'heap-js';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { ReadableStream } from 'node:stream/web';\nimport { type ChatContext, ChatMessage } from '../llm/chat_context.js';\nimport {\n type ChatItem,\n type FunctionCall,\n type GenerationCreatedEvent,\n type InputSpeechStartedEvent,\n type InputSpeechStoppedEvent,\n type InputTranscriptionCompleted,\n LLM,\n RealtimeModel,\n type RealtimeModelError,\n type RealtimeSession,\n type ToolChoice,\n type ToolContext,\n} from '../llm/index.js';\nimport type { LLMError } from '../llm/llm.js';\nimport { log } from '../log.js';\nimport type {\n EOUMetrics,\n LLMMetrics,\n RealtimeModelMetrics,\n STTMetrics,\n TTSMetrics,\n VADMetrics,\n} from '../metrics/base.js';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { STT, type STTError, type SpeechEvent } from '../stt/stt.js';\nimport { splitWords } from '../tokenize/basic/word.js';\nimport { TTS, type TTSError } from '../tts/tts.js';\nimport { Future, Task, cancelAndWait, waitFor } from '../utils.js';\nimport { VAD, type VADEvent } from '../vad.js';\nimport type { Agent, ModelSettings } from './agent.js';\nimport { StopResponse, asyncLocalStorage } from './agent.js';\nimport { type AgentSession, type TurnDetectionMode } from './agent_session.js';\nimport {\n AudioRecognition,\n type EndOfTurnInfo,\n type RecognitionHooks,\n type _TurnDetector,\n} from './audio_recognition.js';\nimport {\n AgentSessionEventTypes,\n createErrorEvent,\n createFunctionToolsExecutedEvent,\n createMetricsCollectedEvent,\n createSpeechCreatedEvent,\n createUserInputTranscribedEvent,\n} from './events.js';\nimport type { ToolExecutionOutput } from './generation.js';\nimport {\n type _AudioOut,\n type _TextOut,\n performAudioForwarding,\n performLLMInference,\n performTTSInference,\n performTextForwarding,\n performToolExecutions,\n removeInstructions,\n updateInstructions,\n} from './generation.js';\nimport { SpeechHandle } from './speech_handle.js';\n\n// equivalent to Python's contextvars\nconst speechHandleStorage = new AsyncLocalStorage<SpeechHandle>();\n\nexport class AgentActivity implements RecognitionHooks {\n private static readonly REPLY_TASK_CANCEL_TIMEOUT = 5000;\n private started = false;\n private audioRecognition?: AudioRecognition;\n private realtimeSession?: RealtimeSession;\n private turnDetectionMode?: Exclude<TurnDetectionMode, _TurnDetector>;\n private logger = log();\n private _draining = false;\n private _currentSpeech?: SpeechHandle;\n private speechQueue: Heap<[number, number, SpeechHandle]>; // [priority, timestamp, speechHandle]\n private q_updated: Future;\n private speechTasks: Set<Task<void>> = new Set();\n private lock = new Mutex();\n private audioStream = new DeferredReadableStream<AudioFrame>();\n // default to null as None, which maps to the default provider tool choice value\n private toolChoice: ToolChoice | null = null;\n\n agent: Agent;\n agentSession: AgentSession;\n\n /** @internal */\n _mainTask?: Task<void>;\n _userTurnCompletedTask?: Promise<void>;\n\n constructor(agent: Agent, agentSession: AgentSession) {\n this.agent = agent;\n this.agentSession = agentSession;\n\n /**\n * Custom comparator to prioritize speech handles with higher priority\n * - Prefer higher priority\n * - Prefer earlier timestamp (so calling a sequence of generateReply() will execute in FIFO order)\n */\n this.speechQueue = new Heap<[number, number, SpeechHandle]>(([p1, t1, _], [p2, t2, __]) => {\n return p1 === p2 ? t1 - t2 : p2 - p1;\n });\n this.q_updated = new Future();\n\n this.turnDetectionMode =\n typeof this.turnDetection === 'string' ? this.turnDetection : undefined;\n\n if (this.turnDetectionMode === 'vad' && this.vad === undefined) {\n this.logger.warn(\n 'turnDetection is set to \"vad\", but no VAD model is provided, ignoring the turnDdetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (this.turnDetectionMode === 'stt' && this.stt === undefined) {\n this.logger.warn(\n 'turnDetection is set to \"stt\", but no STT model is provided, ignoring the turnDetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (this.llm instanceof RealtimeModel) {\n if (this.llm.capabilities.turnDetection && !this.allowInterruptions) {\n this.logger.warn(\n 'the RealtimeModel uses a server-side turn detection, allowInterruptions cannot be false, ' +\n 'disable turnDetection in the RealtimeModel and use VAD on the AgentSession instead',\n );\n }\n\n if (this.turnDetectionMode === 'realtime_llm' && !this.llm.capabilities.turnDetection) {\n this.logger.warn(\n 'turnDetection is set to \"realtime_llm\", but the LLM is not a RealtimeModel or the server-side turn detection is not supported/enabled, ignoring the turnDetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (this.turnDetectionMode === 'stt') {\n this.logger.warn(\n 'turnDetection is set to \"stt\", but the LLM is a RealtimeModel, ignoring the turnDetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (\n this.turnDetectionMode &&\n this.turnDetectionMode !== 'realtime_llm' &&\n this.llm.capabilities.turnDetection\n ) {\n this.logger.warn(\n `turnDetection is set to \"${this.turnDetectionMode}\", but the LLM is a RealtimeModel and server-side turn detection enabled, ignoring the turnDetection setting`,\n );\n this.turnDetectionMode = undefined;\n }\n\n // fallback to VAD if server side turn detection is disabled and VAD is available\n if (\n !this.llm.capabilities.turnDetection &&\n this.vad &&\n this.turnDetectionMode === undefined\n ) {\n this.turnDetectionMode = 'vad';\n }\n } else if (this.turnDetectionMode === 'realtime_llm') {\n this.logger.warn(\n 'turnDetection is set to \"realtime_llm\", but the LLM is not a RealtimeModel',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (\n !this.vad &&\n this.stt &&\n this.llm instanceof LLM &&\n this.allowInterruptions &&\n this.turnDetectionMode === undefined\n ) {\n this.logger.warn(\n 'VAD is not set. Enabling VAD is recommended when using LLM and STT ' +\n 'for more responsive interruption handling.',\n );\n }\n }\n\n async start(): Promise<void> {\n const unlock = await this.lock.lock();\n try {\n this.agent._agentActivity = this;\n\n if (this.llm instanceof RealtimeModel) {\n this.realtimeSession = this.llm.session();\n this.realtimeSession.on('generation_created', (ev) => this.onGenerationCreated(ev));\n this.realtimeSession.on('input_speech_started', (ev) => this.onInputSpeechStarted(ev));\n this.realtimeSession.on('input_speech_stopped', (ev) => this.onInputSpeechStopped(ev));\n this.realtimeSession.on('input_audio_transcription_completed', (ev) =>\n this.onInputAudioTranscriptionCompleted(ev),\n );\n this.realtimeSession.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.realtimeSession.on('error', (ev) => this.onError(ev));\n\n removeInstructions(this.agent._chatCtx);\n try {\n await this.realtimeSession.updateInstructions(this.agent.instructions);\n } catch (error) {\n this.logger.error(error, 'failed to update the instructions');\n }\n\n try {\n await this.realtimeSession.updateChatCtx(this.agent.chatCtx);\n } catch (error) {\n this.logger.error(error, 'failed to update the chat context');\n }\n\n try {\n await this.realtimeSession.updateTools(this.tools);\n } catch (error) {\n this.logger.error(error, 'failed to update the tools');\n }\n } else if (this.llm instanceof LLM) {\n try {\n updateInstructions({\n chatCtx: this.agent._chatCtx,\n instructions: this.agent.instructions,\n addIfMissing: true,\n });\n } catch (error) {\n this.logger.error('failed to update the instructions', error);\n }\n }\n\n // metrics and error handling\n if (this.llm instanceof LLM) {\n this.llm.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.llm.on('error', (ev) => this.onError(ev));\n }\n\n if (this.stt instanceof STT) {\n this.stt.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.stt.on('error', (ev) => this.onError(ev));\n }\n\n if (this.tts instanceof TTS) {\n this.tts.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.tts.on('error', (ev) => this.onError(ev));\n }\n\n if (this.vad instanceof VAD) {\n this.vad.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n }\n\n this.audioRecognition = new AudioRecognition({\n recognitionHooks: this,\n // Disable stt node if stt is not provided\n stt: this.stt ? (...args) => this.agent.sttNode(...args) : undefined,\n vad: this.vad,\n turnDetector: typeof this.turnDetection === 'string' ? undefined : this.turnDetection,\n turnDetectionMode: this.turnDetectionMode,\n minEndpointingDelay: this.agentSession.options.minEndpointingDelay,\n maxEndpointingDelay: this.agentSession.options.maxEndpointingDelay,\n });\n this.audioRecognition.start();\n this.started = true;\n\n this._mainTask = Task.from(({ signal }) => this.mainTask(signal));\n this.createSpeechTask({\n task: Task.from(() => this.agent.onEnter()),\n name: 'AgentActivity_onEnter',\n });\n } finally {\n unlock();\n }\n }\n\n get currentSpeech(): SpeechHandle | undefined {\n return this._currentSpeech;\n }\n\n get vad(): VAD | undefined {\n return this.agent.vad || this.agentSession.vad;\n }\n\n get stt(): STT | undefined {\n return this.agent.stt || this.agentSession.stt;\n }\n\n get llm(): LLM | RealtimeModel | undefined {\n return this.agent.llm || this.agentSession.llm;\n }\n\n get tts(): TTS | undefined {\n return this.agent.tts || this.agentSession.tts;\n }\n\n get tools(): ToolContext {\n return this.agent.toolCtx;\n }\n\n get draining(): boolean {\n return this._draining;\n }\n\n get realtimeLLMSession(): RealtimeSession | undefined {\n return this.realtimeSession;\n }\n\n get allowInterruptions(): boolean {\n // TODO(AJS-51): Allow options to be defined in Agent class\n return this.agentSession.options.allowInterruptions;\n }\n\n get turnDetection(): TurnDetectionMode | undefined {\n // TODO(brian): prioritize using agent.turn_detection\n return this.agentSession.turnDetection;\n }\n\n get toolCtx(): ToolContext {\n return this.agent.toolCtx;\n }\n\n async updateChatCtx(chatCtx: ChatContext): Promise<void> {\n chatCtx = chatCtx.copy({ toolCtx: this.toolCtx });\n\n this.agent._chatCtx = chatCtx;\n\n if (this.realtimeSession) {\n removeInstructions(chatCtx);\n this.realtimeSession.updateChatCtx(chatCtx);\n } else {\n updateInstructions({\n chatCtx,\n instructions: this.agent.instructions,\n addIfMissing: true,\n });\n }\n }\n\n updateOptions({ toolChoice }: { toolChoice?: ToolChoice | null }): void {\n if (toolChoice !== undefined) {\n this.toolChoice = toolChoice;\n }\n\n if (this.realtimeSession) {\n this.realtimeSession.updateOptions({ toolChoice: this.toolChoice });\n }\n }\n\n attachAudioInput(audioStream: ReadableStream<AudioFrame>): void {\n if (this.audioStream.isSourceSet) {\n this.logger.debug('detaching existing audio input in agent activity');\n this.audioStream.detachSource();\n }\n\n /**\n * We need to add a deferred ReadableStream layer on top of the audioStream from the agent session.\n * The tee() operation should be applied to the deferred stream, not the original audioStream.\n * This is important because teeing the original stream directly makes it very difficult—if not\n * impossible—to implement stream unlock logic cleanly.\n */\n this.audioStream.setSource(audioStream);\n const [realtimeAudioStream, recognitionAudioStream] = this.audioStream.stream.tee();\n\n if (this.realtimeSession) {\n this.realtimeSession.setInputAudioStream(realtimeAudioStream);\n }\n\n if (this.audioRecognition) {\n this.audioRecognition.setInputAudioStream(recognitionAudioStream);\n }\n }\n\n detachAudioInput(): void {\n this.audioStream.detachSource();\n }\n\n commitUserTurn() {\n if (!this.audioRecognition) {\n throw new Error('AudioRecognition is not initialized');\n }\n\n // TODO(brian): add audio_detached flag\n const audioDetached = false;\n this.audioRecognition.commitUserTurn(audioDetached);\n }\n\n clearUserTurn() {\n this.audioRecognition?.clearUserTurn();\n this.realtimeSession?.clearAudio();\n }\n\n say(\n text: string | ReadableStream<string>,\n options?: {\n audio?: ReadableStream<AudioFrame>;\n allowInterruptions?: boolean;\n addToChatCtx?: boolean;\n },\n ): SpeechHandle {\n const {\n audio,\n allowInterruptions: defaultAllowInterruptions,\n addToChatCtx = true,\n } = options ?? {};\n let allowInterruptions = defaultAllowInterruptions;\n\n if (\n !audio &&\n !this.tts &&\n this.agentSession.output.audio &&\n this.agentSession.output.audioEnabled\n ) {\n throw new Error('trying to generate speech from text without a TTS model');\n }\n\n if (\n this.llm instanceof RealtimeModel &&\n this.llm.capabilities.turnDetection &&\n allowInterruptions === false\n ) {\n this.logger.warn(\n 'the RealtimeModel uses a server-side turn detection, allowInterruptions cannot be false when using VoiceAgent.say(), ' +\n 'disable turnDetection in the RealtimeModel and use VAD on the AgentTask/VoiceAgent instead',\n );\n allowInterruptions = true;\n }\n\n const handle = SpeechHandle.create({\n allowInterruptions: allowInterruptions ?? this.allowInterruptions,\n });\n\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: true,\n source: 'say',\n speechHandle: handle,\n }),\n );\n const task = this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.ttsTask(handle, text, addToChatCtx, {}, abortController, audio),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.say_tts',\n });\n\n task.finally(() => this.onPipelineReplyDone());\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n return handle;\n }\n\n // -- Metrics and errors --\n\n private onMetricsCollected = (\n ev: STTMetrics | TTSMetrics | VADMetrics | LLMMetrics | RealtimeModelMetrics,\n ) => {\n const speechHandle = speechHandleStorage.getStore();\n if (speechHandle && (ev.type === 'llm_metrics' || ev.type === 'tts_metrics')) {\n ev.speechId = speechHandle.id;\n }\n this.agentSession.emit(\n AgentSessionEventTypes.MetricsCollected,\n createMetricsCollectedEvent({ metrics: ev }),\n );\n };\n\n private onError(ev: RealtimeModelError | STTError | TTSError | LLMError): void {\n if (ev.type === 'realtime_model_error') {\n const errorEvent = createErrorEvent(ev.error, this.llm);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n } else if (ev.type === 'stt_error') {\n const errorEvent = createErrorEvent(ev.error, this.stt);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n } else if (ev.type === 'tts_error') {\n const errorEvent = createErrorEvent(ev.error, this.tts);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n } else if (ev.type === 'llm_error') {\n const errorEvent = createErrorEvent(ev.error, this.llm);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n }\n\n this.agentSession._onError(ev);\n }\n\n // -- Realtime Session events --\n\n onInputSpeechStarted(_ev: InputSpeechStartedEvent): void {\n this.logger.info('onInputSpeechStarted');\n\n if (!this.vad) {\n this.agentSession._updateUserState('speaking');\n }\n\n // this.interrupt() is going to raise when allow_interruptions is False,\n // llm.InputSpeechStartedEvent is only fired by the server when the turn_detection is enabled.\n try {\n this.interrupt();\n } catch (error) {\n this.logger.error(\n 'RealtimeAPI input_speech_started, but current speech is not interruptable, this should never happen!',\n error,\n );\n }\n }\n\n onInputSpeechStopped(ev: InputSpeechStoppedEvent): void {\n this.logger.info(ev, 'onInputSpeechStopped');\n\n if (!this.vad) {\n this.agentSession._updateUserState('listening');\n }\n\n if (ev.userTranscriptionEnabled) {\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n isFinal: false,\n transcript: '',\n }),\n );\n }\n }\n\n onInputAudioTranscriptionCompleted(ev: InputTranscriptionCompleted): void {\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n transcript: ev.transcript,\n isFinal: ev.isFinal,\n }),\n );\n\n if (ev.isFinal) {\n const message = ChatMessage.create({\n role: 'user',\n content: ev.transcript,\n id: ev.itemId,\n });\n this.agent._chatCtx.items.push(message);\n this.agentSession._conversationItemAdded(message);\n }\n }\n\n onGenerationCreated(ev: GenerationCreatedEvent): void {\n if (ev.userInitiated) {\n // user initiated generations are directly handled inside _realtime_reply_task\n return;\n }\n\n if (this.draining) {\n // copied from python:\n // TODO(shubhra): should we \"forward\" this new turn to the next agent?\n this.logger.warn('skipping new realtime generation, the agent is draining');\n return;\n }\n\n const handle = SpeechHandle.create({\n allowInterruptions: this.allowInterruptions,\n });\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: false,\n source: 'generate_reply',\n speechHandle: handle,\n }),\n );\n this.logger.info({ speech_id: handle.id }, 'Creating speech handle');\n\n this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.realtimeGenerationTask(handle, ev, {}, abortController),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.realtimeGeneration',\n });\n\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n }\n\n // recognition hooks\n\n onStartOfSpeech(_ev: VADEvent): void {\n this.agentSession._updateUserState('speaking');\n }\n\n onEndOfSpeech(_ev: VADEvent): void {\n this.agentSession._updateUserState('listening');\n }\n\n onVADInferenceDone(ev: VADEvent): void {\n if (this.turnDetection === 'manual' || this.turnDetection === 'realtime_llm') {\n // skip speech handle interruption for manual and realtime model\n return;\n }\n\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.turnDetection) {\n // skip speech handle interruption if server side turn detection is enabled\n return;\n }\n\n if (ev.speechDuration < this.agentSession.options.minInterruptionDuration) {\n return;\n }\n\n if (this.stt && this.agentSession.options.minInterruptionWords > 0 && this.audioRecognition) {\n const text = this.audioRecognition.currentTranscript;\n\n // TODO(shubhra): better word splitting for multi-language\n if (text && splitWords(text, true).length < this.agentSession.options.minInterruptionWords) {\n return;\n }\n }\n\n this.realtimeSession?.startUserActivity();\n\n if (\n this._currentSpeech &&\n !this._currentSpeech.interrupted &&\n this._currentSpeech.allowInterruptions\n ) {\n this.logger.info({ 'speech id': this._currentSpeech.id }, 'speech interrupted by VAD');\n this.realtimeSession?.interrupt();\n this._currentSpeech.interrupt();\n }\n }\n\n onInterimTranscript(ev: SpeechEvent): void {\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.userTranscription) {\n // skip stt transcription if userTranscription is enabled on the realtime model\n return;\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n transcript: ev.alternatives![0].text,\n isFinal: false,\n language: ev.alternatives![0].language,\n // TODO(AJS-106): add multi participant support\n }),\n );\n }\n\n onFinalTranscript(ev: SpeechEvent): void {\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.userTranscription) {\n // skip stt transcription if userTranscription is enabled on the realtime model\n return;\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n transcript: ev.alternatives![0].text,\n isFinal: true,\n language: ev.alternatives![0].language,\n // TODO(AJS-106): add multi participant support\n }),\n );\n }\n\n private createSpeechTask(options: {\n task: Task<void>;\n ownedSpeechHandle?: SpeechHandle;\n name?: string;\n }): Promise<void> {\n const { task, ownedSpeechHandle } = options;\n\n this.speechTasks.add(task);\n task.addDoneCallback(() => {\n this.speechTasks.delete(task);\n });\n\n if (ownedSpeechHandle) {\n ownedSpeechHandle._tasks.push(task);\n task.addDoneCallback(() => {\n if (ownedSpeechHandle._tasks.every((t) => t.done)) {\n ownedSpeechHandle._markDone();\n }\n });\n }\n\n task.addDoneCallback(() => {\n this.wakeupMainTask();\n });\n\n return task.result;\n }\n\n async onEndOfTurn(info: EndOfTurnInfo): Promise<boolean> {\n if (this.draining) {\n this.logger.warn({ user_input: info.newTranscript }, 'skipping user input, task is draining');\n // copied from python:\n // TODO(shubhra): should we \"forward\" this new turn to the next agent/activity?\n return true;\n }\n\n if (\n this.stt &&\n this.turnDetection !== 'manual' &&\n this._currentSpeech &&\n this._currentSpeech.allowInterruptions &&\n !this._currentSpeech.interrupted &&\n this.agentSession.options.minInterruptionWords > 0 &&\n info.newTranscript.split(' ').length < this.agentSession.options.minInterruptionWords\n ) {\n // avoid interruption if the new_transcript is too short\n this.logger.info('skipping user input, new_transcript is too short');\n return false;\n }\n\n const oldTask = this._userTurnCompletedTask;\n this._userTurnCompletedTask = this.createSpeechTask({\n task: Task.from(() => this.userTurnCompleted(info, oldTask)),\n name: 'AgentActivity.userTurnCompleted',\n });\n return true;\n }\n\n retrieveChatCtx(): ChatContext {\n return this.agentSession.chatCtx;\n }\n\n private async mainTask(signal: AbortSignal): Promise<void> {\n const abortFuture = new Future();\n const abortHandler = () => {\n abortFuture.resolve();\n signal.removeEventListener('abort', abortHandler);\n };\n signal.addEventListener('abort', abortHandler);\n\n while (true) {\n await Promise.race([this.q_updated.await, abortFuture.await]);\n if (signal.aborted) break;\n\n while (this.speechQueue.size() > 0) {\n if (signal.aborted) break;\n\n const heapItem = this.speechQueue.pop();\n if (!heapItem) {\n throw new Error('Speech queue is empty');\n }\n const speechHandle = heapItem[2];\n this._currentSpeech = speechHandle;\n speechHandle._authorizeGeneration();\n await speechHandle._waitForGeneration();\n this._currentSpeech = undefined;\n }\n\n // If we're draining and there are no more speech tasks, we can exit.\n // Only speech tasks can bypass draining to create a tool response\n if (this.draining && this.speechTasks.size === 0) {\n this.logger.info('mainTask: draining and no more speech tasks');\n break;\n }\n\n this.q_updated = new Future();\n }\n\n this.logger.info('AgentActivity mainTask: exiting');\n }\n\n private wakeupMainTask(): void {\n this.q_updated.resolve();\n }\n\n generateReply(options: {\n userMessage?: ChatMessage;\n chatCtx?: ChatContext;\n instructions?: string;\n toolChoice?: ToolChoice | null;\n allowInterruptions?: boolean;\n }): SpeechHandle {\n const {\n userMessage,\n chatCtx,\n instructions: defaultInstructions,\n toolChoice: defaultToolChoice,\n allowInterruptions: defaultAllowInterruptions,\n } = options;\n\n let instructions = defaultInstructions;\n let toolChoice = defaultToolChoice;\n let allowInterruptions = defaultAllowInterruptions;\n\n if (\n this.llm instanceof RealtimeModel &&\n this.llm.capabilities.turnDetection &&\n allowInterruptions === false\n ) {\n this.logger.warn(\n 'the RealtimeModel uses a server-side turn detection, allowInterruptions cannot be false when using VoiceAgent.generateReply(), ' +\n 'disable turnDetection in the RealtimeModel and use VAD on the AgentTask/VoiceAgent instead',\n );\n allowInterruptions = true;\n }\n\n if (this.llm === undefined) {\n throw new Error('trying to generate reply without an LLM model');\n }\n\n const functionCall = asyncLocalStorage.getStore()?.functionCall;\n if (toolChoice === undefined && functionCall !== undefined) {\n // when generateReply is called inside a tool, set toolChoice to 'none' by default\n toolChoice = 'none';\n }\n\n const handle = SpeechHandle.create({\n allowInterruptions: allowInterruptions ?? this.allowInterruptions,\n });\n\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: true,\n source: 'generate_reply',\n speechHandle: handle,\n }),\n );\n this.logger.info({ speech_id: handle.id }, 'Creating speech handle');\n\n if (this.llm instanceof RealtimeModel) {\n this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.realtimeReplyTask({\n speechHandle: handle,\n // TODO(brian): support llm.ChatMessage for the realtime model\n userInput: userMessage?.textContent,\n instructions,\n modelSettings: {\n // isGiven(toolChoice) = toolChoice !== undefined\n toolChoice: toOaiToolChoice(toolChoice !== undefined ? toolChoice : this.toolChoice),\n },\n abortController,\n }),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.realtimeReply',\n });\n } else if (this.llm instanceof LLM) {\n // instructions used inside generateReply are \"extra\" instructions.\n // this matches the behavior of the Realtime API:\n // https://platform.openai.com/docs/api-reference/realtime-client-events/response/create\n if (instructions) {\n instructions = `${this.agent.instructions}\\n${instructions}`;\n }\n\n const task = this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.pipelineReplyTask(\n handle,\n chatCtx ?? this.agent.chatCtx,\n this.agent.toolCtx,\n {\n toolChoice: toOaiToolChoice(toolChoice !== undefined ? toolChoice : this.toolChoice),\n },\n abortController,\n instructions ? `${this.agent.instructions}\\n${instructions}` : instructions,\n userMessage,\n ),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.pipelineReply',\n });\n\n task.finally(() => this.onPipelineReplyDone());\n }\n\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n return handle;\n }\n\n interrupt(): Future<void> {\n const future = new Future<void>();\n const currentSpeech = this._currentSpeech;\n\n //TODO(AJS-273): add interrupt for background speeches\n\n currentSpeech?.interrupt();\n\n for (const [_, __, speech] of this.speechQueue) {\n speech.interrupt();\n }\n\n this.realtimeSession?.interrupt();\n\n if (currentSpeech === undefined) {\n future.resolve();\n } else {\n currentSpeech.addDoneCallback(() => {\n if (future.done) return;\n future.resolve();\n });\n }\n\n return future;\n }\n\n private onPipelineReplyDone(): void {\n if (!this.speechQueue.peek() && (!this._currentSpeech || this._currentSpeech.done())) {\n this.agentSession._updateAgentState('listening');\n }\n }\n\n private async userTurnCompleted(info: EndOfTurnInfo, oldTask?: Promise<void>): Promise<void> {\n if (oldTask) {\n // We never cancel user code as this is very confusing.\n // So we wait for the old execution of onUserTurnCompleted to finish.\n // In practice this is OK because most speeches will be interrupted if a new turn\n // is detected. So the previous execution should complete quickly.\n await oldTask;\n }\n\n // When the audio recognition detects the end of a user turn:\n // - check if realtime model server-side turn detection is enabled\n // - check if there is no current generation happening\n // - cancel the current generation if it allows interruptions (otherwise skip this current\n // turn)\n // - generate a reply to the user input\n\n if (this.llm instanceof RealtimeModel) {\n if (this.llm.capabilities.turnDetection) {\n return;\n }\n this.realtimeSession?.commitAudio();\n }\n\n if (this._currentSpeech) {\n if (!this._currentSpeech.allowInterruptions) {\n this.logger.warn(\n { user_input: info.newTranscript },\n 'skipping user input, current speech generation cannot be interrupted',\n );\n return;\n }\n\n this.logger.info(\n { 'speech id': this._currentSpeech.id },\n 'speech interrupted, new user turn detected',\n );\n\n this._currentSpeech.interrupt();\n this.realtimeSession?.interrupt();\n }\n\n let userMessage: ChatMessage | undefined = ChatMessage.create({\n role: 'user',\n content: info.newTranscript,\n });\n\n // create a temporary mutable chat context to pass to onUserTurnCompleted\n // the user can edit it for the current generation, but changes will not be kept inside the\n // Agent.chatCtx\n const chatCtx = this.agent.chatCtx.copy();\n const startTime = Date.now();\n\n try {\n await this.agent.onUserTurnCompleted(chatCtx, userMessage);\n } catch (e) {\n if (e instanceof StopResponse) {\n return;\n }\n this.logger.error({ error: e }, 'error occurred during onUserTurnCompleted');\n }\n\n const callbackDuration = Date.now() - startTime;\n\n if (this.llm instanceof RealtimeModel) {\n // ignore stt transcription for realtime model\n userMessage = undefined;\n } else if (this.llm === undefined) {\n return;\n }\n\n // Ensure the new message is passed to generateReply\n // This preserves the original message id, making it easier for users to track responses\n const speechHandle = this.generateReply({ userMessage, chatCtx });\n\n const eouMetrics: EOUMetrics = {\n type: 'eou_metrics',\n timestamp: Date.now(),\n endOfUtteranceDelayMs: info.endOfUtteranceDelay,\n transcriptionDelayMs: info.transcriptionDelay,\n onUserTurnCompletedDelayMs: callbackDuration,\n speechId: speechHandle.id,\n };\n\n this.agentSession.emit(\n AgentSessionEventTypes.MetricsCollected,\n createMetricsCollectedEvent({ metrics: eouMetrics }),\n );\n }\n\n private async ttsTask(\n speechHandle: SpeechHandle,\n text: string | ReadableStream<string>,\n addToChatCtx: boolean,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n audio?: ReadableStream<AudioFrame> | null,\n ): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n const transcriptionOutput = this.agentSession.output.transcriptionEnabled\n ? this.agentSession.output.transcription\n : null;\n\n const audioOutput = this.agentSession.output.audioEnabled\n ? this.agentSession.output.audio\n : null;\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n\n if (speechHandle.interrupted) {\n return;\n }\n\n let baseStream: ReadableStream<string>;\n if (text instanceof ReadableStream) {\n baseStream = text;\n } else {\n baseStream = new ReadableStream({\n start(controller) {\n controller.enqueue(text);\n controller.close();\n },\n });\n }\n\n const [textSource, audioSource] = baseStream.tee();\n\n const tasks: Array<Task<void>> = [];\n\n const trNode = await this.agent.transcriptionNode(textSource, {});\n let textOut: _TextOut | null = null;\n if (trNode) {\n const [textForwardTask, _textOut] = performTextForwarding(\n trNode,\n replyAbortController,\n transcriptionOutput,\n );\n textOut = _textOut;\n tasks.push(textForwardTask);\n }\n\n const onFirstFrame = () => {\n this.agentSession._updateAgentState('speaking');\n };\n\n if (!audioOutput) {\n if (textOut) {\n textOut.firstTextFut.await.finally(onFirstFrame);\n }\n } else {\n let audioOut: _AudioOut | null = null;\n if (!audio) {\n // generate audio using TTS\n const [ttsTask, ttsStream] = performTTSInference(\n (...args) => this.agent.ttsNode(...args),\n audioSource,\n modelSettings,\n replyAbortController,\n );\n tasks.push(ttsTask);\n\n const [forwardTask, _audioOut] = performAudioForwarding(\n ttsStream,\n audioOutput,\n replyAbortController,\n );\n tasks.push(forwardTask);\n audioOut = _audioOut;\n } else {\n // use the provided audio\n const [forwardTask, _audioOut] = performAudioForwarding(\n audio,\n audioOutput,\n replyAbortController,\n );\n tasks.push(forwardTask);\n audioOut = _audioOut;\n }\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n }\n\n await speechHandle.waitIfNotInterrupted(tasks.map((task) => task.result));\n\n if (audioOutput) {\n await speechHandle.waitIfNotInterrupted([audioOutput.waitForPlayout()]);\n }\n\n if (speechHandle.interrupted) {\n replyAbortController.abort();\n await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n if (audioOutput) {\n audioOutput.clearBuffer();\n await audioOutput.waitForPlayout();\n }\n }\n\n if (addToChatCtx) {\n const message = ChatMessage.create({\n role: 'assistant',\n content: textOut?.text || '',\n interrupted: speechHandle.interrupted,\n });\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n }\n\n if (this.agentSession.agentState === 'speaking') {\n this.agentSession._updateAgentState('listening');\n }\n }\n\n private async pipelineReplyTask(\n speechHandle: SpeechHandle,\n chatCtx: ChatContext,\n toolCtx: ToolContext,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n instructions?: string,\n newMessage?: ChatMessage,\n toolsMessages?: ChatItem[],\n ): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n const audioOutput = this.agentSession.output.audioEnabled\n ? this.agentSession.output.audio\n : null;\n const transcriptionOutput = this.agentSession.output.transcriptionEnabled\n ? this.agentSession.output.transcription\n : null;\n\n chatCtx = chatCtx.copy();\n\n if (newMessage) {\n chatCtx.insert(newMessage);\n this.agent._chatCtx.insert(newMessage);\n this.agentSession._conversationItemAdded(newMessage);\n }\n\n if (instructions) {\n try {\n updateInstructions({\n chatCtx,\n instructions,\n addIfMissing: true,\n });\n } catch (e) {\n this.logger.error({ error: e }, 'error occurred during updateInstructions');\n }\n }\n\n this.agentSession._updateAgentState('thinking');\n const tasks: Array<Task<void>> = [];\n const [llmTask, llmGenData] = performLLMInference(\n // preserve `this` context in llmNode\n (...args) => this.agent.llmNode(...args),\n chatCtx,\n toolCtx,\n modelSettings,\n replyAbortController,\n );\n tasks.push(llmTask);\n\n const [ttsTextInput, llmOutput] = llmGenData.textStream.tee();\n\n let ttsTask: Task<void> | null = null;\n let ttsStream: ReadableStream<AudioFrame> | null = null;\n if (audioOutput) {\n [ttsTask, ttsStream] = performTTSInference(\n (...args) => this.agent.ttsNode(...args),\n ttsTextInput,\n modelSettings,\n replyAbortController,\n );\n tasks.push(ttsTask);\n }\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForScheduled()]);\n\n if (speechHandle.interrupted) {\n replyAbortController.abort();\n await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n return;\n }\n\n this.agentSession._updateAgentState('thinking');\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n speechHandle._clearAuthorization();\n\n const replyStartedAt = Date.now();\n const trNodeResult = await this.agent.transcriptionNode(llmOutput, modelSettings);\n let textOut: _TextOut | null = null;\n if (trNodeResult) {\n const [textForwardTask, _textOut] = performTextForwarding(\n trNodeResult,\n replyAbortController,\n transcriptionOutput,\n );\n tasks.push(textForwardTask);\n textOut = _textOut;\n }\n\n const onFirstFrame = () => {\n this.agentSession._updateAgentState('speaking');\n };\n\n let audioOut: _AudioOut | null = null;\n if (audioOutput) {\n if (ttsStream) {\n const [forwardTask, _audioOut] = performAudioForwarding(\n ttsStream,\n audioOutput,\n replyAbortController,\n );\n audioOut = _audioOut;\n tasks.push(forwardTask);\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n } else {\n throw Error('ttsStream is null when audioOutput is enabled');\n }\n } else {\n textOut?.firstTextFut.await.finally(onFirstFrame);\n }\n\n //TODO(AJS-272): before executing tools, make sure we generated all the text\n // (this ensure everything is kept ordered)\n\n const onToolExecutionStarted = (_: FunctionCall) => {\n // TODO(brian): handle speech_handle item_added\n };\n\n const onToolExecutionCompleted = (_: ToolExecutionOutput) => {\n // TODO(brian): handle speech_handle item_added\n };\n\n const [executeToolsTask, toolOutput] = performToolExecutions({\n session: this.agentSession,\n speechHandle,\n toolCtx,\n toolChoice: modelSettings.toolChoice,\n toolCallStream: llmGenData.toolCallStream,\n controller: replyAbortController,\n onToolExecutionStarted,\n onToolExecutionCompleted,\n });\n\n await speechHandle.waitIfNotInterrupted(tasks.map((task) => task.result));\n\n if (audioOutput) {\n await speechHandle.waitIfNotInterrupted([audioOutput.waitForPlayout()]);\n }\n\n // add the tools messages that triggers this reply to the chat context\n if (toolsMessages) {\n for (const msg of toolsMessages) {\n msg.createdAt = replyStartedAt;\n }\n this.agent._chatCtx.insert(toolsMessages);\n }\n\n if (speechHandle.interrupted) {\n this.logger.debug(\n { speech_id: speechHandle.id },\n 'Aborting all pipeline reply tasks due to interruption',\n );\n replyAbortController.abort();\n await Promise.allSettled(\n tasks.map((task) => task.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT)),\n );\n\n let forwardedText = textOut?.text || '';\n\n if (audioOutput) {\n audioOutput.clearBuffer();\n const playbackEv = await audioOutput.waitForPlayout();\n if (audioOut?.firstFrameFut.done) {\n // playback EV is valid only if the first frame was already played\n this.logger.info(\n { speech_id: speechHandle.id, playbackPosition: playbackEv.playbackPosition },\n 'playout interrupted',\n );\n if (playbackEv.synchronizedTranscript) {\n forwardedText = playbackEv.synchronizedTranscript;\n }\n } else {\n forwardedText = '';\n }\n }\n\n if (forwardedText) {\n const message = ChatMessage.create({\n role: 'assistant',\n content: forwardedText,\n id: llmGenData.id,\n interrupted: true,\n createdAt: replyStartedAt,\n });\n chatCtx.insert(message);\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n }\n\n if (this.agentSession.agentState === 'speaking') {\n this.agentSession._updateAgentState('listening');\n }\n\n this.logger.info(\n { speech_id: speechHandle.id, message: forwardedText },\n 'playout completed with interrupt',\n );\n // TODO(shubhra) add chat message to speech handle\n speechHandle._markGenerationDone();\n await executeToolsTask.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n return;\n }\n\n if (textOut && textOut.text) {\n const message = ChatMessage.create({\n role: 'assistant',\n id: llmGenData.id,\n interrupted: false,\n createdAt: replyStartedAt,\n content: textOut.text,\n });\n chatCtx.insert(message);\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n this.logger.info(\n { speech_id: speechHandle.id, message: textOut.text },\n 'playout completed without interruption',\n );\n }\n\n if (toolOutput.output.length > 0) {\n this.agentSession._updateAgentState('thinking');\n } else if (this.agentSession.agentState === 'speaking') {\n this.agentSession._updateAgentState('listening');\n }\n\n // mark the playout done before waiting for the tool execution\n speechHandle._markGenerationDone();\n await executeToolsTask.result;\n\n if (toolOutput.output.length === 0) return;\n\n // important: no agent output should be used after this point\n const { maxToolSteps } = this.agentSession.options;\n if (speechHandle.numSteps >= maxToolSteps) {\n this.logger.warn(\n { speech_id: speechHandle.id, max_tool_steps: maxToolSteps },\n 'maximum number of function calls steps reached',\n );\n return;\n }\n\n const functionToolsExecutedEvent = createFunctionToolsExecutedEvent({\n functionCalls: [],\n functionCallOutputs: [],\n });\n let shouldGenerateToolReply: boolean = false;\n let newAgentTask: Agent | null = null;\n let ignoreTaskSwitch: boolean = false;\n\n for (const sanitizedOut of toolOutput.output) {\n if (sanitizedOut.toolCallOutput !== undefined) {\n functionToolsExecutedEvent.functionCalls.push(sanitizedOut.toolCall);\n functionToolsExecutedEvent.functionCallOutputs.push(sanitizedOut.toolCallOutput);\n if (sanitizedOut.replyRequired) {\n shouldGenerateToolReply = true;\n }\n }\n\n if (newAgentTask !== null && sanitizedOut.agentTask !== undefined) {\n this.logger.error('expected to receive only one agent task from the tool executions');\n ignoreTaskSwitch = true;\n // TODO(brian): should we mark the function call as failed to notify the LLM?\n }\n\n newAgentTask = sanitizedOut.agentTask ?? null;\n\n this.logger.debug(\n {\n speechId: speechHandle.id,\n name: sanitizedOut.toolCall?.name,\n args: sanitizedOut.toolCall.args,\n output: sanitizedOut.toolCallOutput?.output,\n isError: sanitizedOut.toolCallOutput?.isError,\n },\n 'Tool call execution finished',\n );\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.FunctionToolsExecuted,\n functionToolsExecutedEvent,\n );\n\n let draining = this.draining;\n if (!ignoreTaskSwitch && newAgentTask !== null) {\n this.agentSession.updateAgent(newAgentTask);\n draining = true;\n }\n\n const toolMessages = [\n ...functionToolsExecutedEvent.functionCalls,\n ...functionToolsExecutedEvent.functionCallOutputs,\n ] as ChatItem[];\n if (shouldGenerateToolReply) {\n chatCtx.insert(toolMessages);\n\n const handle = SpeechHandle.create({\n allowInterruptions: speechHandle.allowInterruptions,\n stepIndex: speechHandle._stepIndex + 1,\n parent: speechHandle,\n });\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: false,\n source: 'tool_response',\n speechHandle: handle,\n }),\n );\n\n // Avoid setting tool_choice to \"required\" or a specific function when\n // passing tool response back to the LLM\n const respondToolChoice = draining || modelSettings.toolChoice === 'none' ? 'none' : 'auto';\n\n const toolResponseTask = this.createSpeechTask({\n task: Task.from(() =>\n this.pipelineReplyTask(\n handle,\n chatCtx,\n toolCtx,\n { toolChoice: respondToolChoice },\n replyAbortController,\n instructions,\n undefined,\n toolMessages,\n ),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.pipelineReply',\n });\n\n toolResponseTask.finally(() => this.onPipelineReplyDone());\n\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL, true);\n } else if (functionToolsExecutedEvent.functionCallOutputs.length > 0) {\n for (const msg of toolMessages) {\n msg.createdAt = replyStartedAt;\n }\n this.agent._chatCtx.insert(toolMessages);\n }\n }\n\n private async realtimeGenerationTask(\n speechHandle: SpeechHandle,\n ev: GenerationCreatedEvent,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n ): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n if (!this.realtimeSession) {\n throw new Error('realtime session is not initialized');\n }\n if (!(this.llm instanceof RealtimeModel)) {\n throw new Error('llm is not a realtime model');\n }\n\n this.logger.debug(\n { speech_id: speechHandle.id, stepIndex: speechHandle.numSteps },\n 'realtime generation started',\n );\n\n const audioOutput = this.agentSession.output.audioEnabled\n ? this.agentSession.output.audio\n : null;\n const textOutput = this.agentSession.output.transcriptionEnabled\n ? this.agentSession.output.transcription\n : null;\n const toolCtx = this.realtimeSession.tools;\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n speechHandle._clearAuthorization();\n\n if (speechHandle.interrupted) {\n return;\n }\n\n const onFirstFrame = () => {\n this.agentSession._updateAgentState('speaking');\n };\n\n const readMessages = async (\n abortController: AbortController,\n outputs: Array<[string, _TextOut | null, _AudioOut | null]>,\n ) => {\n replyAbortController.signal.addEventListener('abort', () => abortController.abort(), {\n once: true,\n });\n\n const forwardTasks: Array<Task<void>> = [];\n try {\n for await (const msg of ev.messageStream) {\n if (forwardTasks.length > 0) {\n this.logger.warn(\n 'expected to receive only one message generation from the realtime API',\n );\n break;\n }\n const trNodeResult = await this.agent.transcriptionNode(msg.textStream, modelSettings);\n let textOut: _TextOut | null = null;\n if (trNodeResult) {\n const [textForwardTask, _textOut] = performTextForwarding(\n trNodeResult,\n abortController,\n textOutput,\n );\n forwardTasks.push(textForwardTask);\n textOut = _textOut;\n }\n let audioOut: _AudioOut | null = null;\n if (audioOutput) {\n const realtimeAudio = await this.agent.realtimeAudioOutputNode(\n msg.audioStream,\n modelSettings,\n );\n if (realtimeAudio) {\n const [forwardTask, _audioOut] = performAudioForwarding(\n realtimeAudio,\n audioOutput,\n abortController,\n );\n forwardTasks.push(forwardTask);\n audioOut = _audioOut;\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n } else {\n this.logger.warn(\n 'audio output is enabled but neither tts nor realtime audio is available',\n );\n }\n } else if (textOut) {\n textOut.firstTextFut.await.finally(onFirstFrame);\n }\n outputs.push([msg.messageId, textOut, audioOut]);\n }\n await waitFor(forwardTasks);\n } catch (error) {\n this.logger.error(error, 'error reading messages from the realtime API');\n } finally {\n await cancelAndWait(forwardTasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n }\n };\n\n const messageOutputs: Array<[string, _TextOut | null, _AudioOut | null]> = [];\n const tasks = [\n Task.from(\n (controller) => readMessages(controller, messageOutputs),\n undefined,\n 'AgentActivity.realtime_generation.read_messages',\n ),\n ];\n\n const [toolCallStream, toolCallStreamForTracing] = ev.functionStream.tee();\n // TODO(brian): append to tracing tees\n const toolCalls: FunctionCall[] = [];\n\n const readToolStreamTask = async (\n controller: AbortController,\n stream: ReadableStream<FunctionCall>,\n ) => {\n const reader = stream.getReader();\n try {\n while (!controller.signal.aborted) {\n const { done, value } = await reader.read();\n if (done) break;\n\n this.logger.debug({ tool_call: value }, 'received tool call from the realtime API');\n toolCalls.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n };\n\n tasks.push(\n Task.from(\n (controller) => readToolStreamTask(controller, toolCallStreamForTracing),\n replyAbortController,\n 'AgentActivity.realtime_generation.read_tool_stream',\n ),\n );\n\n const onToolExecutionStarted = (f: FunctionCall) => {\n speechHandle._itemAdded([f]);\n };\n\n const onToolExecutionCompleted = (out: ToolExecutionOutput) => {\n if (out.toolCallOutput) {\n speechHandle._itemAdded([out.toolCallOutput]);\n }\n };\n\n const [executeToolsTask, toolOutput] = performToolExecutions({\n session: this.agentSession,\n speechHandle,\n toolCtx,\n toolCallStream,\n toolChoice: modelSettings.toolChoice,\n controller: replyAbortController,\n onToolExecutionStarted,\n onToolExecutionCompleted,\n });\n\n await speechHandle.waitIfNotInterrupted(tasks.map((task) => task.result));\n\n // TODO(brian): add tracing span\n\n if (audioOutput) {\n await speechHandle.waitIfNotInterrupted([audioOutput.waitForPlayout()]);\n this.agentSession._updateAgentState('listening');\n }\n\n if (speechHandle.interrupted) {\n this.logger.debug(\n { speech_id: speechHandle.id },\n 'Aborting all realtime generation tasks due to interruption',\n );\n replyAbortController.abort();\n await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n\n if (messageOutputs.length > 0) {\n // there should be only one message\n const [msgId, textOut, audioOut] = messageOutputs[0]!;\n let forwardedText = textOut?.text || '';\n\n if (audioOutput) {\n audioOutput.clearBuffer();\n const playbackEv = await audioOutput.waitForPlayout();\n let playbackPosition = playbackEv.playbackPosition;\n if (audioOut?.firstFrameFut.done) {\n // playback EV is valid only if the first frame was already played\n this.logger.info(\n { speech_id: speechHandle.id, playbackPosition: playbackEv.playbackPosition },\n 'playout interrupted',\n );\n if (playbackEv.synchronizedTranscript) {\n forwardedText = playbackEv.synchronizedTranscript;\n }\n } else {\n forwardedText = '';\n playbackPosition = 0;\n }\n\n // truncate server-side message\n this.realtimeSession.truncate({\n messageId: msgId,\n audioEndMs: Math.floor(playbackPosition),\n });\n }\n\n if (forwardedText) {\n const message = ChatMessage.create({\n role: 'assistant',\n content: forwardedText,\n id: msgId,\n interrupted: true,\n });\n this.agent._chatCtx.insert(message);\n speechHandle._itemAdded([message]);\n this.agentSession._conversationItemAdded(message);\n\n // TODO(brian): add tracing span\n }\n this.logger.info(\n { speech_id: speechHandle.id, message: forwardedText },\n 'playout completed with interrupt',\n );\n }\n speechHandle._markGenerationDone();\n await executeToolsTask.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n\n // TODO(brian): close tees\n return;\n }\n\n if (messageOutputs.length > 0) {\n // there should be only one message\n const [msgId, textOut, _] = messageOutputs[0]!;\n const message = ChatMessage.create({\n role: 'assistant',\n content: textOut?.text || '',\n id: msgId,\n interrupted: false,\n });\n this.agent._chatCtx.insert(message);\n speechHandle._itemAdded([message]);\n this.agentSession._conversationItemAdded(message); // mark the playout done before waiting for the tool execution\\\n // TODO(brian): add tracing span\n }\n\n // mark the playout done before waiting for the tool execution\n speechHandle._markGenerationDone();\n // TODO(brian): close tees\n\n toolOutput.firstToolStartedFuture.await.finally(() => {\n this.agentSession._updateAgentState('thinking');\n });\n\n await executeToolsTask.result;\n\n if (toolOutput.output.length === 0) return;\n\n // important: no agent ouput should be used after this point\n const { maxToolSteps } = this.agentSession.options;\n if (speechHandle.numSteps >= maxToolSteps) {\n this.logger.warn(\n { speech_id: speechHandle.id, max_tool_steps: maxToolSteps },\n 'maximum number of function calls steps reached',\n );\n return;\n }\n\n const functionToolsExecutedEvent = createFunctionToolsExecutedEvent({\n functionCalls: [],\n functionCallOutputs: [],\n });\n let shouldGenerateToolReply: boolean = false;\n let newAgentTask: Agent | null = null;\n let ignoreTaskSwitch: boolean = false;\n\n for (const sanitizedOut of toolOutput.output) {\n if (sanitizedOut.toolCallOutput !== undefined) {\n functionToolsExecutedEvent.functionCallOutputs.push(sanitizedOut.toolCallOutput);\n if (sanitizedOut.replyRequired) {\n shouldGenerateToolReply = true;\n }\n }\n\n if (newAgentTask !== null && sanitizedOut.agentTask !== undefined) {\n this.logger.error('expected to receive only one agent task from the tool executions');\n ignoreTaskSwitch = true;\n }\n\n newAgentTask = sanitizedOut.agentTask ?? null;\n\n this.logger.debug(\n {\n speechId: speechHandle.id,\n name: sanitizedOut.toolCall?.name,\n args: sanitizedOut.toolCall.args,\n output: sanitizedOut.toolCallOutput?.output,\n isError: sanitizedOut.toolCallOutput?.isError,\n },\n 'Tool call execution finished',\n );\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.FunctionToolsExecuted,\n functionToolsExecutedEvent,\n );\n\n let draining = this.draining;\n if (!ignoreTaskSwitch && newAgentTask !== null) {\n this.agentSession.updateAgent(newAgentTask);\n draining = true;\n }\n\n if (functionToolsExecutedEvent.functionCallOutputs.length > 0) {\n // wait all speeches played before updating the tool output and generating the response\n // most realtime models dont support generating multiple responses at the same time\n while (this.currentSpeech || this.speechQueue.size() > 0) {\n if (\n this.currentSpeech &&\n !this.currentSpeech.done() &&\n this.currentSpeech !== speechHandle\n ) {\n await this.currentSpeech.waitForPlayout();\n } else {\n // Don't block the event loop\n await new Promise((resolve) => setImmediate(resolve));\n }\n }\n const chatCtx = this.realtimeSession.chatCtx.copy();\n chatCtx.items.push(...functionToolsExecutedEvent.functionCallOutputs);\n try {\n await this.realtimeSession.updateChatCtx(chatCtx);\n } catch (error) {\n this.logger.warn(\n { error },\n 'failed to update chat context before generating the function calls results',\n );\n }\n }\n\n // skip realtime reply if not required or auto-generated\n if (!shouldGenerateToolReply || this.llm.capabilities.autoToolReplyGeneration) {\n return;\n }\n\n this.realtimeSession.interrupt();\n\n const replySpeechHandle = SpeechHandle.create({\n allowInterruptions: speechHandle.allowInterruptions,\n stepIndex: speechHandle.numSteps + 1,\n parent: speechHandle,\n });\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: false,\n source: 'tool_response',\n speechHandle: replySpeechHandle,\n }),\n );\n\n const toolChoice = draining || modelSettings.toolChoice === 'none' ? 'none' : 'auto';\n this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.realtimeReplyTask({\n speechHandle: replySpeechHandle,\n modelSettings: { toolChoice },\n abortController,\n }),\n ),\n ownedSpeechHandle: replySpeechHandle,\n name: 'AgentActivity.realtime_reply',\n });\n\n this.scheduleSpeech(replySpeechHandle, SpeechHandle.SPEECH_PRIORITY_NORMAL, true);\n }\n\n private async realtimeReplyTask({\n speechHandle,\n modelSettings: { toolChoice },\n userInput,\n instructions,\n abortController,\n }: {\n speechHandle: SpeechHandle;\n modelSettings: ModelSettings;\n abortController: AbortController;\n userInput?: string;\n instructions?: string;\n }): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n if (!this.realtimeSession) {\n throw new Error('realtime session is not available');\n }\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n\n if (userInput) {\n const chatCtx = this.realtimeSession.chatCtx.copy();\n const message = chatCtx.addMessage({\n role: 'user',\n content: userInput,\n });\n await this.realtimeSession.updateChatCtx(chatCtx);\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n }\n\n const originalToolChoice = this.toolChoice;\n if (toolChoice !== undefined) {\n this.realtimeSession.updateOptions({ toolChoice });\n }\n\n try {\n const generationEvent = await this.realtimeSession.generateReply(instructions);\n await this.realtimeGenerationTask(\n speechHandle,\n generationEvent,\n { toolChoice },\n abortController,\n );\n } finally {\n // reset toolChoice value\n if (toolChoice !== undefined && toolChoice !== originalToolChoice) {\n this.realtimeSession.updateOptions({ toolChoice: originalToolChoice });\n }\n }\n }\n\n private scheduleSpeech(\n speechHandle: SpeechHandle,\n priority: number,\n force: boolean = false,\n ): void {\n // when force=true, we allow tool responses to bypass draining\n // This allows for tool responses to be generated before the AgentActivity is finalized\n if (this.draining && !force) {\n throw new Error('cannot schedule new speech, the agent is draining');\n }\n\n // Monotonic time to avoid near 0 collisions\n this.speechQueue.push([priority, Number(process.hrtime.bigint()), speechHandle]);\n speechHandle._markScheduled();\n this.wakeupMainTask();\n }\n\n async drain(): Promise<void> {\n const unlock = await this.lock.lock();\n try {\n if (this._draining) return;\n\n this.createSpeechTask({\n task: Task.from(() => this.agent.onExit()),\n name: 'AgentActivity_onExit',\n });\n\n this.wakeupMainTask();\n this._draining = true;\n await this._mainTask?.result;\n } finally {\n unlock();\n }\n }\n\n async close(): Promise<void> {\n const unlock = await this.lock.lock();\n try {\n if (!this._draining) {\n this.logger.warn('task closing without draining');\n }\n\n // Unregister event handlers to prevent duplicate metrics\n if (this.llm instanceof LLM) {\n this.llm.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.realtimeSession) {\n this.realtimeSession.off('generation_created', this.onGenerationCreated);\n this.realtimeSession.off('input_speech_started', this.onInputSpeechStarted);\n this.realtimeSession.off('input_speech_stopped', this.onInputSpeechStopped);\n this.realtimeSession.off(\n 'input_audio_transcription_completed',\n this.onInputAudioTranscriptionCompleted,\n );\n this.realtimeSession.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.stt instanceof STT) {\n this.stt.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.tts instanceof TTS) {\n this.tts.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.vad instanceof VAD) {\n this.vad.off('metrics_collected', this.onMetricsCollected);\n }\n\n this.detachAudioInput();\n await this.realtimeSession?.close();\n await this.audioRecognition?.close();\n await this._mainTask?.cancelAndWait();\n } finally {\n unlock();\n }\n }\n}\n\nfunction toOaiToolChoice(toolChoice: ToolChoice | null): ToolChoice | undefined {\n // we convert null to undefined, which maps to the default provider tool choice value\n return toolChoice !== null ? toolChoice : undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/voice/agent_activity.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { Heap } from 'heap-js';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { ReadableStream } from 'node:stream/web';\nimport { type ChatContext, ChatMessage } from '../llm/chat_context.js';\nimport {\n type ChatItem,\n type FunctionCall,\n type GenerationCreatedEvent,\n type InputSpeechStartedEvent,\n type InputSpeechStoppedEvent,\n type InputTranscriptionCompleted,\n LLM,\n RealtimeModel,\n type RealtimeModelError,\n type RealtimeSession,\n type ToolChoice,\n type ToolContext,\n} from '../llm/index.js';\nimport type { LLMError } from '../llm/llm.js';\nimport { isSameToolChoice, isSameToolContext } from '../llm/tool_context.js';\nimport { log } from '../log.js';\nimport type {\n EOUMetrics,\n LLMMetrics,\n RealtimeModelMetrics,\n STTMetrics,\n TTSMetrics,\n VADMetrics,\n} from '../metrics/base.js';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { STT, type STTError, type SpeechEvent } from '../stt/stt.js';\nimport { splitWords } from '../tokenize/basic/word.js';\nimport { TTS, type TTSError } from '../tts/tts.js';\nimport { Future, Task, cancelAndWait, waitFor } from '../utils.js';\nimport { VAD, type VADEvent } from '../vad.js';\nimport type { Agent, ModelSettings } from './agent.js';\nimport { StopResponse, asyncLocalStorage } from './agent.js';\nimport { type AgentSession, type TurnDetectionMode } from './agent_session.js';\nimport {\n AudioRecognition,\n type EndOfTurnInfo,\n type PreemptiveGenerationInfo,\n type RecognitionHooks,\n type _TurnDetector,\n} from './audio_recognition.js';\nimport {\n AgentSessionEventTypes,\n createErrorEvent,\n createFunctionToolsExecutedEvent,\n createMetricsCollectedEvent,\n createSpeechCreatedEvent,\n createUserInputTranscribedEvent,\n} from './events.js';\nimport type { ToolExecutionOutput } from './generation.js';\nimport {\n type _AudioOut,\n type _TextOut,\n performAudioForwarding,\n performLLMInference,\n performTTSInference,\n performTextForwarding,\n performToolExecutions,\n removeInstructions,\n updateInstructions,\n} from './generation.js';\nimport { SpeechHandle } from './speech_handle.js';\n\n// equivalent to Python's contextvars\nconst speechHandleStorage = new AsyncLocalStorage<SpeechHandle>();\n\ninterface PreemptiveGeneration {\n speechHandle: SpeechHandle;\n userMessage: ChatMessage;\n info: PreemptiveGenerationInfo;\n chatCtx: ChatContext;\n tools: ToolContext;\n toolChoice: ToolChoice | null;\n createdAt: number;\n}\n\nexport class AgentActivity implements RecognitionHooks {\n private static readonly REPLY_TASK_CANCEL_TIMEOUT = 5000;\n private started = false;\n private audioRecognition?: AudioRecognition;\n private realtimeSession?: RealtimeSession;\n private turnDetectionMode?: Exclude<TurnDetectionMode, _TurnDetector>;\n private logger = log();\n private _draining = false;\n private _currentSpeech?: SpeechHandle;\n private speechQueue: Heap<[number, number, SpeechHandle]>; // [priority, timestamp, speechHandle]\n private q_updated: Future;\n private speechTasks: Set<Task<void>> = new Set();\n private lock = new Mutex();\n private audioStream = new DeferredReadableStream<AudioFrame>();\n // default to null as None, which maps to the default provider tool choice value\n private toolChoice: ToolChoice | null = null;\n private _preemptiveGeneration?: PreemptiveGeneration;\n\n agent: Agent;\n agentSession: AgentSession;\n\n /** @internal */\n _mainTask?: Task<void>;\n _userTurnCompletedTask?: Promise<void>;\n\n constructor(agent: Agent, agentSession: AgentSession) {\n this.agent = agent;\n this.agentSession = agentSession;\n\n /**\n * Custom comparator to prioritize speech handles with higher priority\n * - Prefer higher priority\n * - Prefer earlier timestamp (so calling a sequence of generateReply() will execute in FIFO order)\n */\n this.speechQueue = new Heap<[number, number, SpeechHandle]>(([p1, t1, _], [p2, t2, __]) => {\n return p1 === p2 ? t1 - t2 : p2 - p1;\n });\n this.q_updated = new Future();\n\n this.turnDetectionMode =\n typeof this.turnDetection === 'string' ? this.turnDetection : undefined;\n\n if (this.turnDetectionMode === 'vad' && this.vad === undefined) {\n this.logger.warn(\n 'turnDetection is set to \"vad\", but no VAD model is provided, ignoring the turnDdetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (this.turnDetectionMode === 'stt' && this.stt === undefined) {\n this.logger.warn(\n 'turnDetection is set to \"stt\", but no STT model is provided, ignoring the turnDetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (this.llm instanceof RealtimeModel) {\n if (this.llm.capabilities.turnDetection && !this.allowInterruptions) {\n this.logger.warn(\n 'the RealtimeModel uses a server-side turn detection, allowInterruptions cannot be false, ' +\n 'disable turnDetection in the RealtimeModel and use VAD on the AgentSession instead',\n );\n }\n\n if (this.turnDetectionMode === 'realtime_llm' && !this.llm.capabilities.turnDetection) {\n this.logger.warn(\n 'turnDetection is set to \"realtime_llm\", but the LLM is not a RealtimeModel or the server-side turn detection is not supported/enabled, ignoring the turnDetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (this.turnDetectionMode === 'stt') {\n this.logger.warn(\n 'turnDetection is set to \"stt\", but the LLM is a RealtimeModel, ignoring the turnDetection setting',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (\n this.turnDetectionMode &&\n this.turnDetectionMode !== 'realtime_llm' &&\n this.llm.capabilities.turnDetection\n ) {\n this.logger.warn(\n `turnDetection is set to \"${this.turnDetectionMode}\", but the LLM is a RealtimeModel and server-side turn detection enabled, ignoring the turnDetection setting`,\n );\n this.turnDetectionMode = undefined;\n }\n\n // fallback to VAD if server side turn detection is disabled and VAD is available\n if (\n !this.llm.capabilities.turnDetection &&\n this.vad &&\n this.turnDetectionMode === undefined\n ) {\n this.turnDetectionMode = 'vad';\n }\n } else if (this.turnDetectionMode === 'realtime_llm') {\n this.logger.warn(\n 'turnDetection is set to \"realtime_llm\", but the LLM is not a RealtimeModel',\n );\n this.turnDetectionMode = undefined;\n }\n\n if (\n !this.vad &&\n this.stt &&\n this.llm instanceof LLM &&\n this.allowInterruptions &&\n this.turnDetectionMode === undefined\n ) {\n this.logger.warn(\n 'VAD is not set. Enabling VAD is recommended when using LLM and STT ' +\n 'for more responsive interruption handling.',\n );\n }\n }\n\n async start(): Promise<void> {\n const unlock = await this.lock.lock();\n try {\n this.agent._agentActivity = this;\n\n if (this.llm instanceof RealtimeModel) {\n this.realtimeSession = this.llm.session();\n this.realtimeSession.on('generation_created', (ev) => this.onGenerationCreated(ev));\n this.realtimeSession.on('input_speech_started', (ev) => this.onInputSpeechStarted(ev));\n this.realtimeSession.on('input_speech_stopped', (ev) => this.onInputSpeechStopped(ev));\n this.realtimeSession.on('input_audio_transcription_completed', (ev) =>\n this.onInputAudioTranscriptionCompleted(ev),\n );\n this.realtimeSession.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.realtimeSession.on('error', (ev) => this.onError(ev));\n\n removeInstructions(this.agent._chatCtx);\n try {\n await this.realtimeSession.updateInstructions(this.agent.instructions);\n } catch (error) {\n this.logger.error(error, 'failed to update the instructions');\n }\n\n try {\n await this.realtimeSession.updateChatCtx(this.agent.chatCtx);\n } catch (error) {\n this.logger.error(error, 'failed to update the chat context');\n }\n\n try {\n await this.realtimeSession.updateTools(this.tools);\n } catch (error) {\n this.logger.error(error, 'failed to update the tools');\n }\n } else if (this.llm instanceof LLM) {\n try {\n updateInstructions({\n chatCtx: this.agent._chatCtx,\n instructions: this.agent.instructions,\n addIfMissing: true,\n });\n } catch (error) {\n this.logger.error('failed to update the instructions', error);\n }\n }\n\n // metrics and error handling\n if (this.llm instanceof LLM) {\n this.llm.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.llm.on('error', (ev) => this.onError(ev));\n }\n\n if (this.stt instanceof STT) {\n this.stt.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.stt.on('error', (ev) => this.onError(ev));\n }\n\n if (this.tts instanceof TTS) {\n this.tts.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n this.tts.on('error', (ev) => this.onError(ev));\n }\n\n if (this.vad instanceof VAD) {\n this.vad.on('metrics_collected', (ev) => this.onMetricsCollected(ev));\n }\n\n this.audioRecognition = new AudioRecognition({\n recognitionHooks: this,\n // Disable stt node if stt is not provided\n stt: this.stt ? (...args) => this.agent.sttNode(...args) : undefined,\n vad: this.vad,\n turnDetector: typeof this.turnDetection === 'string' ? undefined : this.turnDetection,\n turnDetectionMode: this.turnDetectionMode,\n minEndpointingDelay: this.agentSession.options.minEndpointingDelay,\n maxEndpointingDelay: this.agentSession.options.maxEndpointingDelay,\n });\n this.audioRecognition.start();\n this.started = true;\n\n this._mainTask = Task.from(({ signal }) => this.mainTask(signal));\n this.createSpeechTask({\n task: Task.from(() => this.agent.onEnter()),\n name: 'AgentActivity_onEnter',\n });\n } finally {\n unlock();\n }\n }\n\n get currentSpeech(): SpeechHandle | undefined {\n return this._currentSpeech;\n }\n\n get vad(): VAD | undefined {\n return this.agent.vad || this.agentSession.vad;\n }\n\n get stt(): STT | undefined {\n return this.agent.stt || this.agentSession.stt;\n }\n\n get llm(): LLM | RealtimeModel | undefined {\n return this.agent.llm || this.agentSession.llm;\n }\n\n get tts(): TTS | undefined {\n return this.agent.tts || this.agentSession.tts;\n }\n\n get tools(): ToolContext {\n return this.agent.toolCtx;\n }\n\n get draining(): boolean {\n return this._draining;\n }\n\n get realtimeLLMSession(): RealtimeSession | undefined {\n return this.realtimeSession;\n }\n\n get allowInterruptions(): boolean {\n // TODO(AJS-51): Allow options to be defined in Agent class\n return this.agentSession.options.allowInterruptions;\n }\n\n get turnDetection(): TurnDetectionMode | undefined {\n // TODO(brian): prioritize using agent.turn_detection\n return this.agentSession.turnDetection;\n }\n\n get toolCtx(): ToolContext {\n return this.agent.toolCtx;\n }\n\n async updateChatCtx(chatCtx: ChatContext): Promise<void> {\n chatCtx = chatCtx.copy({ toolCtx: this.toolCtx });\n\n this.agent._chatCtx = chatCtx;\n\n if (this.realtimeSession) {\n removeInstructions(chatCtx);\n this.realtimeSession.updateChatCtx(chatCtx);\n } else {\n updateInstructions({\n chatCtx,\n instructions: this.agent.instructions,\n addIfMissing: true,\n });\n }\n }\n\n updateOptions({ toolChoice }: { toolChoice?: ToolChoice | null }): void {\n if (toolChoice !== undefined) {\n this.toolChoice = toolChoice;\n }\n\n if (this.realtimeSession) {\n this.realtimeSession.updateOptions({ toolChoice: this.toolChoice });\n }\n }\n\n attachAudioInput(audioStream: ReadableStream<AudioFrame>): void {\n if (this.audioStream.isSourceSet) {\n this.logger.debug('detaching existing audio input in agent activity');\n this.audioStream.detachSource();\n }\n\n /**\n * We need to add a deferred ReadableStream layer on top of the audioStream from the agent session.\n * The tee() operation should be applied to the deferred stream, not the original audioStream.\n * This is important because teeing the original stream directly makes it very difficult—if not\n * impossible—to implement stream unlock logic cleanly.\n */\n this.audioStream.setSource(audioStream);\n const [realtimeAudioStream, recognitionAudioStream] = this.audioStream.stream.tee();\n\n if (this.realtimeSession) {\n this.realtimeSession.setInputAudioStream(realtimeAudioStream);\n }\n\n if (this.audioRecognition) {\n this.audioRecognition.setInputAudioStream(recognitionAudioStream);\n }\n }\n\n detachAudioInput(): void {\n this.audioStream.detachSource();\n }\n\n commitUserTurn() {\n if (!this.audioRecognition) {\n throw new Error('AudioRecognition is not initialized');\n }\n\n // TODO(brian): add audio_detached flag\n const audioDetached = false;\n this.audioRecognition.commitUserTurn(audioDetached);\n }\n\n clearUserTurn() {\n this.audioRecognition?.clearUserTurn();\n this.realtimeSession?.clearAudio();\n }\n\n say(\n text: string | ReadableStream<string>,\n options?: {\n audio?: ReadableStream<AudioFrame>;\n allowInterruptions?: boolean;\n addToChatCtx?: boolean;\n },\n ): SpeechHandle {\n const {\n audio,\n allowInterruptions: defaultAllowInterruptions,\n addToChatCtx = true,\n } = options ?? {};\n let allowInterruptions = defaultAllowInterruptions;\n\n if (\n !audio &&\n !this.tts &&\n this.agentSession.output.audio &&\n this.agentSession.output.audioEnabled\n ) {\n throw new Error('trying to generate speech from text without a TTS model');\n }\n\n if (\n this.llm instanceof RealtimeModel &&\n this.llm.capabilities.turnDetection &&\n allowInterruptions === false\n ) {\n this.logger.warn(\n 'the RealtimeModel uses a server-side turn detection, allowInterruptions cannot be false when using VoiceAgent.say(), ' +\n 'disable turnDetection in the RealtimeModel and use VAD on the AgentTask/VoiceAgent instead',\n );\n allowInterruptions = true;\n }\n\n const handle = SpeechHandle.create({\n allowInterruptions: allowInterruptions ?? this.allowInterruptions,\n });\n\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: true,\n source: 'say',\n speechHandle: handle,\n }),\n );\n const task = this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.ttsTask(handle, text, addToChatCtx, {}, abortController, audio),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.say_tts',\n });\n\n task.finally(() => this.onPipelineReplyDone());\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n return handle;\n }\n\n // -- Metrics and errors --\n\n private onMetricsCollected = (\n ev: STTMetrics | TTSMetrics | VADMetrics | LLMMetrics | RealtimeModelMetrics,\n ) => {\n const speechHandle = speechHandleStorage.getStore();\n if (speechHandle && (ev.type === 'llm_metrics' || ev.type === 'tts_metrics')) {\n ev.speechId = speechHandle.id;\n }\n this.agentSession.emit(\n AgentSessionEventTypes.MetricsCollected,\n createMetricsCollectedEvent({ metrics: ev }),\n );\n };\n\n private onError(ev: RealtimeModelError | STTError | TTSError | LLMError): void {\n if (ev.type === 'realtime_model_error') {\n const errorEvent = createErrorEvent(ev.error, this.llm);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n } else if (ev.type === 'stt_error') {\n const errorEvent = createErrorEvent(ev.error, this.stt);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n } else if (ev.type === 'tts_error') {\n const errorEvent = createErrorEvent(ev.error, this.tts);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n } else if (ev.type === 'llm_error') {\n const errorEvent = createErrorEvent(ev.error, this.llm);\n this.agentSession.emit(AgentSessionEventTypes.Error, errorEvent);\n }\n\n this.agentSession._onError(ev);\n }\n\n // -- Realtime Session events --\n\n onInputSpeechStarted(_ev: InputSpeechStartedEvent): void {\n this.logger.info('onInputSpeechStarted');\n\n if (!this.vad) {\n this.agentSession._updateUserState('speaking');\n }\n\n // this.interrupt() is going to raise when allow_interruptions is False,\n // llm.InputSpeechStartedEvent is only fired by the server when the turn_detection is enabled.\n try {\n this.interrupt();\n } catch (error) {\n this.logger.error(\n 'RealtimeAPI input_speech_started, but current speech is not interruptable, this should never happen!',\n error,\n );\n }\n }\n\n onInputSpeechStopped(ev: InputSpeechStoppedEvent): void {\n this.logger.info(ev, 'onInputSpeechStopped');\n\n if (!this.vad) {\n this.agentSession._updateUserState('listening');\n }\n\n if (ev.userTranscriptionEnabled) {\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n isFinal: false,\n transcript: '',\n }),\n );\n }\n }\n\n onInputAudioTranscriptionCompleted(ev: InputTranscriptionCompleted): void {\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n transcript: ev.transcript,\n isFinal: ev.isFinal,\n }),\n );\n\n if (ev.isFinal) {\n const message = ChatMessage.create({\n role: 'user',\n content: ev.transcript,\n id: ev.itemId,\n });\n this.agent._chatCtx.items.push(message);\n this.agentSession._conversationItemAdded(message);\n }\n }\n\n onGenerationCreated(ev: GenerationCreatedEvent): void {\n if (ev.userInitiated) {\n // user initiated generations are directly handled inside _realtime_reply_task\n return;\n }\n\n if (this.draining) {\n // copied from python:\n // TODO(shubhra): should we \"forward\" this new turn to the next agent?\n this.logger.warn('skipping new realtime generation, the agent is draining');\n return;\n }\n\n const handle = SpeechHandle.create({\n allowInterruptions: this.allowInterruptions,\n });\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: false,\n source: 'generate_reply',\n speechHandle: handle,\n }),\n );\n this.logger.info({ speech_id: handle.id }, 'Creating speech handle');\n\n this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.realtimeGenerationTask(handle, ev, {}, abortController),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.realtimeGeneration',\n });\n\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n }\n\n // recognition hooks\n\n onStartOfSpeech(_ev: VADEvent): void {\n this.agentSession._updateUserState('speaking');\n }\n\n onEndOfSpeech(ev: VADEvent): void {\n let speechEndTime = Date.now();\n if (ev) {\n speechEndTime = speechEndTime - ev.silenceDuration;\n }\n this.agentSession._updateUserState('listening', speechEndTime);\n }\n\n onVADInferenceDone(ev: VADEvent): void {\n if (this.turnDetection === 'manual' || this.turnDetection === 'realtime_llm') {\n // skip speech handle interruption for manual and realtime model\n return;\n }\n\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.turnDetection) {\n // skip speech handle interruption if server side turn detection is enabled\n return;\n }\n\n if (ev.speechDuration < this.agentSession.options.minInterruptionDuration) {\n return;\n }\n\n if (this.stt && this.agentSession.options.minInterruptionWords > 0 && this.audioRecognition) {\n const text = this.audioRecognition.currentTranscript;\n\n // TODO(shubhra): better word splitting for multi-language\n if (text && splitWords(text, true).length < this.agentSession.options.minInterruptionWords) {\n return;\n }\n }\n\n this.realtimeSession?.startUserActivity();\n\n if (\n this._currentSpeech &&\n !this._currentSpeech.interrupted &&\n this._currentSpeech.allowInterruptions\n ) {\n this.logger.info({ 'speech id': this._currentSpeech.id }, 'speech interrupted by VAD');\n this.realtimeSession?.interrupt();\n this._currentSpeech.interrupt();\n }\n }\n\n onInterimTranscript(ev: SpeechEvent): void {\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.userTranscription) {\n // skip stt transcription if userTranscription is enabled on the realtime model\n return;\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n transcript: ev.alternatives![0].text,\n isFinal: false,\n language: ev.alternatives![0].language,\n // TODO(AJS-106): add multi participant support\n }),\n );\n }\n\n onFinalTranscript(ev: SpeechEvent): void {\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.userTranscription) {\n // skip stt transcription if userTranscription is enabled on the realtime model\n return;\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.UserInputTranscribed,\n createUserInputTranscribedEvent({\n transcript: ev.alternatives![0].text,\n isFinal: true,\n language: ev.alternatives![0].language,\n // TODO(AJS-106): add multi participant support\n }),\n );\n }\n\n onPreemptiveGeneration(info: PreemptiveGenerationInfo): void {\n if (\n !this.agentSession.options.preemptiveGeneration ||\n this.draining ||\n (this._currentSpeech !== undefined && !this._currentSpeech.interrupted) ||\n !(this.llm instanceof LLM)\n ) {\n return;\n }\n\n this.cancelPreemptiveGeneration();\n\n this.logger.info(\n {\n newTranscript: info.newTranscript,\n transcriptConfidence: info.transcriptConfidence,\n },\n 'starting preemptive generation',\n );\n\n const userMessage = ChatMessage.create({\n role: 'user',\n content: info.newTranscript,\n });\n const chatCtx = this.agent.chatCtx.copy();\n const speechHandle = this.generateReply({\n userMessage,\n chatCtx,\n scheduleSpeech: false,\n });\n\n this._preemptiveGeneration = {\n speechHandle,\n userMessage,\n info,\n chatCtx: chatCtx.copy(),\n tools: { ...this.tools },\n toolChoice: this.toolChoice,\n createdAt: Date.now(),\n };\n }\n\n private cancelPreemptiveGeneration(): void {\n if (this._preemptiveGeneration !== undefined) {\n this._preemptiveGeneration.speechHandle._cancel();\n this._preemptiveGeneration = undefined;\n }\n }\n\n private createSpeechTask(options: {\n task: Task<void>;\n ownedSpeechHandle?: SpeechHandle;\n name?: string;\n }): Promise<void> {\n const { task, ownedSpeechHandle } = options;\n\n this.speechTasks.add(task);\n task.addDoneCallback(() => {\n this.speechTasks.delete(task);\n });\n\n if (ownedSpeechHandle) {\n ownedSpeechHandle._tasks.push(task);\n task.addDoneCallback(() => {\n if (ownedSpeechHandle._tasks.every((t) => t.done)) {\n ownedSpeechHandle._markDone();\n }\n });\n }\n\n task.addDoneCallback(() => {\n this.wakeupMainTask();\n });\n\n return task.result;\n }\n\n async onEndOfTurn(info: EndOfTurnInfo): Promise<boolean> {\n if (this.draining) {\n this.cancelPreemptiveGeneration();\n this.logger.warn({ user_input: info.newTranscript }, 'skipping user input, task is draining');\n // copied from python:\n // TODO(shubhra): should we \"forward\" this new turn to the next agent/activity?\n return true;\n }\n\n if (\n this.stt &&\n this.turnDetection !== 'manual' &&\n this._currentSpeech &&\n this._currentSpeech.allowInterruptions &&\n !this._currentSpeech.interrupted &&\n this.agentSession.options.minInterruptionWords > 0 &&\n info.newTranscript.split(' ').length < this.agentSession.options.minInterruptionWords\n ) {\n // avoid interruption if the new_transcript is too short\n this.cancelPreemptiveGeneration();\n this.logger.info('skipping user input, new_transcript is too short');\n return false;\n }\n\n const oldTask = this._userTurnCompletedTask;\n this._userTurnCompletedTask = this.createSpeechTask({\n task: Task.from(() => this.userTurnCompleted(info, oldTask)),\n name: 'AgentActivity.userTurnCompleted',\n });\n return true;\n }\n\n retrieveChatCtx(): ChatContext {\n return this.agentSession.chatCtx;\n }\n\n private async mainTask(signal: AbortSignal): Promise<void> {\n const abortFuture = new Future();\n const abortHandler = () => {\n abortFuture.resolve();\n signal.removeEventListener('abort', abortHandler);\n };\n signal.addEventListener('abort', abortHandler);\n\n while (true) {\n await Promise.race([this.q_updated.await, abortFuture.await]);\n if (signal.aborted) break;\n\n while (this.speechQueue.size() > 0) {\n if (signal.aborted) break;\n\n const heapItem = this.speechQueue.pop();\n if (!heapItem) {\n throw new Error('Speech queue is empty');\n }\n const speechHandle = heapItem[2];\n this._currentSpeech = speechHandle;\n speechHandle._authorizeGeneration();\n await speechHandle._waitForGeneration();\n this._currentSpeech = undefined;\n }\n\n // If we're draining and there are no more speech tasks, we can exit.\n // Only speech tasks can bypass draining to create a tool response\n if (this.draining && this.speechTasks.size === 0) {\n this.logger.info('mainTask: draining and no more speech tasks');\n break;\n }\n\n this.q_updated = new Future();\n }\n\n this.logger.info('AgentActivity mainTask: exiting');\n }\n\n private wakeupMainTask(): void {\n this.q_updated.resolve();\n }\n\n generateReply(options: {\n userMessage?: ChatMessage;\n chatCtx?: ChatContext;\n instructions?: string;\n toolChoice?: ToolChoice | null;\n allowInterruptions?: boolean;\n scheduleSpeech?: boolean;\n }): SpeechHandle {\n const {\n userMessage,\n chatCtx,\n instructions: defaultInstructions,\n toolChoice: defaultToolChoice,\n allowInterruptions: defaultAllowInterruptions,\n scheduleSpeech = true,\n } = options;\n\n let instructions = defaultInstructions;\n let toolChoice = defaultToolChoice;\n let allowInterruptions = defaultAllowInterruptions;\n\n if (\n this.llm instanceof RealtimeModel &&\n this.llm.capabilities.turnDetection &&\n allowInterruptions === false\n ) {\n this.logger.warn(\n 'the RealtimeModel uses a server-side turn detection, allowInterruptions cannot be false when using VoiceAgent.generateReply(), ' +\n 'disable turnDetection in the RealtimeModel and use VAD on the AgentTask/VoiceAgent instead',\n );\n allowInterruptions = true;\n }\n\n if (this.llm === undefined) {\n throw new Error('trying to generate reply without an LLM model');\n }\n\n const functionCall = asyncLocalStorage.getStore()?.functionCall;\n if (toolChoice === undefined && functionCall !== undefined) {\n // when generateReply is called inside a tool, set toolChoice to 'none' by default\n toolChoice = 'none';\n }\n\n const handle = SpeechHandle.create({\n allowInterruptions: allowInterruptions ?? this.allowInterruptions,\n });\n\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: true,\n source: 'generate_reply',\n speechHandle: handle,\n }),\n );\n this.logger.info({ speech_id: handle.id }, 'Creating speech handle');\n\n if (this.llm instanceof RealtimeModel) {\n this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.realtimeReplyTask({\n speechHandle: handle,\n // TODO(brian): support llm.ChatMessage for the realtime model\n userInput: userMessage?.textContent,\n instructions,\n modelSettings: {\n // isGiven(toolChoice) = toolChoice !== undefined\n toolChoice: toOaiToolChoice(toolChoice !== undefined ? toolChoice : this.toolChoice),\n },\n abortController,\n }),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.realtimeReply',\n });\n } else if (this.llm instanceof LLM) {\n // instructions used inside generateReply are \"extra\" instructions.\n // this matches the behavior of the Realtime API:\n // https://platform.openai.com/docs/api-reference/realtime-client-events/response/create\n if (instructions) {\n instructions = `${this.agent.instructions}\\n${instructions}`;\n }\n\n const task = this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.pipelineReplyTask(\n handle,\n chatCtx ?? this.agent.chatCtx,\n this.agent.toolCtx,\n {\n toolChoice: toOaiToolChoice(toolChoice !== undefined ? toolChoice : this.toolChoice),\n },\n abortController,\n instructions ? `${this.agent.instructions}\\n${instructions}` : instructions,\n userMessage,\n ),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.pipelineReply',\n });\n\n task.finally(() => this.onPipelineReplyDone());\n }\n\n if (scheduleSpeech) {\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n }\n return handle;\n }\n\n interrupt(): Future<void> {\n const future = new Future<void>();\n const currentSpeech = this._currentSpeech;\n\n //TODO(AJS-273): add interrupt for background speeches\n\n currentSpeech?.interrupt();\n\n for (const [_, __, speech] of this.speechQueue) {\n speech.interrupt();\n }\n\n this.realtimeSession?.interrupt();\n\n if (currentSpeech === undefined) {\n future.resolve();\n } else {\n currentSpeech.addDoneCallback(() => {\n if (future.done) return;\n future.resolve();\n });\n }\n\n return future;\n }\n\n private onPipelineReplyDone(): void {\n if (!this.speechQueue.peek() && (!this._currentSpeech || this._currentSpeech.done())) {\n this.agentSession._updateAgentState('listening');\n }\n }\n\n private async userTurnCompleted(info: EndOfTurnInfo, oldTask?: Promise<void>): Promise<void> {\n if (oldTask) {\n // We never cancel user code as this is very confusing.\n // So we wait for the old execution of onUserTurnCompleted to finish.\n // In practice this is OK because most speeches will be interrupted if a new turn\n // is detected. So the previous execution should complete quickly.\n await oldTask;\n }\n\n // When the audio recognition detects the end of a user turn:\n // - check if realtime model server-side turn detection is enabled\n // - check if there is no current generation happening\n // - cancel the current generation if it allows interruptions (otherwise skip this current\n // turn)\n // - generate a reply to the user input\n\n if (this.llm instanceof RealtimeModel) {\n if (this.llm.capabilities.turnDetection) {\n return;\n }\n this.realtimeSession?.commitAudio();\n }\n\n if (this._currentSpeech) {\n if (!this._currentSpeech.allowInterruptions) {\n this.logger.warn(\n { user_input: info.newTranscript },\n 'skipping user input, current speech generation cannot be interrupted',\n );\n return;\n }\n\n this.logger.info(\n { 'speech id': this._currentSpeech.id },\n 'speech interrupted, new user turn detected',\n );\n\n this._currentSpeech.interrupt();\n this.realtimeSession?.interrupt();\n }\n\n let userMessage: ChatMessage | undefined = ChatMessage.create({\n role: 'user',\n content: info.newTranscript,\n });\n\n // create a temporary mutable chat context to pass to onUserTurnCompleted\n // the user can edit it for the current generation, but changes will not be kept inside the\n // Agent.chatCtx\n const chatCtx = this.agent.chatCtx.copy();\n const startTime = Date.now();\n\n try {\n await this.agent.onUserTurnCompleted(chatCtx, userMessage);\n } catch (e) {\n if (e instanceof StopResponse) {\n return;\n }\n this.logger.error({ error: e }, 'error occurred during onUserTurnCompleted');\n }\n\n const callbackDuration = Date.now() - startTime;\n\n if (this.llm instanceof RealtimeModel) {\n // ignore stt transcription for realtime model\n userMessage = undefined;\n } else if (this.llm === undefined) {\n return;\n }\n\n let speechHandle: SpeechHandle | undefined;\n if (this._preemptiveGeneration !== undefined) {\n const preemptive = this._preemptiveGeneration;\n // make sure the onUserTurnCompleted didn't change some request parameters\n // otherwise invalidate the preemptive generation\n if (\n preemptive.info.newTranscript === userMessage?.textContent &&\n preemptive.chatCtx.isEquivalent(chatCtx) &&\n isSameToolContext(preemptive.tools, this.tools) &&\n isSameToolChoice(preemptive.toolChoice, this.toolChoice)\n ) {\n speechHandle = preemptive.speechHandle;\n this.scheduleSpeech(speechHandle, SpeechHandle.SPEECH_PRIORITY_NORMAL);\n this.logger.debug(\n {\n preemptiveLeadTime: Date.now() - preemptive.createdAt,\n },\n 'using preemptive generation',\n );\n } else {\n this.logger.warn(\n 'preemptive generation enabled but chat context or tools have changed after `onUserTurnCompleted`',\n );\n preemptive.speechHandle._cancel();\n }\n\n this._preemptiveGeneration = undefined;\n }\n\n if (speechHandle === undefined) {\n // Ensure the new message is passed to generateReply\n // This preserves the original message id, making it easier for users to track responses\n speechHandle = this.generateReply({ userMessage, chatCtx });\n }\n\n const eouMetrics: EOUMetrics = {\n type: 'eou_metrics',\n timestamp: Date.now(),\n endOfUtteranceDelayMs: info.endOfUtteranceDelay,\n transcriptionDelayMs: info.transcriptionDelay,\n onUserTurnCompletedDelayMs: callbackDuration,\n lastSpeakingTimeMs: info.stoppedSpeakingAt ?? 0,\n speechId: speechHandle.id,\n };\n\n this.agentSession.emit(\n AgentSessionEventTypes.MetricsCollected,\n createMetricsCollectedEvent({ metrics: eouMetrics }),\n );\n }\n\n private async ttsTask(\n speechHandle: SpeechHandle,\n text: string | ReadableStream<string>,\n addToChatCtx: boolean,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n audio?: ReadableStream<AudioFrame> | null,\n ): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n const transcriptionOutput = this.agentSession.output.transcriptionEnabled\n ? this.agentSession.output.transcription\n : null;\n\n const audioOutput = this.agentSession.output.audioEnabled\n ? this.agentSession.output.audio\n : null;\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n\n if (speechHandle.interrupted) {\n return;\n }\n\n let baseStream: ReadableStream<string>;\n if (text instanceof ReadableStream) {\n baseStream = text;\n } else {\n baseStream = new ReadableStream({\n start(controller) {\n controller.enqueue(text);\n controller.close();\n },\n });\n }\n\n const [textSource, audioSource] = baseStream.tee();\n\n const tasks: Array<Task<void>> = [];\n\n const trNode = await this.agent.transcriptionNode(textSource, {});\n let textOut: _TextOut | null = null;\n if (trNode) {\n const [textForwardTask, _textOut] = performTextForwarding(\n trNode,\n replyAbortController,\n transcriptionOutput,\n );\n textOut = _textOut;\n tasks.push(textForwardTask);\n }\n\n const onFirstFrame = () => {\n this.agentSession._updateAgentState('speaking');\n };\n\n if (!audioOutput) {\n if (textOut) {\n textOut.firstTextFut.await.finally(onFirstFrame);\n }\n } else {\n let audioOut: _AudioOut | null = null;\n if (!audio) {\n // generate audio using TTS\n const [ttsTask, ttsStream] = performTTSInference(\n (...args) => this.agent.ttsNode(...args),\n audioSource,\n modelSettings,\n replyAbortController,\n );\n tasks.push(ttsTask);\n\n const [forwardTask, _audioOut] = performAudioForwarding(\n ttsStream,\n audioOutput,\n replyAbortController,\n );\n tasks.push(forwardTask);\n audioOut = _audioOut;\n } else {\n // use the provided audio\n const [forwardTask, _audioOut] = performAudioForwarding(\n audio,\n audioOutput,\n replyAbortController,\n );\n tasks.push(forwardTask);\n audioOut = _audioOut;\n }\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n }\n\n await speechHandle.waitIfNotInterrupted(tasks.map((task) => task.result));\n\n if (audioOutput) {\n await speechHandle.waitIfNotInterrupted([audioOutput.waitForPlayout()]);\n }\n\n if (speechHandle.interrupted) {\n replyAbortController.abort();\n await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n if (audioOutput) {\n audioOutput.clearBuffer();\n await audioOutput.waitForPlayout();\n }\n }\n\n if (addToChatCtx) {\n const message = ChatMessage.create({\n role: 'assistant',\n content: textOut?.text || '',\n interrupted: speechHandle.interrupted,\n });\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n }\n\n if (this.agentSession.agentState === 'speaking') {\n this.agentSession._updateAgentState('listening');\n }\n }\n\n private async pipelineReplyTask(\n speechHandle: SpeechHandle,\n chatCtx: ChatContext,\n toolCtx: ToolContext,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n instructions?: string,\n newMessage?: ChatMessage,\n toolsMessages?: ChatItem[],\n ): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n const audioOutput = this.agentSession.output.audioEnabled\n ? this.agentSession.output.audio\n : null;\n const transcriptionOutput = this.agentSession.output.transcriptionEnabled\n ? this.agentSession.output.transcription\n : null;\n\n chatCtx = chatCtx.copy();\n\n // Insert new message into temporary chat context for LLM inference\n if (newMessage) {\n chatCtx.insert(newMessage);\n }\n\n if (instructions) {\n try {\n updateInstructions({\n chatCtx,\n instructions,\n addIfMissing: true,\n });\n } catch (e) {\n this.logger.error({ error: e }, 'error occurred during updateInstructions');\n }\n }\n\n const tasks: Array<Task<void>> = [];\n const [llmTask, llmGenData] = performLLMInference(\n // preserve `this` context in llmNode\n (...args) => this.agent.llmNode(...args),\n chatCtx,\n toolCtx,\n modelSettings,\n replyAbortController,\n );\n tasks.push(llmTask);\n\n const [ttsTextInput, llmOutput] = llmGenData.textStream.tee();\n\n let ttsTask: Task<void> | null = null;\n let ttsStream: ReadableStream<AudioFrame> | null = null;\n if (audioOutput) {\n [ttsTask, ttsStream] = performTTSInference(\n (...args) => this.agent.ttsNode(...args),\n ttsTextInput,\n modelSettings,\n replyAbortController,\n );\n tasks.push(ttsTask);\n }\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForScheduled()]);\n\n // Add new message to actual chat context if the speech is scheduled\n if (newMessage && speechHandle.scheduled) {\n this.agent._chatCtx.insert(newMessage);\n this.agentSession._conversationItemAdded(newMessage);\n }\n\n if (speechHandle.interrupted) {\n replyAbortController.abort();\n await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n return;\n }\n\n this.agentSession._updateAgentState('thinking');\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n speechHandle._clearAuthorization();\n\n const replyStartedAt = Date.now();\n const trNodeResult = await this.agent.transcriptionNode(llmOutput, modelSettings);\n let textOut: _TextOut | null = null;\n if (trNodeResult) {\n const [textForwardTask, _textOut] = performTextForwarding(\n trNodeResult,\n replyAbortController,\n transcriptionOutput,\n );\n tasks.push(textForwardTask);\n textOut = _textOut;\n }\n\n const onFirstFrame = () => {\n this.agentSession._updateAgentState('speaking');\n };\n\n let audioOut: _AudioOut | null = null;\n if (audioOutput) {\n if (ttsStream) {\n const [forwardTask, _audioOut] = performAudioForwarding(\n ttsStream,\n audioOutput,\n replyAbortController,\n );\n audioOut = _audioOut;\n tasks.push(forwardTask);\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n } else {\n throw Error('ttsStream is null when audioOutput is enabled');\n }\n } else {\n textOut?.firstTextFut.await.finally(onFirstFrame);\n }\n\n //TODO(AJS-272): before executing tools, make sure we generated all the text\n // (this ensure everything is kept ordered)\n\n const onToolExecutionStarted = (_: FunctionCall) => {\n // TODO(brian): handle speech_handle item_added\n };\n\n const onToolExecutionCompleted = (_: ToolExecutionOutput) => {\n // TODO(brian): handle speech_handle item_added\n };\n\n const [executeToolsTask, toolOutput] = performToolExecutions({\n session: this.agentSession,\n speechHandle,\n toolCtx,\n toolChoice: modelSettings.toolChoice,\n toolCallStream: llmGenData.toolCallStream,\n controller: replyAbortController,\n onToolExecutionStarted,\n onToolExecutionCompleted,\n });\n\n await speechHandle.waitIfNotInterrupted(tasks.map((task) => task.result));\n\n if (audioOutput) {\n await speechHandle.waitIfNotInterrupted([audioOutput.waitForPlayout()]);\n }\n\n // add the tools messages that triggers this reply to the chat context\n if (toolsMessages) {\n for (const msg of toolsMessages) {\n msg.createdAt = replyStartedAt;\n }\n this.agent._chatCtx.insert(toolsMessages);\n }\n\n if (speechHandle.interrupted) {\n this.logger.debug(\n { speech_id: speechHandle.id },\n 'Aborting all pipeline reply tasks due to interruption',\n );\n replyAbortController.abort();\n await Promise.allSettled(\n tasks.map((task) => task.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT)),\n );\n\n let forwardedText = textOut?.text || '';\n\n if (audioOutput) {\n audioOutput.clearBuffer();\n const playbackEv = await audioOutput.waitForPlayout();\n if (audioOut?.firstFrameFut.done) {\n // playback EV is valid only if the first frame was already played\n this.logger.info(\n { speech_id: speechHandle.id, playbackPosition: playbackEv.playbackPosition },\n 'playout interrupted',\n );\n if (playbackEv.synchronizedTranscript) {\n forwardedText = playbackEv.synchronizedTranscript;\n }\n } else {\n forwardedText = '';\n }\n }\n\n if (forwardedText) {\n const message = ChatMessage.create({\n role: 'assistant',\n content: forwardedText,\n id: llmGenData.id,\n interrupted: true,\n createdAt: replyStartedAt,\n });\n chatCtx.insert(message);\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n }\n\n if (this.agentSession.agentState === 'speaking') {\n this.agentSession._updateAgentState('listening');\n }\n\n this.logger.info(\n { speech_id: speechHandle.id, message: forwardedText },\n 'playout completed with interrupt',\n );\n // TODO(shubhra) add chat message to speech handle\n speechHandle._markGenerationDone();\n await executeToolsTask.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n return;\n }\n\n if (textOut && textOut.text) {\n const message = ChatMessage.create({\n role: 'assistant',\n id: llmGenData.id,\n interrupted: false,\n createdAt: replyStartedAt,\n content: textOut.text,\n });\n chatCtx.insert(message);\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n this.logger.info(\n { speech_id: speechHandle.id, message: textOut.text },\n 'playout completed without interruption',\n );\n }\n\n if (toolOutput.output.length > 0) {\n this.agentSession._updateAgentState('thinking');\n } else if (this.agentSession.agentState === 'speaking') {\n this.agentSession._updateAgentState('listening');\n }\n\n // mark the playout done before waiting for the tool execution\n speechHandle._markGenerationDone();\n await executeToolsTask.result;\n\n if (toolOutput.output.length === 0) return;\n\n // important: no agent output should be used after this point\n const { maxToolSteps } = this.agentSession.options;\n if (speechHandle.numSteps >= maxToolSteps) {\n this.logger.warn(\n { speech_id: speechHandle.id, max_tool_steps: maxToolSteps },\n 'maximum number of function calls steps reached',\n );\n return;\n }\n\n const functionToolsExecutedEvent = createFunctionToolsExecutedEvent({\n functionCalls: [],\n functionCallOutputs: [],\n });\n let shouldGenerateToolReply: boolean = false;\n let newAgentTask: Agent | null = null;\n let ignoreTaskSwitch: boolean = false;\n\n for (const sanitizedOut of toolOutput.output) {\n if (sanitizedOut.toolCallOutput !== undefined) {\n functionToolsExecutedEvent.functionCalls.push(sanitizedOut.toolCall);\n functionToolsExecutedEvent.functionCallOutputs.push(sanitizedOut.toolCallOutput);\n if (sanitizedOut.replyRequired) {\n shouldGenerateToolReply = true;\n }\n }\n\n if (newAgentTask !== null && sanitizedOut.agentTask !== undefined) {\n this.logger.error('expected to receive only one agent task from the tool executions');\n ignoreTaskSwitch = true;\n // TODO(brian): should we mark the function call as failed to notify the LLM?\n }\n\n newAgentTask = sanitizedOut.agentTask ?? null;\n\n this.logger.debug(\n {\n speechId: speechHandle.id,\n name: sanitizedOut.toolCall?.name,\n args: sanitizedOut.toolCall.args,\n output: sanitizedOut.toolCallOutput?.output,\n isError: sanitizedOut.toolCallOutput?.isError,\n },\n 'Tool call execution finished',\n );\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.FunctionToolsExecuted,\n functionToolsExecutedEvent,\n );\n\n let draining = this.draining;\n if (!ignoreTaskSwitch && newAgentTask !== null) {\n this.agentSession.updateAgent(newAgentTask);\n draining = true;\n }\n\n const toolMessages = [\n ...functionToolsExecutedEvent.functionCalls,\n ...functionToolsExecutedEvent.functionCallOutputs,\n ] as ChatItem[];\n if (shouldGenerateToolReply) {\n chatCtx.insert(toolMessages);\n\n const handle = SpeechHandle.create({\n allowInterruptions: speechHandle.allowInterruptions,\n stepIndex: speechHandle._stepIndex + 1,\n parent: speechHandle,\n });\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: false,\n source: 'tool_response',\n speechHandle: handle,\n }),\n );\n\n // Avoid setting tool_choice to \"required\" or a specific function when\n // passing tool response back to the LLM\n const respondToolChoice = draining || modelSettings.toolChoice === 'none' ? 'none' : 'auto';\n\n const toolResponseTask = this.createSpeechTask({\n task: Task.from(() =>\n this.pipelineReplyTask(\n handle,\n chatCtx,\n toolCtx,\n { toolChoice: respondToolChoice },\n replyAbortController,\n instructions,\n undefined,\n toolMessages,\n ),\n ),\n ownedSpeechHandle: handle,\n name: 'AgentActivity.pipelineReply',\n });\n\n toolResponseTask.finally(() => this.onPipelineReplyDone());\n\n this.scheduleSpeech(handle, SpeechHandle.SPEECH_PRIORITY_NORMAL, true);\n } else if (functionToolsExecutedEvent.functionCallOutputs.length > 0) {\n for (const msg of toolMessages) {\n msg.createdAt = replyStartedAt;\n }\n this.agent._chatCtx.insert(toolMessages);\n }\n }\n\n private async realtimeGenerationTask(\n speechHandle: SpeechHandle,\n ev: GenerationCreatedEvent,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n ): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n if (!this.realtimeSession) {\n throw new Error('realtime session is not initialized');\n }\n if (!(this.llm instanceof RealtimeModel)) {\n throw new Error('llm is not a realtime model');\n }\n\n this.logger.debug(\n { speech_id: speechHandle.id, stepIndex: speechHandle.numSteps },\n 'realtime generation started',\n );\n\n const audioOutput = this.agentSession.output.audioEnabled\n ? this.agentSession.output.audio\n : null;\n const textOutput = this.agentSession.output.transcriptionEnabled\n ? this.agentSession.output.transcription\n : null;\n const toolCtx = this.realtimeSession.tools;\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n speechHandle._clearAuthorization();\n\n if (speechHandle.interrupted) {\n return;\n }\n\n const onFirstFrame = () => {\n this.agentSession._updateAgentState('speaking');\n };\n\n const readMessages = async (\n abortController: AbortController,\n outputs: Array<[string, _TextOut | null, _AudioOut | null]>,\n ) => {\n replyAbortController.signal.addEventListener('abort', () => abortController.abort(), {\n once: true,\n });\n\n const forwardTasks: Array<Task<void>> = [];\n try {\n for await (const msg of ev.messageStream) {\n if (forwardTasks.length > 0) {\n this.logger.warn(\n 'expected to receive only one message generation from the realtime API',\n );\n break;\n }\n const trNodeResult = await this.agent.transcriptionNode(msg.textStream, modelSettings);\n let textOut: _TextOut | null = null;\n if (trNodeResult) {\n const [textForwardTask, _textOut] = performTextForwarding(\n trNodeResult,\n abortController,\n textOutput,\n );\n forwardTasks.push(textForwardTask);\n textOut = _textOut;\n }\n let audioOut: _AudioOut | null = null;\n if (audioOutput) {\n const realtimeAudio = await this.agent.realtimeAudioOutputNode(\n msg.audioStream,\n modelSettings,\n );\n if (realtimeAudio) {\n const [forwardTask, _audioOut] = performAudioForwarding(\n realtimeAudio,\n audioOutput,\n abortController,\n );\n forwardTasks.push(forwardTask);\n audioOut = _audioOut;\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n } else {\n this.logger.warn(\n 'audio output is enabled but neither tts nor realtime audio is available',\n );\n }\n } else if (textOut) {\n textOut.firstTextFut.await.finally(onFirstFrame);\n }\n outputs.push([msg.messageId, textOut, audioOut]);\n }\n await waitFor(forwardTasks);\n } catch (error) {\n this.logger.error(error, 'error reading messages from the realtime API');\n } finally {\n await cancelAndWait(forwardTasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n }\n };\n\n const messageOutputs: Array<[string, _TextOut | null, _AudioOut | null]> = [];\n const tasks = [\n Task.from(\n (controller) => readMessages(controller, messageOutputs),\n undefined,\n 'AgentActivity.realtime_generation.read_messages',\n ),\n ];\n\n const [toolCallStream, toolCallStreamForTracing] = ev.functionStream.tee();\n // TODO(brian): append to tracing tees\n const toolCalls: FunctionCall[] = [];\n\n const readToolStreamTask = async (\n controller: AbortController,\n stream: ReadableStream<FunctionCall>,\n ) => {\n const reader = stream.getReader();\n try {\n while (!controller.signal.aborted) {\n const { done, value } = await reader.read();\n if (done) break;\n\n this.logger.debug({ tool_call: value }, 'received tool call from the realtime API');\n toolCalls.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n };\n\n tasks.push(\n Task.from(\n (controller) => readToolStreamTask(controller, toolCallStreamForTracing),\n replyAbortController,\n 'AgentActivity.realtime_generation.read_tool_stream',\n ),\n );\n\n const onToolExecutionStarted = (f: FunctionCall) => {\n speechHandle._itemAdded([f]);\n };\n\n const onToolExecutionCompleted = (out: ToolExecutionOutput) => {\n if (out.toolCallOutput) {\n speechHandle._itemAdded([out.toolCallOutput]);\n }\n };\n\n const [executeToolsTask, toolOutput] = performToolExecutions({\n session: this.agentSession,\n speechHandle,\n toolCtx,\n toolCallStream,\n toolChoice: modelSettings.toolChoice,\n controller: replyAbortController,\n onToolExecutionStarted,\n onToolExecutionCompleted,\n });\n\n await speechHandle.waitIfNotInterrupted(tasks.map((task) => task.result));\n\n // TODO(brian): add tracing span\n\n if (audioOutput) {\n await speechHandle.waitIfNotInterrupted([audioOutput.waitForPlayout()]);\n this.agentSession._updateAgentState('listening');\n }\n\n if (speechHandle.interrupted) {\n this.logger.debug(\n { speech_id: speechHandle.id },\n 'Aborting all realtime generation tasks due to interruption',\n );\n replyAbortController.abort();\n await cancelAndWait(tasks, AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n\n if (messageOutputs.length > 0) {\n // there should be only one message\n const [msgId, textOut, audioOut] = messageOutputs[0]!;\n let forwardedText = textOut?.text || '';\n\n if (audioOutput) {\n audioOutput.clearBuffer();\n const playbackEv = await audioOutput.waitForPlayout();\n let playbackPosition = playbackEv.playbackPosition;\n if (audioOut?.firstFrameFut.done) {\n // playback EV is valid only if the first frame was already played\n this.logger.info(\n { speech_id: speechHandle.id, playbackPosition: playbackEv.playbackPosition },\n 'playout interrupted',\n );\n if (playbackEv.synchronizedTranscript) {\n forwardedText = playbackEv.synchronizedTranscript;\n }\n } else {\n forwardedText = '';\n playbackPosition = 0;\n }\n\n // truncate server-side message\n this.realtimeSession.truncate({\n messageId: msgId,\n audioEndMs: Math.floor(playbackPosition),\n });\n }\n\n if (forwardedText) {\n const message = ChatMessage.create({\n role: 'assistant',\n content: forwardedText,\n id: msgId,\n interrupted: true,\n });\n this.agent._chatCtx.insert(message);\n speechHandle._itemAdded([message]);\n this.agentSession._conversationItemAdded(message);\n\n // TODO(brian): add tracing span\n }\n this.logger.info(\n { speech_id: speechHandle.id, message: forwardedText },\n 'playout completed with interrupt',\n );\n }\n speechHandle._markGenerationDone();\n await executeToolsTask.cancelAndWait(AgentActivity.REPLY_TASK_CANCEL_TIMEOUT);\n\n // TODO(brian): close tees\n return;\n }\n\n if (messageOutputs.length > 0) {\n // there should be only one message\n const [msgId, textOut, _] = messageOutputs[0]!;\n const message = ChatMessage.create({\n role: 'assistant',\n content: textOut?.text || '',\n id: msgId,\n interrupted: false,\n });\n this.agent._chatCtx.insert(message);\n speechHandle._itemAdded([message]);\n this.agentSession._conversationItemAdded(message); // mark the playout done before waiting for the tool execution\\\n // TODO(brian): add tracing span\n }\n\n // mark the playout done before waiting for the tool execution\n speechHandle._markGenerationDone();\n // TODO(brian): close tees\n\n toolOutput.firstToolStartedFuture.await.finally(() => {\n this.agentSession._updateAgentState('thinking');\n });\n\n await executeToolsTask.result;\n\n if (toolOutput.output.length === 0) return;\n\n // important: no agent ouput should be used after this point\n const { maxToolSteps } = this.agentSession.options;\n if (speechHandle.numSteps >= maxToolSteps) {\n this.logger.warn(\n { speech_id: speechHandle.id, max_tool_steps: maxToolSteps },\n 'maximum number of function calls steps reached',\n );\n return;\n }\n\n const functionToolsExecutedEvent = createFunctionToolsExecutedEvent({\n functionCalls: [],\n functionCallOutputs: [],\n });\n let shouldGenerateToolReply: boolean = false;\n let newAgentTask: Agent | null = null;\n let ignoreTaskSwitch: boolean = false;\n\n for (const sanitizedOut of toolOutput.output) {\n if (sanitizedOut.toolCallOutput !== undefined) {\n functionToolsExecutedEvent.functionCallOutputs.push(sanitizedOut.toolCallOutput);\n if (sanitizedOut.replyRequired) {\n shouldGenerateToolReply = true;\n }\n }\n\n if (newAgentTask !== null && sanitizedOut.agentTask !== undefined) {\n this.logger.error('expected to receive only one agent task from the tool executions');\n ignoreTaskSwitch = true;\n }\n\n newAgentTask = sanitizedOut.agentTask ?? null;\n\n this.logger.debug(\n {\n speechId: speechHandle.id,\n name: sanitizedOut.toolCall?.name,\n args: sanitizedOut.toolCall.args,\n output: sanitizedOut.toolCallOutput?.output,\n isError: sanitizedOut.toolCallOutput?.isError,\n },\n 'Tool call execution finished',\n );\n }\n\n this.agentSession.emit(\n AgentSessionEventTypes.FunctionToolsExecuted,\n functionToolsExecutedEvent,\n );\n\n let draining = this.draining;\n if (!ignoreTaskSwitch && newAgentTask !== null) {\n this.agentSession.updateAgent(newAgentTask);\n draining = true;\n }\n\n if (functionToolsExecutedEvent.functionCallOutputs.length > 0) {\n // wait all speeches played before updating the tool output and generating the response\n // most realtime models dont support generating multiple responses at the same time\n while (this.currentSpeech || this.speechQueue.size() > 0) {\n if (\n this.currentSpeech &&\n !this.currentSpeech.done() &&\n this.currentSpeech !== speechHandle\n ) {\n await this.currentSpeech.waitForPlayout();\n } else {\n // Don't block the event loop\n await new Promise((resolve) => setImmediate(resolve));\n }\n }\n const chatCtx = this.realtimeSession.chatCtx.copy();\n chatCtx.items.push(...functionToolsExecutedEvent.functionCallOutputs);\n try {\n await this.realtimeSession.updateChatCtx(chatCtx);\n } catch (error) {\n this.logger.warn(\n { error },\n 'failed to update chat context before generating the function calls results',\n );\n }\n }\n\n // skip realtime reply if not required or auto-generated\n if (!shouldGenerateToolReply || this.llm.capabilities.autoToolReplyGeneration) {\n return;\n }\n\n this.realtimeSession.interrupt();\n\n const replySpeechHandle = SpeechHandle.create({\n allowInterruptions: speechHandle.allowInterruptions,\n stepIndex: speechHandle.numSteps + 1,\n parent: speechHandle,\n });\n this.agentSession.emit(\n AgentSessionEventTypes.SpeechCreated,\n createSpeechCreatedEvent({\n userInitiated: false,\n source: 'tool_response',\n speechHandle: replySpeechHandle,\n }),\n );\n\n const toolChoice = draining || modelSettings.toolChoice === 'none' ? 'none' : 'auto';\n this.createSpeechTask({\n task: Task.from((abortController: AbortController) =>\n this.realtimeReplyTask({\n speechHandle: replySpeechHandle,\n modelSettings: { toolChoice },\n abortController,\n }),\n ),\n ownedSpeechHandle: replySpeechHandle,\n name: 'AgentActivity.realtime_reply',\n });\n\n this.scheduleSpeech(replySpeechHandle, SpeechHandle.SPEECH_PRIORITY_NORMAL, true);\n }\n\n private async realtimeReplyTask({\n speechHandle,\n modelSettings: { toolChoice },\n userInput,\n instructions,\n abortController,\n }: {\n speechHandle: SpeechHandle;\n modelSettings: ModelSettings;\n abortController: AbortController;\n userInput?: string;\n instructions?: string;\n }): Promise<void> {\n speechHandleStorage.enterWith(speechHandle);\n\n if (!this.realtimeSession) {\n throw new Error('realtime session is not available');\n }\n\n await speechHandle.waitIfNotInterrupted([speechHandle._waitForAuthorization()]);\n\n if (userInput) {\n const chatCtx = this.realtimeSession.chatCtx.copy();\n const message = chatCtx.addMessage({\n role: 'user',\n content: userInput,\n });\n await this.realtimeSession.updateChatCtx(chatCtx);\n this.agent._chatCtx.insert(message);\n this.agentSession._conversationItemAdded(message);\n }\n\n const originalToolChoice = this.toolChoice;\n if (toolChoice !== undefined) {\n this.realtimeSession.updateOptions({ toolChoice });\n }\n\n try {\n const generationEvent = await this.realtimeSession.generateReply(instructions);\n await this.realtimeGenerationTask(\n speechHandle,\n generationEvent,\n { toolChoice },\n abortController,\n );\n } finally {\n // reset toolChoice value\n if (toolChoice !== undefined && toolChoice !== originalToolChoice) {\n this.realtimeSession.updateOptions({ toolChoice: originalToolChoice });\n }\n }\n }\n\n private scheduleSpeech(\n speechHandle: SpeechHandle,\n priority: number,\n force: boolean = false,\n ): void {\n // when force=true, we allow tool responses to bypass draining\n // This allows for tool responses to be generated before the AgentActivity is finalized\n if (this.draining && !force) {\n throw new Error('cannot schedule new speech, the agent is draining');\n }\n\n // Monotonic time to avoid near 0 collisions\n this.speechQueue.push([priority, Number(process.hrtime.bigint()), speechHandle]);\n speechHandle._markScheduled();\n this.wakeupMainTask();\n }\n\n async drain(): Promise<void> {\n const unlock = await this.lock.lock();\n try {\n if (this._draining) return;\n\n this.cancelPreemptiveGeneration();\n this.createSpeechTask({\n task: Task.from(() => this.agent.onExit()),\n name: 'AgentActivity_onExit',\n });\n\n this.wakeupMainTask();\n this._draining = true;\n await this._mainTask?.result;\n } finally {\n unlock();\n }\n }\n\n async close(): Promise<void> {\n const unlock = await this.lock.lock();\n try {\n if (!this._draining) {\n this.logger.warn('task closing without draining');\n }\n\n this.cancelPreemptiveGeneration();\n // Unregister event handlers to prevent duplicate metrics\n if (this.llm instanceof LLM) {\n this.llm.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.realtimeSession) {\n this.realtimeSession.off('generation_created', this.onGenerationCreated);\n this.realtimeSession.off('input_speech_started', this.onInputSpeechStarted);\n this.realtimeSession.off('input_speech_stopped', this.onInputSpeechStopped);\n this.realtimeSession.off(\n 'input_audio_transcription_completed',\n this.onInputAudioTranscriptionCompleted,\n );\n this.realtimeSession.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.stt instanceof STT) {\n this.stt.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.tts instanceof TTS) {\n this.tts.off('metrics_collected', this.onMetricsCollected);\n }\n if (this.vad instanceof VAD) {\n this.vad.off('metrics_collected', this.onMetricsCollected);\n }\n\n this.detachAudioInput();\n await this.realtimeSession?.close();\n await this.audioRecognition?.close();\n await this._mainTask?.cancelAndWait();\n } finally {\n unlock();\n }\n }\n}\n\nfunction toOaiToolChoice(toolChoice: ToolChoice | null): ToolChoice | undefined {\n // we convert null to undefined, which maps to the default provider tool choice value\n return toolChoice !== null ? toolChoice : undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -37,7 +37,8 @@ const defaultVoiceOptions = {
|
|
|
37
37
|
minInterruptionWords: 0,
|
|
38
38
|
minEndpointingDelay: 500,
|
|
39
39
|
maxEndpointingDelay: 6e3,
|
|
40
|
-
maxToolSteps: 3
|
|
40
|
+
maxToolSteps: 3,
|
|
41
|
+
preemptiveGeneration: false
|
|
41
42
|
};
|
|
42
43
|
class AgentSession extends import_node_events.EventEmitter {
|
|
43
44
|
vad;
|
|
@@ -274,7 +275,7 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
274
275
|
);
|
|
275
276
|
}
|
|
276
277
|
/** @internal */
|
|
277
|
-
_updateUserState(state) {
|
|
278
|
+
_updateUserState(state, _lastSpeakingTime) {
|
|
278
279
|
if (this.userState === state) {
|
|
279
280
|
return;
|
|
280
281
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/voice/agent_session.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, Room } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { EventEmitter } from 'node:events';\nimport type { ReadableStream } from 'node:stream/web';\nimport {\n LLM as InferenceLLM,\n STT as InferenceSTT,\n TTS as InferenceTTS,\n type LLMModels,\n type STTModelString,\n type TTSModelString,\n} from '../inference/index.js';\nimport { getJobContext } from '../job.js';\nimport { ChatContext, ChatMessage } from '../llm/chat_context.js';\nimport type { LLM, RealtimeModel, RealtimeModelError, ToolChoice } from '../llm/index.js';\nimport type { LLMError } from '../llm/llm.js';\nimport { log } from '../log.js';\nimport type { STT } from '../stt/index.js';\nimport type { STTError } from '../stt/stt.js';\nimport type { TTS, TTSError } from '../tts/tts.js';\nimport type { VAD } from '../vad.js';\nimport type { Agent } from './agent.js';\nimport { AgentActivity } from './agent_activity.js';\nimport type { _TurnDetector } from './audio_recognition.js';\nimport {\n AgentSessionEventTypes,\n type AgentState,\n type AgentStateChangedEvent,\n type CloseEvent,\n CloseReason,\n type ConversationItemAddedEvent,\n type ErrorEvent,\n type FunctionToolsExecutedEvent,\n type MetricsCollectedEvent,\n type SpeechCreatedEvent,\n type UserInputTranscribedEvent,\n type UserState,\n type UserStateChangedEvent,\n createAgentStateChangedEvent,\n createCloseEvent,\n createConversationItemAddedEvent,\n createUserStateChangedEvent,\n} from './events.js';\nimport { AgentInput, AgentOutput } from './io.js';\nimport { RoomIO, type RoomInputOptions, type RoomOutputOptions } from './room_io/index.js';\nimport type { UnknownUserData } from './run_context.js';\nimport type { SpeechHandle } from './speech_handle.js';\n\nexport interface VoiceOptions {\n allowInterruptions: boolean;\n discardAudioIfUninterruptible: boolean;\n minInterruptionDuration: number;\n minInterruptionWords: number;\n minEndpointingDelay: number;\n maxEndpointingDelay: number;\n maxToolSteps: number;\n}\n\nconst defaultVoiceOptions: VoiceOptions = {\n allowInterruptions: true,\n discardAudioIfUninterruptible: true,\n minInterruptionDuration: 500,\n minInterruptionWords: 0,\n minEndpointingDelay: 500,\n maxEndpointingDelay: 6000,\n maxToolSteps: 3,\n} as const;\n\nexport type TurnDetectionMode = 'stt' | 'vad' | 'realtime_llm' | 'manual' | _TurnDetector;\n\nexport type AgentSessionCallbacks = {\n [AgentSessionEventTypes.UserInputTranscribed]: (ev: UserInputTranscribedEvent) => void;\n [AgentSessionEventTypes.AgentStateChanged]: (ev: AgentStateChangedEvent) => void;\n [AgentSessionEventTypes.UserStateChanged]: (ev: UserStateChangedEvent) => void;\n [AgentSessionEventTypes.ConversationItemAdded]: (ev: ConversationItemAddedEvent) => void;\n [AgentSessionEventTypes.FunctionToolsExecuted]: (ev: FunctionToolsExecutedEvent) => void;\n [AgentSessionEventTypes.MetricsCollected]: (ev: MetricsCollectedEvent) => void;\n [AgentSessionEventTypes.SpeechCreated]: (ev: SpeechCreatedEvent) => void;\n [AgentSessionEventTypes.Error]: (ev: ErrorEvent) => void;\n [AgentSessionEventTypes.Close]: (ev: CloseEvent) => void;\n};\n\nexport type AgentSessionOptions<UserData = UnknownUserData> = {\n turnDetection?: TurnDetectionMode;\n stt?: STT | STTModelString;\n vad?: VAD;\n llm?: LLM | RealtimeModel | LLMModels;\n tts?: TTS | TTSModelString;\n userData?: UserData;\n voiceOptions?: Partial<VoiceOptions>;\n};\n\nexport class AgentSession<\n UserData = UnknownUserData,\n> extends (EventEmitter as new () => TypedEmitter<AgentSessionCallbacks>) {\n vad?: VAD;\n stt?: STT;\n llm?: LLM | RealtimeModel;\n tts?: TTS;\n turnDetection?: TurnDetectionMode;\n\n readonly options: VoiceOptions;\n\n private agent?: Agent;\n private activity?: AgentActivity;\n private nextActivity?: AgentActivity;\n private started = false;\n private userState: UserState = 'listening';\n\n private roomIO?: RoomIO;\n private logger = log();\n\n private _chatCtx: ChatContext;\n private _userData: UserData | undefined;\n private _agentState: AgentState = 'initializing';\n\n private _input: AgentInput;\n private _output: AgentOutput;\n\n private closingTask: Promise<void> | null = null;\n\n constructor(opts: AgentSessionOptions<UserData>) {\n super();\n\n const {\n vad,\n stt,\n llm,\n tts,\n turnDetection,\n userData,\n voiceOptions = defaultVoiceOptions,\n } = opts;\n\n this.vad = vad;\n\n if (typeof stt === 'string') {\n this.stt = InferenceSTT.fromModelString(stt);\n } else {\n this.stt = stt;\n }\n\n if (typeof llm === 'string') {\n this.llm = InferenceLLM.fromModelString(llm);\n } else {\n this.llm = llm;\n }\n\n if (typeof tts === 'string') {\n this.tts = InferenceTTS.fromModelString(tts);\n } else {\n this.tts = tts;\n }\n\n this.turnDetection = turnDetection;\n this._userData = userData;\n\n // configurable IO\n this._input = new AgentInput(this.onAudioInputChanged);\n this._output = new AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);\n\n // This is the \"global\" chat context, it holds the entire conversation history\n this._chatCtx = ChatContext.empty();\n this.options = { ...defaultVoiceOptions, ...voiceOptions };\n }\n\n get input(): AgentInput {\n return this._input;\n }\n\n get output(): AgentOutput {\n return this._output;\n }\n\n get userData(): UserData {\n if (this._userData === undefined) {\n throw new Error('Voice agent userData is not set');\n }\n\n return this._userData;\n }\n\n get history(): ChatContext {\n return this._chatCtx;\n }\n\n set userData(value: UserData) {\n this._userData = value;\n }\n\n async start({\n agent,\n room,\n inputOptions,\n outputOptions,\n }: {\n agent: Agent;\n room: Room;\n inputOptions?: Partial<RoomInputOptions>;\n outputOptions?: Partial<RoomOutputOptions>;\n }): Promise<void> {\n if (this.started) {\n return;\n }\n\n this.agent = agent;\n this._updateAgentState('initializing');\n\n const tasks: Promise<void>[] = [];\n // Check for existing input/output configuration and warn if needed\n if (this.input.audio && inputOptions?.audioEnabled !== false) {\n this.logger.warn('RoomIO audio input is enabled but input.audio is already set, ignoring..');\n }\n\n if (this.output.audio && outputOptions?.audioEnabled !== false) {\n this.logger.warn(\n 'RoomIO audio output is enabled but output.audio is already set, ignoring..',\n );\n }\n\n if (this.output.transcription && outputOptions?.transcriptionEnabled !== false) {\n this.logger.warn(\n 'RoomIO transcription output is enabled but output.transcription is already set, ignoring..',\n );\n }\n\n this.roomIO = new RoomIO({\n agentSession: this,\n room,\n inputOptions,\n outputOptions,\n });\n this.roomIO.start();\n\n const ctx = getJobContext();\n if (ctx && ctx.room === room && !room.isConnected) {\n this.logger.debug('Auto-connecting to room via job context');\n tasks.push(ctx.connect());\n }\n // TODO(AJS-265): add shutdown callback to job context\n tasks.push(this.updateActivity(this.agent));\n\n await Promise.allSettled(tasks);\n\n // Log used IO configuration\n this.logger.debug(\n `using audio io: ${this.input.audio ? '`' + this.input.audio.constructor.name + '`' : '(none)'} -> \\`AgentSession\\` -> ${this.output.audio ? '`' + this.output.audio.constructor.name + '`' : '(none)'}`,\n );\n\n this.logger.debug(\n `using transcript io: \\`AgentSession\\` -> ${this.output.transcription ? '`' + this.output.transcription.constructor.name + '`' : '(none)'}`,\n );\n\n this.started = true;\n this._updateAgentState('listening');\n }\n\n updateAgent(agent: Agent): void {\n this.agent = agent;\n\n if (this.started) {\n this.updateActivity(agent);\n }\n }\n\n commitUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n this.activity.commitUserTurn();\n }\n\n clearUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n this.activity.clearUserTurn();\n }\n\n say(\n text: string | ReadableStream<string>,\n options?: {\n audio?: ReadableStream<AudioFrame>;\n allowInterruptions?: boolean;\n addToChatCtx?: boolean;\n },\n ): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n return this.activity.say(text, options);\n }\n\n interrupt() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n return this.activity.interrupt();\n }\n\n generateReply(options?: {\n userInput?: string;\n instructions?: string;\n toolChoice?: ToolChoice;\n allowInterruptions?: boolean;\n }): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n const userMessage = options?.userInput\n ? new ChatMessage({\n role: 'user',\n content: options.userInput,\n })\n : undefined;\n\n if (this.activity.draining) {\n if (!this.nextActivity) {\n throw new Error('AgentSession is closing, cannot use generateReply()');\n }\n return this.nextActivity.generateReply({ userMessage, ...options });\n }\n\n return this.activity.generateReply({ userMessage, ...options });\n }\n\n private async updateActivity(agent: Agent): Promise<void> {\n // TODO(AJS-129): add lock to agent activity core lifecycle\n this.nextActivity = new AgentActivity(agent, this);\n\n if (this.activity) {\n await this.activity.drain();\n await this.activity.close();\n }\n\n this.activity = this.nextActivity;\n this.nextActivity = undefined;\n\n await this.activity.start();\n\n if (this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n get chatCtx(): ChatContext {\n return this._chatCtx.copy();\n }\n\n get agentState(): AgentState {\n return this._agentState;\n }\n\n get currentAgent(): Agent {\n if (!this.agent) {\n throw new Error('AgentSession is not running');\n }\n\n return this.agent;\n }\n\n async close(): Promise<void> {\n await this.closeImpl(CloseReason.USER_INITIATED);\n }\n\n /** @internal */\n _closeSoon({\n reason,\n drain = false,\n error = null,\n }: {\n reason: CloseReason;\n drain?: boolean;\n error?: RealtimeModelError | STTError | TTSError | LLMError | null;\n }): void {\n if (this.closingTask) {\n return;\n }\n this.closeImpl(reason, error, drain);\n }\n\n /** @internal */\n _onError(error: RealtimeModelError | STTError | TTSError | LLMError): void {\n if (this.closingTask || error.recoverable) {\n return;\n }\n\n this.logger.error(error, 'AgentSession is closing due to unrecoverable error');\n\n this.closingTask = (async () => {\n await this.closeImpl(CloseReason.ERROR, error);\n })().then(() => {\n this.closingTask = null;\n });\n }\n\n /** @internal */\n _conversationItemAdded(item: ChatMessage): void {\n this._chatCtx.insert(item);\n this.emit(AgentSessionEventTypes.ConversationItemAdded, createConversationItemAddedEvent(item));\n }\n\n /** @internal */\n _updateAgentState(state: AgentState) {\n if (this._agentState === state) {\n return;\n }\n\n const oldState = this._agentState;\n this._agentState = state;\n this.emit(\n AgentSessionEventTypes.AgentStateChanged,\n createAgentStateChangedEvent(oldState, state),\n );\n }\n\n /** @internal */\n _updateUserState(state: UserState) {\n if (this.userState === state) {\n return;\n }\n\n const oldState = this.userState;\n this.userState = state;\n this.emit(\n AgentSessionEventTypes.UserStateChanged,\n createUserStateChangedEvent(oldState, state),\n );\n }\n\n // -- User changed input/output streams/sinks --\n private onAudioInputChanged(): void {\n if (!this.started) {\n return;\n }\n\n if (this.activity && this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n private onAudioOutputChanged(): void {}\n\n private onTextOutputChanged(): void {}\n\n private async closeImpl(\n reason: CloseReason,\n error: RealtimeModelError | LLMError | TTSError | STTError | null = null,\n drain: boolean = false,\n ): Promise<void> {\n if (!this.started) {\n return;\n }\n\n if (this.activity) {\n if (!drain) {\n try {\n this.activity.interrupt();\n } catch (error) {\n // uninterruptible speech [copied from python]\n // TODO(shubhra): force interrupt or wait for it to finish?\n // it might be an audio played from the error callback\n }\n }\n await this.activity.drain();\n // wait any uninterruptible speech to finish\n await this.activity.currentSpeech?.waitForPlayout();\n this.activity.detachAudioInput();\n }\n\n // detach the inputs and outputs\n this.input.audio = null;\n this.output.audio = null;\n this.output.transcription = null;\n\n await this.roomIO?.close();\n this.roomIO = undefined;\n\n await this.activity?.close();\n this.activity = undefined;\n\n this.started = false;\n\n this.emit(AgentSessionEventTypes.Close, createCloseEvent(reason, error));\n\n this.userState = 'listening';\n this._agentState = 'initializing';\n\n this.logger.info({ reason, error }, 'AgentSession closed');\n }\n}\n"],"mappings":"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","names":["InferenceSTT","InferenceLLM","InferenceTTS","error"]}
|
|
1
|
+
{"version":3,"sources":["../../src/voice/agent_session.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, Room } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { EventEmitter } from 'node:events';\nimport type { ReadableStream } from 'node:stream/web';\nimport {\n LLM as InferenceLLM,\n STT as InferenceSTT,\n TTS as InferenceTTS,\n type LLMModels,\n type STTModelString,\n type TTSModelString,\n} from '../inference/index.js';\nimport { getJobContext } from '../job.js';\nimport { ChatContext, ChatMessage } from '../llm/chat_context.js';\nimport type { LLM, RealtimeModel, RealtimeModelError, ToolChoice } from '../llm/index.js';\nimport type { LLMError } from '../llm/llm.js';\nimport { log } from '../log.js';\nimport type { STT } from '../stt/index.js';\nimport type { STTError } from '../stt/stt.js';\nimport type { TTS, TTSError } from '../tts/tts.js';\nimport type { VAD } from '../vad.js';\nimport type { Agent } from './agent.js';\nimport { AgentActivity } from './agent_activity.js';\nimport type { _TurnDetector } from './audio_recognition.js';\nimport {\n AgentSessionEventTypes,\n type AgentState,\n type AgentStateChangedEvent,\n type CloseEvent,\n CloseReason,\n type ConversationItemAddedEvent,\n type ErrorEvent,\n type FunctionToolsExecutedEvent,\n type MetricsCollectedEvent,\n type SpeechCreatedEvent,\n type UserInputTranscribedEvent,\n type UserState,\n type UserStateChangedEvent,\n createAgentStateChangedEvent,\n createCloseEvent,\n createConversationItemAddedEvent,\n createUserStateChangedEvent,\n} from './events.js';\nimport { AgentInput, AgentOutput } from './io.js';\nimport { RoomIO, type RoomInputOptions, type RoomOutputOptions } from './room_io/index.js';\nimport type { UnknownUserData } from './run_context.js';\nimport type { SpeechHandle } from './speech_handle.js';\n\nexport interface VoiceOptions {\n allowInterruptions: boolean;\n discardAudioIfUninterruptible: boolean;\n minInterruptionDuration: number;\n minInterruptionWords: number;\n minEndpointingDelay: number;\n maxEndpointingDelay: number;\n maxToolSteps: number;\n preemptiveGeneration: boolean;\n}\n\nconst defaultVoiceOptions: VoiceOptions = {\n allowInterruptions: true,\n discardAudioIfUninterruptible: true,\n minInterruptionDuration: 500,\n minInterruptionWords: 0,\n minEndpointingDelay: 500,\n maxEndpointingDelay: 6000,\n maxToolSteps: 3,\n preemptiveGeneration: false,\n} as const;\n\nexport type TurnDetectionMode = 'stt' | 'vad' | 'realtime_llm' | 'manual' | _TurnDetector;\n\nexport type AgentSessionCallbacks = {\n [AgentSessionEventTypes.UserInputTranscribed]: (ev: UserInputTranscribedEvent) => void;\n [AgentSessionEventTypes.AgentStateChanged]: (ev: AgentStateChangedEvent) => void;\n [AgentSessionEventTypes.UserStateChanged]: (ev: UserStateChangedEvent) => void;\n [AgentSessionEventTypes.ConversationItemAdded]: (ev: ConversationItemAddedEvent) => void;\n [AgentSessionEventTypes.FunctionToolsExecuted]: (ev: FunctionToolsExecutedEvent) => void;\n [AgentSessionEventTypes.MetricsCollected]: (ev: MetricsCollectedEvent) => void;\n [AgentSessionEventTypes.SpeechCreated]: (ev: SpeechCreatedEvent) => void;\n [AgentSessionEventTypes.Error]: (ev: ErrorEvent) => void;\n [AgentSessionEventTypes.Close]: (ev: CloseEvent) => void;\n};\n\nexport type AgentSessionOptions<UserData = UnknownUserData> = {\n turnDetection?: TurnDetectionMode;\n stt?: STT | STTModelString;\n vad?: VAD;\n llm?: LLM | RealtimeModel | LLMModels;\n tts?: TTS | TTSModelString;\n userData?: UserData;\n voiceOptions?: Partial<VoiceOptions>;\n};\n\nexport class AgentSession<\n UserData = UnknownUserData,\n> extends (EventEmitter as new () => TypedEmitter<AgentSessionCallbacks>) {\n vad?: VAD;\n stt?: STT;\n llm?: LLM | RealtimeModel;\n tts?: TTS;\n turnDetection?: TurnDetectionMode;\n\n readonly options: VoiceOptions;\n\n private agent?: Agent;\n private activity?: AgentActivity;\n private nextActivity?: AgentActivity;\n private started = false;\n private userState: UserState = 'listening';\n\n private roomIO?: RoomIO;\n private logger = log();\n\n private _chatCtx: ChatContext;\n private _userData: UserData | undefined;\n private _agentState: AgentState = 'initializing';\n\n private _input: AgentInput;\n private _output: AgentOutput;\n\n private closingTask: Promise<void> | null = null;\n\n constructor(opts: AgentSessionOptions<UserData>) {\n super();\n\n const {\n vad,\n stt,\n llm,\n tts,\n turnDetection,\n userData,\n voiceOptions = defaultVoiceOptions,\n } = opts;\n\n this.vad = vad;\n\n if (typeof stt === 'string') {\n this.stt = InferenceSTT.fromModelString(stt);\n } else {\n this.stt = stt;\n }\n\n if (typeof llm === 'string') {\n this.llm = InferenceLLM.fromModelString(llm);\n } else {\n this.llm = llm;\n }\n\n if (typeof tts === 'string') {\n this.tts = InferenceTTS.fromModelString(tts);\n } else {\n this.tts = tts;\n }\n\n this.turnDetection = turnDetection;\n this._userData = userData;\n\n // configurable IO\n this._input = new AgentInput(this.onAudioInputChanged);\n this._output = new AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);\n\n // This is the \"global\" chat context, it holds the entire conversation history\n this._chatCtx = ChatContext.empty();\n this.options = { ...defaultVoiceOptions, ...voiceOptions };\n }\n\n get input(): AgentInput {\n return this._input;\n }\n\n get output(): AgentOutput {\n return this._output;\n }\n\n get userData(): UserData {\n if (this._userData === undefined) {\n throw new Error('Voice agent userData is not set');\n }\n\n return this._userData;\n }\n\n get history(): ChatContext {\n return this._chatCtx;\n }\n\n set userData(value: UserData) {\n this._userData = value;\n }\n\n async start({\n agent,\n room,\n inputOptions,\n outputOptions,\n }: {\n agent: Agent;\n room: Room;\n inputOptions?: Partial<RoomInputOptions>;\n outputOptions?: Partial<RoomOutputOptions>;\n }): Promise<void> {\n if (this.started) {\n return;\n }\n\n this.agent = agent;\n this._updateAgentState('initializing');\n\n const tasks: Promise<void>[] = [];\n // Check for existing input/output configuration and warn if needed\n if (this.input.audio && inputOptions?.audioEnabled !== false) {\n this.logger.warn('RoomIO audio input is enabled but input.audio is already set, ignoring..');\n }\n\n if (this.output.audio && outputOptions?.audioEnabled !== false) {\n this.logger.warn(\n 'RoomIO audio output is enabled but output.audio is already set, ignoring..',\n );\n }\n\n if (this.output.transcription && outputOptions?.transcriptionEnabled !== false) {\n this.logger.warn(\n 'RoomIO transcription output is enabled but output.transcription is already set, ignoring..',\n );\n }\n\n this.roomIO = new RoomIO({\n agentSession: this,\n room,\n inputOptions,\n outputOptions,\n });\n this.roomIO.start();\n\n const ctx = getJobContext();\n if (ctx && ctx.room === room && !room.isConnected) {\n this.logger.debug('Auto-connecting to room via job context');\n tasks.push(ctx.connect());\n }\n // TODO(AJS-265): add shutdown callback to job context\n tasks.push(this.updateActivity(this.agent));\n\n await Promise.allSettled(tasks);\n\n // Log used IO configuration\n this.logger.debug(\n `using audio io: ${this.input.audio ? '`' + this.input.audio.constructor.name + '`' : '(none)'} -> \\`AgentSession\\` -> ${this.output.audio ? '`' + this.output.audio.constructor.name + '`' : '(none)'}`,\n );\n\n this.logger.debug(\n `using transcript io: \\`AgentSession\\` -> ${this.output.transcription ? '`' + this.output.transcription.constructor.name + '`' : '(none)'}`,\n );\n\n this.started = true;\n this._updateAgentState('listening');\n }\n\n updateAgent(agent: Agent): void {\n this.agent = agent;\n\n if (this.started) {\n this.updateActivity(agent);\n }\n }\n\n commitUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n this.activity.commitUserTurn();\n }\n\n clearUserTurn() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n this.activity.clearUserTurn();\n }\n\n say(\n text: string | ReadableStream<string>,\n options?: {\n audio?: ReadableStream<AudioFrame>;\n allowInterruptions?: boolean;\n addToChatCtx?: boolean;\n },\n ): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n return this.activity.say(text, options);\n }\n\n interrupt() {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n return this.activity.interrupt();\n }\n\n generateReply(options?: {\n userInput?: string;\n instructions?: string;\n toolChoice?: ToolChoice;\n allowInterruptions?: boolean;\n }): SpeechHandle {\n if (!this.activity) {\n throw new Error('AgentSession is not running');\n }\n\n const userMessage = options?.userInput\n ? new ChatMessage({\n role: 'user',\n content: options.userInput,\n })\n : undefined;\n\n if (this.activity.draining) {\n if (!this.nextActivity) {\n throw new Error('AgentSession is closing, cannot use generateReply()');\n }\n return this.nextActivity.generateReply({ userMessage, ...options });\n }\n\n return this.activity.generateReply({ userMessage, ...options });\n }\n\n private async updateActivity(agent: Agent): Promise<void> {\n // TODO(AJS-129): add lock to agent activity core lifecycle\n this.nextActivity = new AgentActivity(agent, this);\n\n if (this.activity) {\n await this.activity.drain();\n await this.activity.close();\n }\n\n this.activity = this.nextActivity;\n this.nextActivity = undefined;\n\n await this.activity.start();\n\n if (this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n get chatCtx(): ChatContext {\n return this._chatCtx.copy();\n }\n\n get agentState(): AgentState {\n return this._agentState;\n }\n\n get currentAgent(): Agent {\n if (!this.agent) {\n throw new Error('AgentSession is not running');\n }\n\n return this.agent;\n }\n\n async close(): Promise<void> {\n await this.closeImpl(CloseReason.USER_INITIATED);\n }\n\n /** @internal */\n _closeSoon({\n reason,\n drain = false,\n error = null,\n }: {\n reason: CloseReason;\n drain?: boolean;\n error?: RealtimeModelError | STTError | TTSError | LLMError | null;\n }): void {\n if (this.closingTask) {\n return;\n }\n this.closeImpl(reason, error, drain);\n }\n\n /** @internal */\n _onError(error: RealtimeModelError | STTError | TTSError | LLMError): void {\n if (this.closingTask || error.recoverable) {\n return;\n }\n\n this.logger.error(error, 'AgentSession is closing due to unrecoverable error');\n\n this.closingTask = (async () => {\n await this.closeImpl(CloseReason.ERROR, error);\n })().then(() => {\n this.closingTask = null;\n });\n }\n\n /** @internal */\n _conversationItemAdded(item: ChatMessage): void {\n this._chatCtx.insert(item);\n this.emit(AgentSessionEventTypes.ConversationItemAdded, createConversationItemAddedEvent(item));\n }\n\n /** @internal */\n _updateAgentState(state: AgentState) {\n if (this._agentState === state) {\n return;\n }\n\n const oldState = this._agentState;\n this._agentState = state;\n this.emit(\n AgentSessionEventTypes.AgentStateChanged,\n createAgentStateChangedEvent(oldState, state),\n );\n }\n\n /** @internal */\n _updateUserState(state: UserState, _lastSpeakingTime?: number) {\n if (this.userState === state) {\n return;\n }\n\n const oldState = this.userState;\n this.userState = state;\n this.emit(\n AgentSessionEventTypes.UserStateChanged,\n createUserStateChangedEvent(oldState, state),\n );\n }\n\n // -- User changed input/output streams/sinks --\n private onAudioInputChanged(): void {\n if (!this.started) {\n return;\n }\n\n if (this.activity && this._input.audio) {\n this.activity.attachAudioInput(this._input.audio.stream);\n }\n }\n\n private onAudioOutputChanged(): void {}\n\n private onTextOutputChanged(): void {}\n\n private async closeImpl(\n reason: CloseReason,\n error: RealtimeModelError | LLMError | TTSError | STTError | null = null,\n drain: boolean = false,\n ): Promise<void> {\n if (!this.started) {\n return;\n }\n\n if (this.activity) {\n if (!drain) {\n try {\n this.activity.interrupt();\n } catch (error) {\n // uninterruptible speech [copied from python]\n // TODO(shubhra): force interrupt or wait for it to finish?\n // it might be an audio played from the error callback\n }\n }\n await this.activity.drain();\n // wait any uninterruptible speech to finish\n await this.activity.currentSpeech?.waitForPlayout();\n this.activity.detachAudioInput();\n }\n\n // detach the inputs and outputs\n this.input.audio = null;\n this.output.audio = null;\n this.output.transcription = null;\n\n await this.roomIO?.close();\n this.roomIO = undefined;\n\n await this.activity?.close();\n this.activity = undefined;\n\n this.started = false;\n\n this.emit(AgentSessionEventTypes.Close, createCloseEvent(reason, error));\n\n this.userState = 'listening';\n this._agentState = 'initializing';\n\n this.logger.info({ reason, error }, 'AgentSession closed');\n }\n}\n"],"mappings":"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","names":["InferenceSTT","InferenceLLM","InferenceTTS","error"]}
|