@livekit/agents 1.0.27 → 1.0.30
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/connection_pool.cjs +242 -0
- package/dist/connection_pool.cjs.map +1 -0
- package/dist/connection_pool.d.cts +123 -0
- package/dist/connection_pool.d.ts +123 -0
- package/dist/connection_pool.d.ts.map +1 -0
- package/dist/connection_pool.js +218 -0
- package/dist/connection_pool.js.map +1 -0
- package/dist/connection_pool.test.cjs +256 -0
- package/dist/connection_pool.test.cjs.map +1 -0
- package/dist/connection_pool.test.js +255 -0
- package/dist/connection_pool.test.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inference/tts.cjs +172 -56
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +3 -0
- package/dist/inference/tts.d.ts +3 -0
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +173 -57
- package/dist/inference/tts.js.map +1 -1
- package/dist/utils.cjs +20 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +7 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -1
- package/dist/voice/agent_activity.cjs +3 -1
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +3 -1
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +4 -1
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +4 -1
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/avatar/datastream_io.cjs +1 -1
- package/dist/voice/avatar/datastream_io.cjs.map +1 -1
- package/dist/voice/avatar/datastream_io.js +1 -1
- package/dist/voice/avatar/datastream_io.js.map +1 -1
- package/dist/voice/background_audio.cjs +77 -37
- package/dist/voice/background_audio.cjs.map +1 -1
- package/dist/voice/background_audio.d.cts +10 -3
- package/dist/voice/background_audio.d.ts +10 -3
- package/dist/voice/background_audio.d.ts.map +1 -1
- package/dist/voice/background_audio.js +78 -37
- package/dist/voice/background_audio.js.map +1 -1
- package/dist/voice/index.cjs +1 -0
- package/dist/voice/index.cjs.map +1 -1
- package/dist/voice/index.d.cts +1 -0
- package/dist/voice/index.d.ts +1 -0
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +1 -0
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/io.cjs +10 -1
- package/dist/voice/io.cjs.map +1 -1
- package/dist/voice/io.d.cts +18 -1
- package/dist/voice/io.d.ts +18 -1
- package/dist/voice/io.d.ts.map +1 -1
- package/dist/voice/io.js +10 -1
- package/dist/voice/io.js.map +1 -1
- package/dist/voice/recorder_io/recorder_io.cjs +1 -1
- package/dist/voice/recorder_io/recorder_io.cjs.map +1 -1
- package/dist/voice/recorder_io/recorder_io.js +1 -1
- package/dist/voice/recorder_io/recorder_io.js.map +1 -1
- package/dist/voice/room_io/_output.cjs +1 -1
- package/dist/voice/room_io/_output.cjs.map +1 -1
- package/dist/voice/room_io/_output.js +1 -1
- package/dist/voice/room_io/_output.js.map +1 -1
- package/dist/voice/transcription/synchronizer.cjs +1 -1
- package/dist/voice/transcription/synchronizer.cjs.map +1 -1
- package/dist/voice/transcription/synchronizer.js +1 -1
- package/dist/voice/transcription/synchronizer.js.map +1 -1
- package/dist/worker.cjs +4 -6
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +4 -6
- package/dist/worker.js.map +1 -1
- package/package.json +3 -3
- package/src/connection_pool.test.ts +346 -0
- package/src/connection_pool.ts +307 -0
- package/src/index.ts +1 -0
- package/src/inference/tts.ts +206 -63
- package/src/utils.ts +25 -0
- package/src/voice/agent_activity.ts +7 -1
- package/src/voice/agent_session.ts +4 -1
- package/src/voice/avatar/datastream_io.ts +1 -1
- package/src/voice/background_audio.ts +95 -55
- package/src/voice/index.ts +1 -0
- package/src/voice/io.ts +24 -0
- package/src/voice/recorder_io/recorder_io.ts +1 -1
- package/src/voice/room_io/_output.ts +1 -1
- package/src/voice/transcription/synchronizer.ts +1 -1
- package/src/worker.ts +4 -7
|
@@ -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 type { Span } from '@opentelemetry/api';\nimport { ROOT_CONTEXT, trace } from '@opentelemetry/api';\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 FunctionCallOutput,\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 { traceTypes, tracer } from '../telemetry/index.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\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 // Create start_agent_activity as a ROOT span (new trace) to match Python behavior\n const startSpan = tracer.startSpan({\n name: 'start_agent_activity',\n attributes: { [traceTypes.ATTR_AGENT_LABEL]: this.agent.id },\n context: ROOT_CONTEXT,\n });\n\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\n if (!this.llm.capabilities.audioOutput && !this.tts && this.agentSession.output.audio) {\n this.logger.error(\n 'audio output is enabled but RealtimeModel has no audio modality ' +\n 'and no TTS is set. Either enable audio modality in the RealtimeModel ' +\n 'or set a TTS model.',\n );\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 rootSpanContext: this.agentSession.rootSpanContext,\n });\n this.audioRecognition.start();\n this.started = true;\n\n this._mainTask = Task.from(({ signal }) => this.mainTask(signal));\n\n // Create on_enter as a child of start_agent_activity in the new trace\n const onEnterTask = tracer.startActiveSpan(async () => this.agent.onEnter(), {\n name: 'on_enter',\n context: trace.setSpan(ROOT_CONTEXT, startSpan),\n attributes: { [traceTypes.ATTR_AGENT_LABEL]: this.agent.id },\n });\n\n this.createSpeechTask({\n task: Task.from(() => onEnterTask),\n name: 'AgentActivity_onEnter',\n });\n\n startSpan.end();\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 // 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 // Refactored interruption word count check:\n // - Always apply minInterruptionWords filtering when STT is available and minInterruptionWords > 0\n // - Apply check to all STT results: empty string, undefined, or any length\n // - This ensures consistent behavior across all interruption scenarios\n if (this.stt && this.agentSession.options.minInterruptionWords > 0 && this.audioRecognition) {\n const text = this.audioRecognition.currentTranscript;\n // TODO(shubhra): better word splitting for multi-language\n\n // Normalize text: convert undefined/null to empty string for consistent word counting\n const normalizedText = text ?? '';\n const wordCount = splitWords(normalizedText, true).length;\n\n // Only allow interruption if word count meets or exceeds minInterruptionWords\n // This applies to all cases: empty strings, partial speech, and full speech\n if (wordCount < 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 // TODO(shubhra): should we \"forward\" this new turn to the next agent/activity?\n return true;\n }\n\n // Refactored interruption word count check for consistency with onVADInferenceDone:\n // - Always apply minInterruptionWords filtering when STT is available and minInterruptionWords > 0\n // - Use consistent word splitting logic with splitWords (matching onVADInferenceDone pattern)\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 ) {\n const wordCount = splitWords(info.newTranscript, true).length;\n if (wordCount < this.agentSession.options.minInterruptionWords) {\n // avoid interruption if the new_transcript contains fewer words than minInterruptionWords\n this.cancelPreemptiveGeneration();\n this.logger.info(\n {\n wordCount,\n minInterruptionWords: this.agentSession.options.minInterruptionWords,\n },\n 'skipping user input, word count below minimum interruption threshold',\n );\n return false;\n }\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 _pipelineReplyTaskImpl = async ({\n speechHandle,\n chatCtx,\n toolCtx,\n modelSettings,\n replyAbortController,\n instructions,\n newMessage,\n toolsMessages,\n span,\n }: {\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 span: Span;\n }): Promise<void> => {\n span.setAttribute(traceTypes.ATTR_SPEECH_ID, speechHandle.id);\n if (instructions) {\n span.setAttribute(traceTypes.ATTR_INSTRUCTIONS, instructions);\n }\n if (newMessage) {\n span.setAttribute(traceTypes.ATTR_USER_INPUT, newMessage.textContent || '');\n }\n\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 // Also add to session history (matches Python agent_session.py _tool_items_added)\n this.agentSession._toolItemsAdded(toolsMessages as (FunctionCall | FunctionCallOutput)[]);\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 this.agentSession._toolItemsAdded(toolMessages as (FunctionCall | FunctionCallOutput)[]);\n }\n };\n\n private pipelineReplyTask = async (\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 tracer.startActiveSpan(\n async (span) =>\n this._pipelineReplyTaskImpl({\n speechHandle,\n chatCtx,\n toolCtx,\n modelSettings,\n replyAbortController,\n instructions,\n newMessage,\n toolsMessages,\n span,\n }),\n {\n name: 'agent_turn',\n context: this.agentSession.rootSpanContext,\n },\n );\n\n private async realtimeGenerationTask(\n speechHandle: SpeechHandle,\n ev: GenerationCreatedEvent,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n ): Promise<void> {\n return tracer.startActiveSpan(\n async (span) =>\n this._realtimeGenerationTaskImpl({\n speechHandle,\n ev,\n modelSettings,\n replyAbortController,\n span,\n }),\n {\n name: 'agent_turn',\n context: this.agentSession.rootSpanContext,\n },\n );\n }\n\n private async _realtimeGenerationTaskImpl({\n speechHandle,\n ev,\n modelSettings,\n replyAbortController,\n span,\n }: {\n speechHandle: SpeechHandle;\n ev: GenerationCreatedEvent;\n modelSettings: ModelSettings;\n replyAbortController: AbortController;\n span: Span;\n }): Promise<void> {\n span.setAttribute(traceTypes.ATTR_SPEECH_ID, speechHandle.id);\n\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, ('text' | 'audio')[] | undefined]>,\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\n const msgModalities = msg.modalities ? await msg.modalities : undefined;\n let ttsTextInput: ReadableStream<string> | null = null;\n let trTextInput: ReadableStream<string>;\n\n if (msgModalities && !msgModalities.includes('audio') && this.tts) {\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.audioOutput) {\n this.logger.warn(\n 'text response received from realtime API, falling back to use a TTS model.',\n );\n }\n const [_ttsTextInput, _trTextInput] = msg.textStream.tee();\n ttsTextInput = _ttsTextInput;\n trTextInput = _trTextInput;\n } else {\n trTextInput = msg.textStream;\n }\n\n const trNodeResult = await this.agent.transcriptionNode(trTextInput, 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\n let audioOut: _AudioOut | null = null;\n if (audioOutput) {\n let realtimeAudioResult: ReadableStream<AudioFrame> | null = null;\n\n if (ttsTextInput) {\n const [ttsTask, ttsStream] = performTTSInference(\n (...args) => this.agent.ttsNode(...args),\n ttsTextInput,\n modelSettings,\n abortController,\n );\n tasks.push(ttsTask);\n realtimeAudioResult = ttsStream;\n } else if (msgModalities && msgModalities.includes('audio')) {\n realtimeAudioResult = await this.agent.realtimeAudioOutputNode(\n msg.audioStream,\n modelSettings,\n );\n } else if (this.llm instanceof RealtimeModel && this.llm.capabilities.audioOutput) {\n this.logger.error(\n 'Text message received from Realtime API with audio modality. ' +\n 'This usually happens when text chat context is synced to the API. ' +\n 'Try to add a TTS model as fallback or use text modality with TTS instead.',\n );\n } else {\n this.logger.warn(\n 'audio output is enabled but neither tts nor realtime audio is available',\n );\n }\n\n if (realtimeAudioResult) {\n const [forwardTask, _audioOut] = performAudioForwarding(\n realtimeAudioResult,\n audioOutput,\n abortController,\n );\n forwardTasks.push(forwardTask);\n audioOut = _audioOut;\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n }\n } else if (textOut) {\n textOut.firstTextFut.await.finally(onFirstFrame);\n }\n outputs.push([msg.messageId, textOut, audioOut, msgModalities]);\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<\n [string, _TextOut | null, _AudioOut | null, ('text' | 'audio')[] | undefined]\n > = [];\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 this.agent._chatCtx.items.push(f);\n this.agentSession._toolItemsAdded([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, msgModalities] = 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 modalities: msgModalities,\n audioTranscript: forwardedText,\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\n this.agentSession._toolItemsAdded(\n functionToolsExecutedEvent.functionCallOutputs as FunctionCallOutput[],\n );\n\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 // Create drain_agent_activity as a ROOT span (new trace) to match Python behavior\n return tracer.startActiveSpan(async (span) => this._drainImpl(span), {\n name: 'drain_agent_activity',\n context: ROOT_CONTEXT,\n });\n }\n\n private async _drainImpl(span: Span): Promise<void> {\n span.setAttribute(traceTypes.ATTR_AGENT_LABEL, this.agent.id);\n\n const unlock = await this.lock.lock();\n try {\n if (this._draining) return;\n\n this.cancelPreemptiveGeneration();\n\n const onExitTask = tracer.startActiveSpan(async () => this.agent.onExit(), {\n name: 'on_exit',\n attributes: { [traceTypes.ATTR_AGENT_LABEL]: this.agent.id },\n });\n\n this.createSpeechTask({\n task: Task.from(() => onExitTask),\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":[]}
|
|
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 type { Span } from '@opentelemetry/api';\nimport { ROOT_CONTEXT, trace } from '@opentelemetry/api';\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 FunctionCallOutput,\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 { traceTypes, tracer } from '../telemetry/index.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\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 // Create start_agent_activity as a ROOT span (new trace) to match Python behavior\n const startSpan = tracer.startSpan({\n name: 'start_agent_activity',\n attributes: { [traceTypes.ATTR_AGENT_LABEL]: this.agent.id },\n context: ROOT_CONTEXT,\n });\n\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\n if (!this.llm.capabilities.audioOutput && !this.tts && this.agentSession.output.audio) {\n this.logger.error(\n 'audio output is enabled but RealtimeModel has no audio modality ' +\n 'and no TTS is set. Either enable audio modality in the RealtimeModel ' +\n 'or set a TTS model.',\n );\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 rootSpanContext: this.agentSession.rootSpanContext,\n });\n this.audioRecognition.start();\n this.started = true;\n\n this._mainTask = Task.from(({ signal }) => this.mainTask(signal));\n\n // Create on_enter as a child of start_agent_activity in the new trace\n const onEnterTask = tracer.startActiveSpan(async () => this.agent.onEnter(), {\n name: 'on_enter',\n context: trace.setSpan(ROOT_CONTEXT, startSpan),\n attributes: { [traceTypes.ATTR_AGENT_LABEL]: this.agent.id },\n });\n\n this.createSpeechTask({\n task: Task.from(() => onEnterTask),\n name: 'AgentActivity_onEnter',\n });\n\n startSpan.end();\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 // 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 // Refactored interruption word count check:\n // - Always apply minInterruptionWords filtering when STT is available and minInterruptionWords > 0\n // - Apply check to all STT results: empty string, undefined, or any length\n // - This ensures consistent behavior across all interruption scenarios\n if (this.stt && this.agentSession.options.minInterruptionWords > 0 && this.audioRecognition) {\n const text = this.audioRecognition.currentTranscript;\n // TODO(shubhra): better word splitting for multi-language\n\n // Normalize text: convert undefined/null to empty string for consistent word counting\n const normalizedText = text ?? '';\n const wordCount = splitWords(normalizedText, true).length;\n\n // Only allow interruption if word count meets or exceeds minInterruptionWords\n // This applies to all cases: empty strings, partial speech, and full speech\n if (wordCount < 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 // TODO(shubhra): should we \"forward\" this new turn to the next agent/activity?\n return true;\n }\n\n // Refactored interruption word count check for consistency with onVADInferenceDone:\n // - Always apply minInterruptionWords filtering when STT is available and minInterruptionWords > 0\n // - Use consistent word splitting logic with splitWords (matching onVADInferenceDone pattern)\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 ) {\n const wordCount = splitWords(info.newTranscript, true).length;\n if (wordCount < this.agentSession.options.minInterruptionWords) {\n // avoid interruption if the new_transcript contains fewer words than minInterruptionWords\n this.cancelPreemptiveGeneration();\n this.logger.info(\n {\n wordCount,\n minInterruptionWords: this.agentSession.options.minInterruptionWords,\n },\n 'skipping user input, word count below minimum interruption threshold',\n );\n return false;\n }\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 _pipelineReplyTaskImpl = async ({\n speechHandle,\n chatCtx,\n toolCtx,\n modelSettings,\n replyAbortController,\n instructions,\n newMessage,\n toolsMessages,\n span,\n }: {\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 span: Span;\n }): Promise<void> => {\n span.setAttribute(traceTypes.ATTR_SPEECH_ID, speechHandle.id);\n if (instructions) {\n span.setAttribute(traceTypes.ATTR_INSTRUCTIONS, instructions);\n }\n if (newMessage) {\n span.setAttribute(traceTypes.ATTR_USER_INPUT, newMessage.textContent || '');\n }\n\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 // Also add to session history (matches Python agent_session.py _tool_items_added)\n this.agentSession._toolItemsAdded(toolsMessages as (FunctionCall | FunctionCallOutput)[]);\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\n // Stop playout ASAP (don't wait for cancellations), otherwise the segment may finish and we\n // will correctly (but undesirably) commit a long transcript even though the user said \"stop\".\n if (audioOutput) {\n audioOutput.clearBuffer();\n }\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 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 this.agentSession._toolItemsAdded(toolMessages as (FunctionCall | FunctionCallOutput)[]);\n }\n };\n\n private pipelineReplyTask = async (\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 tracer.startActiveSpan(\n async (span) =>\n this._pipelineReplyTaskImpl({\n speechHandle,\n chatCtx,\n toolCtx,\n modelSettings,\n replyAbortController,\n instructions,\n newMessage,\n toolsMessages,\n span,\n }),\n {\n name: 'agent_turn',\n context: this.agentSession.rootSpanContext,\n },\n );\n\n private async realtimeGenerationTask(\n speechHandle: SpeechHandle,\n ev: GenerationCreatedEvent,\n modelSettings: ModelSettings,\n replyAbortController: AbortController,\n ): Promise<void> {\n return tracer.startActiveSpan(\n async (span) =>\n this._realtimeGenerationTaskImpl({\n speechHandle,\n ev,\n modelSettings,\n replyAbortController,\n span,\n }),\n {\n name: 'agent_turn',\n context: this.agentSession.rootSpanContext,\n },\n );\n }\n\n private async _realtimeGenerationTaskImpl({\n speechHandle,\n ev,\n modelSettings,\n replyAbortController,\n span,\n }: {\n speechHandle: SpeechHandle;\n ev: GenerationCreatedEvent;\n modelSettings: ModelSettings;\n replyAbortController: AbortController;\n span: Span;\n }): Promise<void> {\n span.setAttribute(traceTypes.ATTR_SPEECH_ID, speechHandle.id);\n\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, ('text' | 'audio')[] | undefined]>,\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\n const msgModalities = msg.modalities ? await msg.modalities : undefined;\n let ttsTextInput: ReadableStream<string> | null = null;\n let trTextInput: ReadableStream<string>;\n\n if (msgModalities && !msgModalities.includes('audio') && this.tts) {\n if (this.llm instanceof RealtimeModel && this.llm.capabilities.audioOutput) {\n this.logger.warn(\n 'text response received from realtime API, falling back to use a TTS model.',\n );\n }\n const [_ttsTextInput, _trTextInput] = msg.textStream.tee();\n ttsTextInput = _ttsTextInput;\n trTextInput = _trTextInput;\n } else {\n trTextInput = msg.textStream;\n }\n\n const trNodeResult = await this.agent.transcriptionNode(trTextInput, 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\n let audioOut: _AudioOut | null = null;\n if (audioOutput) {\n let realtimeAudioResult: ReadableStream<AudioFrame> | null = null;\n\n if (ttsTextInput) {\n const [ttsTask, ttsStream] = performTTSInference(\n (...args) => this.agent.ttsNode(...args),\n ttsTextInput,\n modelSettings,\n abortController,\n );\n tasks.push(ttsTask);\n realtimeAudioResult = ttsStream;\n } else if (msgModalities && msgModalities.includes('audio')) {\n realtimeAudioResult = await this.agent.realtimeAudioOutputNode(\n msg.audioStream,\n modelSettings,\n );\n } else if (this.llm instanceof RealtimeModel && this.llm.capabilities.audioOutput) {\n this.logger.error(\n 'Text message received from Realtime API with audio modality. ' +\n 'This usually happens when text chat context is synced to the API. ' +\n 'Try to add a TTS model as fallback or use text modality with TTS instead.',\n );\n } else {\n this.logger.warn(\n 'audio output is enabled but neither tts nor realtime audio is available',\n );\n }\n\n if (realtimeAudioResult) {\n const [forwardTask, _audioOut] = performAudioForwarding(\n realtimeAudioResult,\n audioOutput,\n abortController,\n );\n forwardTasks.push(forwardTask);\n audioOut = _audioOut;\n audioOut.firstFrameFut.await.finally(onFirstFrame);\n }\n } else if (textOut) {\n textOut.firstTextFut.await.finally(onFirstFrame);\n }\n outputs.push([msg.messageId, textOut, audioOut, msgModalities]);\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<\n [string, _TextOut | null, _AudioOut | null, ('text' | 'audio')[] | undefined]\n > = [];\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 this.agent._chatCtx.items.push(f);\n this.agentSession._toolItemsAdded([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, msgModalities] = 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 modalities: msgModalities,\n audioTranscript: forwardedText,\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\n this.agentSession._toolItemsAdded(\n functionToolsExecutedEvent.functionCallOutputs as FunctionCallOutput[],\n );\n\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 // Create drain_agent_activity as a ROOT span (new trace) to match Python behavior\n return tracer.startActiveSpan(async (span) => this._drainImpl(span), {\n name: 'drain_agent_activity',\n context: ROOT_CONTEXT,\n });\n }\n\n private async _drainImpl(span: Span): Promise<void> {\n span.setAttribute(traceTypes.ATTR_AGENT_LABEL, this.agent.id);\n\n const unlock = await this.lock.lock();\n try {\n if (this._draining) return;\n\n this.cancelPreemptiveGeneration();\n\n const onExitTask = tracer.startActiveSpan(async () => this.agent.onExit(), {\n name: 'on_exit',\n attributes: { [traceTypes.ATTR_AGENT_LABEL]: this.agent.id },\n });\n\n this.createSpeechTask({\n task: Task.from(() => onExitTask),\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":[]}
|
|
@@ -340,7 +340,10 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
340
340
|
newAgentId: agent.id
|
|
341
341
|
})
|
|
342
342
|
);
|
|
343
|
-
this.logger.debug(
|
|
343
|
+
this.logger.debug(
|
|
344
|
+
{ previousAgentId: previousActivity == null ? void 0 : previousActivity.agent.id, newAgentId: agent.id },
|
|
345
|
+
"Agent handoff inserted into chat context"
|
|
346
|
+
);
|
|
344
347
|
await this.activity.start();
|
|
345
348
|
if (this._input.audio) {
|
|
346
349
|
this.activity.attachAudioInput(this._input.audio.stream);
|