@livekit/agents-plugin-deepgram 1.6.4 → 1.7.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/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/stt.cjs +5 -2
- package/dist/stt.cjs.map +1 -1
- package/dist/stt.d.ts.map +1 -1
- package/dist/stt.js +5 -2
- package/dist/stt.js.map +1 -1
- package/dist/stt_logging.test.cjs +69 -0
- package/dist/stt_logging.test.cjs.map +1 -0
- package/dist/stt_logging.test.d.cts +2 -0
- package/dist/stt_logging.test.d.ts +2 -0
- package/dist/stt_logging.test.d.ts.map +1 -0
- package/dist/stt_logging.test.js +68 -0
- package/dist/stt_logging.test.js.map +1 -0
- package/dist/stt_v2.cjs +2 -2
- package/dist/stt_v2.cjs.map +1 -1
- package/dist/stt_v2.js +2 -2
- package/dist/stt_v2.js.map +1 -1
- package/dist/tts.cjs +1 -1
- package/dist/tts.cjs.map +1 -1
- package/dist/tts.js +1 -1
- package/dist/tts.js.map +1 -1
- package/package.json +9 -9
- package/src/stt.ts +7 -2
- package/src/stt_logging.test.ts +78 -0
- package/src/stt_v2.ts +2 -2
- package/src/tts.ts +1 -1
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/dist/stt.cjs
CHANGED
|
@@ -440,7 +440,7 @@ class SpeechStream extends import_agents.stt.SpeechStream {
|
|
|
440
440
|
break;
|
|
441
441
|
}
|
|
442
442
|
default: {
|
|
443
|
-
this.#logger.child({
|
|
443
|
+
this.#logger.child({ "lk.pii.message": json }).warn("received unexpected message from Deepgram");
|
|
444
444
|
break;
|
|
445
445
|
}
|
|
446
446
|
}
|
|
@@ -448,7 +448,10 @@ class SpeechStream extends import_agents.stt.SpeechStream {
|
|
|
448
448
|
resolve();
|
|
449
449
|
}
|
|
450
450
|
} catch (err) {
|
|
451
|
-
this.#logger.error(
|
|
451
|
+
this.#logger.error(
|
|
452
|
+
{ error: err, "lk.pii.message": msg.toString() },
|
|
453
|
+
"Deepgram STT failed to process message"
|
|
454
|
+
);
|
|
452
455
|
reject(err);
|
|
453
456
|
}
|
|
454
457
|
});
|
package/dist/stt.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n type AudioBuffer,\n AudioByteStream,\n AudioEnergyFilter,\n Future,\n Task,\n createTimedString,\n getBaseLanguage,\n log,\n mergeFrames,\n normalizeLanguage,\n stt,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { PeriodicCollector } from './_utils.js';\nimport type { STTLanguages, STTModels } from './models.js';\n\nexport interface STTOptions {\n apiKey?: string;\n language?: STTLanguages | string;\n detectLanguage: boolean;\n interimResults: boolean;\n punctuate: boolean;\n model: STTModels | string;\n smartFormat: boolean;\n noDelay: boolean;\n endpointing: number;\n fillerWords: boolean;\n sampleRate: number;\n numChannels: number;\n keywords: [string, number][];\n keyterm: string[];\n profanityFilter: boolean;\n /**\n * Redact sensitive information from the transcription. Accepts a single value or list of\n * values. Supported values: \"pci\", \"numbers\", \"ssn\", \"true\" (redact all).\n * See https://developers.deepgram.com/docs/redaction for details.\n */\n redact: string | string[];\n utteranceEndMs?: number | null;\n replace?: Record<string, string> | null;\n search?: string[] | null;\n dictation: boolean;\n diarize: boolean;\n numerals: boolean;\n mipOptOut: boolean;\n baseUrl: string;\n}\n\nconst defaultSTTOptions: STTOptions = {\n apiKey: process.env.DEEPGRAM_API_KEY,\n language: 'en-US',\n detectLanguage: false,\n interimResults: true,\n punctuate: true,\n model: 'nova-3',\n smartFormat: true,\n noDelay: true,\n endpointing: 25,\n fillerWords: false,\n sampleRate: 16000,\n numChannels: 1,\n keywords: [],\n keyterm: [],\n profanityFilter: false,\n redact: [],\n utteranceEndMs: null,\n replace: null,\n search: null,\n dictation: false,\n diarize: false,\n numerals: false,\n mipOptOut: false,\n baseUrl: 'wss://api.deepgram.com',\n};\n\nexport class STT extends stt.STT {\n #opts: STTOptions;\n #logger = log();\n // session keyterm propagation)\n #streams = new Set<WeakRef<SpeechStream>>();\n #userKeyterm: string[];\n #sessionKeyterms: string[] = [];\n label = 'deepgram.STT';\n private abortController = new AbortController();\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Deepgram';\n }\n\n constructor(opts: Partial<STTOptions> = defaultSTTOptions) {\n super({\n streaming: true,\n interimResults: opts.interimResults ?? defaultSTTOptions.interimResults,\n alignedTranscript: 'word',\n keyterms: true,\n });\n if (opts.apiKey === undefined && defaultSTTOptions.apiKey === undefined) {\n throw new Error(\n 'Deepgram API key is required, whether as an argument or as $DEEPGRAM_API_KEY',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n language: opts.language ? normalizeLanguage(opts.language) : defaultSTTOptions.language,\n };\n\n if (this.#opts.detectLanguage) {\n this.#opts.language = undefined;\n } else {\n this.#opts.model = this.#validateModel(this.#opts.model, this.#opts.language);\n }\n\n this.#userKeyterm = [...this.#opts.keyterm];\n }\n\n async _recognize(buffer: AudioBuffer, abortSignal?: AbortSignal): Promise<stt.SpeechEvent> {\n const frame = mergeFrames(buffer);\n const listenURL = new URL(`${this.#opts.baseUrl.replace(/^ws/, 'http')}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n detect_language: this.#opts.detectLanguage,\n smart_format: this.#opts.smartFormat,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n profanity_filter: this.#opts.profanityFilter,\n numerals: this.#opts.numerals,\n mip_opt_out: this.#opts.mipOptOut,\n language: this.#opts.language,\n ...(this.#opts.keyterm.length > 0 ? { keyterm: this.#opts.keyterm } : {}),\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n\n appendSearchParams(listenURL, params);\n\n if (this.#opts.diarize) {\n this.#logger.warn('speaker diarization is not supported in non-streaming mode, ignoring');\n }\n\n const resp = await fetch(listenURL, {\n method: 'POST',\n headers: {\n Authorization: `Token ${this.#opts.apiKey}`,\n Accept: 'application/json',\n 'Content-Type': 'audio/wav',\n },\n body: new Blob([new Uint8Array(createWav(frame))], { type: 'audio/wav' }),\n signal: abortSignal ?? null,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Deepgram STT REST request failed with status ${resp.status}: ${await resp.text()}`,\n );\n }\n\n return prerecordedTranscriptionToSpeechEvent(\n this.#opts.language,\n await resp.json(),\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n const language =\n opts.language !== undefined ? normalizeLanguage(opts.language) : this.#opts.language;\n const model =\n opts.model !== undefined || opts.language !== undefined\n ? this.#validateModel(opts.model ?? this.#opts.model, language)\n : this.#opts.model;\n\n const nextOpts = { ...opts };\n if (nextOpts.keyterm !== undefined) {\n this.#userKeyterm = [...nextOpts.keyterm];\n nextOpts.keyterm = [...new Set([...this.#userKeyterm, ...this.#sessionKeyterms])];\n }\n\n this.#opts = {\n ...this.#opts,\n ...nextOpts,\n language,\n model,\n };\n }\n\n override _updateSessionKeyterms(keyterms: string[]): void {\n if (\n keyterms.length === this.#sessionKeyterms.length &&\n keyterms.every((t, i) => t === this.#sessionKeyterms[i])\n ) {\n return;\n }\n this.#sessionKeyterms = [...keyterms];\n const merged = [...new Set([...this.#userKeyterm, ...keyterms])];\n this.#opts.keyterm = merged;\n for (const ref of this.#streams) {\n const stream = ref.deref();\n if (!stream) {\n this.#streams.delete(ref);\n continue;\n }\n if (stream._speaking) {\n // defer the reconnect to the end of the utterance so we don't cut it off\n stream._pendingKeyterm = merged;\n } else {\n stream.updateOptions({ keyterm: merged });\n }\n }\n }\n\n #validateModel(model: STTModels | string, language?: string) {\n if (\n language &&\n getBaseLanguage(language) !== 'en' &&\n [\n 'nova-2-meeting',\n 'nova-2-phonecall',\n 'nova-2-finance',\n 'nova-2-conversationalai',\n 'nova-2-voicemail',\n 'nova-2-video',\n 'nova-2-medical',\n 'nova-2-drivethru',\n 'nova-2-automotive',\n 'nova-3-general',\n ].includes(model)\n ) {\n this.#logger.warn(\n `${model} does not support language ${language}, falling back to nova-2-general`,\n );\n return 'nova-2-general';\n }\n\n return model;\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SpeechStream {\n // Deepgram only supports language detection for prerecorded audio, not streaming.\n if (this.#opts.detectLanguage || this.#opts.language === undefined) {\n throw new Error(\n 'language detection is not supported in streaming mode, please disable it and specify a language',\n );\n }\n const stream = new SpeechStream(this, this.#opts, options?.connOptions);\n this.#streams.add(new WeakRef(stream));\n return stream;\n }\n\n async close() {\n this.abortController.abort();\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: STTOptions;\n #audioEnergyFilter: AudioEnergyFilter;\n #logger = log();\n #speaking = false;\n #resetWS = new Future();\n #requestId = '';\n // keyterms set while the user is speaking; applied at END_OF_SPEECH (latest wins)\n /** @internal */\n _pendingKeyterm: string[] | null = null;\n #audioDurationCollector: PeriodicCollector<number>;\n label = 'deepgram.SpeechStream';\n\n constructor(stt: STT, opts: STTOptions, connOptions?: APIConnectOptions) {\n super(stt, opts.sampleRate, connOptions);\n this.#opts = opts;\n this.closed = false;\n this.#audioEnergyFilter = new AudioEnergyFilter();\n this.#audioDurationCollector = new PeriodicCollector(\n (duration) => this.onAudioDurationReport(duration),\n { duration: 5.0 },\n );\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n let ws: WebSocket;\n\n while (!this.input.closed && !this.closed) {\n const streamURL = new URL(`${this.#opts.baseUrl}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n smart_format: this.#opts.smartFormat,\n dictation: this.#opts.dictation,\n diarize: this.#opts.diarize,\n numerals: this.#opts.numerals,\n no_delay: this.#opts.noDelay,\n interim_results: this.#opts.interimResults,\n encoding: 'linear16',\n vad_events: true,\n sample_rate: this.#opts.sampleRate,\n channels: this.#opts.numChannels,\n endpointing: this.#opts.endpointing || false,\n filler_words: this.#opts.fillerWords,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n keyterm: this.#opts.keyterm,\n profanity_filter: this.#opts.profanityFilter,\n utterance_end_ms: this.#opts.utteranceEndMs,\n replace: this.#opts.replace,\n search: this.#opts.search,\n language: this.#opts.language,\n mip_opt_out: this.#opts.mipOptOut,\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n appendSearchParams(streamURL, params);\n\n ws = new WebSocket(streamURL, {\n headers: { Authorization: `Token ${this.#opts.apiKey}` },\n });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Deepgram after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Deepgram, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Deepgram disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n /** @internal */\n get _speaking(): boolean {\n return this.#speaking;\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.keyterm !== undefined) {\n this._pendingKeyterm = null;\n }\n this.#resetWS.resolve();\n }\n\n #onEndOfSpeech() {\n if (this._pendingKeyterm !== null) {\n this.updateOptions({ keyterm: this._pendingKeyterm });\n this._pendingKeyterm = null;\n }\n }\n\n async #runWS(ws: WebSocket) {\n this.#resetWS = new Future();\n let closing = false;\n\n const keepalive = setInterval(() => {\n try {\n ws.send(JSON.stringify({ type: 'KeepAlive' }));\n } catch {\n clearInterval(keepalive);\n return;\n }\n }, 5000);\n\n // gets cancelled also when sendTask is complete\n const wsMonitor = Task.from(async (controller) => {\n const closed = new Promise<void>(async (_, reject) => {\n ws.once('close', (code, reason) => {\n if (!closing) {\n this.#logger.error(`WebSocket closed with code ${code}: ${reason}`);\n reject(new Error('WebSocket closed'));\n }\n });\n });\n\n await Promise.race([closed, waitForAbort(controller.signal)]);\n });\n\n const sendTask = async () => {\n const samples100Ms = Math.floor(this.#opts.sampleRate / 10);\n const stream = new AudioByteStream(\n this.#opts.sampleRate,\n this.#opts.numChannels,\n samples100Ms,\n );\n\n // waitForAbort internally sets up an abort listener on the abort signal\n // we need to put it outside loop to avoid constant re-registration of the listener\n const abortPromise = waitForAbort(this.abortSignal);\n\n try {\n while (!this.closed) {\n const result = await Promise.race([this.input.next(), abortPromise]);\n\n if (result === undefined) return; // aborted\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n let hasEnded = false;\n if (data === SpeechStream.FLUSH_SENTINEL) {\n frames = stream.flush();\n hasEnded = true;\n } else if (\n data.sampleRate === this.#opts.sampleRate &&\n data.channels === this.#opts.numChannels\n ) {\n frames = stream.write(data.data.buffer as ArrayBuffer);\n } else {\n throw new Error(`sample rate or channel count of frame does not match`);\n }\n\n for await (const frame of frames) {\n if (this.#audioEnergyFilter.pushFrame(frame)) {\n const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n this.#audioDurationCollector.push(frameDuration);\n ws.send(frame.data.buffer);\n }\n }\n\n if (hasEnded) {\n this.#audioDurationCollector.flush();\n ws.send(JSON.stringify({ type: 'Finalize' }));\n }\n }\n } finally {\n closing = true;\n ws.send(JSON.stringify({ type: 'CloseStream' }));\n wsMonitor.cancel();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const putMessage = (message: stt.SpeechEvent) => {\n if (!this.queue.closed) {\n try {\n this.queue.put(message);\n } catch (e) {\n // ignore\n }\n }\n };\n\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (msg) => {\n try {\n const json = JSON.parse(msg.toString());\n switch (json['type']) {\n case 'SpeechStarted': {\n // This is a normal case. Deepgram's SpeechStarted events\n // are not correlated with speech_final or utterance end.\n // It's possible that we receive two in a row without an endpoint\n // It's also possible we receive a transcript without a SpeechStarted event.\n if (this.#speaking) return;\n this.#speaking = true;\n putMessage({ type: stt.SpeechEventType.START_OF_SPEECH });\n break;\n }\n // see this page:\n // https://developers.deepgram.com/docs/understand-endpointing-interim-results#using-endpointing-speech_final\n // for more information about the different types of events\n case 'Results': {\n const metadata = json['metadata'];\n const requestId = metadata['request_id'];\n const isFinal = json['is_final'];\n const isEndpoint = json['speech_final'];\n this.#requestId = requestId;\n\n const alternatives = liveTranscriptionToSpeechData(\n this.#opts.language!,\n json,\n this.startTimeOffset,\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n\n // If, for some reason, we didn't get a SpeechStarted event but we got\n // a transcript with text, we should start speaking. It's rare but has\n // been observed.\n if (alternatives[0] && alternatives[0].text) {\n if (!this.#speaking) {\n this.#speaking = true;\n putMessage({\n type: stt.SpeechEventType.START_OF_SPEECH,\n });\n }\n\n if (isFinal) {\n putMessage({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n } else {\n putMessage({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n }\n }\n\n // if we receive an endpoint, only end the speech if\n // we either had a SpeechStarted event or we have a seen\n // a non-empty transcript (deepgram doesn't have a SpeechEnded event)\n if (isEndpoint && this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n\n break;\n }\n case 'Metadata': {\n break;\n }\n case 'UtteranceEnd': {\n if (this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n break;\n }\n default: {\n this.#logger.child({ msg: json }).warn('received unexpected message from Deepgram');\n break;\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(`STT: Error processing message: ${msg}`);\n reject(err);\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.race([\n this.#resetWS.await,\n Promise.all([sendTask(), listenTask.result, wsMonitor]),\n ]);\n closing = true;\n ws.close();\n clearInterval(keepalive);\n }\n\n private onAudioDurationReport(duration: number) {\n const usageEvent: stt.SpeechEvent = {\n type: stt.SpeechEventType.RECOGNITION_USAGE,\n requestId: this.#requestId,\n recognitionUsage: {\n audioDuration: duration,\n },\n };\n this.queue.put(usageEvent);\n }\n}\n\nfunction appendSearchParams(url: URL, params: Record<string, unknown>) {\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => url.searchParams.append(key, String(item)));\n } else if (typeof value === 'object') {\n Object.entries(value).forEach(([term, replacement]) => {\n url.searchParams.append(key, `${term}:${replacement}`);\n });\n } else {\n url.searchParams.append(key, String(value));\n }\n });\n}\n\nfunction createWav(frame: AudioFrame): Buffer {\n const bitsPerSample = 16;\n const byteRate = (frame.sampleRate * frame.channels * bitsPerSample) / 8;\n const blockAlign = (frame.channels * bitsPerSample) / 8;\n\n const header = Buffer.alloc(44);\n header.write('RIFF', 0);\n header.writeUInt32LE(36 + frame.data.byteLength, 4);\n header.write('WAVE', 8);\n header.write('fmt ', 12);\n header.writeUInt32LE(16, 16);\n header.writeUInt16LE(1, 20);\n header.writeUInt16LE(frame.channels, 22);\n header.writeUInt32LE(frame.sampleRate, 24);\n header.writeUInt32LE(byteRate, 28);\n header.writeUInt16LE(blockAlign, 32);\n header.writeUInt16LE(bitsPerSample, 34);\n header.write('data', 36);\n header.writeUInt32LE(frame.data.byteLength, 40);\n\n const pcm = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength);\n return Buffer.concat([header, pcm]);\n}\n\n/**\n * Return the word form matching the transcript requested from Deepgram.\n *\n * Both punctuate and smart_format produce a formatted transcript. When either is enabled,\n * Deepgram provides punctuated_word alongside word. Fall back to word when it is absent or empty.\n */\nfunction wordText(word: { [id: string]: any }, usePunctuatedWord: boolean): string {\n const raw = word['word'] ?? '';\n return usePunctuatedWord ? word['punctuated_word'] || raw : raw;\n}\n\nconst liveTranscriptionToSpeechData = (\n language: STTLanguages | string,\n data: { [id: string]: any },\n startTimeOffset: number = 0,\n usePunctuatedWord: boolean = true,\n): stt.SpeechData[] => {\n const alts: any[] = data['channel']['alternatives'];\n\n return alts.map((alt) => {\n const wordsData: any[] = alt['words'] ?? [];\n const detectedLanguage =\n language === 'multi' ? alt['languages']?.[0] ?? language : alt['language'] ?? language;\n\n return {\n language: normalizeLanguage(detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] + startTimeOffset : startTimeOffset,\n endTime: wordsData.length\n ? wordsData[wordsData.length - 1]['end'] + startTimeOffset\n : startTimeOffset,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: (word['start'] ?? 0) + startTimeOffset,\n endTime: (word['end'] ?? 0) + startTimeOffset,\n confidence: word['confidence'] ?? 0.0,\n startTimeOffset,\n }),\n ),\n };\n });\n};\n\nfunction prerecordedTranscriptionToSpeechEvent(\n language: STTLanguages | string | undefined,\n data: { [id: string]: any },\n usePunctuatedWord: boolean = true,\n): stt.SpeechEvent {\n const channel = data['results']['channels'][0];\n const detectedLanguage = channel['detected_language'] ?? '';\n const alternatives = channel['alternatives'].map((alt: { [id: string]: any }) => {\n const wordsData: any[] = alt['words'] ?? [];\n\n return {\n language: normalizeLanguage(language ?? detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] : 0,\n endTime: wordsData.length ? wordsData[wordsData.length - 1]['end'] : 0,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: word['start'] ?? 0,\n endTime: word['end'] ?? 0,\n confidence: word['confidence'] ?? 0.0,\n }),\n ),\n };\n }) as stt.SpeechData[];\n\n return {\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n requestId: data['metadata']['request_id'],\n ...(alternatives.length\n ? { alternatives: alternatives as [stt.SpeechData, ...stt.SpeechData[]] }\n : {}),\n };\n}\n"],"mappings":"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","names":["stt"]}
|
|
1
|
+
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n type AudioBuffer,\n AudioByteStream,\n AudioEnergyFilter,\n Future,\n Task,\n createTimedString,\n getBaseLanguage,\n log,\n mergeFrames,\n normalizeLanguage,\n stt,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { PeriodicCollector } from './_utils.js';\nimport type { STTLanguages, STTModels } from './models.js';\n\nexport interface STTOptions {\n apiKey?: string;\n language?: STTLanguages | string;\n detectLanguage: boolean;\n interimResults: boolean;\n punctuate: boolean;\n model: STTModels | string;\n smartFormat: boolean;\n noDelay: boolean;\n endpointing: number;\n fillerWords: boolean;\n sampleRate: number;\n numChannels: number;\n keywords: [string, number][];\n keyterm: string[];\n profanityFilter: boolean;\n /**\n * Redact sensitive information from the transcription. Accepts a single value or list of\n * values. Supported values: \"pci\", \"numbers\", \"ssn\", \"true\" (redact all).\n * See https://developers.deepgram.com/docs/redaction for details.\n */\n redact: string | string[];\n utteranceEndMs?: number | null;\n replace?: Record<string, string> | null;\n search?: string[] | null;\n dictation: boolean;\n diarize: boolean;\n numerals: boolean;\n mipOptOut: boolean;\n baseUrl: string;\n}\n\nconst defaultSTTOptions: STTOptions = {\n apiKey: process.env.DEEPGRAM_API_KEY,\n language: 'en-US',\n detectLanguage: false,\n interimResults: true,\n punctuate: true,\n model: 'nova-3',\n smartFormat: true,\n noDelay: true,\n endpointing: 25,\n fillerWords: false,\n sampleRate: 16000,\n numChannels: 1,\n keywords: [],\n keyterm: [],\n profanityFilter: false,\n redact: [],\n utteranceEndMs: null,\n replace: null,\n search: null,\n dictation: false,\n diarize: false,\n numerals: false,\n mipOptOut: false,\n baseUrl: 'wss://api.deepgram.com',\n};\n\nexport class STT extends stt.STT {\n #opts: STTOptions;\n #logger = log();\n // session keyterm propagation)\n #streams = new Set<WeakRef<SpeechStream>>();\n #userKeyterm: string[];\n #sessionKeyterms: string[] = [];\n label = 'deepgram.STT';\n private abortController = new AbortController();\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Deepgram';\n }\n\n constructor(opts: Partial<STTOptions> = defaultSTTOptions) {\n super({\n streaming: true,\n interimResults: opts.interimResults ?? defaultSTTOptions.interimResults,\n alignedTranscript: 'word',\n keyterms: true,\n });\n if (opts.apiKey === undefined && defaultSTTOptions.apiKey === undefined) {\n throw new Error(\n 'Deepgram API key is required, whether as an argument or as $DEEPGRAM_API_KEY',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n language: opts.language ? normalizeLanguage(opts.language) : defaultSTTOptions.language,\n };\n\n if (this.#opts.detectLanguage) {\n this.#opts.language = undefined;\n } else {\n this.#opts.model = this.#validateModel(this.#opts.model, this.#opts.language);\n }\n\n this.#userKeyterm = [...this.#opts.keyterm];\n }\n\n async _recognize(buffer: AudioBuffer, abortSignal?: AbortSignal): Promise<stt.SpeechEvent> {\n const frame = mergeFrames(buffer);\n const listenURL = new URL(`${this.#opts.baseUrl.replace(/^ws/, 'http')}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n detect_language: this.#opts.detectLanguage,\n smart_format: this.#opts.smartFormat,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n profanity_filter: this.#opts.profanityFilter,\n numerals: this.#opts.numerals,\n mip_opt_out: this.#opts.mipOptOut,\n language: this.#opts.language,\n ...(this.#opts.keyterm.length > 0 ? { keyterm: this.#opts.keyterm } : {}),\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n\n appendSearchParams(listenURL, params);\n\n if (this.#opts.diarize) {\n this.#logger.warn('speaker diarization is not supported in non-streaming mode, ignoring');\n }\n\n const resp = await fetch(listenURL, {\n method: 'POST',\n headers: {\n Authorization: `Token ${this.#opts.apiKey}`,\n Accept: 'application/json',\n 'Content-Type': 'audio/wav',\n },\n body: new Blob([new Uint8Array(createWav(frame))], { type: 'audio/wav' }),\n signal: abortSignal ?? null,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Deepgram STT REST request failed with status ${resp.status}: ${await resp.text()}`,\n );\n }\n\n return prerecordedTranscriptionToSpeechEvent(\n this.#opts.language,\n await resp.json(),\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n const language =\n opts.language !== undefined ? normalizeLanguage(opts.language) : this.#opts.language;\n const model =\n opts.model !== undefined || opts.language !== undefined\n ? this.#validateModel(opts.model ?? this.#opts.model, language)\n : this.#opts.model;\n\n const nextOpts = { ...opts };\n if (nextOpts.keyterm !== undefined) {\n this.#userKeyterm = [...nextOpts.keyterm];\n nextOpts.keyterm = [...new Set([...this.#userKeyterm, ...this.#sessionKeyterms])];\n }\n\n this.#opts = {\n ...this.#opts,\n ...nextOpts,\n language,\n model,\n };\n }\n\n override _updateSessionKeyterms(keyterms: string[]): void {\n if (\n keyterms.length === this.#sessionKeyterms.length &&\n keyterms.every((t, i) => t === this.#sessionKeyterms[i])\n ) {\n return;\n }\n this.#sessionKeyterms = [...keyterms];\n const merged = [...new Set([...this.#userKeyterm, ...keyterms])];\n this.#opts.keyterm = merged;\n for (const ref of this.#streams) {\n const stream = ref.deref();\n if (!stream) {\n this.#streams.delete(ref);\n continue;\n }\n if (stream._speaking) {\n // defer the reconnect to the end of the utterance so we don't cut it off\n stream._pendingKeyterm = merged;\n } else {\n stream.updateOptions({ keyterm: merged });\n }\n }\n }\n\n #validateModel(model: STTModels | string, language?: string) {\n if (\n language &&\n getBaseLanguage(language) !== 'en' &&\n [\n 'nova-2-meeting',\n 'nova-2-phonecall',\n 'nova-2-finance',\n 'nova-2-conversationalai',\n 'nova-2-voicemail',\n 'nova-2-video',\n 'nova-2-medical',\n 'nova-2-drivethru',\n 'nova-2-automotive',\n 'nova-3-general',\n ].includes(model)\n ) {\n this.#logger.warn(\n `${model} does not support language ${language}, falling back to nova-2-general`,\n );\n return 'nova-2-general';\n }\n\n return model;\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SpeechStream {\n // Deepgram only supports language detection for prerecorded audio, not streaming.\n if (this.#opts.detectLanguage || this.#opts.language === undefined) {\n throw new Error(\n 'language detection is not supported in streaming mode, please disable it and specify a language',\n );\n }\n const stream = new SpeechStream(this, this.#opts, options?.connOptions);\n this.#streams.add(new WeakRef(stream));\n return stream;\n }\n\n async close() {\n this.abortController.abort();\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: STTOptions;\n #audioEnergyFilter: AudioEnergyFilter;\n #logger = log();\n #speaking = false;\n #resetWS = new Future();\n #requestId = '';\n // keyterms set while the user is speaking; applied at END_OF_SPEECH (latest wins)\n /** @internal */\n _pendingKeyterm: string[] | null = null;\n #audioDurationCollector: PeriodicCollector<number>;\n label = 'deepgram.SpeechStream';\n\n constructor(stt: STT, opts: STTOptions, connOptions?: APIConnectOptions) {\n super(stt, opts.sampleRate, connOptions);\n this.#opts = opts;\n this.closed = false;\n this.#audioEnergyFilter = new AudioEnergyFilter();\n this.#audioDurationCollector = new PeriodicCollector(\n (duration) => this.onAudioDurationReport(duration),\n { duration: 5.0 },\n );\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n let ws: WebSocket;\n\n while (!this.input.closed && !this.closed) {\n const streamURL = new URL(`${this.#opts.baseUrl}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n smart_format: this.#opts.smartFormat,\n dictation: this.#opts.dictation,\n diarize: this.#opts.diarize,\n numerals: this.#opts.numerals,\n no_delay: this.#opts.noDelay,\n interim_results: this.#opts.interimResults,\n encoding: 'linear16',\n vad_events: true,\n sample_rate: this.#opts.sampleRate,\n channels: this.#opts.numChannels,\n endpointing: this.#opts.endpointing || false,\n filler_words: this.#opts.fillerWords,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n keyterm: this.#opts.keyterm,\n profanity_filter: this.#opts.profanityFilter,\n utterance_end_ms: this.#opts.utteranceEndMs,\n replace: this.#opts.replace,\n search: this.#opts.search,\n language: this.#opts.language,\n mip_opt_out: this.#opts.mipOptOut,\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n appendSearchParams(streamURL, params);\n\n ws = new WebSocket(streamURL, {\n headers: { Authorization: `Token ${this.#opts.apiKey}` },\n });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Deepgram after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Deepgram, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Deepgram disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n /** @internal */\n get _speaking(): boolean {\n return this.#speaking;\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.keyterm !== undefined) {\n this._pendingKeyterm = null;\n }\n this.#resetWS.resolve();\n }\n\n #onEndOfSpeech() {\n if (this._pendingKeyterm !== null) {\n this.updateOptions({ keyterm: this._pendingKeyterm });\n this._pendingKeyterm = null;\n }\n }\n\n async #runWS(ws: WebSocket) {\n this.#resetWS = new Future();\n let closing = false;\n\n const keepalive = setInterval(() => {\n try {\n ws.send(JSON.stringify({ type: 'KeepAlive' }));\n } catch {\n clearInterval(keepalive);\n return;\n }\n }, 5000);\n\n // gets cancelled also when sendTask is complete\n const wsMonitor = Task.from(async (controller) => {\n const closed = new Promise<void>(async (_, reject) => {\n ws.once('close', (code, reason) => {\n if (!closing) {\n this.#logger.error(`WebSocket closed with code ${code}: ${reason}`);\n reject(new Error('WebSocket closed'));\n }\n });\n });\n\n await Promise.race([closed, waitForAbort(controller.signal)]);\n });\n\n const sendTask = async () => {\n const samples100Ms = Math.floor(this.#opts.sampleRate / 10);\n const stream = new AudioByteStream(\n this.#opts.sampleRate,\n this.#opts.numChannels,\n samples100Ms,\n );\n\n // waitForAbort internally sets up an abort listener on the abort signal\n // we need to put it outside loop to avoid constant re-registration of the listener\n const abortPromise = waitForAbort(this.abortSignal);\n\n try {\n while (!this.closed) {\n const result = await Promise.race([this.input.next(), abortPromise]);\n\n if (result === undefined) return; // aborted\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n let hasEnded = false;\n if (data === SpeechStream.FLUSH_SENTINEL) {\n frames = stream.flush();\n hasEnded = true;\n } else if (\n data.sampleRate === this.#opts.sampleRate &&\n data.channels === this.#opts.numChannels\n ) {\n frames = stream.write(data.data.buffer as ArrayBuffer);\n } else {\n throw new Error(`sample rate or channel count of frame does not match`);\n }\n\n for await (const frame of frames) {\n if (this.#audioEnergyFilter.pushFrame(frame)) {\n const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n this.#audioDurationCollector.push(frameDuration);\n ws.send(frame.data.buffer);\n }\n }\n\n if (hasEnded) {\n this.#audioDurationCollector.flush();\n ws.send(JSON.stringify({ type: 'Finalize' }));\n }\n }\n } finally {\n closing = true;\n ws.send(JSON.stringify({ type: 'CloseStream' }));\n wsMonitor.cancel();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const putMessage = (message: stt.SpeechEvent) => {\n if (!this.queue.closed) {\n try {\n this.queue.put(message);\n } catch (e) {\n // ignore\n }\n }\n };\n\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (msg) => {\n try {\n const json = JSON.parse(msg.toString());\n switch (json['type']) {\n case 'SpeechStarted': {\n // This is a normal case. Deepgram's SpeechStarted events\n // are not correlated with speech_final or utterance end.\n // It's possible that we receive two in a row without an endpoint\n // It's also possible we receive a transcript without a SpeechStarted event.\n if (this.#speaking) return;\n this.#speaking = true;\n putMessage({ type: stt.SpeechEventType.START_OF_SPEECH });\n break;\n }\n // see this page:\n // https://developers.deepgram.com/docs/understand-endpointing-interim-results#using-endpointing-speech_final\n // for more information about the different types of events\n case 'Results': {\n const metadata = json['metadata'];\n const requestId = metadata['request_id'];\n const isFinal = json['is_final'];\n const isEndpoint = json['speech_final'];\n this.#requestId = requestId;\n\n const alternatives = liveTranscriptionToSpeechData(\n this.#opts.language!,\n json,\n this.startTimeOffset,\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n\n // If, for some reason, we didn't get a SpeechStarted event but we got\n // a transcript with text, we should start speaking. It's rare but has\n // been observed.\n if (alternatives[0] && alternatives[0].text) {\n if (!this.#speaking) {\n this.#speaking = true;\n putMessage({\n type: stt.SpeechEventType.START_OF_SPEECH,\n });\n }\n\n if (isFinal) {\n putMessage({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n } else {\n putMessage({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n }\n }\n\n // if we receive an endpoint, only end the speech if\n // we either had a SpeechStarted event or we have a seen\n // a non-empty transcript (deepgram doesn't have a SpeechEnded event)\n if (isEndpoint && this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n\n break;\n }\n case 'Metadata': {\n break;\n }\n case 'UtteranceEnd': {\n if (this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n break;\n }\n default: {\n this.#logger\n .child({ 'lk.pii.message': json })\n .warn('received unexpected message from Deepgram');\n break;\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(\n { error: err, 'lk.pii.message': msg.toString() },\n 'Deepgram STT failed to process message',\n );\n reject(err);\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.race([\n this.#resetWS.await,\n Promise.all([sendTask(), listenTask.result, wsMonitor]),\n ]);\n closing = true;\n ws.close();\n clearInterval(keepalive);\n }\n\n private onAudioDurationReport(duration: number) {\n const usageEvent: stt.SpeechEvent = {\n type: stt.SpeechEventType.RECOGNITION_USAGE,\n requestId: this.#requestId,\n recognitionUsage: {\n audioDuration: duration,\n },\n };\n this.queue.put(usageEvent);\n }\n}\n\nfunction appendSearchParams(url: URL, params: Record<string, unknown>) {\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => url.searchParams.append(key, String(item)));\n } else if (typeof value === 'object') {\n Object.entries(value).forEach(([term, replacement]) => {\n url.searchParams.append(key, `${term}:${replacement}`);\n });\n } else {\n url.searchParams.append(key, String(value));\n }\n });\n}\n\nfunction createWav(frame: AudioFrame): Buffer {\n const bitsPerSample = 16;\n const byteRate = (frame.sampleRate * frame.channels * bitsPerSample) / 8;\n const blockAlign = (frame.channels * bitsPerSample) / 8;\n\n const header = Buffer.alloc(44);\n header.write('RIFF', 0);\n header.writeUInt32LE(36 + frame.data.byteLength, 4);\n header.write('WAVE', 8);\n header.write('fmt ', 12);\n header.writeUInt32LE(16, 16);\n header.writeUInt16LE(1, 20);\n header.writeUInt16LE(frame.channels, 22);\n header.writeUInt32LE(frame.sampleRate, 24);\n header.writeUInt32LE(byteRate, 28);\n header.writeUInt16LE(blockAlign, 32);\n header.writeUInt16LE(bitsPerSample, 34);\n header.write('data', 36);\n header.writeUInt32LE(frame.data.byteLength, 40);\n\n const pcm = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength);\n return Buffer.concat([header, pcm]);\n}\n\n/**\n * Return the word form matching the transcript requested from Deepgram.\n *\n * Both punctuate and smart_format produce a formatted transcript. When either is enabled,\n * Deepgram provides punctuated_word alongside word. Fall back to word when it is absent or empty.\n */\nfunction wordText(word: { [id: string]: any }, usePunctuatedWord: boolean): string {\n const raw = word['word'] ?? '';\n return usePunctuatedWord ? word['punctuated_word'] || raw : raw;\n}\n\nconst liveTranscriptionToSpeechData = (\n language: STTLanguages | string,\n data: { [id: string]: any },\n startTimeOffset: number = 0,\n usePunctuatedWord: boolean = true,\n): stt.SpeechData[] => {\n const alts: any[] = data['channel']['alternatives'];\n\n return alts.map((alt) => {\n const wordsData: any[] = alt['words'] ?? [];\n const detectedLanguage =\n language === 'multi' ? alt['languages']?.[0] ?? language : alt['language'] ?? language;\n\n return {\n language: normalizeLanguage(detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] + startTimeOffset : startTimeOffset,\n endTime: wordsData.length\n ? wordsData[wordsData.length - 1]['end'] + startTimeOffset\n : startTimeOffset,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: (word['start'] ?? 0) + startTimeOffset,\n endTime: (word['end'] ?? 0) + startTimeOffset,\n confidence: word['confidence'] ?? 0.0,\n startTimeOffset,\n }),\n ),\n };\n });\n};\n\nfunction prerecordedTranscriptionToSpeechEvent(\n language: STTLanguages | string | undefined,\n data: { [id: string]: any },\n usePunctuatedWord: boolean = true,\n): stt.SpeechEvent {\n const channel = data['results']['channels'][0];\n const detectedLanguage = channel['detected_language'] ?? '';\n const alternatives = channel['alternatives'].map((alt: { [id: string]: any }) => {\n const wordsData: any[] = alt['words'] ?? [];\n\n return {\n language: normalizeLanguage(language ?? detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] : 0,\n endTime: wordsData.length ? wordsData[wordsData.length - 1]['end'] : 0,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: word['start'] ?? 0,\n endTime: word['end'] ?? 0,\n confidence: word['confidence'] ?? 0.0,\n }),\n ),\n };\n }) as stt.SpeechData[];\n\n return {\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n requestId: data['metadata']['request_id'],\n ...(alternatives.length\n ? { alternatives: alternatives as [stt.SpeechData, ...stt.SpeechData[]] }\n : {}),\n };\n}\n"],"mappings":"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","names":["stt"]}
|
package/dist/stt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stt.d.ts","sourceRoot":"","sources":["../src/stt.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAUhB,GAAG,EAEJ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AA6BD,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAO9B,KAAK,SAAkB;IACvB,OAAO,CAAC,eAAe,CAAyB;IAEhD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAEW,IAAI,GAAE,OAAO,CAAC,UAAU,CAAqB;IA4BnD,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA+C1F,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;IAsB9B,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAmDzD,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,YAAY;IAY7D,KAAK;CAGZ;AAED,qBAAa,YAAa,SAAQ,GAAG,CAAC,YAAY;;IAQhD,gBAAgB;IAChB,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAQ;IAExC,KAAK,SAA2B;gBAEpB,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,iBAAiB;cAWvD,GAAG;IAsEnB,gBAAgB;IAChB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"stt.d.ts","sourceRoot":"","sources":["../src/stt.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAUhB,GAAG,EAEJ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AA6BD,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAO9B,KAAK,SAAkB;IACvB,OAAO,CAAC,eAAe,CAAyB;IAEhD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAEW,IAAI,GAAE,OAAO,CAAC,UAAU,CAAqB;IA4BnD,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA+C1F,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;IAsB9B,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAmDzD,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,YAAY;IAY7D,KAAK;CAGZ;AAED,qBAAa,YAAa,SAAQ,GAAG,CAAC,YAAY;;IAQhD,gBAAgB;IAChB,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAQ;IAExC,KAAK,SAA2B;gBAEpB,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,iBAAiB;cAWvD,GAAG;IAsEnB,gBAAgB;IAChB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;IA6NvC,OAAO,CAAC,qBAAqB;CAU9B"}
|
package/dist/stt.js
CHANGED
|
@@ -428,7 +428,7 @@ class SpeechStream extends stt.SpeechStream {
|
|
|
428
428
|
break;
|
|
429
429
|
}
|
|
430
430
|
default: {
|
|
431
|
-
this.#logger.child({
|
|
431
|
+
this.#logger.child({ "lk.pii.message": json }).warn("received unexpected message from Deepgram");
|
|
432
432
|
break;
|
|
433
433
|
}
|
|
434
434
|
}
|
|
@@ -436,7 +436,10 @@ class SpeechStream extends stt.SpeechStream {
|
|
|
436
436
|
resolve();
|
|
437
437
|
}
|
|
438
438
|
} catch (err) {
|
|
439
|
-
this.#logger.error(
|
|
439
|
+
this.#logger.error(
|
|
440
|
+
{ error: err, "lk.pii.message": msg.toString() },
|
|
441
|
+
"Deepgram STT failed to process message"
|
|
442
|
+
);
|
|
440
443
|
reject(err);
|
|
441
444
|
}
|
|
442
445
|
});
|
package/dist/stt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n type AudioBuffer,\n AudioByteStream,\n AudioEnergyFilter,\n Future,\n Task,\n createTimedString,\n getBaseLanguage,\n log,\n mergeFrames,\n normalizeLanguage,\n stt,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { PeriodicCollector } from './_utils.js';\nimport type { STTLanguages, STTModels } from './models.js';\n\nexport interface STTOptions {\n apiKey?: string;\n language?: STTLanguages | string;\n detectLanguage: boolean;\n interimResults: boolean;\n punctuate: boolean;\n model: STTModels | string;\n smartFormat: boolean;\n noDelay: boolean;\n endpointing: number;\n fillerWords: boolean;\n sampleRate: number;\n numChannels: number;\n keywords: [string, number][];\n keyterm: string[];\n profanityFilter: boolean;\n /**\n * Redact sensitive information from the transcription. Accepts a single value or list of\n * values. Supported values: \"pci\", \"numbers\", \"ssn\", \"true\" (redact all).\n * See https://developers.deepgram.com/docs/redaction for details.\n */\n redact: string | string[];\n utteranceEndMs?: number | null;\n replace?: Record<string, string> | null;\n search?: string[] | null;\n dictation: boolean;\n diarize: boolean;\n numerals: boolean;\n mipOptOut: boolean;\n baseUrl: string;\n}\n\nconst defaultSTTOptions: STTOptions = {\n apiKey: process.env.DEEPGRAM_API_KEY,\n language: 'en-US',\n detectLanguage: false,\n interimResults: true,\n punctuate: true,\n model: 'nova-3',\n smartFormat: true,\n noDelay: true,\n endpointing: 25,\n fillerWords: false,\n sampleRate: 16000,\n numChannels: 1,\n keywords: [],\n keyterm: [],\n profanityFilter: false,\n redact: [],\n utteranceEndMs: null,\n replace: null,\n search: null,\n dictation: false,\n diarize: false,\n numerals: false,\n mipOptOut: false,\n baseUrl: 'wss://api.deepgram.com',\n};\n\nexport class STT extends stt.STT {\n #opts: STTOptions;\n #logger = log();\n // session keyterm propagation)\n #streams = new Set<WeakRef<SpeechStream>>();\n #userKeyterm: string[];\n #sessionKeyterms: string[] = [];\n label = 'deepgram.STT';\n private abortController = new AbortController();\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Deepgram';\n }\n\n constructor(opts: Partial<STTOptions> = defaultSTTOptions) {\n super({\n streaming: true,\n interimResults: opts.interimResults ?? defaultSTTOptions.interimResults,\n alignedTranscript: 'word',\n keyterms: true,\n });\n if (opts.apiKey === undefined && defaultSTTOptions.apiKey === undefined) {\n throw new Error(\n 'Deepgram API key is required, whether as an argument or as $DEEPGRAM_API_KEY',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n language: opts.language ? normalizeLanguage(opts.language) : defaultSTTOptions.language,\n };\n\n if (this.#opts.detectLanguage) {\n this.#opts.language = undefined;\n } else {\n this.#opts.model = this.#validateModel(this.#opts.model, this.#opts.language);\n }\n\n this.#userKeyterm = [...this.#opts.keyterm];\n }\n\n async _recognize(buffer: AudioBuffer, abortSignal?: AbortSignal): Promise<stt.SpeechEvent> {\n const frame = mergeFrames(buffer);\n const listenURL = new URL(`${this.#opts.baseUrl.replace(/^ws/, 'http')}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n detect_language: this.#opts.detectLanguage,\n smart_format: this.#opts.smartFormat,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n profanity_filter: this.#opts.profanityFilter,\n numerals: this.#opts.numerals,\n mip_opt_out: this.#opts.mipOptOut,\n language: this.#opts.language,\n ...(this.#opts.keyterm.length > 0 ? { keyterm: this.#opts.keyterm } : {}),\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n\n appendSearchParams(listenURL, params);\n\n if (this.#opts.diarize) {\n this.#logger.warn('speaker diarization is not supported in non-streaming mode, ignoring');\n }\n\n const resp = await fetch(listenURL, {\n method: 'POST',\n headers: {\n Authorization: `Token ${this.#opts.apiKey}`,\n Accept: 'application/json',\n 'Content-Type': 'audio/wav',\n },\n body: new Blob([new Uint8Array(createWav(frame))], { type: 'audio/wav' }),\n signal: abortSignal ?? null,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Deepgram STT REST request failed with status ${resp.status}: ${await resp.text()}`,\n );\n }\n\n return prerecordedTranscriptionToSpeechEvent(\n this.#opts.language,\n await resp.json(),\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n const language =\n opts.language !== undefined ? normalizeLanguage(opts.language) : this.#opts.language;\n const model =\n opts.model !== undefined || opts.language !== undefined\n ? this.#validateModel(opts.model ?? this.#opts.model, language)\n : this.#opts.model;\n\n const nextOpts = { ...opts };\n if (nextOpts.keyterm !== undefined) {\n this.#userKeyterm = [...nextOpts.keyterm];\n nextOpts.keyterm = [...new Set([...this.#userKeyterm, ...this.#sessionKeyterms])];\n }\n\n this.#opts = {\n ...this.#opts,\n ...nextOpts,\n language,\n model,\n };\n }\n\n override _updateSessionKeyterms(keyterms: string[]): void {\n if (\n keyterms.length === this.#sessionKeyterms.length &&\n keyterms.every((t, i) => t === this.#sessionKeyterms[i])\n ) {\n return;\n }\n this.#sessionKeyterms = [...keyterms];\n const merged = [...new Set([...this.#userKeyterm, ...keyterms])];\n this.#opts.keyterm = merged;\n for (const ref of this.#streams) {\n const stream = ref.deref();\n if (!stream) {\n this.#streams.delete(ref);\n continue;\n }\n if (stream._speaking) {\n // defer the reconnect to the end of the utterance so we don't cut it off\n stream._pendingKeyterm = merged;\n } else {\n stream.updateOptions({ keyterm: merged });\n }\n }\n }\n\n #validateModel(model: STTModels | string, language?: string) {\n if (\n language &&\n getBaseLanguage(language) !== 'en' &&\n [\n 'nova-2-meeting',\n 'nova-2-phonecall',\n 'nova-2-finance',\n 'nova-2-conversationalai',\n 'nova-2-voicemail',\n 'nova-2-video',\n 'nova-2-medical',\n 'nova-2-drivethru',\n 'nova-2-automotive',\n 'nova-3-general',\n ].includes(model)\n ) {\n this.#logger.warn(\n `${model} does not support language ${language}, falling back to nova-2-general`,\n );\n return 'nova-2-general';\n }\n\n return model;\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SpeechStream {\n // Deepgram only supports language detection for prerecorded audio, not streaming.\n if (this.#opts.detectLanguage || this.#opts.language === undefined) {\n throw new Error(\n 'language detection is not supported in streaming mode, please disable it and specify a language',\n );\n }\n const stream = new SpeechStream(this, this.#opts, options?.connOptions);\n this.#streams.add(new WeakRef(stream));\n return stream;\n }\n\n async close() {\n this.abortController.abort();\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: STTOptions;\n #audioEnergyFilter: AudioEnergyFilter;\n #logger = log();\n #speaking = false;\n #resetWS = new Future();\n #requestId = '';\n // keyterms set while the user is speaking; applied at END_OF_SPEECH (latest wins)\n /** @internal */\n _pendingKeyterm: string[] | null = null;\n #audioDurationCollector: PeriodicCollector<number>;\n label = 'deepgram.SpeechStream';\n\n constructor(stt: STT, opts: STTOptions, connOptions?: APIConnectOptions) {\n super(stt, opts.sampleRate, connOptions);\n this.#opts = opts;\n this.closed = false;\n this.#audioEnergyFilter = new AudioEnergyFilter();\n this.#audioDurationCollector = new PeriodicCollector(\n (duration) => this.onAudioDurationReport(duration),\n { duration: 5.0 },\n );\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n let ws: WebSocket;\n\n while (!this.input.closed && !this.closed) {\n const streamURL = new URL(`${this.#opts.baseUrl}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n smart_format: this.#opts.smartFormat,\n dictation: this.#opts.dictation,\n diarize: this.#opts.diarize,\n numerals: this.#opts.numerals,\n no_delay: this.#opts.noDelay,\n interim_results: this.#opts.interimResults,\n encoding: 'linear16',\n vad_events: true,\n sample_rate: this.#opts.sampleRate,\n channels: this.#opts.numChannels,\n endpointing: this.#opts.endpointing || false,\n filler_words: this.#opts.fillerWords,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n keyterm: this.#opts.keyterm,\n profanity_filter: this.#opts.profanityFilter,\n utterance_end_ms: this.#opts.utteranceEndMs,\n replace: this.#opts.replace,\n search: this.#opts.search,\n language: this.#opts.language,\n mip_opt_out: this.#opts.mipOptOut,\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n appendSearchParams(streamURL, params);\n\n ws = new WebSocket(streamURL, {\n headers: { Authorization: `Token ${this.#opts.apiKey}` },\n });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Deepgram after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Deepgram, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Deepgram disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n /** @internal */\n get _speaking(): boolean {\n return this.#speaking;\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.keyterm !== undefined) {\n this._pendingKeyterm = null;\n }\n this.#resetWS.resolve();\n }\n\n #onEndOfSpeech() {\n if (this._pendingKeyterm !== null) {\n this.updateOptions({ keyterm: this._pendingKeyterm });\n this._pendingKeyterm = null;\n }\n }\n\n async #runWS(ws: WebSocket) {\n this.#resetWS = new Future();\n let closing = false;\n\n const keepalive = setInterval(() => {\n try {\n ws.send(JSON.stringify({ type: 'KeepAlive' }));\n } catch {\n clearInterval(keepalive);\n return;\n }\n }, 5000);\n\n // gets cancelled also when sendTask is complete\n const wsMonitor = Task.from(async (controller) => {\n const closed = new Promise<void>(async (_, reject) => {\n ws.once('close', (code, reason) => {\n if (!closing) {\n this.#logger.error(`WebSocket closed with code ${code}: ${reason}`);\n reject(new Error('WebSocket closed'));\n }\n });\n });\n\n await Promise.race([closed, waitForAbort(controller.signal)]);\n });\n\n const sendTask = async () => {\n const samples100Ms = Math.floor(this.#opts.sampleRate / 10);\n const stream = new AudioByteStream(\n this.#opts.sampleRate,\n this.#opts.numChannels,\n samples100Ms,\n );\n\n // waitForAbort internally sets up an abort listener on the abort signal\n // we need to put it outside loop to avoid constant re-registration of the listener\n const abortPromise = waitForAbort(this.abortSignal);\n\n try {\n while (!this.closed) {\n const result = await Promise.race([this.input.next(), abortPromise]);\n\n if (result === undefined) return; // aborted\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n let hasEnded = false;\n if (data === SpeechStream.FLUSH_SENTINEL) {\n frames = stream.flush();\n hasEnded = true;\n } else if (\n data.sampleRate === this.#opts.sampleRate &&\n data.channels === this.#opts.numChannels\n ) {\n frames = stream.write(data.data.buffer as ArrayBuffer);\n } else {\n throw new Error(`sample rate or channel count of frame does not match`);\n }\n\n for await (const frame of frames) {\n if (this.#audioEnergyFilter.pushFrame(frame)) {\n const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n this.#audioDurationCollector.push(frameDuration);\n ws.send(frame.data.buffer);\n }\n }\n\n if (hasEnded) {\n this.#audioDurationCollector.flush();\n ws.send(JSON.stringify({ type: 'Finalize' }));\n }\n }\n } finally {\n closing = true;\n ws.send(JSON.stringify({ type: 'CloseStream' }));\n wsMonitor.cancel();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const putMessage = (message: stt.SpeechEvent) => {\n if (!this.queue.closed) {\n try {\n this.queue.put(message);\n } catch (e) {\n // ignore\n }\n }\n };\n\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (msg) => {\n try {\n const json = JSON.parse(msg.toString());\n switch (json['type']) {\n case 'SpeechStarted': {\n // This is a normal case. Deepgram's SpeechStarted events\n // are not correlated with speech_final or utterance end.\n // It's possible that we receive two in a row without an endpoint\n // It's also possible we receive a transcript without a SpeechStarted event.\n if (this.#speaking) return;\n this.#speaking = true;\n putMessage({ type: stt.SpeechEventType.START_OF_SPEECH });\n break;\n }\n // see this page:\n // https://developers.deepgram.com/docs/understand-endpointing-interim-results#using-endpointing-speech_final\n // for more information about the different types of events\n case 'Results': {\n const metadata = json['metadata'];\n const requestId = metadata['request_id'];\n const isFinal = json['is_final'];\n const isEndpoint = json['speech_final'];\n this.#requestId = requestId;\n\n const alternatives = liveTranscriptionToSpeechData(\n this.#opts.language!,\n json,\n this.startTimeOffset,\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n\n // If, for some reason, we didn't get a SpeechStarted event but we got\n // a transcript with text, we should start speaking. It's rare but has\n // been observed.\n if (alternatives[0] && alternatives[0].text) {\n if (!this.#speaking) {\n this.#speaking = true;\n putMessage({\n type: stt.SpeechEventType.START_OF_SPEECH,\n });\n }\n\n if (isFinal) {\n putMessage({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n } else {\n putMessage({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n }\n }\n\n // if we receive an endpoint, only end the speech if\n // we either had a SpeechStarted event or we have a seen\n // a non-empty transcript (deepgram doesn't have a SpeechEnded event)\n if (isEndpoint && this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n\n break;\n }\n case 'Metadata': {\n break;\n }\n case 'UtteranceEnd': {\n if (this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n break;\n }\n default: {\n this.#logger.child({ msg: json }).warn('received unexpected message from Deepgram');\n break;\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(`STT: Error processing message: ${msg}`);\n reject(err);\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.race([\n this.#resetWS.await,\n Promise.all([sendTask(), listenTask.result, wsMonitor]),\n ]);\n closing = true;\n ws.close();\n clearInterval(keepalive);\n }\n\n private onAudioDurationReport(duration: number) {\n const usageEvent: stt.SpeechEvent = {\n type: stt.SpeechEventType.RECOGNITION_USAGE,\n requestId: this.#requestId,\n recognitionUsage: {\n audioDuration: duration,\n },\n };\n this.queue.put(usageEvent);\n }\n}\n\nfunction appendSearchParams(url: URL, params: Record<string, unknown>) {\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => url.searchParams.append(key, String(item)));\n } else if (typeof value === 'object') {\n Object.entries(value).forEach(([term, replacement]) => {\n url.searchParams.append(key, `${term}:${replacement}`);\n });\n } else {\n url.searchParams.append(key, String(value));\n }\n });\n}\n\nfunction createWav(frame: AudioFrame): Buffer {\n const bitsPerSample = 16;\n const byteRate = (frame.sampleRate * frame.channels * bitsPerSample) / 8;\n const blockAlign = (frame.channels * bitsPerSample) / 8;\n\n const header = Buffer.alloc(44);\n header.write('RIFF', 0);\n header.writeUInt32LE(36 + frame.data.byteLength, 4);\n header.write('WAVE', 8);\n header.write('fmt ', 12);\n header.writeUInt32LE(16, 16);\n header.writeUInt16LE(1, 20);\n header.writeUInt16LE(frame.channels, 22);\n header.writeUInt32LE(frame.sampleRate, 24);\n header.writeUInt32LE(byteRate, 28);\n header.writeUInt16LE(blockAlign, 32);\n header.writeUInt16LE(bitsPerSample, 34);\n header.write('data', 36);\n header.writeUInt32LE(frame.data.byteLength, 40);\n\n const pcm = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength);\n return Buffer.concat([header, pcm]);\n}\n\n/**\n * Return the word form matching the transcript requested from Deepgram.\n *\n * Both punctuate and smart_format produce a formatted transcript. When either is enabled,\n * Deepgram provides punctuated_word alongside word. Fall back to word when it is absent or empty.\n */\nfunction wordText(word: { [id: string]: any }, usePunctuatedWord: boolean): string {\n const raw = word['word'] ?? '';\n return usePunctuatedWord ? word['punctuated_word'] || raw : raw;\n}\n\nconst liveTranscriptionToSpeechData = (\n language: STTLanguages | string,\n data: { [id: string]: any },\n startTimeOffset: number = 0,\n usePunctuatedWord: boolean = true,\n): stt.SpeechData[] => {\n const alts: any[] = data['channel']['alternatives'];\n\n return alts.map((alt) => {\n const wordsData: any[] = alt['words'] ?? [];\n const detectedLanguage =\n language === 'multi' ? alt['languages']?.[0] ?? language : alt['language'] ?? language;\n\n return {\n language: normalizeLanguage(detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] + startTimeOffset : startTimeOffset,\n endTime: wordsData.length\n ? wordsData[wordsData.length - 1]['end'] + startTimeOffset\n : startTimeOffset,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: (word['start'] ?? 0) + startTimeOffset,\n endTime: (word['end'] ?? 0) + startTimeOffset,\n confidence: word['confidence'] ?? 0.0,\n startTimeOffset,\n }),\n ),\n };\n });\n};\n\nfunction prerecordedTranscriptionToSpeechEvent(\n language: STTLanguages | string | undefined,\n data: { [id: string]: any },\n usePunctuatedWord: boolean = true,\n): stt.SpeechEvent {\n const channel = data['results']['channels'][0];\n const detectedLanguage = channel['detected_language'] ?? '';\n const alternatives = channel['alternatives'].map((alt: { [id: string]: any }) => {\n const wordsData: any[] = alt['words'] ?? [];\n\n return {\n language: normalizeLanguage(language ?? detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] : 0,\n endTime: wordsData.length ? wordsData[wordsData.length - 1]['end'] : 0,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: word['start'] ?? 0,\n endTime: word['end'] ?? 0,\n confidence: word['confidence'] ?? 0.0,\n }),\n ),\n };\n }) as stt.SpeechData[];\n\n return {\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n requestId: data['metadata']['request_id'],\n ...(alternatives.length\n ? { alternatives: alternatives as [stt.SpeechData, ...stt.SpeechData[]] }\n : {}),\n };\n}\n"],"mappings":"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","names":["stt"]}
|
|
1
|
+
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n type AudioBuffer,\n AudioByteStream,\n AudioEnergyFilter,\n Future,\n Task,\n createTimedString,\n getBaseLanguage,\n log,\n mergeFrames,\n normalizeLanguage,\n stt,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { PeriodicCollector } from './_utils.js';\nimport type { STTLanguages, STTModels } from './models.js';\n\nexport interface STTOptions {\n apiKey?: string;\n language?: STTLanguages | string;\n detectLanguage: boolean;\n interimResults: boolean;\n punctuate: boolean;\n model: STTModels | string;\n smartFormat: boolean;\n noDelay: boolean;\n endpointing: number;\n fillerWords: boolean;\n sampleRate: number;\n numChannels: number;\n keywords: [string, number][];\n keyterm: string[];\n profanityFilter: boolean;\n /**\n * Redact sensitive information from the transcription. Accepts a single value or list of\n * values. Supported values: \"pci\", \"numbers\", \"ssn\", \"true\" (redact all).\n * See https://developers.deepgram.com/docs/redaction for details.\n */\n redact: string | string[];\n utteranceEndMs?: number | null;\n replace?: Record<string, string> | null;\n search?: string[] | null;\n dictation: boolean;\n diarize: boolean;\n numerals: boolean;\n mipOptOut: boolean;\n baseUrl: string;\n}\n\nconst defaultSTTOptions: STTOptions = {\n apiKey: process.env.DEEPGRAM_API_KEY,\n language: 'en-US',\n detectLanguage: false,\n interimResults: true,\n punctuate: true,\n model: 'nova-3',\n smartFormat: true,\n noDelay: true,\n endpointing: 25,\n fillerWords: false,\n sampleRate: 16000,\n numChannels: 1,\n keywords: [],\n keyterm: [],\n profanityFilter: false,\n redact: [],\n utteranceEndMs: null,\n replace: null,\n search: null,\n dictation: false,\n diarize: false,\n numerals: false,\n mipOptOut: false,\n baseUrl: 'wss://api.deepgram.com',\n};\n\nexport class STT extends stt.STT {\n #opts: STTOptions;\n #logger = log();\n // session keyterm propagation)\n #streams = new Set<WeakRef<SpeechStream>>();\n #userKeyterm: string[];\n #sessionKeyterms: string[] = [];\n label = 'deepgram.STT';\n private abortController = new AbortController();\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Deepgram';\n }\n\n constructor(opts: Partial<STTOptions> = defaultSTTOptions) {\n super({\n streaming: true,\n interimResults: opts.interimResults ?? defaultSTTOptions.interimResults,\n alignedTranscript: 'word',\n keyterms: true,\n });\n if (opts.apiKey === undefined && defaultSTTOptions.apiKey === undefined) {\n throw new Error(\n 'Deepgram API key is required, whether as an argument or as $DEEPGRAM_API_KEY',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n language: opts.language ? normalizeLanguage(opts.language) : defaultSTTOptions.language,\n };\n\n if (this.#opts.detectLanguage) {\n this.#opts.language = undefined;\n } else {\n this.#opts.model = this.#validateModel(this.#opts.model, this.#opts.language);\n }\n\n this.#userKeyterm = [...this.#opts.keyterm];\n }\n\n async _recognize(buffer: AudioBuffer, abortSignal?: AbortSignal): Promise<stt.SpeechEvent> {\n const frame = mergeFrames(buffer);\n const listenURL = new URL(`${this.#opts.baseUrl.replace(/^ws/, 'http')}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n detect_language: this.#opts.detectLanguage,\n smart_format: this.#opts.smartFormat,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n profanity_filter: this.#opts.profanityFilter,\n numerals: this.#opts.numerals,\n mip_opt_out: this.#opts.mipOptOut,\n language: this.#opts.language,\n ...(this.#opts.keyterm.length > 0 ? { keyterm: this.#opts.keyterm } : {}),\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n\n appendSearchParams(listenURL, params);\n\n if (this.#opts.diarize) {\n this.#logger.warn('speaker diarization is not supported in non-streaming mode, ignoring');\n }\n\n const resp = await fetch(listenURL, {\n method: 'POST',\n headers: {\n Authorization: `Token ${this.#opts.apiKey}`,\n Accept: 'application/json',\n 'Content-Type': 'audio/wav',\n },\n body: new Blob([new Uint8Array(createWav(frame))], { type: 'audio/wav' }),\n signal: abortSignal ?? null,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Deepgram STT REST request failed with status ${resp.status}: ${await resp.text()}`,\n );\n }\n\n return prerecordedTranscriptionToSpeechEvent(\n this.#opts.language,\n await resp.json(),\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n const language =\n opts.language !== undefined ? normalizeLanguage(opts.language) : this.#opts.language;\n const model =\n opts.model !== undefined || opts.language !== undefined\n ? this.#validateModel(opts.model ?? this.#opts.model, language)\n : this.#opts.model;\n\n const nextOpts = { ...opts };\n if (nextOpts.keyterm !== undefined) {\n this.#userKeyterm = [...nextOpts.keyterm];\n nextOpts.keyterm = [...new Set([...this.#userKeyterm, ...this.#sessionKeyterms])];\n }\n\n this.#opts = {\n ...this.#opts,\n ...nextOpts,\n language,\n model,\n };\n }\n\n override _updateSessionKeyterms(keyterms: string[]): void {\n if (\n keyterms.length === this.#sessionKeyterms.length &&\n keyterms.every((t, i) => t === this.#sessionKeyterms[i])\n ) {\n return;\n }\n this.#sessionKeyterms = [...keyterms];\n const merged = [...new Set([...this.#userKeyterm, ...keyterms])];\n this.#opts.keyterm = merged;\n for (const ref of this.#streams) {\n const stream = ref.deref();\n if (!stream) {\n this.#streams.delete(ref);\n continue;\n }\n if (stream._speaking) {\n // defer the reconnect to the end of the utterance so we don't cut it off\n stream._pendingKeyterm = merged;\n } else {\n stream.updateOptions({ keyterm: merged });\n }\n }\n }\n\n #validateModel(model: STTModels | string, language?: string) {\n if (\n language &&\n getBaseLanguage(language) !== 'en' &&\n [\n 'nova-2-meeting',\n 'nova-2-phonecall',\n 'nova-2-finance',\n 'nova-2-conversationalai',\n 'nova-2-voicemail',\n 'nova-2-video',\n 'nova-2-medical',\n 'nova-2-drivethru',\n 'nova-2-automotive',\n 'nova-3-general',\n ].includes(model)\n ) {\n this.#logger.warn(\n `${model} does not support language ${language}, falling back to nova-2-general`,\n );\n return 'nova-2-general';\n }\n\n return model;\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SpeechStream {\n // Deepgram only supports language detection for prerecorded audio, not streaming.\n if (this.#opts.detectLanguage || this.#opts.language === undefined) {\n throw new Error(\n 'language detection is not supported in streaming mode, please disable it and specify a language',\n );\n }\n const stream = new SpeechStream(this, this.#opts, options?.connOptions);\n this.#streams.add(new WeakRef(stream));\n return stream;\n }\n\n async close() {\n this.abortController.abort();\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: STTOptions;\n #audioEnergyFilter: AudioEnergyFilter;\n #logger = log();\n #speaking = false;\n #resetWS = new Future();\n #requestId = '';\n // keyterms set while the user is speaking; applied at END_OF_SPEECH (latest wins)\n /** @internal */\n _pendingKeyterm: string[] | null = null;\n #audioDurationCollector: PeriodicCollector<number>;\n label = 'deepgram.SpeechStream';\n\n constructor(stt: STT, opts: STTOptions, connOptions?: APIConnectOptions) {\n super(stt, opts.sampleRate, connOptions);\n this.#opts = opts;\n this.closed = false;\n this.#audioEnergyFilter = new AudioEnergyFilter();\n this.#audioDurationCollector = new PeriodicCollector(\n (duration) => this.onAudioDurationReport(duration),\n { duration: 5.0 },\n );\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n let ws: WebSocket;\n\n while (!this.input.closed && !this.closed) {\n const streamURL = new URL(`${this.#opts.baseUrl}/v1/listen`);\n const params = {\n model: this.#opts.model,\n punctuate: this.#opts.punctuate,\n smart_format: this.#opts.smartFormat,\n dictation: this.#opts.dictation,\n diarize: this.#opts.diarize,\n numerals: this.#opts.numerals,\n no_delay: this.#opts.noDelay,\n interim_results: this.#opts.interimResults,\n encoding: 'linear16',\n vad_events: true,\n sample_rate: this.#opts.sampleRate,\n channels: this.#opts.numChannels,\n endpointing: this.#opts.endpointing || false,\n filler_words: this.#opts.fillerWords,\n keywords: this.#opts.keywords.map((x) => x.join(':')),\n keyterm: this.#opts.keyterm,\n profanity_filter: this.#opts.profanityFilter,\n utterance_end_ms: this.#opts.utteranceEndMs,\n replace: this.#opts.replace,\n search: this.#opts.search,\n language: this.#opts.language,\n mip_opt_out: this.#opts.mipOptOut,\n ...(this.#opts.redact.length > 0 ? { redact: this.#opts.redact } : {}),\n };\n appendSearchParams(streamURL, params);\n\n ws = new WebSocket(streamURL, {\n headers: { Authorization: `Token ${this.#opts.apiKey}` },\n });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Deepgram after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Deepgram, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Deepgram disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n /** @internal */\n get _speaking(): boolean {\n return this.#speaking;\n }\n\n updateOptions(opts: Partial<STTOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.keyterm !== undefined) {\n this._pendingKeyterm = null;\n }\n this.#resetWS.resolve();\n }\n\n #onEndOfSpeech() {\n if (this._pendingKeyterm !== null) {\n this.updateOptions({ keyterm: this._pendingKeyterm });\n this._pendingKeyterm = null;\n }\n }\n\n async #runWS(ws: WebSocket) {\n this.#resetWS = new Future();\n let closing = false;\n\n const keepalive = setInterval(() => {\n try {\n ws.send(JSON.stringify({ type: 'KeepAlive' }));\n } catch {\n clearInterval(keepalive);\n return;\n }\n }, 5000);\n\n // gets cancelled also when sendTask is complete\n const wsMonitor = Task.from(async (controller) => {\n const closed = new Promise<void>(async (_, reject) => {\n ws.once('close', (code, reason) => {\n if (!closing) {\n this.#logger.error(`WebSocket closed with code ${code}: ${reason}`);\n reject(new Error('WebSocket closed'));\n }\n });\n });\n\n await Promise.race([closed, waitForAbort(controller.signal)]);\n });\n\n const sendTask = async () => {\n const samples100Ms = Math.floor(this.#opts.sampleRate / 10);\n const stream = new AudioByteStream(\n this.#opts.sampleRate,\n this.#opts.numChannels,\n samples100Ms,\n );\n\n // waitForAbort internally sets up an abort listener on the abort signal\n // we need to put it outside loop to avoid constant re-registration of the listener\n const abortPromise = waitForAbort(this.abortSignal);\n\n try {\n while (!this.closed) {\n const result = await Promise.race([this.input.next(), abortPromise]);\n\n if (result === undefined) return; // aborted\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n let hasEnded = false;\n if (data === SpeechStream.FLUSH_SENTINEL) {\n frames = stream.flush();\n hasEnded = true;\n } else if (\n data.sampleRate === this.#opts.sampleRate &&\n data.channels === this.#opts.numChannels\n ) {\n frames = stream.write(data.data.buffer as ArrayBuffer);\n } else {\n throw new Error(`sample rate or channel count of frame does not match`);\n }\n\n for await (const frame of frames) {\n if (this.#audioEnergyFilter.pushFrame(frame)) {\n const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n this.#audioDurationCollector.push(frameDuration);\n ws.send(frame.data.buffer);\n }\n }\n\n if (hasEnded) {\n this.#audioDurationCollector.flush();\n ws.send(JSON.stringify({ type: 'Finalize' }));\n }\n }\n } finally {\n closing = true;\n ws.send(JSON.stringify({ type: 'CloseStream' }));\n wsMonitor.cancel();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const putMessage = (message: stt.SpeechEvent) => {\n if (!this.queue.closed) {\n try {\n this.queue.put(message);\n } catch (e) {\n // ignore\n }\n }\n };\n\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (msg) => {\n try {\n const json = JSON.parse(msg.toString());\n switch (json['type']) {\n case 'SpeechStarted': {\n // This is a normal case. Deepgram's SpeechStarted events\n // are not correlated with speech_final or utterance end.\n // It's possible that we receive two in a row without an endpoint\n // It's also possible we receive a transcript without a SpeechStarted event.\n if (this.#speaking) return;\n this.#speaking = true;\n putMessage({ type: stt.SpeechEventType.START_OF_SPEECH });\n break;\n }\n // see this page:\n // https://developers.deepgram.com/docs/understand-endpointing-interim-results#using-endpointing-speech_final\n // for more information about the different types of events\n case 'Results': {\n const metadata = json['metadata'];\n const requestId = metadata['request_id'];\n const isFinal = json['is_final'];\n const isEndpoint = json['speech_final'];\n this.#requestId = requestId;\n\n const alternatives = liveTranscriptionToSpeechData(\n this.#opts.language!,\n json,\n this.startTimeOffset,\n this.#opts.punctuate || this.#opts.smartFormat,\n );\n\n // If, for some reason, we didn't get a SpeechStarted event but we got\n // a transcript with text, we should start speaking. It's rare but has\n // been observed.\n if (alternatives[0] && alternatives[0].text) {\n if (!this.#speaking) {\n this.#speaking = true;\n putMessage({\n type: stt.SpeechEventType.START_OF_SPEECH,\n });\n }\n\n if (isFinal) {\n putMessage({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n } else {\n putMessage({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [alternatives[0], ...alternatives.slice(1)],\n });\n }\n }\n\n // if we receive an endpoint, only end the speech if\n // we either had a SpeechStarted event or we have a seen\n // a non-empty transcript (deepgram doesn't have a SpeechEnded event)\n if (isEndpoint && this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n\n break;\n }\n case 'Metadata': {\n break;\n }\n case 'UtteranceEnd': {\n if (this.#speaking) {\n this.#speaking = false;\n putMessage({ type: stt.SpeechEventType.END_OF_SPEECH });\n this.#onEndOfSpeech();\n }\n break;\n }\n default: {\n this.#logger\n .child({ 'lk.pii.message': json })\n .warn('received unexpected message from Deepgram');\n break;\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(\n { error: err, 'lk.pii.message': msg.toString() },\n 'Deepgram STT failed to process message',\n );\n reject(err);\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.race([\n this.#resetWS.await,\n Promise.all([sendTask(), listenTask.result, wsMonitor]),\n ]);\n closing = true;\n ws.close();\n clearInterval(keepalive);\n }\n\n private onAudioDurationReport(duration: number) {\n const usageEvent: stt.SpeechEvent = {\n type: stt.SpeechEventType.RECOGNITION_USAGE,\n requestId: this.#requestId,\n recognitionUsage: {\n audioDuration: duration,\n },\n };\n this.queue.put(usageEvent);\n }\n}\n\nfunction appendSearchParams(url: URL, params: Record<string, unknown>) {\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => url.searchParams.append(key, String(item)));\n } else if (typeof value === 'object') {\n Object.entries(value).forEach(([term, replacement]) => {\n url.searchParams.append(key, `${term}:${replacement}`);\n });\n } else {\n url.searchParams.append(key, String(value));\n }\n });\n}\n\nfunction createWav(frame: AudioFrame): Buffer {\n const bitsPerSample = 16;\n const byteRate = (frame.sampleRate * frame.channels * bitsPerSample) / 8;\n const blockAlign = (frame.channels * bitsPerSample) / 8;\n\n const header = Buffer.alloc(44);\n header.write('RIFF', 0);\n header.writeUInt32LE(36 + frame.data.byteLength, 4);\n header.write('WAVE', 8);\n header.write('fmt ', 12);\n header.writeUInt32LE(16, 16);\n header.writeUInt16LE(1, 20);\n header.writeUInt16LE(frame.channels, 22);\n header.writeUInt32LE(frame.sampleRate, 24);\n header.writeUInt32LE(byteRate, 28);\n header.writeUInt16LE(blockAlign, 32);\n header.writeUInt16LE(bitsPerSample, 34);\n header.write('data', 36);\n header.writeUInt32LE(frame.data.byteLength, 40);\n\n const pcm = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength);\n return Buffer.concat([header, pcm]);\n}\n\n/**\n * Return the word form matching the transcript requested from Deepgram.\n *\n * Both punctuate and smart_format produce a formatted transcript. When either is enabled,\n * Deepgram provides punctuated_word alongside word. Fall back to word when it is absent or empty.\n */\nfunction wordText(word: { [id: string]: any }, usePunctuatedWord: boolean): string {\n const raw = word['word'] ?? '';\n return usePunctuatedWord ? word['punctuated_word'] || raw : raw;\n}\n\nconst liveTranscriptionToSpeechData = (\n language: STTLanguages | string,\n data: { [id: string]: any },\n startTimeOffset: number = 0,\n usePunctuatedWord: boolean = true,\n): stt.SpeechData[] => {\n const alts: any[] = data['channel']['alternatives'];\n\n return alts.map((alt) => {\n const wordsData: any[] = alt['words'] ?? [];\n const detectedLanguage =\n language === 'multi' ? alt['languages']?.[0] ?? language : alt['language'] ?? language;\n\n return {\n language: normalizeLanguage(detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] + startTimeOffset : startTimeOffset,\n endTime: wordsData.length\n ? wordsData[wordsData.length - 1]['end'] + startTimeOffset\n : startTimeOffset,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: (word['start'] ?? 0) + startTimeOffset,\n endTime: (word['end'] ?? 0) + startTimeOffset,\n confidence: word['confidence'] ?? 0.0,\n startTimeOffset,\n }),\n ),\n };\n });\n};\n\nfunction prerecordedTranscriptionToSpeechEvent(\n language: STTLanguages | string | undefined,\n data: { [id: string]: any },\n usePunctuatedWord: boolean = true,\n): stt.SpeechEvent {\n const channel = data['results']['channels'][0];\n const detectedLanguage = channel['detected_language'] ?? '';\n const alternatives = channel['alternatives'].map((alt: { [id: string]: any }) => {\n const wordsData: any[] = alt['words'] ?? [];\n\n return {\n language: normalizeLanguage(language ?? detectedLanguage),\n startTime: wordsData.length ? wordsData[0]['start'] : 0,\n endTime: wordsData.length ? wordsData[wordsData.length - 1]['end'] : 0,\n confidence: alt['confidence'],\n text: alt['transcript'],\n words: wordsData.map((word) =>\n createTimedString({\n text: wordText(word, usePunctuatedWord),\n startTime: word['start'] ?? 0,\n endTime: word['end'] ?? 0,\n confidence: word['confidence'] ?? 0.0,\n }),\n ),\n };\n }) as stt.SpeechData[];\n\n return {\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n requestId: data['metadata']['request_id'],\n ...(alternatives.length\n ? { alternatives: alternatives as [stt.SpeechData, ...stt.SpeechData[]] }\n : {}),\n };\n}\n"],"mappings":"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","names":["stt"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_agents = require("@livekit/agents");
|
|
3
|
+
var import_node_events = require("node:events");
|
|
4
|
+
var import_vitest = require("vitest");
|
|
5
|
+
var import_ws = require("ws");
|
|
6
|
+
var import_stt = require("./stt.cjs");
|
|
7
|
+
var import_stt_v2 = require("./stt_v2.cjs");
|
|
8
|
+
const servers = [];
|
|
9
|
+
const streams = [];
|
|
10
|
+
async function startServer(onConnection) {
|
|
11
|
+
const server = new import_ws.WebSocketServer({ host: "127.0.0.1", port: 0 });
|
|
12
|
+
servers.push(server);
|
|
13
|
+
await (0, import_node_events.once)(server, "listening");
|
|
14
|
+
if (onConnection) server.on("connection", onConnection);
|
|
15
|
+
const address = server.address();
|
|
16
|
+
if (address === null || typeof address === "string") {
|
|
17
|
+
throw new Error("expected a TCP address");
|
|
18
|
+
}
|
|
19
|
+
return `ws://127.0.0.1:${address.port}`;
|
|
20
|
+
}
|
|
21
|
+
(0, import_vitest.afterEach)(async () => {
|
|
22
|
+
for (const stream of streams.splice(0)) stream.close();
|
|
23
|
+
await Promise.all(
|
|
24
|
+
servers.splice(0).map(async (server) => {
|
|
25
|
+
for (const client of server.clients) client.terminate();
|
|
26
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
import_vitest.vi.restoreAllMocks();
|
|
30
|
+
});
|
|
31
|
+
(0, import_vitest.describe)("Deepgram STT logging", () => {
|
|
32
|
+
(0, import_vitest.it)("logs invalid messages as text with the parse error", async () => {
|
|
33
|
+
const errorSpy = import_vitest.vi.spyOn((0, import_agents.log)(), "error");
|
|
34
|
+
const baseUrl = await startServer((socket) => {
|
|
35
|
+
setTimeout(() => socket.send("not-json"), 10);
|
|
36
|
+
});
|
|
37
|
+
const recognizer = new import_stt.STT({ apiKey: "test", baseUrl });
|
|
38
|
+
const stream = recognizer.stream();
|
|
39
|
+
streams.push(stream);
|
|
40
|
+
await import_vitest.vi.waitFor(() => {
|
|
41
|
+
(0, import_vitest.expect)(errorSpy).toHaveBeenCalledWith(
|
|
42
|
+
{
|
|
43
|
+
error: import_vitest.expect.any(SyntaxError),
|
|
44
|
+
"lk.pii.message": "not-json"
|
|
45
|
+
},
|
|
46
|
+
"Deepgram STT failed to process message"
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
(0, import_vitest.it)("does not log the v2 connection URL", async () => {
|
|
51
|
+
const debugSpy = import_vitest.vi.spyOn((0, import_agents.log)(), "debug");
|
|
52
|
+
const baseUrl = await startServer();
|
|
53
|
+
const recognizer = new import_stt_v2.STTv2({
|
|
54
|
+
apiKey: "test",
|
|
55
|
+
endpointUrl: `${baseUrl}/v2/listen`,
|
|
56
|
+
keyterms: ["private-keyterm"]
|
|
57
|
+
});
|
|
58
|
+
const stream = recognizer.stream();
|
|
59
|
+
streams.push(stream);
|
|
60
|
+
await import_vitest.vi.waitFor(() => {
|
|
61
|
+
const connectionCalls = debugSpy.mock.calls.filter(
|
|
62
|
+
(call) => call.includes("connecting to Deepgram")
|
|
63
|
+
);
|
|
64
|
+
(0, import_vitest.expect)(connectionCalls).toEqual([["connecting to Deepgram"]]);
|
|
65
|
+
(0, import_vitest.expect)(JSON.stringify(connectionCalls)).not.toContain("private-keyterm");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=stt_logging.test.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stt_logging.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '@livekit/agents';\nimport { once } from 'node:events';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { type WebSocket, WebSocketServer } from 'ws';\nimport { STT } from './stt.js';\nimport { STTv2 } from './stt_v2.js';\n\nconst servers: WebSocketServer[] = [];\nconst streams: Array<{ close(): void }> = [];\n\nasync function startServer(onConnection?: (socket: WebSocket) => void): Promise<string> {\n const server = new WebSocketServer({ host: '127.0.0.1', port: 0 });\n servers.push(server);\n await once(server, 'listening');\n if (onConnection) server.on('connection', onConnection);\n\n const address = server.address();\n if (address === null || typeof address === 'string') {\n throw new Error('expected a TCP address');\n }\n return `ws://127.0.0.1:${address.port}`;\n}\n\nafterEach(async () => {\n for (const stream of streams.splice(0)) stream.close();\n await Promise.all(\n servers.splice(0).map(async (server) => {\n for (const client of server.clients) client.terminate();\n await new Promise<void>((resolve) => server.close(() => resolve()));\n }),\n );\n vi.restoreAllMocks();\n});\n\ndescribe('Deepgram STT logging', () => {\n it('logs invalid messages as text with the parse error', async () => {\n const errorSpy = vi.spyOn(log(), 'error');\n const baseUrl = await startServer((socket) => {\n setTimeout(() => socket.send('not-json'), 10);\n });\n const recognizer = new STT({ apiKey: 'test', baseUrl });\n const stream = recognizer.stream();\n streams.push(stream);\n\n await vi.waitFor(() => {\n expect(errorSpy).toHaveBeenCalledWith(\n {\n error: expect.any(SyntaxError),\n 'lk.pii.message': 'not-json',\n },\n 'Deepgram STT failed to process message',\n );\n });\n });\n\n it('does not log the v2 connection URL', async () => {\n const debugSpy = vi.spyOn(log(), 'debug');\n const baseUrl = await startServer();\n const recognizer = new STTv2({\n apiKey: 'test',\n endpointUrl: `${baseUrl}/v2/listen`,\n keyterms: ['private-keyterm'],\n });\n const stream = recognizer.stream();\n streams.push(stream);\n\n await vi.waitFor(() => {\n const connectionCalls = debugSpy.mock.calls.filter((call) =>\n call.includes('connecting to Deepgram'),\n );\n expect(connectionCalls).toEqual([['connecting to Deepgram']]);\n expect(JSON.stringify(connectionCalls)).not.toContain('private-keyterm');\n });\n });\n});\n"],"mappings":";AAGA,oBAAoB;AACpB,yBAAqB;AACrB,oBAAoD;AACpD,gBAAgD;AAChD,iBAAoB;AACpB,oBAAsB;AAEtB,MAAM,UAA6B,CAAC;AACpC,MAAM,UAAoC,CAAC;AAE3C,eAAe,YAAY,cAA6D;AACtF,QAAM,SAAS,IAAI,0BAAgB,EAAE,MAAM,aAAa,MAAM,EAAE,CAAC;AACjE,UAAQ,KAAK,MAAM;AACnB,YAAM,yBAAK,QAAQ,WAAW;AAC9B,MAAI,aAAc,QAAO,GAAG,cAAc,YAAY;AAEtD,QAAM,UAAU,OAAO,QAAQ;AAC/B,MAAI,YAAY,QAAQ,OAAO,YAAY,UAAU;AACnD,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO,kBAAkB,QAAQ,IAAI;AACvC;AAAA,IAEA,yBAAU,YAAY;AACpB,aAAW,UAAU,QAAQ,OAAO,CAAC,EAAG,QAAO,MAAM;AACrD,QAAM,QAAQ;AAAA,IACZ,QAAQ,OAAO,CAAC,EAAE,IAAI,OAAO,WAAW;AACtC,iBAAW,UAAU,OAAO,QAAS,QAAO,UAAU;AACtD,YAAM,IAAI,QAAc,CAAC,YAAY,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AACA,mBAAG,gBAAgB;AACrB,CAAC;AAAA,IAED,wBAAS,wBAAwB,MAAM;AACrC,wBAAG,sDAAsD,YAAY;AACnE,UAAM,WAAW,iBAAG,UAAM,mBAAI,GAAG,OAAO;AACxC,UAAM,UAAU,MAAM,YAAY,CAAC,WAAW;AAC5C,iBAAW,MAAM,OAAO,KAAK,UAAU,GAAG,EAAE;AAAA,IAC9C,CAAC;AACD,UAAM,aAAa,IAAI,eAAI,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AACtD,UAAM,SAAS,WAAW,OAAO;AACjC,YAAQ,KAAK,MAAM;AAEnB,UAAM,iBAAG,QAAQ,MAAM;AACrB,gCAAO,QAAQ,EAAE;AAAA,QACf;AAAA,UACE,OAAO,qBAAO,IAAI,WAAW;AAAA,UAC7B,kBAAkB;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,wBAAG,sCAAsC,YAAY;AACnD,UAAM,WAAW,iBAAG,UAAM,mBAAI,GAAG,OAAO;AACxC,UAAM,UAAU,MAAM,YAAY;AAClC,UAAM,aAAa,IAAI,oBAAM;AAAA,MAC3B,QAAQ;AAAA,MACR,aAAa,GAAG,OAAO;AAAA,MACvB,UAAU,CAAC,iBAAiB;AAAA,IAC9B,CAAC;AACD,UAAM,SAAS,WAAW,OAAO;AACjC,YAAQ,KAAK,MAAM;AAEnB,UAAM,iBAAG,QAAQ,MAAM;AACrB,YAAM,kBAAkB,SAAS,KAAK,MAAM;AAAA,QAAO,CAAC,SAClD,KAAK,SAAS,wBAAwB;AAAA,MACxC;AACA,gCAAO,eAAe,EAAE,QAAQ,CAAC,CAAC,wBAAwB,CAAC,CAAC;AAC5D,gCAAO,KAAK,UAAU,eAAe,CAAC,EAAE,IAAI,UAAU,iBAAiB;AAAA,IACzE,CAAC;AAAA,EACH,CAAC;AACH,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stt_logging.test.d.ts","sourceRoot":"","sources":["../src/stt_logging.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { log } from "@livekit/agents";
|
|
2
|
+
import { once } from "node:events";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { WebSocketServer } from "ws";
|
|
5
|
+
import { STT } from "./stt.js";
|
|
6
|
+
import { STTv2 } from "./stt_v2.js";
|
|
7
|
+
const servers = [];
|
|
8
|
+
const streams = [];
|
|
9
|
+
async function startServer(onConnection) {
|
|
10
|
+
const server = new WebSocketServer({ host: "127.0.0.1", port: 0 });
|
|
11
|
+
servers.push(server);
|
|
12
|
+
await once(server, "listening");
|
|
13
|
+
if (onConnection) server.on("connection", onConnection);
|
|
14
|
+
const address = server.address();
|
|
15
|
+
if (address === null || typeof address === "string") {
|
|
16
|
+
throw new Error("expected a TCP address");
|
|
17
|
+
}
|
|
18
|
+
return `ws://127.0.0.1:${address.port}`;
|
|
19
|
+
}
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
for (const stream of streams.splice(0)) stream.close();
|
|
22
|
+
await Promise.all(
|
|
23
|
+
servers.splice(0).map(async (server) => {
|
|
24
|
+
for (const client of server.clients) client.terminate();
|
|
25
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
vi.restoreAllMocks();
|
|
29
|
+
});
|
|
30
|
+
describe("Deepgram STT logging", () => {
|
|
31
|
+
it("logs invalid messages as text with the parse error", async () => {
|
|
32
|
+
const errorSpy = vi.spyOn(log(), "error");
|
|
33
|
+
const baseUrl = await startServer((socket) => {
|
|
34
|
+
setTimeout(() => socket.send("not-json"), 10);
|
|
35
|
+
});
|
|
36
|
+
const recognizer = new STT({ apiKey: "test", baseUrl });
|
|
37
|
+
const stream = recognizer.stream();
|
|
38
|
+
streams.push(stream);
|
|
39
|
+
await vi.waitFor(() => {
|
|
40
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
41
|
+
{
|
|
42
|
+
error: expect.any(SyntaxError),
|
|
43
|
+
"lk.pii.message": "not-json"
|
|
44
|
+
},
|
|
45
|
+
"Deepgram STT failed to process message"
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
it("does not log the v2 connection URL", async () => {
|
|
50
|
+
const debugSpy = vi.spyOn(log(), "debug");
|
|
51
|
+
const baseUrl = await startServer();
|
|
52
|
+
const recognizer = new STTv2({
|
|
53
|
+
apiKey: "test",
|
|
54
|
+
endpointUrl: `${baseUrl}/v2/listen`,
|
|
55
|
+
keyterms: ["private-keyterm"]
|
|
56
|
+
});
|
|
57
|
+
const stream = recognizer.stream();
|
|
58
|
+
streams.push(stream);
|
|
59
|
+
await vi.waitFor(() => {
|
|
60
|
+
const connectionCalls = debugSpy.mock.calls.filter(
|
|
61
|
+
(call) => call.includes("connecting to Deepgram")
|
|
62
|
+
);
|
|
63
|
+
expect(connectionCalls).toEqual([["connecting to Deepgram"]]);
|
|
64
|
+
expect(JSON.stringify(connectionCalls)).not.toContain("private-keyterm");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=stt_logging.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stt_logging.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '@livekit/agents';\nimport { once } from 'node:events';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { type WebSocket, WebSocketServer } from 'ws';\nimport { STT } from './stt.js';\nimport { STTv2 } from './stt_v2.js';\n\nconst servers: WebSocketServer[] = [];\nconst streams: Array<{ close(): void }> = [];\n\nasync function startServer(onConnection?: (socket: WebSocket) => void): Promise<string> {\n const server = new WebSocketServer({ host: '127.0.0.1', port: 0 });\n servers.push(server);\n await once(server, 'listening');\n if (onConnection) server.on('connection', onConnection);\n\n const address = server.address();\n if (address === null || typeof address === 'string') {\n throw new Error('expected a TCP address');\n }\n return `ws://127.0.0.1:${address.port}`;\n}\n\nafterEach(async () => {\n for (const stream of streams.splice(0)) stream.close();\n await Promise.all(\n servers.splice(0).map(async (server) => {\n for (const client of server.clients) client.terminate();\n await new Promise<void>((resolve) => server.close(() => resolve()));\n }),\n );\n vi.restoreAllMocks();\n});\n\ndescribe('Deepgram STT logging', () => {\n it('logs invalid messages as text with the parse error', async () => {\n const errorSpy = vi.spyOn(log(), 'error');\n const baseUrl = await startServer((socket) => {\n setTimeout(() => socket.send('not-json'), 10);\n });\n const recognizer = new STT({ apiKey: 'test', baseUrl });\n const stream = recognizer.stream();\n streams.push(stream);\n\n await vi.waitFor(() => {\n expect(errorSpy).toHaveBeenCalledWith(\n {\n error: expect.any(SyntaxError),\n 'lk.pii.message': 'not-json',\n },\n 'Deepgram STT failed to process message',\n );\n });\n });\n\n it('does not log the v2 connection URL', async () => {\n const debugSpy = vi.spyOn(log(), 'debug');\n const baseUrl = await startServer();\n const recognizer = new STTv2({\n apiKey: 'test',\n endpointUrl: `${baseUrl}/v2/listen`,\n keyterms: ['private-keyterm'],\n });\n const stream = recognizer.stream();\n streams.push(stream);\n\n await vi.waitFor(() => {\n const connectionCalls = debugSpy.mock.calls.filter((call) =>\n call.includes('connecting to Deepgram'),\n );\n expect(connectionCalls).toEqual([['connecting to Deepgram']]);\n expect(JSON.stringify(connectionCalls)).not.toContain('private-keyterm');\n });\n });\n});\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,YAAY;AACrB,SAAS,WAAW,UAAU,QAAQ,IAAI,UAAU;AACpD,SAAyB,uBAAuB;AAChD,SAAS,WAAW;AACpB,SAAS,aAAa;AAEtB,MAAM,UAA6B,CAAC;AACpC,MAAM,UAAoC,CAAC;AAE3C,eAAe,YAAY,cAA6D;AACtF,QAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,aAAa,MAAM,EAAE,CAAC;AACjE,UAAQ,KAAK,MAAM;AACnB,QAAM,KAAK,QAAQ,WAAW;AAC9B,MAAI,aAAc,QAAO,GAAG,cAAc,YAAY;AAEtD,QAAM,UAAU,OAAO,QAAQ;AAC/B,MAAI,YAAY,QAAQ,OAAO,YAAY,UAAU;AACnD,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO,kBAAkB,QAAQ,IAAI;AACvC;AAEA,UAAU,YAAY;AACpB,aAAW,UAAU,QAAQ,OAAO,CAAC,EAAG,QAAO,MAAM;AACrD,QAAM,QAAQ;AAAA,IACZ,QAAQ,OAAO,CAAC,EAAE,IAAI,OAAO,WAAW;AACtC,iBAAW,UAAU,OAAO,QAAS,QAAO,UAAU;AACtD,YAAM,IAAI,QAAc,CAAC,YAAY,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AACA,KAAG,gBAAgB;AACrB,CAAC;AAED,SAAS,wBAAwB,MAAM;AACrC,KAAG,sDAAsD,YAAY;AACnE,UAAM,WAAW,GAAG,MAAM,IAAI,GAAG,OAAO;AACxC,UAAM,UAAU,MAAM,YAAY,CAAC,WAAW;AAC5C,iBAAW,MAAM,OAAO,KAAK,UAAU,GAAG,EAAE;AAAA,IAC9C,CAAC;AACD,UAAM,aAAa,IAAI,IAAI,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AACtD,UAAM,SAAS,WAAW,OAAO;AACjC,YAAQ,KAAK,MAAM;AAEnB,UAAM,GAAG,QAAQ,MAAM;AACrB,aAAO,QAAQ,EAAE;AAAA,QACf;AAAA,UACE,OAAO,OAAO,IAAI,WAAW;AAAA,UAC7B,kBAAkB;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,KAAG,sCAAsC,YAAY;AACnD,UAAM,WAAW,GAAG,MAAM,IAAI,GAAG,OAAO;AACxC,UAAM,UAAU,MAAM,YAAY;AAClC,UAAM,aAAa,IAAI,MAAM;AAAA,MAC3B,QAAQ;AAAA,MACR,aAAa,GAAG,OAAO;AAAA,MACvB,UAAU,CAAC,iBAAiB;AAAA,IAC9B,CAAC;AACD,UAAM,SAAS,WAAW,OAAO;AACjC,YAAQ,KAAK,MAAM;AAEnB,UAAM,GAAG,QAAQ,MAAM;AACrB,YAAM,kBAAkB,SAAS,KAAK,MAAM;AAAA,QAAO,CAAC,SAClD,KAAK,SAAS,wBAAwB;AAAA,MACxC;AACA,aAAO,eAAe,EAAE,QAAQ,CAAC,CAAC,wBAAwB,CAAC,CAAC;AAC5D,aAAO,KAAK,UAAU,eAAe,CAAC,EAAE,IAAI,UAAU,iBAAiB;AAAA,IACzE,CAAC;AAAA,EACH,CAAC;AACH,CAAC;","names":[]}
|