@livekit/agents 1.1.0-dev.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +2 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/constants.cjs +3 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/cpu.cjs +189 -0
- package/dist/cpu.cjs.map +1 -0
- package/dist/cpu.d.cts +24 -0
- package/dist/cpu.d.ts +24 -0
- package/dist/cpu.d.ts.map +1 -0
- package/dist/cpu.js +152 -0
- package/dist/cpu.js.map +1 -0
- package/dist/cpu.test.cjs +227 -0
- package/dist/cpu.test.cjs.map +1 -0
- package/dist/cpu.test.js +204 -0
- package/dist/cpu.test.js.map +1 -0
- package/dist/index.cjs +12 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/inference/interruption/defaults.cjs +1 -1
- package/dist/inference/interruption/defaults.cjs.map +1 -1
- package/dist/inference/interruption/defaults.d.cts +1 -1
- package/dist/inference/interruption/defaults.d.ts +1 -1
- package/dist/inference/interruption/defaults.d.ts.map +1 -1
- package/dist/inference/interruption/defaults.js +1 -1
- package/dist/inference/interruption/defaults.js.map +1 -1
- package/dist/inference/interruption/http_transport.cjs +44 -28
- package/dist/inference/interruption/http_transport.cjs.map +1 -1
- package/dist/inference/interruption/http_transport.d.ts.map +1 -1
- package/dist/inference/interruption/http_transport.js +45 -29
- package/dist/inference/interruption/http_transport.js.map +1 -1
- package/dist/inference/interruption/interruption_detector.cjs +22 -5
- package/dist/inference/interruption/interruption_detector.cjs.map +1 -1
- package/dist/inference/interruption/interruption_detector.d.cts +2 -2
- package/dist/inference/interruption/interruption_detector.d.ts +2 -2
- package/dist/inference/interruption/interruption_detector.d.ts.map +1 -1
- package/dist/inference/interruption/interruption_detector.js +22 -5
- package/dist/inference/interruption/interruption_detector.js.map +1 -1
- package/dist/inference/interruption/interruption_stream.cjs +4 -4
- package/dist/inference/interruption/interruption_stream.cjs.map +1 -1
- package/dist/inference/interruption/interruption_stream.js +4 -4
- package/dist/inference/interruption/interruption_stream.js.map +1 -1
- package/dist/inference/interruption/types.cjs.map +1 -1
- package/dist/inference/interruption/types.d.cts +2 -2
- package/dist/inference/interruption/types.d.ts +2 -2
- package/dist/inference/interruption/types.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.cjs +60 -47
- package/dist/inference/interruption/ws_transport.cjs.map +1 -1
- package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.js +60 -47
- package/dist/inference/interruption/ws_transport.js.map +1 -1
- package/dist/inference/llm.cjs.map +1 -1
- package/dist/inference/llm.d.cts +1 -1
- package/dist/inference/llm.d.ts +1 -1
- package/dist/inference/llm.d.ts.map +1 -1
- package/dist/inference/llm.js.map +1 -1
- package/dist/inference/stt.cjs +20 -12
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.cts +3 -2
- package/dist/inference/stt.d.ts +3 -2
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +20 -12
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/stt.test.cjs +14 -0
- package/dist/inference/stt.test.cjs.map +1 -1
- package/dist/inference/stt.test.js +14 -0
- package/dist/inference/stt.test.js.map +1 -1
- package/dist/inference/tts.cjs +13 -4
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +8 -1
- package/dist/inference/tts.d.ts +8 -1
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +13 -4
- package/dist/inference/tts.js.map +1 -1
- package/dist/inference/tts.test.cjs +10 -0
- package/dist/inference/tts.test.cjs.map +1 -1
- package/dist/inference/tts.test.js +10 -0
- package/dist/inference/tts.test.js.map +1 -1
- package/dist/ipc/job_proc_lazy_main.cjs +41 -23
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
- package/dist/ipc/job_proc_lazy_main.js +41 -23
- package/dist/ipc/job_proc_lazy_main.js.map +1 -1
- package/dist/job.cjs +1 -1
- package/dist/job.cjs.map +1 -1
- package/dist/job.js +1 -1
- package/dist/job.js.map +1 -1
- package/dist/language.cjs +394 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +15 -0
- package/dist/language.d.ts +15 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +363 -0
- package/dist/language.js.map +1 -0
- package/dist/language.test.cjs +43 -0
- package/dist/language.test.cjs.map +1 -0
- package/dist/language.test.js +49 -0
- package/dist/language.test.js.map +1 -0
- package/dist/llm/index.cjs +2 -0
- package/dist/llm/index.cjs.map +1 -1
- package/dist/llm/index.d.cts +1 -1
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +2 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/stream/deferred_stream.cjs +6 -2
- package/dist/stream/deferred_stream.cjs.map +1 -1
- package/dist/stream/deferred_stream.d.ts.map +1 -1
- package/dist/stream/deferred_stream.js +6 -2
- package/dist/stream/deferred_stream.js.map +1 -1
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +2 -1
- package/dist/stt/stt.d.ts +2 -1
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js.map +1 -1
- package/dist/utils.cjs +15 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +8 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +13 -0
- package/dist/utils.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/agent.cjs +14 -17
- package/dist/voice/agent.cjs.map +1 -1
- package/dist/voice/agent.d.cts +10 -11
- package/dist/voice/agent.d.ts +10 -11
- package/dist/voice/agent.d.ts.map +1 -1
- package/dist/voice/agent.js +15 -18
- package/dist/voice/agent.js.map +1 -1
- package/dist/voice/agent.test.cjs +194 -0
- package/dist/voice/agent.test.cjs.map +1 -1
- package/dist/voice/agent.test.js +195 -1
- package/dist/voice/agent.test.js.map +1 -1
- package/dist/voice/agent_activity.cjs +116 -39
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +2 -0
- package/dist/voice/agent_activity.d.ts +2 -0
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +117 -40
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_activity.test.cjs +135 -0
- package/dist/voice/agent_activity.test.cjs.map +1 -0
- package/dist/voice/agent_activity.test.js +134 -0
- package/dist/voice/agent_activity.test.js.map +1 -0
- package/dist/voice/agent_session.cjs +38 -38
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +65 -56
- package/dist/voice/agent_session.d.ts +65 -56
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +37 -37
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +106 -52
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +4 -2
- package/dist/voice/audio_recognition.d.ts +4 -2
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +106 -52
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/audio_recognition_span.test.cjs +84 -22
- package/dist/voice/audio_recognition_span.test.cjs.map +1 -1
- package/dist/voice/audio_recognition_span.test.js +90 -23
- package/dist/voice/audio_recognition_span.test.js.map +1 -1
- package/dist/voice/events.cjs +1 -1
- package/dist/voice/events.cjs.map +1 -1
- package/dist/voice/events.d.cts +4 -3
- package/dist/voice/events.d.ts +4 -3
- package/dist/voice/events.d.ts.map +1 -1
- package/dist/voice/events.js +1 -1
- package/dist/voice/events.js.map +1 -1
- package/dist/voice/index.cjs +9 -1
- package/dist/voice/index.cjs.map +1 -1
- package/dist/voice/index.d.cts +1 -1
- package/dist/voice/index.d.ts +1 -1
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +10 -1
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/remote_session.cjs +922 -0
- package/dist/voice/remote_session.cjs.map +1 -0
- package/dist/voice/remote_session.d.cts +108 -0
- package/dist/voice/remote_session.d.ts +108 -0
- package/dist/voice/remote_session.d.ts.map +1 -0
- package/dist/voice/remote_session.js +887 -0
- package/dist/voice/remote_session.js.map +1 -0
- package/dist/voice/report.cjs +11 -10
- package/dist/voice/report.cjs.map +1 -1
- package/dist/voice/report.d.cts +5 -3
- package/dist/voice/report.d.ts +5 -3
- package/dist/voice/report.d.ts.map +1 -1
- package/dist/voice/report.js +11 -10
- package/dist/voice/report.js.map +1 -1
- package/dist/voice/report.test.cjs +15 -0
- package/dist/voice/report.test.cjs.map +1 -1
- package/dist/voice/report.test.js +15 -0
- package/dist/voice/report.test.js.map +1 -1
- package/dist/voice/room_io/room_io.cjs +39 -0
- package/dist/voice/room_io/room_io.cjs.map +1 -1
- package/dist/voice/room_io/room_io.d.cts +3 -1
- package/dist/voice/room_io/room_io.d.ts +3 -1
- package/dist/voice/room_io/room_io.d.ts.map +1 -1
- package/dist/voice/room_io/room_io.js +40 -1
- package/dist/voice/room_io/room_io.js.map +1 -1
- package/dist/voice/turn_config/interruption.cjs.map +1 -1
- package/dist/voice/turn_config/interruption.d.cts +1 -1
- package/dist/voice/turn_config/interruption.d.ts +1 -1
- package/dist/voice/turn_config/interruption.d.ts.map +1 -1
- package/dist/voice/turn_config/interruption.js.map +1 -1
- package/dist/voice/turn_config/utils.cjs +95 -35
- package/dist/voice/turn_config/utils.cjs.map +1 -1
- package/dist/voice/turn_config/utils.d.cts +17 -5
- package/dist/voice/turn_config/utils.d.ts +17 -5
- package/dist/voice/turn_config/utils.d.ts.map +1 -1
- package/dist/voice/turn_config/utils.js +93 -35
- package/dist/voice/turn_config/utils.js.map +1 -1
- package/dist/voice/turn_config/utils.test.cjs +83 -41
- package/dist/voice/turn_config/utils.test.cjs.map +1 -1
- package/dist/voice/turn_config/utils.test.js +84 -42
- package/dist/voice/turn_config/utils.test.js.map +1 -1
- package/dist/worker.cjs +6 -29
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +6 -19
- package/dist/worker.js.map +1 -1
- package/package.json +3 -2
- package/src/cli.ts +2 -0
- package/src/constants.ts +1 -0
- package/src/cpu.test.ts +239 -0
- package/src/cpu.ts +173 -0
- package/src/index.ts +13 -15
- package/src/inference/interruption/defaults.ts +1 -1
- package/src/inference/interruption/http_transport.ts +49 -30
- package/src/inference/interruption/interruption_detector.ts +22 -6
- package/src/inference/interruption/interruption_stream.ts +4 -4
- package/src/inference/interruption/types.ts +2 -2
- package/src/inference/interruption/ws_transport.ts +63 -59
- package/src/inference/llm.ts +3 -1
- package/src/inference/stt.test.ts +17 -0
- package/src/inference/stt.ts +22 -14
- package/src/inference/tts.test.ts +12 -0
- package/src/inference/tts.ts +22 -6
- package/src/ipc/job_proc_lazy_main.ts +44 -24
- package/src/job.ts +1 -1
- package/src/language.test.ts +62 -0
- package/src/language.ts +380 -0
- package/src/llm/index.ts +2 -0
- package/src/stream/deferred_stream.ts +5 -1
- package/src/stt/stt.ts +2 -1
- package/src/utils.ts +20 -0
- package/src/voice/agent.test.ts +208 -1
- package/src/voice/agent.ts +21 -22
- package/src/voice/agent_activity.test.ts +194 -0
- package/src/voice/agent_activity.ts +161 -43
- package/src/voice/agent_session.ts +103 -92
- package/src/voice/audio_recognition.ts +124 -61
- package/src/voice/audio_recognition_span.test.ts +115 -35
- package/src/voice/events.ts +4 -3
- package/src/voice/index.ts +10 -1
- package/src/voice/remote_session.ts +1083 -0
- package/src/voice/report.test.ts +22 -3
- package/src/voice/report.ts +31 -14
- package/src/voice/room_io/room_io.ts +52 -2
- package/src/voice/turn_config/interruption.ts +1 -1
- package/src/voice/turn_config/utils.test.ts +91 -43
- package/src/voice/turn_config/utils.ts +120 -56
- package/src/worker.ts +34 -50
- package/dist/voice/client_events.cjs +0 -554
- package/dist/voice/client_events.cjs.map +0 -1
- package/dist/voice/client_events.d.cts +0 -195
- package/dist/voice/client_events.d.ts +0 -195
- package/dist/voice/client_events.d.ts.map +0 -1
- package/dist/voice/client_events.js +0 -548
- package/dist/voice/client_events.js.map +0 -1
- package/dist/voice/wire_format.cjs +0 -798
- package/dist/voice/wire_format.cjs.map +0 -1
- package/dist/voice/wire_format.d.cts +0 -5503
- package/dist/voice/wire_format.d.ts +0 -5503
- package/dist/voice/wire_format.d.ts.map +0 -1
- package/dist/voice/wire_format.js +0 -728
- package/dist/voice/wire_format.js.map +0 -1
- package/src/voice/client_events.ts +0 -838
- package/src/voice/wire_format.ts +0 -827
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/voice/audio_recognition.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ParticipantKind } from '@livekit/rtc-node';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport {\n type Context,\n ROOT_CONTEXT,\n type Span,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport type { WritableStreamDefaultWriter } from 'node:stream/web';\nimport { ReadableStream } from 'node:stream/web';\nimport { InterruptionDetectionError } from '../inference/interruption/errors.js';\nimport type { AdaptiveInterruptionDetector } from '../inference/interruption/interruption_detector.js';\nimport { InterruptionStreamSentinel } from '../inference/interruption/interruption_stream.js';\nimport {\n type InterruptionSentinel,\n type OverlappingSpeechEvent,\n} from '../inference/interruption/types.js';\nimport { type ChatContext } from '../llm/chat_context.js';\nimport { log } from '../log.js';\nimport { DeferredReadableStream, isStreamReaderReleaseError } from '../stream/deferred_stream.js';\nimport { IdentityTransform } from '../stream/identity_transform.js';\nimport { mergeReadableStreams } from '../stream/merge_readable_streams.js';\nimport { type StreamChannel, createStreamChannel } from '../stream/stream_channel.js';\nimport { type SpeechEvent, SpeechEventType } from '../stt/stt.js';\nimport { traceTypes, tracer } from '../telemetry/index.js';\nimport { Task, delay, waitForAbort } from '../utils.js';\nimport { type VAD, type VADEvent, VADEventType } from '../vad.js';\nimport type { TurnDetectionMode } from './agent_session.js';\nimport type { STTNode } from './io.js';\nimport { setParticipantSpanAttributes } from './utils.js';\n\nexport interface EndOfTurnInfo {\n /** The new transcript text from the user's speech. */\n newTranscript: string;\n /** Confidence score of the transcript (0-1). */\n transcriptConfidence: number;\n /** Delay from speech stop to final transcription in milliseconds. */\n transcriptionDelay: number;\n /** Delay from speech stop to end of utterance detection in milliseconds. */\n endOfUtteranceDelay: number;\n /** Timestamp when user started speaking (milliseconds since epoch). */\n startedSpeakingAt: number | undefined;\n /** Timestamp when user stopped speaking (milliseconds since epoch). */\n stoppedSpeakingAt: number | undefined;\n}\n\nexport interface PreemptiveGenerationInfo {\n newTranscript: string;\n transcriptConfidence: number;\n}\n\nexport interface RecognitionHooks {\n onInterruption: (ev: OverlappingSpeechEvent) => void;\n onStartOfSpeech: (ev: VADEvent) => void;\n onVADInferenceDone: (ev: VADEvent) => void;\n onEndOfSpeech: (ev: VADEvent) => void;\n onInterimTranscript: (ev: SpeechEvent) => void;\n onFinalTranscript: (ev: SpeechEvent) => void;\n onEndOfTurn: (info: EndOfTurnInfo) => Promise<boolean>;\n onPreemptiveGeneration: (info: PreemptiveGenerationInfo) => void;\n\n retrieveChatCtx: () => ChatContext;\n}\n\nexport interface _TurnDetector {\n /** The model name used by this turn detector. */\n readonly model: string;\n /** The provider name for this turn detector. */\n readonly provider: string;\n unlikelyThreshold: (language?: string) => Promise<number | undefined>;\n supportsLanguage: (language?: string) => Promise<boolean>;\n predictEndOfTurn(chatCtx: ChatContext, timeout?: number): Promise<number>;\n}\n\nexport interface AudioRecognitionOptions {\n /** Hooks for recognition events. */\n recognitionHooks: RecognitionHooks;\n /** Speech-to-text node. */\n stt?: STTNode;\n /** Voice activity detection. */\n vad?: VAD;\n /** Turn detector for end-of-turn prediction. */\n turnDetector?: _TurnDetector;\n /** Turn detection mode. */\n turnDetectionMode?: TurnDetectionMode;\n interruptionDetection?: AdaptiveInterruptionDetector;\n /** Minimum endpointing delay in milliseconds. */\n minEndpointingDelay: number;\n /** Maximum endpointing delay in milliseconds. */\n maxEndpointingDelay: number;\n /** Root span context for tracing. */\n rootSpanContext?: Context;\n /** STT model name for tracing */\n sttModel?: string;\n /** STT provider name for tracing */\n sttProvider?: string;\n /** Getter for linked participant for span attribution */\n getLinkedParticipant?: () => ParticipantLike | undefined;\n}\n\n/**\n * Minimal participant shape for span attribution.\n * Compatible with both `LocalParticipant` and `RemoteParticipant` from `@livekit/rtc-node`.\n */\nexport interface ParticipantLike {\n sid: string | undefined;\n identity: string;\n kind: ParticipantKind;\n}\n\n// TODO add ability to update stt/vad/interruption-detection\nexport class AudioRecognition {\n private hooks: RecognitionHooks;\n private stt?: STTNode;\n private vad?: VAD;\n private turnDetector?: _TurnDetector;\n private turnDetectionMode?: TurnDetectionMode;\n private minEndpointingDelay: number;\n private maxEndpointingDelay: number;\n private lastLanguage?: string;\n private rootSpanContext?: Context;\n private sttModel?: string;\n private sttProvider?: string;\n private getLinkedParticipant?: () => ParticipantLike | undefined;\n\n private deferredInputStream: DeferredReadableStream<AudioFrame>;\n private logger = log();\n private lastFinalTranscriptTime = 0;\n private audioTranscript = '';\n private audioInterimTranscript = '';\n private audioPreflightTranscript = '';\n private finalTranscriptConfidence: number[] = [];\n private lastSpeakingTime: number | undefined;\n private speechStartTime: number | undefined;\n private userTurnCommitted = false;\n private speaking = false;\n private sampleRate?: number;\n\n private userTurnSpan?: Span;\n\n private vadInputStream: ReadableStream<AudioFrame>;\n private sttInputStream: ReadableStream<AudioFrame>;\n private silenceAudioTransform = new IdentityTransform<AudioFrame>();\n private silenceAudioWriter: WritableStreamDefaultWriter<AudioFrame>;\n\n // all cancellable tasks\n private bounceEOUTask?: Task<void>;\n private commitUserTurnTask?: Task<void>;\n private vadTask?: Task<void>;\n private sttTask?: Task<void>;\n private interruptionTask?: Task<void>;\n\n // interruption detection\n private interruptionDetection?: AdaptiveInterruptionDetector;\n private _inputStartedAt?: number;\n private ignoreUserTranscriptUntil?: number;\n private transcriptBuffer: SpeechEvent[];\n private isInterruptionEnabled: boolean;\n private isAgentSpeaking: boolean;\n private interruptionStreamChannel?: StreamChannel<InterruptionSentinel | AudioFrame>;\n\n constructor(opts: AudioRecognitionOptions) {\n this.hooks = opts.recognitionHooks;\n this.stt = opts.stt;\n this.vad = opts.vad;\n this.turnDetector = opts.turnDetector;\n this.turnDetectionMode = opts.turnDetectionMode;\n this.minEndpointingDelay = opts.minEndpointingDelay;\n this.maxEndpointingDelay = opts.maxEndpointingDelay;\n this.lastLanguage = undefined;\n this.rootSpanContext = opts.rootSpanContext;\n this.sttModel = opts.sttModel;\n this.sttProvider = opts.sttProvider;\n this.getLinkedParticipant = opts.getLinkedParticipant;\n\n this.deferredInputStream = new DeferredReadableStream<AudioFrame>();\n this.interruptionDetection = opts.interruptionDetection;\n this.transcriptBuffer = [];\n this.isInterruptionEnabled = !!(opts.interruptionDetection && opts.vad);\n this.isAgentSpeaking = false;\n\n if (opts.interruptionDetection) {\n const [vadInputStream, teedInput] = this.deferredInputStream.stream.tee();\n const [inputStream, sttInputStream] = teedInput.tee();\n this.vadInputStream = vadInputStream;\n this.sttInputStream = mergeReadableStreams(\n sttInputStream,\n this.silenceAudioTransform.readable,\n );\n this.interruptionStreamChannel = createStreamChannel();\n this.interruptionStreamChannel.addStreamInput(inputStream);\n } else {\n const [vadInputStream, sttInputStream] = this.deferredInputStream.stream.tee();\n this.vadInputStream = vadInputStream;\n this.sttInputStream = mergeReadableStreams(\n sttInputStream,\n this.silenceAudioTransform.readable,\n );\n }\n this.silenceAudioWriter = this.silenceAudioTransform.writable.getWriter();\n }\n\n /**\n * Current transcript of the user's speech, including interim transcript if available.\n */\n get currentTranscript(): string {\n if (this.audioInterimTranscript) {\n return `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n return this.audioTranscript;\n }\n\n /** @internal */\n get inputStartedAt() {\n return this._inputStartedAt;\n }\n\n /** @internal */\n updateOptions(options: { turnDetection: TurnDetectionMode | undefined }): void {\n this.turnDetectionMode = options.turnDetection;\n }\n\n async start() {\n this.vadTask = Task.from(({ signal }) => this.createVadTask(this.vad, signal));\n this.vadTask.result.catch((err) => {\n this.logger.error(`Error running VAD task: ${err}`);\n });\n\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n\n this.interruptionTask = Task.from(({ signal }) =>\n this.createInterruptionTask(this.interruptionDetection, signal),\n );\n this.interruptionTask.result.catch((err) => {\n this.logger.error(`Error running interruption task: ${err}`);\n });\n }\n\n async stop() {\n await this.sttTask?.cancelAndWait();\n await this.vadTask?.cancelAndWait();\n await this.interruptionTask?.cancelAndWait();\n }\n\n async onStartOfAgentSpeech() {\n this.isAgentSpeaking = true;\n return this.trySendInterruptionSentinel(InterruptionStreamSentinel.agentSpeechStarted());\n }\n\n async onEndOfAgentSpeech(ignoreUserTranscriptUntil: number) {\n if (!this.isInterruptionEnabled) {\n this.isAgentSpeaking = false;\n return;\n }\n\n const inputOpen = await this.trySendInterruptionSentinel(\n InterruptionStreamSentinel.agentSpeechEnded(),\n );\n if (!inputOpen) {\n this.isAgentSpeaking = false;\n return;\n }\n\n if (this.isAgentSpeaking) {\n if (this.ignoreUserTranscriptUntil === undefined) {\n this.onEndOfOverlapSpeech(Date.now());\n }\n this.ignoreUserTranscriptUntil = this.ignoreUserTranscriptUntil\n ? Math.min(ignoreUserTranscriptUntil, this.ignoreUserTranscriptUntil)\n : ignoreUserTranscriptUntil;\n\n // flush held transcripts if possible\n await this.flushHeldTranscripts();\n }\n this.isAgentSpeaking = false;\n }\n\n /** Start interruption inference when agent is speaking and overlap speech starts. */\n async onStartOfOverlapSpeech(speechDuration: number, startedAt: number, userSpeakingSpan?: Span) {\n if (this.isAgentSpeaking) {\n this.trySendInterruptionSentinel(\n InterruptionStreamSentinel.overlapSpeechStarted(\n speechDuration,\n startedAt,\n userSpeakingSpan,\n ),\n );\n }\n }\n\n /** End interruption inference when overlap speech ends. */\n async onEndOfOverlapSpeech(endedAt: number, userSpeakingSpan?: Span) {\n if (!this.isInterruptionEnabled) {\n return;\n }\n if (userSpeakingSpan && userSpeakingSpan.isRecording()) {\n userSpeakingSpan.setAttribute(traceTypes.ATTR_IS_INTERRUPTION, 'false');\n }\n\n return this.trySendInterruptionSentinel(InterruptionStreamSentinel.overlapSpeechEnded(endedAt));\n }\n\n /**\n * Flush held transcripts whose *end time* is after the ignoreUserTranscriptUntil timestamp.\n * If the event has no timestamps, we assume it is the same as the next valid event.\n */\n private async flushHeldTranscripts() {\n if (\n !this.isInterruptionEnabled ||\n this.ignoreUserTranscriptUntil === undefined ||\n this.transcriptBuffer.length === 0\n ) {\n return;\n }\n\n if (!this._inputStartedAt) {\n this.transcriptBuffer = [];\n this.ignoreUserTranscriptUntil = undefined;\n return;\n }\n\n let emitFromIndex: number | null = null;\n let shouldFlush = false;\n\n for (let i = 0; i < this.transcriptBuffer.length; i++) {\n const ev = this.transcriptBuffer[i];\n if (!ev || !ev.alternatives || ev.alternatives.length === 0) {\n emitFromIndex = Math.min(emitFromIndex ?? i, i);\n continue;\n }\n const firstAlternative = ev.alternatives[0];\n if (\n firstAlternative.startTime === firstAlternative.endTime &&\n firstAlternative.startTime === 0\n ) {\n this.transcriptBuffer = [];\n this.ignoreUserTranscriptUntil = undefined;\n return;\n }\n\n if (this.#alternativeEndsBeforeIgnoreWindow(firstAlternative)) {\n emitFromIndex = null;\n } else {\n emitFromIndex = Math.min(emitFromIndex ?? i, i);\n shouldFlush = true;\n break;\n }\n }\n\n const eventsToEmit =\n emitFromIndex !== null && shouldFlush ? this.transcriptBuffer.slice(emitFromIndex) : [];\n\n this.transcriptBuffer = [];\n this.ignoreUserTranscriptUntil = undefined;\n\n for (const event of eventsToEmit) {\n this.logger.trace(\n {\n event: event.type,\n },\n 're-emitting held user transcript',\n );\n this.onSTTEvent(event);\n }\n }\n\n #alternativeEndsBeforeIgnoreWindow(\n alternative: NonNullable<SpeechEvent['alternatives']>[number],\n ): boolean {\n if (\n this.ignoreUserTranscriptUntil === undefined ||\n !this._inputStartedAt ||\n alternative.startTime <= 0\n ) {\n return false;\n }\n\n // `SpeechData.startTime` is in seconds relative to audio start, while `inputStartedAt` and\n // `ignoreUserTranscriptUntil` are epoch milliseconds.\n return alternative.startTime * 1000 + this._inputStartedAt < this.ignoreUserTranscriptUntil;\n }\n\n private shouldHoldSttEvent(ev: SpeechEvent): boolean {\n if (!this.isInterruptionEnabled) {\n return false;\n }\n if (this.isAgentSpeaking) {\n return true;\n }\n\n // reset when the user starts speaking after the agent speech\n if (ev.type === SpeechEventType.START_OF_SPEECH) {\n this.ignoreUserTranscriptUntil = undefined;\n this.transcriptBuffer = [];\n return false;\n }\n\n if (this.ignoreUserTranscriptUntil === undefined) {\n return false;\n }\n // sentinel events are always held until we have something concrete to release them\n if (!ev.alternatives || ev.alternatives.length === 0) {\n return true;\n }\n\n const alternative = ev.alternatives[0];\n\n if (\n alternative.startTime !== alternative.endTime &&\n this.#alternativeEndsBeforeIgnoreWindow(alternative)\n ) {\n return true;\n }\n return false;\n }\n\n private async trySendInterruptionSentinel(\n frame: AudioFrame | InterruptionSentinel,\n ): Promise<boolean> {\n if (\n this.isInterruptionEnabled &&\n this.interruptionStreamChannel &&\n !this.interruptionStreamChannel.closed\n ) {\n try {\n await this.interruptionStreamChannel.write(frame);\n return true;\n } catch (e: unknown) {\n this.logger.warn(\n `could not forward interruption sentinel: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n return false;\n }\n\n private ensureUserTurnSpan(startTime?: number): Span {\n if (this.userTurnSpan && this.userTurnSpan.isRecording()) {\n return this.userTurnSpan;\n }\n\n this.userTurnSpan = tracer.startSpan({\n name: 'user_turn',\n context: this.rootSpanContext,\n startTime,\n });\n\n const participant = this.getLinkedParticipant?.();\n if (participant) {\n setParticipantSpanAttributes(this.userTurnSpan, participant);\n }\n\n if (this.sttModel) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_REQUEST_MODEL, this.sttModel);\n }\n if (this.sttProvider) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_PROVIDER_NAME, this.sttProvider);\n }\n\n return this.userTurnSpan;\n }\n\n private userTurnContext(span: Span): Context {\n const base = this.rootSpanContext ?? ROOT_CONTEXT;\n return trace.setSpan(base, span);\n }\n\n private async onSTTEvent(ev: SpeechEvent) {\n if (\n this.turnDetectionMode === 'manual' &&\n this.userTurnCommitted &&\n (this.bounceEOUTask === undefined ||\n this.bounceEOUTask.done ||\n ev.type == SpeechEventType.INTERIM_TRANSCRIPT)\n ) {\n // ignore stt event if user turn already committed and EOU task is done\n // or it's an interim transcript\n this.logger.debug(\n {\n userTurnCommitted: this.userTurnCommitted,\n eouTaskDone: this.bounceEOUTask?.done,\n evType: ev.type,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'ignoring stt event',\n );\n return;\n }\n\n // handle interruption detection\n // - hold the event until the ignore_user_transcript_until expires\n // - release only relevant events\n // - allow RECOGNITION_USAGE to pass through immediately\n\n if (ev.type !== SpeechEventType.RECOGNITION_USAGE && this.isInterruptionEnabled) {\n if (this.shouldHoldSttEvent(ev)) {\n this.logger.trace(\n { event: ev.type, ignoreUserTranscriptUntil: this.ignoreUserTranscriptUntil },\n 'holding STT event until ignore_user_transcript_until expires',\n );\n this.transcriptBuffer.push(ev);\n return;\n } else {\n await this.flushHeldTranscripts();\n // no return here to allow the new event to be processed normally\n }\n }\n\n switch (ev.type) {\n case SpeechEventType.FINAL_TRANSCRIPT:\n const transcript = ev.alternatives?.[0]?.text;\n const confidence = ev.alternatives?.[0]?.confidence ?? 0;\n this.lastLanguage = ev.alternatives?.[0]?.language;\n\n if (!transcript) {\n // stt final transcript received but no transcript\n return;\n }\n\n this.hooks.onFinalTranscript(ev);\n\n this.logger.debug(\n {\n user_transcript: transcript,\n language: this.lastLanguage,\n },\n 'received user transcript',\n );\n\n this.lastFinalTranscriptTime = Date.now();\n this.audioTranscript += ` ${transcript}`;\n this.audioTranscript = this.audioTranscript.trimStart();\n this.finalTranscriptConfidence.push(confidence);\n const transcriptChanged = this.audioTranscript !== this.audioPreflightTranscript;\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n // TODO: this would screw up transcription latency metrics\n // but we'll live with it for now.\n // the correct way is to ensure STT fires SpeechEventType.END_OF_SPEECH\n // and using that timestamp for lastSpeakingTime\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.vadBaseTurnDetection || this.userTurnCommitted) {\n if (transcriptChanged) {\n this.logger.debug(\n { transcript: this.audioTranscript },\n 'triggering preemptive generation (FINAL_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioTranscript,\n transcriptConfidence:\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0,\n });\n }\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt FINAL_TRANSCRIPT');\n this.runEOUDetection(chatCtx);\n }\n }\n break;\n case SpeechEventType.PREFLIGHT_TRANSCRIPT:\n this.hooks.onInterimTranscript(ev);\n const preflightTranscript = ev.alternatives?.[0]?.text ?? '';\n const preflightConfidence = ev.alternatives?.[0]?.confidence ?? 0;\n const preflightLanguage = ev.alternatives?.[0]?.language;\n\n const MIN_LANGUAGE_DETECTION_LENGTH = 5;\n if (\n !this.lastLanguage ||\n (preflightLanguage && preflightTranscript.length > MIN_LANGUAGE_DETECTION_LENGTH)\n ) {\n this.lastLanguage = preflightLanguage;\n }\n\n if (!preflightTranscript) {\n return;\n }\n\n this.logger.debug(\n {\n user_transcript: preflightTranscript,\n language: this.lastLanguage,\n },\n 'received user preflight transcript',\n );\n\n // still need to increment it as it's used for turn detection,\n this.lastFinalTranscriptTime = Date.now();\n // preflight transcript includes all pre-committed transcripts (including final transcript from the previous STT run)\n this.audioPreflightTranscript =\n `${this.audioTranscript} ${preflightTranscript}`.trimStart();\n this.audioInterimTranscript = preflightTranscript;\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.turnDetectionMode !== 'manual' || this.userTurnCommitted) {\n const confidenceVals = [...this.finalTranscriptConfidence, preflightConfidence];\n this.logger.debug(\n {\n transcript:\n this.audioPreflightTranscript.length > 100\n ? this.audioPreflightTranscript.slice(0, 100) + '...'\n : this.audioPreflightTranscript,\n },\n 'triggering preemptive generation (PREFLIGHT_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioPreflightTranscript,\n transcriptConfidence:\n confidenceVals.length > 0\n ? confidenceVals.reduce((a, b) => a + b, 0) / confidenceVals.length\n : 0,\n });\n }\n break;\n case SpeechEventType.INTERIM_TRANSCRIPT:\n this.logger.debug({ transcript: ev.alternatives?.[0]?.text }, 'interim transcript');\n this.hooks.onInterimTranscript(ev);\n this.audioInterimTranscript = ev.alternatives?.[0]?.text ?? '';\n break;\n case SpeechEventType.START_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan(Date.now());\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onStartOfSpeech({\n type: VADEventType.START_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: true,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = true;\n this.lastSpeakingTime = Date.now();\n\n this.bounceEOUTask?.cancel();\n break;\n case SpeechEventType.END_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onEndOfSpeech({\n type: VADEventType.END_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: false,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = false;\n this.userTurnCommitted = true;\n this.lastSpeakingTime = Date.now();\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt END_OF_SPEECH');\n this.runEOUDetection(chatCtx);\n }\n }\n }\n\n private onOverlapSpeechEvent(ev: OverlappingSpeechEvent) {\n if (ev.isInterruption) {\n this.hooks.onInterruption(ev);\n }\n }\n\n private runEOUDetection(chatCtx: ChatContext) {\n this.logger.debug(\n {\n stt: this.stt,\n audioTranscript: this.audioTranscript,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'running EOU detection',\n );\n\n if (this.stt && !this.audioTranscript && this.turnDetectionMode !== 'manual') {\n // stt enabled but no transcript yet\n this.logger.debug('skipping EOU detection');\n return;\n }\n\n chatCtx = chatCtx.copy();\n chatCtx.addMessage({ role: 'user', content: this.audioTranscript });\n\n const turnDetector =\n // disable EOU model if manual turn detection enabled\n this.audioTranscript && this.turnDetectionMode !== 'manual' ? this.turnDetector : undefined;\n\n const bounceEOUTask =\n (\n lastSpeakingTime: number | undefined,\n lastFinalTranscriptTime: number,\n speechStartTime: number | undefined,\n ) =>\n async (controller: AbortController) => {\n let endpointingDelay = this.minEndpointingDelay;\n\n const userTurnSpan = this.ensureUserTurnSpan();\n const userTurnCtx = this.userTurnContext(userTurnSpan);\n\n if (turnDetector) {\n await tracer.startActiveSpan(\n async (span) => {\n this.logger.debug('Running turn detector model');\n\n let endOfTurnProbability = 0.0;\n let unlikelyThreshold: number | undefined;\n\n if (!(await turnDetector.supportsLanguage(this.lastLanguage))) {\n this.logger.debug(`Turn detector does not support language ${this.lastLanguage}`);\n } else {\n try {\n endOfTurnProbability = await turnDetector.predictEndOfTurn(chatCtx);\n unlikelyThreshold = await turnDetector.unlikelyThreshold(this.lastLanguage);\n\n this.logger.debug(\n { endOfTurnProbability, unlikelyThreshold, language: this.lastLanguage },\n 'end of turn probability',\n );\n\n if (unlikelyThreshold && endOfTurnProbability < unlikelyThreshold) {\n endpointingDelay = this.maxEndpointingDelay;\n }\n } catch (error) {\n this.logger.error(error, 'Error predicting end of turn');\n }\n }\n\n span.setAttribute(\n traceTypes.ATTR_CHAT_CTX,\n JSON.stringify(chatCtx.toJSON({ excludeTimestamp: false })),\n );\n span.setAttribute(traceTypes.ATTR_EOU_PROBABILITY, endOfTurnProbability);\n span.setAttribute(traceTypes.ATTR_EOU_UNLIKELY_THRESHOLD, unlikelyThreshold ?? 0);\n span.setAttribute(traceTypes.ATTR_EOU_DELAY, endpointingDelay);\n span.setAttribute(traceTypes.ATTR_EOU_LANGUAGE, this.lastLanguage ?? '');\n },\n {\n name: 'eou_detection',\n context: userTurnCtx,\n },\n );\n }\n\n let extraSleep = endpointingDelay;\n if (lastSpeakingTime !== undefined) {\n extraSleep += lastSpeakingTime - Date.now();\n }\n\n if (extraSleep > 0) {\n // add delay to see if there's a potential upcoming EOU task that cancels this one\n await delay(Math.max(extraSleep, 0), { signal: controller.signal });\n }\n\n this.logger.debug({ transcript: this.audioTranscript }, 'end of user turn');\n\n const confidenceAvg =\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0;\n\n let startedSpeakingAt: number | undefined;\n let stoppedSpeakingAt: number | undefined;\n let transcriptionDelay: number | undefined;\n let endOfUtteranceDelay: number | undefined;\n\n // sometimes, we can't calculate the metrics because VAD was unreliable.\n // in this case, we just ignore the calculation, it's better than providing likely wrong values\n if (\n lastFinalTranscriptTime !== 0 &&\n lastSpeakingTime !== undefined &&\n speechStartTime !== undefined\n ) {\n startedSpeakingAt = speechStartTime;\n stoppedSpeakingAt = lastSpeakingTime;\n transcriptionDelay = Math.max(lastFinalTranscriptTime - lastSpeakingTime, 0);\n endOfUtteranceDelay = Date.now() - lastSpeakingTime;\n }\n\n const committed = await this.hooks.onEndOfTurn({\n newTranscript: this.audioTranscript,\n transcriptConfidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n startedSpeakingAt,\n stoppedSpeakingAt,\n });\n\n if (committed) {\n this._endUserTurnSpan({\n transcript: this.audioTranscript,\n confidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n });\n\n // clear the transcript if the user turn was committed\n this.audioTranscript = '';\n this.finalTranscriptConfidence = [];\n this.lastSpeakingTime = undefined;\n this.lastFinalTranscriptTime = 0;\n this.speechStartTime = undefined;\n }\n\n this.userTurnCommitted = false;\n };\n\n // cancel any existing EOU task\n this.bounceEOUTask?.cancel();\n // copy the values before awaiting (the values can change)\n this.bounceEOUTask = Task.from(\n bounceEOUTask(this.lastSpeakingTime, this.lastFinalTranscriptTime, this.speechStartTime),\n );\n\n this.bounceEOUTask.result\n .then(() => {\n this.logger.debug('EOU detection task completed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.message.includes('This operation was aborted')) {\n // ignore aborted errors\n return;\n }\n this.logger.error(err, 'Error in EOU detection task:');\n });\n }\n\n private async createSttTask(stt: STTNode | undefined, signal: AbortSignal) {\n if (!stt) return;\n\n this.logger.debug('createSttTask: create stt stream from stt node');\n\n const sttStream = await stt(this.sttInputStream, {});\n\n if (signal.aborted || sttStream === null) return;\n\n if (sttStream instanceof ReadableStream) {\n const reader = sttStream.getReader();\n\n signal.addEventListener('abort', async () => {\n try {\n reader.releaseLock();\n await sttStream?.cancel();\n } catch (e) {\n this.logger.debug('createSttTask: error during abort handler:', e);\n }\n });\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value: ev } = await reader.read();\n if (done) break;\n\n if (typeof ev === 'string') {\n throw new Error('STT node must yield SpeechEvent');\n } else {\n await this.onSTTEvent(ev);\n }\n }\n } catch (e) {\n if (isStreamReaderReleaseError(e)) {\n return;\n }\n this.logger.error({ error: e }, 'createSttTask: error reading sttStream');\n } finally {\n reader.releaseLock();\n try {\n await sttStream.cancel();\n } catch (e) {\n this.logger.debug(\n 'createSttTask: error cancelling sttStream (may already be cancelled):',\n e,\n );\n }\n }\n }\n }\n\n private async createVadTask(vad: VAD | undefined, signal: AbortSignal) {\n if (!vad) return;\n\n const vadStream = vad.stream();\n vadStream.updateInputStream(this.vadInputStream);\n\n const abortHandler = () => {\n vadStream.detachInputStream();\n vadStream.close();\n signal.removeEventListener('abort', abortHandler);\n };\n signal.addEventListener('abort', abortHandler);\n\n try {\n for await (const ev of vadStream) {\n if (signal.aborted) break;\n\n switch (ev.type) {\n case VADEventType.START_OF_SPEECH:\n this.logger.debug('VAD task: START_OF_SPEECH');\n {\n const startTime = Date.now() - ev.speechDuration;\n const span = this.ensureUserTurnSpan(startTime);\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onStartOfSpeech(ev));\n }\n this.speaking = true;\n\n // Capture sample rate from the first VAD event if not already set\n if (ev.frames.length > 0 && ev.frames[0]) {\n this.sampleRate = ev.frames[0].sampleRate;\n }\n\n this.bounceEOUTask?.cancel();\n break;\n case VADEventType.INFERENCE_DONE:\n this.hooks.onVADInferenceDone(ev);\n // for metrics, get the \"earliest\" signal of speech as possible\n if (ev.rawAccumulatedSpeech > 0.0) {\n this.lastSpeakingTime = Date.now();\n\n if (this.speechStartTime === undefined) {\n // Backdate speechStartTime to the actual start of accumulated speech.\n // ev.rawAccumulatedSpeech is in ms (VADEvent durations are all ms in TS).\n this.speechStartTime = Date.now() - ev.rawAccumulatedSpeech;\n }\n }\n break;\n case VADEventType.END_OF_SPEECH:\n this.logger.debug('VAD task: END_OF_SPEECH');\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onEndOfSpeech(ev));\n }\n\n // when VAD fires END_OF_SPEECH, it already waited for the silence_duration\n this.speaking = false;\n\n if (\n this.vadBaseTurnDetection ||\n (this.turnDetectionMode === 'stt' && this.userTurnCommitted)\n ) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.runEOUDetection(chatCtx);\n }\n break;\n }\n }\n } catch (e) {\n this.logger.error(e, 'Error in VAD task');\n } finally {\n this.logger.debug('VAD task closed');\n }\n }\n\n private async createInterruptionTask(\n interruptionDetection: AdaptiveInterruptionDetector | undefined,\n signal: AbortSignal,\n ) {\n if (!interruptionDetection || !this.interruptionStreamChannel) return;\n\n const stream = interruptionDetection.createStream();\n const inputReader = this.interruptionStreamChannel.stream().getReader();\n\n const cleanup = async () => {\n try {\n signal.removeEventListener('abort', abortHandler);\n eventReader.releaseLock();\n await stream.close();\n } catch (e) {\n this.logger.debug('createInterruptionTask: error during abort handler:', e);\n }\n };\n\n // Forward input frames/sentinels to the interruption stream\n const forwardTask = (async () => {\n try {\n const abortPromise = waitForAbort(signal);\n while (!signal.aborted) {\n const res = await Promise.race([inputReader.read(), abortPromise]);\n if (!res) break;\n const { value, done } = res;\n if (done) break;\n // Backdate to the actual start of the audio frame, not when it was received.\n if (value instanceof AudioFrame) {\n const frameDurationMs = (value.samplesPerChannel / value.sampleRate) * 1000;\n this._inputStartedAt ??= Date.now() - frameDurationMs;\n } else {\n this._inputStartedAt ??= Date.now();\n }\n await stream.pushFrame(value);\n }\n } finally {\n inputReader.releaseLock();\n }\n })();\n\n // Read output events from the interruption stream\n const eventReader = stream.stream().getReader();\n const abortHandler = async () => {\n await cleanup();\n };\n signal.addEventListener('abort', abortHandler);\n\n try {\n const abortPromise = waitForAbort(signal);\n\n while (!signal.aborted) {\n const res = await Promise.race([eventReader.read(), abortPromise]);\n if (!res) break;\n const { done, value: ev } = res;\n if (done) break;\n this.onOverlapSpeechEvent(ev);\n }\n } catch (e) {\n if (!signal.aborted) {\n const cause = e instanceof Error ? e : new Error(String(e));\n interruptionDetection.emitError(\n new InterruptionDetectionError(\n cause.message,\n Date.now(),\n interruptionDetection.label,\n false,\n ),\n );\n this.logger.error(e, 'Error in interruption task');\n }\n } finally {\n await cleanup();\n await forwardTask;\n this.logger.debug('Interruption task closed');\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>) {\n this.deferredInputStream.setSource(audioStream);\n }\n\n detachInputAudioStream() {\n this.deferredInputStream.detachSource();\n }\n\n clearUserTurn() {\n this.audioTranscript = '';\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n this.finalTranscriptConfidence = [];\n this.userTurnCommitted = false;\n\n this.sttTask?.cancelAndWait().finally(() => {\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n });\n }\n\n commitUserTurn(audioDetached: boolean) {\n const commitUserTurnTask =\n (delayDuration: number = 500) =>\n async (controller: AbortController) => {\n if (Date.now() - this.lastFinalTranscriptTime > delayDuration) {\n // flush the stt by pushing silence\n if (audioDetached && this.sampleRate !== undefined) {\n const numSamples = Math.floor(this.sampleRate * 0.5);\n const silence = new Int16Array(numSamples * 2);\n const silenceFrame = new AudioFrame(silence, this.sampleRate, 1, numSamples);\n this.silenceAudioWriter.write(silenceFrame);\n }\n\n // wait for the final transcript to be available\n await delay(delayDuration, { signal: controller.signal });\n }\n\n if (this.audioInterimTranscript) {\n // append interim transcript in case the final transcript is not ready\n this.audioTranscript = `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n this.audioInterimTranscript = '';\n\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on commitUserTurn');\n this.runEOUDetection(chatCtx);\n this.userTurnCommitted = true;\n };\n\n // cancel any existing commit user turn task\n this.commitUserTurnTask?.cancel();\n this.commitUserTurnTask = Task.from(commitUserTurnTask());\n\n this.commitUserTurnTask.result\n .then(() => {\n this.logger.debug('User turn committed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.name === 'AbortError') {\n this.logger.debug('User turn commit task cancelled');\n return;\n }\n this.logger.error(err, 'Error in user turn commit task:');\n });\n }\n\n async close() {\n this.detachInputAudioStream();\n this.silenceAudioWriter.releaseLock();\n await this.commitUserTurnTask?.cancelAndWait();\n await this.sttTask?.cancelAndWait();\n await this.vadTask?.cancelAndWait();\n await this.bounceEOUTask?.cancelAndWait();\n await this.interruptionTask?.cancelAndWait();\n await this.interruptionStreamChannel?.close();\n }\n\n private _endUserTurnSpan({\n transcript,\n confidence,\n transcriptionDelay,\n endOfUtteranceDelay,\n }: {\n transcript: string;\n confidence: number;\n transcriptionDelay: number;\n endOfUtteranceDelay: number;\n }): void {\n if (this.userTurnSpan) {\n this.userTurnSpan.setAttributes({\n [traceTypes.ATTR_USER_TRANSCRIPT]: transcript,\n [traceTypes.ATTR_TRANSCRIPT_CONFIDENCE]: confidence,\n [traceTypes.ATTR_TRANSCRIPTION_DELAY]: transcriptionDelay,\n [traceTypes.ATTR_END_OF_TURN_DELAY]: endOfUtteranceDelay,\n });\n this.userTurnSpan.end();\n this.userTurnSpan = undefined;\n }\n }\n\n private get vadBaseTurnDetection() {\n if (typeof this.turnDetectionMode === 'object') {\n return false;\n }\n\n if (this.turnDetectionMode === undefined || this.turnDetectionMode === 'vad') {\n return true;\n }\n\n return false;\n }\n}\n"],"mappings":"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","names":["import_utils","otelContext"]}
|
|
1
|
+
{"version":3,"sources":["../../src/voice/audio_recognition.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ParticipantKind } from '@livekit/rtc-node';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport {\n type Context,\n ROOT_CONTEXT,\n type Span,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport type { WritableStreamDefaultWriter } from 'node:stream/web';\nimport { ReadableStream } from 'node:stream/web';\nimport { isAPIError } from '../_exceptions.js';\nimport { apiConnectDefaults, intervalForRetry } from '../inference/interruption/defaults.js';\nimport { InterruptionDetectionError } from '../inference/interruption/errors.js';\nimport type { AdaptiveInterruptionDetector } from '../inference/interruption/interruption_detector.js';\nimport { InterruptionStreamSentinel } from '../inference/interruption/interruption_stream.js';\nimport {\n type InterruptionSentinel,\n type OverlappingSpeechEvent,\n} from '../inference/interruption/types.js';\nimport type { LanguageCode } from '../language.js';\nimport { type ChatContext } from '../llm/chat_context.js';\nimport { log } from '../log.js';\nimport { DeferredReadableStream, isStreamReaderReleaseError } from '../stream/deferred_stream.js';\nimport { IdentityTransform } from '../stream/identity_transform.js';\nimport { mergeReadableStreams } from '../stream/merge_readable_streams.js';\nimport { type StreamChannel, createStreamChannel } from '../stream/stream_channel.js';\nimport { type SpeechEvent, SpeechEventType } from '../stt/stt.js';\nimport { traceTypes, tracer } from '../telemetry/index.js';\nimport { Task, delay, waitForAbort } from '../utils.js';\nimport { type VAD, type VADEvent, VADEventType } from '../vad.js';\nimport type { TurnDetectionMode } from './agent_session.js';\nimport type { STTNode } from './io.js';\nimport { setParticipantSpanAttributes } from './utils.js';\n\nexport interface EndOfTurnInfo {\n /** The new transcript text from the user's speech. */\n newTranscript: string;\n /** Confidence score of the transcript (0-1). */\n transcriptConfidence: number;\n /** Delay from speech stop to final transcription in milliseconds. */\n transcriptionDelay: number;\n /** Delay from speech stop to end of utterance detection in milliseconds. */\n endOfUtteranceDelay: number;\n /** Timestamp when user started speaking (milliseconds since epoch). */\n startedSpeakingAt: number | undefined;\n /** Timestamp when user stopped speaking (milliseconds since epoch). */\n stoppedSpeakingAt: number | undefined;\n}\n\nexport interface PreemptiveGenerationInfo {\n newTranscript: string;\n transcriptConfidence: number;\n}\n\nexport interface RecognitionHooks {\n onInterruption: (ev: OverlappingSpeechEvent) => void;\n onStartOfSpeech: (ev: VADEvent) => void;\n onVADInferenceDone: (ev: VADEvent) => void;\n onEndOfSpeech: (ev: VADEvent) => void;\n onInterimTranscript: (ev: SpeechEvent) => void;\n onFinalTranscript: (ev: SpeechEvent) => void;\n onEndOfTurn: (info: EndOfTurnInfo) => Promise<boolean>;\n onPreemptiveGeneration: (info: PreemptiveGenerationInfo) => void;\n\n retrieveChatCtx: () => ChatContext;\n}\n\nexport interface _TurnDetector {\n /** The model name used by this turn detector. */\n readonly model: string;\n /** The provider name for this turn detector. */\n readonly provider: string;\n unlikelyThreshold: (language?: LanguageCode) => Promise<number | undefined>;\n supportsLanguage: (language?: LanguageCode) => Promise<boolean>;\n predictEndOfTurn(chatCtx: ChatContext, timeout?: number): Promise<number>;\n}\n\nexport interface AudioRecognitionOptions {\n /** Hooks for recognition events. */\n recognitionHooks: RecognitionHooks;\n /** Speech-to-text node. */\n stt?: STTNode;\n /** Voice activity detection. */\n vad?: VAD;\n /** Turn detector for end-of-turn prediction. */\n turnDetector?: _TurnDetector;\n /** Turn detection mode. */\n turnDetectionMode?: TurnDetectionMode;\n interruptionDetection?: AdaptiveInterruptionDetector;\n /** Minimum endpointing delay in milliseconds. */\n minEndpointingDelay: number;\n /** Maximum endpointing delay in milliseconds. */\n maxEndpointingDelay: number;\n /** Root span context for tracing. */\n rootSpanContext?: Context;\n /** STT model name for tracing */\n sttModel?: string;\n /** STT provider name for tracing */\n sttProvider?: string;\n /** Getter for linked participant for span attribution */\n getLinkedParticipant?: () => ParticipantLike | undefined;\n}\n\n/**\n * Minimal participant shape for span attribution.\n * Compatible with both `LocalParticipant` and `RemoteParticipant` from `@livekit/rtc-node`.\n */\nexport interface ParticipantLike {\n sid: string | undefined;\n identity: string;\n kind: ParticipantKind;\n}\n\n// TODO add ability to update stt/vad/interruption-detection\nexport class AudioRecognition {\n private hooks: RecognitionHooks;\n private stt?: STTNode;\n private vad?: VAD;\n private turnDetector?: _TurnDetector;\n private turnDetectionMode?: TurnDetectionMode;\n private minEndpointingDelay: number;\n private maxEndpointingDelay: number;\n private lastLanguage?: LanguageCode;\n private rootSpanContext?: Context;\n private sttModel?: string;\n private sttProvider?: string;\n private getLinkedParticipant?: () => ParticipantLike | undefined;\n\n private deferredInputStream: DeferredReadableStream<AudioFrame>;\n private logger = log();\n private lastFinalTranscriptTime = 0;\n private audioTranscript = '';\n private audioInterimTranscript = '';\n private audioPreflightTranscript = '';\n private finalTranscriptConfidence: number[] = [];\n private lastSpeakingTime: number | undefined;\n private speechStartTime: number | undefined;\n private userTurnCommitted = false;\n private speaking = false;\n private sampleRate?: number;\n\n private userTurnSpan?: Span;\n\n private vadInputStream: ReadableStream<AudioFrame>;\n private sttInputStream: ReadableStream<AudioFrame>;\n private silenceAudioTransform = new IdentityTransform<AudioFrame>();\n private silenceAudioWriter: WritableStreamDefaultWriter<AudioFrame>;\n\n // all cancellable tasks\n private bounceEOUTask?: Task<void>;\n private commitUserTurnTask?: Task<void>;\n private vadTask?: Task<void>;\n private sttTask?: Task<void>;\n private interruptionTask?: Task<void>;\n\n // interruption detection\n private interruptionDetection?: AdaptiveInterruptionDetector;\n private _inputStartedAt?: number;\n private ignoreUserTranscriptUntil?: number;\n private transcriptBuffer: SpeechEvent[];\n private isInterruptionEnabled: boolean;\n private isAgentSpeaking: boolean;\n private interruptionStreamChannel?: StreamChannel<InterruptionSentinel | AudioFrame>;\n\n constructor(opts: AudioRecognitionOptions) {\n this.hooks = opts.recognitionHooks;\n this.stt = opts.stt;\n this.vad = opts.vad;\n this.turnDetector = opts.turnDetector;\n this.turnDetectionMode = opts.turnDetectionMode;\n this.minEndpointingDelay = opts.minEndpointingDelay;\n this.maxEndpointingDelay = opts.maxEndpointingDelay;\n this.lastLanguage = undefined;\n this.rootSpanContext = opts.rootSpanContext;\n this.sttModel = opts.sttModel;\n this.sttProvider = opts.sttProvider;\n this.getLinkedParticipant = opts.getLinkedParticipant;\n\n this.deferredInputStream = new DeferredReadableStream<AudioFrame>();\n this.interruptionDetection = opts.interruptionDetection;\n this.transcriptBuffer = [];\n this.isInterruptionEnabled = !!(opts.interruptionDetection && opts.vad);\n this.isAgentSpeaking = false;\n\n if (opts.interruptionDetection) {\n const [vadInputStream, teedInput] = this.deferredInputStream.stream.tee();\n const [inputStream, sttInputStream] = teedInput.tee();\n this.vadInputStream = vadInputStream;\n this.sttInputStream = mergeReadableStreams(\n sttInputStream,\n this.silenceAudioTransform.readable,\n );\n this.interruptionStreamChannel = createStreamChannel();\n this.interruptionStreamChannel.addStreamInput(inputStream);\n } else {\n const [vadInputStream, sttInputStream] = this.deferredInputStream.stream.tee();\n this.vadInputStream = vadInputStream;\n this.sttInputStream = mergeReadableStreams(\n sttInputStream,\n this.silenceAudioTransform.readable,\n );\n }\n this.silenceAudioWriter = this.silenceAudioTransform.writable.getWriter();\n }\n\n /**\n * Current transcript of the user's speech, including interim transcript if available.\n */\n get currentTranscript(): string {\n if (this.audioInterimTranscript) {\n return `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n return this.audioTranscript;\n }\n\n /** @internal */\n get inputStartedAt() {\n return this._inputStartedAt;\n }\n\n /** @internal */\n updateOptions(options: { turnDetection: TurnDetectionMode | undefined }): void {\n this.turnDetectionMode = options.turnDetection;\n }\n\n async start() {\n this.vadTask = Task.from(({ signal }) => this.createVadTask(this.vad, signal));\n this.vadTask.result.catch((err) => {\n this.logger.error(`Error running VAD task: ${err}`);\n });\n\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n\n this.interruptionTask = Task.from(({ signal }) =>\n this.createInterruptionTask(this.interruptionDetection, signal),\n );\n this.interruptionTask.result.catch((err) => {\n this.logger.error(`Error running interruption task: ${err}`);\n });\n }\n\n async stop() {\n await this.sttTask?.cancelAndWait();\n await this.vadTask?.cancelAndWait();\n await this.interruptionTask?.cancelAndWait();\n }\n\n async disableInterruptionDetection(): Promise<void> {\n this.isInterruptionEnabled = false;\n this.interruptionDetection = undefined;\n await this.interruptionTask?.cancelAndWait();\n this.interruptionTask = undefined;\n await this.interruptionStreamChannel?.close();\n this.interruptionStreamChannel = undefined;\n }\n\n async onStartOfAgentSpeech() {\n this.isAgentSpeaking = true;\n return this.trySendInterruptionSentinel(InterruptionStreamSentinel.agentSpeechStarted());\n }\n\n async onEndOfAgentSpeech(ignoreUserTranscriptUntil: number) {\n if (!this.isInterruptionEnabled) {\n this.isAgentSpeaking = false;\n return;\n }\n\n const inputOpen = await this.trySendInterruptionSentinel(\n InterruptionStreamSentinel.agentSpeechEnded(),\n );\n if (!inputOpen) {\n this.isAgentSpeaking = false;\n return;\n }\n\n if (this.isAgentSpeaking) {\n if (this.ignoreUserTranscriptUntil === undefined) {\n this.onEndOfOverlapSpeech(Date.now());\n }\n this.ignoreUserTranscriptUntil = this.ignoreUserTranscriptUntil\n ? Math.min(ignoreUserTranscriptUntil, this.ignoreUserTranscriptUntil)\n : ignoreUserTranscriptUntil;\n\n // flush held transcripts if possible\n await this.flushHeldTranscripts();\n }\n this.isAgentSpeaking = false;\n }\n\n /** Start interruption inference when agent is speaking and overlap speech starts. */\n async onStartOfOverlapSpeech(speechDuration: number, startedAt: number, userSpeakingSpan?: Span) {\n if (this.isAgentSpeaking) {\n this.trySendInterruptionSentinel(\n InterruptionStreamSentinel.overlapSpeechStarted(\n speechDuration,\n startedAt,\n userSpeakingSpan,\n ),\n );\n }\n }\n\n /** End interruption inference when overlap speech ends. */\n async onEndOfOverlapSpeech(endedAt: number, userSpeakingSpan?: Span) {\n if (!this.isInterruptionEnabled) {\n return;\n }\n if (userSpeakingSpan && userSpeakingSpan.isRecording()) {\n userSpeakingSpan.setAttribute(traceTypes.ATTR_IS_INTERRUPTION, 'false');\n }\n\n return this.trySendInterruptionSentinel(InterruptionStreamSentinel.overlapSpeechEnded(endedAt));\n }\n\n /**\n * Flush held transcripts whose *end time* is after the ignoreUserTranscriptUntil timestamp.\n * If the event has no timestamps, we assume it is the same as the next valid event.\n */\n private async flushHeldTranscripts() {\n if (\n !this.isInterruptionEnabled ||\n this.ignoreUserTranscriptUntil === undefined ||\n this.transcriptBuffer.length === 0\n ) {\n return;\n }\n\n if (!this._inputStartedAt) {\n this.transcriptBuffer = [];\n this.ignoreUserTranscriptUntil = undefined;\n return;\n }\n\n let emitFromIndex: number | null = null;\n let shouldFlush = false;\n\n for (let i = 0; i < this.transcriptBuffer.length; i++) {\n const ev = this.transcriptBuffer[i];\n if (!ev || !ev.alternatives || ev.alternatives.length === 0) {\n emitFromIndex = Math.min(emitFromIndex ?? i, i);\n continue;\n }\n const firstAlternative = ev.alternatives[0];\n if (\n firstAlternative.startTime === firstAlternative.endTime &&\n firstAlternative.startTime === 0\n ) {\n this.transcriptBuffer = [];\n this.ignoreUserTranscriptUntil = undefined;\n return;\n }\n\n if (this.#alternativeEndsBeforeIgnoreWindow(firstAlternative)) {\n emitFromIndex = null;\n } else {\n emitFromIndex = Math.min(emitFromIndex ?? i, i);\n shouldFlush = true;\n break;\n }\n }\n\n const eventsToEmit =\n emitFromIndex !== null && shouldFlush ? this.transcriptBuffer.slice(emitFromIndex) : [];\n\n this.transcriptBuffer = [];\n this.ignoreUserTranscriptUntil = undefined;\n\n for (const event of eventsToEmit) {\n this.logger.trace(\n {\n event: event.type,\n },\n 're-emitting held user transcript',\n );\n this.onSTTEvent(event);\n }\n }\n\n #alternativeEndsBeforeIgnoreWindow(\n alternative: NonNullable<SpeechEvent['alternatives']>[number],\n ): boolean {\n if (\n this.ignoreUserTranscriptUntil === undefined ||\n !this._inputStartedAt ||\n alternative.startTime <= 0\n ) {\n return false;\n }\n\n // `SpeechData.startTime` is in seconds relative to audio start, while `inputStartedAt` and\n // `ignoreUserTranscriptUntil` are epoch milliseconds.\n return alternative.startTime * 1000 + this._inputStartedAt < this.ignoreUserTranscriptUntil;\n }\n\n private shouldHoldSttEvent(ev: SpeechEvent): boolean {\n if (!this.isInterruptionEnabled) {\n return false;\n }\n if (this.isAgentSpeaking) {\n return true;\n }\n\n // reset when the user starts speaking after the agent speech\n if (ev.type === SpeechEventType.START_OF_SPEECH) {\n this.ignoreUserTranscriptUntil = undefined;\n this.transcriptBuffer = [];\n return false;\n }\n\n if (this.ignoreUserTranscriptUntil === undefined) {\n return false;\n }\n // sentinel events are always held until we have something concrete to release them\n if (!ev.alternatives || ev.alternatives.length === 0) {\n return true;\n }\n\n const alternative = ev.alternatives[0];\n\n if (\n alternative.startTime !== alternative.endTime &&\n this.#alternativeEndsBeforeIgnoreWindow(alternative)\n ) {\n return true;\n }\n return false;\n }\n\n private async trySendInterruptionSentinel(\n frame: AudioFrame | InterruptionSentinel,\n ): Promise<boolean> {\n if (\n this.isInterruptionEnabled &&\n this.interruptionStreamChannel &&\n !this.interruptionStreamChannel.closed\n ) {\n try {\n await this.interruptionStreamChannel.write(frame);\n return true;\n } catch (e: unknown) {\n this.logger.warn(\n `could not forward interruption sentinel: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n return false;\n }\n\n private ensureUserTurnSpan(startTime?: number): Span {\n if (this.userTurnSpan && this.userTurnSpan.isRecording()) {\n return this.userTurnSpan;\n }\n\n this.userTurnSpan = tracer.startSpan({\n name: 'user_turn',\n context: this.rootSpanContext,\n startTime,\n });\n\n const participant = this.getLinkedParticipant?.();\n if (participant) {\n setParticipantSpanAttributes(this.userTurnSpan, participant);\n }\n\n if (this.sttModel) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_REQUEST_MODEL, this.sttModel);\n }\n if (this.sttProvider) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_PROVIDER_NAME, this.sttProvider);\n }\n\n return this.userTurnSpan;\n }\n\n private userTurnContext(span: Span): Context {\n const base = this.rootSpanContext ?? ROOT_CONTEXT;\n return trace.setSpan(base, span);\n }\n\n private async onSTTEvent(ev: SpeechEvent) {\n if (\n this.turnDetectionMode === 'manual' &&\n this.userTurnCommitted &&\n (this.bounceEOUTask === undefined ||\n this.bounceEOUTask.done ||\n ev.type == SpeechEventType.INTERIM_TRANSCRIPT)\n ) {\n // ignore stt event if user turn already committed and EOU task is done\n // or it's an interim transcript\n this.logger.debug(\n {\n userTurnCommitted: this.userTurnCommitted,\n eouTaskDone: this.bounceEOUTask?.done,\n evType: ev.type,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'ignoring stt event',\n );\n return;\n }\n\n // handle interruption detection\n // - hold the event until the ignore_user_transcript_until expires\n // - release only relevant events\n // - allow RECOGNITION_USAGE to pass through immediately\n\n if (ev.type !== SpeechEventType.RECOGNITION_USAGE && this.isInterruptionEnabled) {\n if (this.shouldHoldSttEvent(ev)) {\n this.logger.trace(\n { event: ev.type, ignoreUserTranscriptUntil: this.ignoreUserTranscriptUntil },\n 'holding STT event until ignore_user_transcript_until expires',\n );\n this.transcriptBuffer.push(ev);\n return;\n } else {\n await this.flushHeldTranscripts();\n // no return here to allow the new event to be processed normally\n }\n }\n\n switch (ev.type) {\n case SpeechEventType.FINAL_TRANSCRIPT:\n const transcript = ev.alternatives?.[0]?.text;\n const confidence = ev.alternatives?.[0]?.confidence ?? 0;\n this.lastLanguage = ev.alternatives?.[0]?.language;\n\n if (!transcript) {\n // stt final transcript received but no transcript\n return;\n }\n\n this.hooks.onFinalTranscript(ev);\n\n this.logger.debug(\n {\n user_transcript: transcript,\n language: this.lastLanguage,\n },\n 'received user transcript',\n );\n\n this.lastFinalTranscriptTime = Date.now();\n this.audioTranscript += ` ${transcript}`;\n this.audioTranscript = this.audioTranscript.trimStart();\n this.finalTranscriptConfidence.push(confidence);\n const transcriptChanged = this.audioTranscript !== this.audioPreflightTranscript;\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n // TODO: this would screw up transcription latency metrics\n // but we'll live with it for now.\n // the correct way is to ensure STT fires SpeechEventType.END_OF_SPEECH\n // and using that timestamp for lastSpeakingTime\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.vadBaseTurnDetection || this.userTurnCommitted) {\n if (transcriptChanged) {\n this.logger.debug(\n { transcript: this.audioTranscript },\n 'triggering preemptive generation (FINAL_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioTranscript,\n transcriptConfidence:\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0,\n });\n }\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt FINAL_TRANSCRIPT');\n this.runEOUDetection(chatCtx);\n }\n }\n break;\n case SpeechEventType.PREFLIGHT_TRANSCRIPT:\n this.hooks.onInterimTranscript(ev);\n const preflightTranscript = ev.alternatives?.[0]?.text ?? '';\n const preflightConfidence = ev.alternatives?.[0]?.confidence ?? 0;\n const preflightLanguage = ev.alternatives?.[0]?.language;\n\n const MIN_LANGUAGE_DETECTION_LENGTH = 5;\n if (\n !this.lastLanguage ||\n (preflightLanguage && preflightTranscript.length > MIN_LANGUAGE_DETECTION_LENGTH)\n ) {\n this.lastLanguage = preflightLanguage;\n }\n\n if (!preflightTranscript) {\n return;\n }\n\n this.logger.debug(\n {\n user_transcript: preflightTranscript,\n language: this.lastLanguage,\n },\n 'received user preflight transcript',\n );\n\n // still need to increment it as it's used for turn detection,\n this.lastFinalTranscriptTime = Date.now();\n // preflight transcript includes all pre-committed transcripts (including final transcript from the previous STT run)\n this.audioPreflightTranscript =\n `${this.audioTranscript} ${preflightTranscript}`.trimStart();\n this.audioInterimTranscript = preflightTranscript;\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.turnDetectionMode !== 'manual' || this.userTurnCommitted) {\n const confidenceVals = [...this.finalTranscriptConfidence, preflightConfidence];\n this.logger.debug(\n {\n transcript:\n this.audioPreflightTranscript.length > 100\n ? this.audioPreflightTranscript.slice(0, 100) + '...'\n : this.audioPreflightTranscript,\n },\n 'triggering preemptive generation (PREFLIGHT_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioPreflightTranscript,\n transcriptConfidence:\n confidenceVals.length > 0\n ? confidenceVals.reduce((a, b) => a + b, 0) / confidenceVals.length\n : 0,\n });\n }\n break;\n case SpeechEventType.INTERIM_TRANSCRIPT:\n this.logger.debug({ transcript: ev.alternatives?.[0]?.text }, 'interim transcript');\n this.hooks.onInterimTranscript(ev);\n this.audioInterimTranscript = ev.alternatives?.[0]?.text ?? '';\n break;\n case SpeechEventType.START_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan(Date.now());\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onStartOfSpeech({\n type: VADEventType.START_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: true,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = true;\n this.lastSpeakingTime = Date.now();\n\n this.bounceEOUTask?.cancel();\n break;\n case SpeechEventType.END_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onEndOfSpeech({\n type: VADEventType.END_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: false,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = false;\n this.userTurnCommitted = true;\n this.lastSpeakingTime = Date.now();\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt END_OF_SPEECH');\n this.runEOUDetection(chatCtx);\n }\n }\n }\n\n private onOverlapSpeechEvent(ev: OverlappingSpeechEvent) {\n if (ev.isInterruption) {\n this.hooks.onInterruption(ev);\n }\n }\n\n private runEOUDetection(chatCtx: ChatContext) {\n this.logger.debug(\n {\n stt: this.stt,\n audioTranscript: this.audioTranscript,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'running EOU detection',\n );\n\n if (this.stt && !this.audioTranscript && this.turnDetectionMode !== 'manual') {\n // stt enabled but no transcript yet\n this.logger.debug('skipping EOU detection');\n return;\n }\n\n chatCtx = chatCtx.copy();\n chatCtx.addMessage({ role: 'user', content: this.audioTranscript });\n\n const turnDetector =\n // disable EOU model if manual turn detection enabled\n this.audioTranscript && this.turnDetectionMode !== 'manual' ? this.turnDetector : undefined;\n\n const bounceEOUTask =\n (\n lastSpeakingTime: number | undefined,\n lastFinalTranscriptTime: number,\n speechStartTime: number | undefined,\n ) =>\n async (controller: AbortController) => {\n let endpointingDelay = this.minEndpointingDelay;\n\n const userTurnSpan = this.ensureUserTurnSpan();\n const userTurnCtx = this.userTurnContext(userTurnSpan);\n\n if (turnDetector) {\n await tracer.startActiveSpan(\n async (span) => {\n this.logger.debug('Running turn detector model');\n\n let endOfTurnProbability = 0.0;\n let unlikelyThreshold: number | undefined;\n\n if (!(await turnDetector.supportsLanguage(this.lastLanguage))) {\n this.logger.debug(`Turn detector does not support language ${this.lastLanguage}`);\n } else {\n try {\n endOfTurnProbability = await turnDetector.predictEndOfTurn(chatCtx);\n unlikelyThreshold = await turnDetector.unlikelyThreshold(this.lastLanguage);\n\n this.logger.debug(\n { endOfTurnProbability, unlikelyThreshold, language: this.lastLanguage },\n 'end of turn probability',\n );\n\n if (unlikelyThreshold && endOfTurnProbability < unlikelyThreshold) {\n endpointingDelay = this.maxEndpointingDelay;\n }\n } catch (error) {\n this.logger.error(error, 'Error predicting end of turn');\n }\n }\n\n span.setAttribute(\n traceTypes.ATTR_CHAT_CTX,\n JSON.stringify(chatCtx.toJSON({ excludeTimestamp: false })),\n );\n span.setAttribute(traceTypes.ATTR_EOU_PROBABILITY, endOfTurnProbability);\n span.setAttribute(traceTypes.ATTR_EOU_UNLIKELY_THRESHOLD, unlikelyThreshold ?? 0);\n span.setAttribute(traceTypes.ATTR_EOU_DELAY, endpointingDelay);\n span.setAttribute(traceTypes.ATTR_EOU_LANGUAGE, this.lastLanguage ?? '');\n },\n {\n name: 'eou_detection',\n context: userTurnCtx,\n },\n );\n }\n\n let extraSleep = endpointingDelay;\n if (lastSpeakingTime !== undefined) {\n extraSleep += lastSpeakingTime - Date.now();\n }\n\n if (extraSleep > 0) {\n // add delay to see if there's a potential upcoming EOU task that cancels this one\n await delay(Math.max(extraSleep, 0), { signal: controller.signal });\n }\n\n this.logger.debug({ transcript: this.audioTranscript }, 'end of user turn');\n\n const confidenceAvg =\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0;\n\n let startedSpeakingAt: number | undefined;\n let stoppedSpeakingAt: number | undefined;\n let transcriptionDelay: number | undefined;\n let endOfUtteranceDelay: number | undefined;\n\n // sometimes, we can't calculate the metrics because VAD was unreliable.\n // in this case, we just ignore the calculation, it's better than providing likely wrong values\n if (\n lastFinalTranscriptTime !== 0 &&\n lastSpeakingTime !== undefined &&\n speechStartTime !== undefined\n ) {\n startedSpeakingAt = speechStartTime;\n stoppedSpeakingAt = lastSpeakingTime;\n transcriptionDelay = Math.max(lastFinalTranscriptTime - lastSpeakingTime, 0);\n endOfUtteranceDelay = Date.now() - lastSpeakingTime;\n }\n\n const committed = await this.hooks.onEndOfTurn({\n newTranscript: this.audioTranscript,\n transcriptConfidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n startedSpeakingAt,\n stoppedSpeakingAt,\n });\n\n if (committed) {\n this._endUserTurnSpan({\n transcript: this.audioTranscript,\n confidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n });\n\n // clear the transcript if the user turn was committed\n this.audioTranscript = '';\n this.finalTranscriptConfidence = [];\n this.lastSpeakingTime = undefined;\n this.lastFinalTranscriptTime = 0;\n this.speechStartTime = undefined;\n }\n\n this.userTurnCommitted = false;\n };\n\n // cancel any existing EOU task\n this.bounceEOUTask?.cancel();\n // copy the values before awaiting (the values can change)\n this.bounceEOUTask = Task.from(\n bounceEOUTask(this.lastSpeakingTime, this.lastFinalTranscriptTime, this.speechStartTime),\n );\n\n this.bounceEOUTask.result\n .then(() => {\n this.logger.debug('EOU detection task completed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.message.includes('This operation was aborted')) {\n // ignore aborted errors\n return;\n }\n this.logger.error(err, 'Error in EOU detection task:');\n });\n }\n\n private async createSttTask(stt: STTNode | undefined, signal: AbortSignal) {\n if (!stt) return;\n\n this.logger.debug('createSttTask: create stt stream from stt node');\n\n const sttStream = await stt(this.sttInputStream, {});\n\n if (signal.aborted || sttStream === null) return;\n\n if (sttStream instanceof ReadableStream) {\n const reader = sttStream.getReader();\n\n signal.addEventListener('abort', async () => {\n try {\n reader.releaseLock();\n await sttStream?.cancel();\n } catch (e) {\n this.logger.debug('createSttTask: error during abort handler:', e);\n }\n });\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value: ev } = await reader.read();\n if (done) break;\n\n if (typeof ev === 'string') {\n throw new Error('STT node must yield SpeechEvent');\n } else {\n await this.onSTTEvent(ev);\n }\n }\n } catch (e) {\n if (isStreamReaderReleaseError(e)) {\n return;\n }\n this.logger.error({ error: e }, 'createSttTask: error reading sttStream');\n } finally {\n reader.releaseLock();\n try {\n await sttStream.cancel();\n } catch (e) {\n this.logger.debug(\n 'createSttTask: error cancelling sttStream (may already be cancelled):',\n e,\n );\n }\n }\n }\n }\n\n private async createVadTask(vad: VAD | undefined, signal: AbortSignal) {\n if (!vad) return;\n\n const vadStream = vad.stream();\n vadStream.updateInputStream(this.vadInputStream);\n\n const abortHandler = () => {\n vadStream.detachInputStream();\n vadStream.close();\n signal.removeEventListener('abort', abortHandler);\n };\n signal.addEventListener('abort', abortHandler);\n\n try {\n for await (const ev of vadStream) {\n if (signal.aborted) break;\n\n switch (ev.type) {\n case VADEventType.START_OF_SPEECH:\n this.logger.debug('VAD task: START_OF_SPEECH');\n {\n const startTime = Date.now() - ev.speechDuration;\n const span = this.ensureUserTurnSpan(startTime);\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onStartOfSpeech(ev));\n }\n this.speaking = true;\n\n // Capture sample rate from the first VAD event if not already set\n if (ev.frames.length > 0 && ev.frames[0]) {\n this.sampleRate = ev.frames[0].sampleRate;\n }\n\n this.bounceEOUTask?.cancel();\n break;\n case VADEventType.INFERENCE_DONE:\n this.hooks.onVADInferenceDone(ev);\n // for metrics, get the \"earliest\" signal of speech as possible\n if (ev.rawAccumulatedSpeech > 0.0) {\n this.lastSpeakingTime = Date.now();\n\n if (this.speechStartTime === undefined) {\n // Backdate speechStartTime to the actual start of accumulated speech.\n // ev.rawAccumulatedSpeech is in ms (VADEvent durations are all ms in TS).\n this.speechStartTime = Date.now() - ev.rawAccumulatedSpeech;\n }\n }\n break;\n case VADEventType.END_OF_SPEECH:\n this.logger.debug('VAD task: END_OF_SPEECH');\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onEndOfSpeech(ev));\n }\n\n // when VAD fires END_OF_SPEECH, it already waited for the silence_duration\n this.speaking = false;\n\n if (\n this.vadBaseTurnDetection ||\n (this.turnDetectionMode === 'stt' && this.userTurnCommitted)\n ) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.runEOUDetection(chatCtx);\n }\n break;\n }\n }\n } catch (e) {\n this.logger.error(e, 'Error in VAD task');\n } finally {\n this.logger.debug('VAD task closed');\n }\n }\n\n private async createInterruptionTask(\n interruptionDetection: AdaptiveInterruptionDetector | undefined,\n signal: AbortSignal,\n ) {\n if (!interruptionDetection || !this.interruptionStreamChannel) return;\n\n let numRetries = 0;\n const maxRetries = apiConnectDefaults.maxRetries;\n\n while (!signal.aborted) {\n const stream = interruptionDetection.createStream();\n const eventReader = stream.stream().getReader();\n\n const cleanup = async () => {\n try {\n signal.removeEventListener('abort', cleanup);\n eventReader.releaseLock();\n await stream.close();\n } catch (e) {\n this.logger.debug('createInterruptionTask: error during cleanup:', e);\n }\n };\n\n signal.addEventListener('abort', cleanup, { once: true });\n\n let forwardTask: Promise<void> | undefined;\n\n try {\n // Unlike Python where _agent_speech_started lives on `self` and survives retries,\n // JS creates a fresh InterruptionStreamBase per retry with agentSpeechStarted = false.\n // Re-inject the sentinel so the new stream knows the agent is mid-speech.\n if (numRetries > 0 && this.isAgentSpeaking) {\n await stream.pushFrame(InterruptionStreamSentinel.agentSpeechStarted());\n }\n\n forwardTask = (async () => {\n const inputReader = this.interruptionStreamChannel!.stream().getReader();\n const abortPromise = waitForAbort(signal);\n\n try {\n while (!signal.aborted) {\n const res = await Promise.race([inputReader.read(), abortPromise]);\n if (!res) break;\n\n const { value, done } = res;\n if (done) break;\n\n if (value instanceof AudioFrame) {\n const frameDurationMs = (value.samplesPerChannel / value.sampleRate) * 1000;\n this._inputStartedAt ??= Date.now() - frameDurationMs;\n } else {\n this._inputStartedAt ??= Date.now();\n }\n\n await stream.pushFrame(value);\n }\n } finally {\n inputReader.releaseLock();\n }\n })();\n\n const abortPromise = waitForAbort(signal);\n\n while (!signal.aborted) {\n const res = await Promise.race([eventReader.read(), abortPromise]);\n if (!res) break;\n const { done, value: ev } = res;\n if (done) break;\n this.onOverlapSpeechEvent(ev);\n }\n break;\n } catch (e) {\n if (signal.aborted) break;\n\n if (isAPIError(e)) {\n if (maxRetries === 0 || !e.retryable) {\n interruptionDetection.emitError(\n new InterruptionDetectionError(\n e.message,\n Date.now(),\n interruptionDetection.label,\n false,\n ),\n );\n break;\n } else if (numRetries >= maxRetries) {\n interruptionDetection.emitError(\n new InterruptionDetectionError(\n `failed to detect interruption after ${numRetries} attempts`,\n Date.now(),\n interruptionDetection.label,\n false,\n ),\n );\n break;\n } else {\n const retryInterval = intervalForRetry(numRetries);\n interruptionDetection.emitError(\n new InterruptionDetectionError(\n e.message,\n Date.now(),\n interruptionDetection.label,\n true,\n ),\n );\n this.logger.warn(\n { model: interruptionDetection.label, attempt: numRetries },\n `failed to detect interruption, retrying in ${retryInterval}ms`,\n );\n numRetries++;\n await delay(retryInterval, { signal });\n }\n } else {\n const msg = e instanceof Error ? e.message : String(e);\n interruptionDetection.emitError(\n new InterruptionDetectionError(msg, Date.now(), interruptionDetection.label, false),\n );\n this.logger.error(e, 'Error in interruption task');\n break;\n }\n } finally {\n await cleanup();\n await forwardTask?.catch((e) => {\n this.logger.debug({ err: e }, 'interruption task exited with error');\n });\n }\n }\n this.logger.debug('Interruption task closed');\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>) {\n this.deferredInputStream.setSource(audioStream);\n }\n\n detachInputAudioStream() {\n this.deferredInputStream.detachSource();\n }\n\n clearUserTurn() {\n this.audioTranscript = '';\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n this.finalTranscriptConfidence = [];\n this.userTurnCommitted = false;\n\n this.sttTask?.cancelAndWait().finally(() => {\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n });\n }\n\n commitUserTurn(audioDetached: boolean) {\n const commitUserTurnTask =\n (delayDuration: number = 500) =>\n async (controller: AbortController) => {\n if (Date.now() - this.lastFinalTranscriptTime > delayDuration) {\n // flush the stt by pushing silence\n if (audioDetached && this.sampleRate !== undefined) {\n const numSamples = Math.floor(this.sampleRate * 0.5);\n const silence = new Int16Array(numSamples * 2);\n const silenceFrame = new AudioFrame(silence, this.sampleRate, 1, numSamples);\n this.silenceAudioWriter.write(silenceFrame);\n }\n\n // wait for the final transcript to be available\n await delay(delayDuration, { signal: controller.signal });\n }\n\n if (this.audioInterimTranscript) {\n // append interim transcript in case the final transcript is not ready\n this.audioTranscript = `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n this.audioInterimTranscript = '';\n\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on commitUserTurn');\n this.runEOUDetection(chatCtx);\n this.userTurnCommitted = true;\n };\n\n // cancel any existing commit user turn task\n this.commitUserTurnTask?.cancel();\n this.commitUserTurnTask = Task.from(commitUserTurnTask());\n\n this.commitUserTurnTask.result\n .then(() => {\n this.logger.debug('User turn committed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.name === 'AbortError') {\n this.logger.debug('User turn commit task cancelled');\n return;\n }\n this.logger.error(err, 'Error in user turn commit task:');\n });\n }\n\n async close() {\n this.detachInputAudioStream();\n this.silenceAudioWriter.releaseLock();\n await this.commitUserTurnTask?.cancelAndWait();\n await this.sttTask?.cancelAndWait();\n await this.vadTask?.cancelAndWait();\n await this.bounceEOUTask?.cancelAndWait();\n await this.interruptionTask?.cancelAndWait();\n await this.interruptionStreamChannel?.close();\n }\n\n private _endUserTurnSpan({\n transcript,\n confidence,\n transcriptionDelay,\n endOfUtteranceDelay,\n }: {\n transcript: string;\n confidence: number;\n transcriptionDelay: number;\n endOfUtteranceDelay: number;\n }): void {\n if (this.userTurnSpan) {\n this.userTurnSpan.setAttributes({\n [traceTypes.ATTR_USER_TRANSCRIPT]: transcript,\n [traceTypes.ATTR_TRANSCRIPT_CONFIDENCE]: confidence,\n [traceTypes.ATTR_TRANSCRIPTION_DELAY]: transcriptionDelay,\n [traceTypes.ATTR_END_OF_TURN_DELAY]: endOfUtteranceDelay,\n });\n this.userTurnSpan.end();\n this.userTurnSpan = undefined;\n }\n }\n\n private get vadBaseTurnDetection() {\n if (typeof this.turnDetectionMode === 'object') {\n return false;\n }\n\n if (this.turnDetectionMode === undefined || this.turnDetectionMode === 'vad') {\n return true;\n }\n\n return false;\n }\n}\n"],"mappings":"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","names":["import_utils","otelContext","abortPromise"]}
|
|
@@ -5,6 +5,7 @@ import { type Context, type Span } from '@opentelemetry/api';
|
|
|
5
5
|
import { ReadableStream } from 'node:stream/web';
|
|
6
6
|
import type { AdaptiveInterruptionDetector } from '../inference/interruption/interruption_detector.js';
|
|
7
7
|
import { type OverlappingSpeechEvent } from '../inference/interruption/types.js';
|
|
8
|
+
import type { LanguageCode } from '../language.js';
|
|
8
9
|
import { type ChatContext } from '../llm/chat_context.js';
|
|
9
10
|
import { type SpeechEvent } from '../stt/stt.js';
|
|
10
11
|
import { type VAD, type VADEvent } from '../vad.js';
|
|
@@ -44,8 +45,8 @@ export interface _TurnDetector {
|
|
|
44
45
|
readonly model: string;
|
|
45
46
|
/** The provider name for this turn detector. */
|
|
46
47
|
readonly provider: string;
|
|
47
|
-
unlikelyThreshold: (language?:
|
|
48
|
-
supportsLanguage: (language?:
|
|
48
|
+
unlikelyThreshold: (language?: LanguageCode) => Promise<number | undefined>;
|
|
49
|
+
supportsLanguage: (language?: LanguageCode) => Promise<boolean>;
|
|
49
50
|
predictEndOfTurn(chatCtx: ChatContext, timeout?: number): Promise<number>;
|
|
50
51
|
}
|
|
51
52
|
export interface AudioRecognitionOptions {
|
|
@@ -138,6 +139,7 @@ export declare class AudioRecognition {
|
|
|
138
139
|
}): void;
|
|
139
140
|
start(): Promise<void>;
|
|
140
141
|
stop(): Promise<void>;
|
|
142
|
+
disableInterruptionDetection(): Promise<void>;
|
|
141
143
|
onStartOfAgentSpeech(): Promise<boolean>;
|
|
142
144
|
onEndOfAgentSpeech(ignoreUserTranscriptUntil: number): Promise<void>;
|
|
143
145
|
/** Start interruption inference when agent is speaking and overlap speech starts. */
|
|
@@ -5,6 +5,7 @@ import { type Context, type Span } from '@opentelemetry/api';
|
|
|
5
5
|
import { ReadableStream } from 'node:stream/web';
|
|
6
6
|
import type { AdaptiveInterruptionDetector } from '../inference/interruption/interruption_detector.js';
|
|
7
7
|
import { type OverlappingSpeechEvent } from '../inference/interruption/types.js';
|
|
8
|
+
import type { LanguageCode } from '../language.js';
|
|
8
9
|
import { type ChatContext } from '../llm/chat_context.js';
|
|
9
10
|
import { type SpeechEvent } from '../stt/stt.js';
|
|
10
11
|
import { type VAD, type VADEvent } from '../vad.js';
|
|
@@ -44,8 +45,8 @@ export interface _TurnDetector {
|
|
|
44
45
|
readonly model: string;
|
|
45
46
|
/** The provider name for this turn detector. */
|
|
46
47
|
readonly provider: string;
|
|
47
|
-
unlikelyThreshold: (language?:
|
|
48
|
-
supportsLanguage: (language?:
|
|
48
|
+
unlikelyThreshold: (language?: LanguageCode) => Promise<number | undefined>;
|
|
49
|
+
supportsLanguage: (language?: LanguageCode) => Promise<boolean>;
|
|
49
50
|
predictEndOfTurn(chatCtx: ChatContext, timeout?: number): Promise<number>;
|
|
50
51
|
}
|
|
51
52
|
export interface AudioRecognitionOptions {
|
|
@@ -138,6 +139,7 @@ export declare class AudioRecognition {
|
|
|
138
139
|
}): void;
|
|
139
140
|
start(): Promise<void>;
|
|
140
141
|
stop(): Promise<void>;
|
|
142
|
+
disableInterruptionDetection(): Promise<void>;
|
|
141
143
|
onStartOfAgentSpeech(): Promise<boolean>;
|
|
142
144
|
onEndOfAgentSpeech(ignoreUserTranscriptUntil: number): Promise<void>;
|
|
143
145
|
/** Start interruption inference when agent is speaking and overlap speech starts. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_recognition.d.ts","sourceRoot":"","sources":["../../src/voice/audio_recognition.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,IAAI,EAGV,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"audio_recognition.d.ts","sourceRoot":"","sources":["../../src/voice/audio_recognition.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
trace
|
|
6
6
|
} from "@opentelemetry/api";
|
|
7
7
|
import { ReadableStream } from "node:stream/web";
|
|
8
|
+
import { isAPIError } from "../_exceptions.js";
|
|
9
|
+
import { apiConnectDefaults, intervalForRetry } from "../inference/interruption/defaults.js";
|
|
8
10
|
import { InterruptionDetectionError } from "../inference/interruption/errors.js";
|
|
9
11
|
import { InterruptionStreamSentinel } from "../inference/interruption/interruption_stream.js";
|
|
10
12
|
import {
|
|
@@ -141,6 +143,15 @@ class AudioRecognition {
|
|
|
141
143
|
await ((_b = this.vadTask) == null ? void 0 : _b.cancelAndWait());
|
|
142
144
|
await ((_c = this.interruptionTask) == null ? void 0 : _c.cancelAndWait());
|
|
143
145
|
}
|
|
146
|
+
async disableInterruptionDetection() {
|
|
147
|
+
var _a, _b;
|
|
148
|
+
this.isInterruptionEnabled = false;
|
|
149
|
+
this.interruptionDetection = void 0;
|
|
150
|
+
await ((_a = this.interruptionTask) == null ? void 0 : _a.cancelAndWait());
|
|
151
|
+
this.interruptionTask = void 0;
|
|
152
|
+
await ((_b = this.interruptionStreamChannel) == null ? void 0 : _b.close());
|
|
153
|
+
this.interruptionStreamChannel = void 0;
|
|
154
|
+
}
|
|
144
155
|
async onStartOfAgentSpeech() {
|
|
145
156
|
this.isAgentSpeaking = true;
|
|
146
157
|
return this.trySendInterruptionSentinel(InterruptionStreamSentinel.agentSpeechStarted());
|
|
@@ -702,69 +713,112 @@ class AudioRecognition {
|
|
|
702
713
|
}
|
|
703
714
|
async createInterruptionTask(interruptionDetection, signal) {
|
|
704
715
|
if (!interruptionDetection || !this.interruptionStreamChannel) return;
|
|
705
|
-
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
716
|
+
let numRetries = 0;
|
|
717
|
+
const maxRetries = apiConnectDefaults.maxRetries;
|
|
718
|
+
while (!signal.aborted) {
|
|
719
|
+
const stream = interruptionDetection.createStream();
|
|
720
|
+
const eventReader = stream.stream().getReader();
|
|
721
|
+
const cleanup = async () => {
|
|
722
|
+
try {
|
|
723
|
+
signal.removeEventListener("abort", cleanup);
|
|
724
|
+
eventReader.releaseLock();
|
|
725
|
+
await stream.close();
|
|
726
|
+
} catch (e) {
|
|
727
|
+
this.logger.debug("createInterruptionTask: error during cleanup:", e);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
signal.addEventListener("abort", cleanup, { once: true });
|
|
731
|
+
let forwardTask;
|
|
717
732
|
try {
|
|
733
|
+
if (numRetries > 0 && this.isAgentSpeaking) {
|
|
734
|
+
await stream.pushFrame(InterruptionStreamSentinel.agentSpeechStarted());
|
|
735
|
+
}
|
|
736
|
+
forwardTask = (async () => {
|
|
737
|
+
const inputReader = this.interruptionStreamChannel.stream().getReader();
|
|
738
|
+
const abortPromise2 = waitForAbort(signal);
|
|
739
|
+
try {
|
|
740
|
+
while (!signal.aborted) {
|
|
741
|
+
const res = await Promise.race([inputReader.read(), abortPromise2]);
|
|
742
|
+
if (!res) break;
|
|
743
|
+
const { value, done } = res;
|
|
744
|
+
if (done) break;
|
|
745
|
+
if (value instanceof AudioFrame) {
|
|
746
|
+
const frameDurationMs = value.samplesPerChannel / value.sampleRate * 1e3;
|
|
747
|
+
this._inputStartedAt ??= Date.now() - frameDurationMs;
|
|
748
|
+
} else {
|
|
749
|
+
this._inputStartedAt ??= Date.now();
|
|
750
|
+
}
|
|
751
|
+
await stream.pushFrame(value);
|
|
752
|
+
}
|
|
753
|
+
} finally {
|
|
754
|
+
inputReader.releaseLock();
|
|
755
|
+
}
|
|
756
|
+
})();
|
|
718
757
|
const abortPromise = waitForAbort(signal);
|
|
719
758
|
while (!signal.aborted) {
|
|
720
|
-
const res = await Promise.race([
|
|
759
|
+
const res = await Promise.race([eventReader.read(), abortPromise]);
|
|
721
760
|
if (!res) break;
|
|
722
|
-
const {
|
|
761
|
+
const { done, value: ev } = res;
|
|
723
762
|
if (done) break;
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
763
|
+
this.onOverlapSpeechEvent(ev);
|
|
764
|
+
}
|
|
765
|
+
break;
|
|
766
|
+
} catch (e) {
|
|
767
|
+
if (signal.aborted) break;
|
|
768
|
+
if (isAPIError(e)) {
|
|
769
|
+
if (maxRetries === 0 || !e.retryable) {
|
|
770
|
+
interruptionDetection.emitError(
|
|
771
|
+
new InterruptionDetectionError(
|
|
772
|
+
e.message,
|
|
773
|
+
Date.now(),
|
|
774
|
+
interruptionDetection.label,
|
|
775
|
+
false
|
|
776
|
+
)
|
|
777
|
+
);
|
|
778
|
+
break;
|
|
779
|
+
} else if (numRetries >= maxRetries) {
|
|
780
|
+
interruptionDetection.emitError(
|
|
781
|
+
new InterruptionDetectionError(
|
|
782
|
+
`failed to detect interruption after ${numRetries} attempts`,
|
|
783
|
+
Date.now(),
|
|
784
|
+
interruptionDetection.label,
|
|
785
|
+
false
|
|
786
|
+
)
|
|
787
|
+
);
|
|
788
|
+
break;
|
|
727
789
|
} else {
|
|
728
|
-
|
|
790
|
+
const retryInterval = intervalForRetry(numRetries);
|
|
791
|
+
interruptionDetection.emitError(
|
|
792
|
+
new InterruptionDetectionError(
|
|
793
|
+
e.message,
|
|
794
|
+
Date.now(),
|
|
795
|
+
interruptionDetection.label,
|
|
796
|
+
true
|
|
797
|
+
)
|
|
798
|
+
);
|
|
799
|
+
this.logger.warn(
|
|
800
|
+
{ model: interruptionDetection.label, attempt: numRetries },
|
|
801
|
+
`failed to detect interruption, retrying in ${retryInterval}ms`
|
|
802
|
+
);
|
|
803
|
+
numRetries++;
|
|
804
|
+
await delay(retryInterval, { signal });
|
|
729
805
|
}
|
|
730
|
-
|
|
806
|
+
} else {
|
|
807
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
808
|
+
interruptionDetection.emitError(
|
|
809
|
+
new InterruptionDetectionError(msg, Date.now(), interruptionDetection.label, false)
|
|
810
|
+
);
|
|
811
|
+
this.logger.error(e, "Error in interruption task");
|
|
812
|
+
break;
|
|
731
813
|
}
|
|
732
814
|
} finally {
|
|
733
|
-
|
|
815
|
+
await cleanup();
|
|
816
|
+
await (forwardTask == null ? void 0 : forwardTask.catch((e) => {
|
|
817
|
+
this.logger.debug({ err: e }, "interruption task exited with error");
|
|
818
|
+
}));
|
|
734
819
|
}
|
|
735
|
-
})();
|
|
736
|
-
const eventReader = stream.stream().getReader();
|
|
737
|
-
const abortHandler = async () => {
|
|
738
|
-
await cleanup();
|
|
739
|
-
};
|
|
740
|
-
signal.addEventListener("abort", abortHandler);
|
|
741
|
-
try {
|
|
742
|
-
const abortPromise = waitForAbort(signal);
|
|
743
|
-
while (!signal.aborted) {
|
|
744
|
-
const res = await Promise.race([eventReader.read(), abortPromise]);
|
|
745
|
-
if (!res) break;
|
|
746
|
-
const { done, value: ev } = res;
|
|
747
|
-
if (done) break;
|
|
748
|
-
this.onOverlapSpeechEvent(ev);
|
|
749
|
-
}
|
|
750
|
-
} catch (e) {
|
|
751
|
-
if (!signal.aborted) {
|
|
752
|
-
const cause = e instanceof Error ? e : new Error(String(e));
|
|
753
|
-
interruptionDetection.emitError(
|
|
754
|
-
new InterruptionDetectionError(
|
|
755
|
-
cause.message,
|
|
756
|
-
Date.now(),
|
|
757
|
-
interruptionDetection.label,
|
|
758
|
-
false
|
|
759
|
-
)
|
|
760
|
-
);
|
|
761
|
-
this.logger.error(e, "Error in interruption task");
|
|
762
|
-
}
|
|
763
|
-
} finally {
|
|
764
|
-
await cleanup();
|
|
765
|
-
await forwardTask;
|
|
766
|
-
this.logger.debug("Interruption task closed");
|
|
767
820
|
}
|
|
821
|
+
this.logger.debug("Interruption task closed");
|
|
768
822
|
}
|
|
769
823
|
setInputAudioStream(audioStream) {
|
|
770
824
|
this.deferredInputStream.setSource(audioStream);
|