@livekit/agents-plugin-elevenlabs 1.0.40 → 1.0.41

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/tts.cjs CHANGED
@@ -72,7 +72,7 @@ function stripUndefined(obj) {
72
72
  }
73
73
  return result;
74
74
  }
75
- function toTimedWords(text, startTimesMs, durationsMs, flush = false) {
75
+ function toTimedWords(text, startTimesMs, durationsMs, flush = false, firstWordOffsetMs = 0) {
76
76
  if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {
77
77
  return [[], text || ""];
78
78
  }
@@ -90,23 +90,27 @@ function toTimedWords(text, startTimesMs, durationsMs, flush = false) {
90
90
  const start = startIndices[i];
91
91
  const nextStart = startIndices[i + 1];
92
92
  end = nextStart;
93
- const startT = (timestamps[start] ?? 0) / 1e3;
94
- const endT = (timestamps[nextStart] ?? 0) / 1e3;
95
- timedWords.push({
96
- text: text.slice(start, nextStart),
97
- startTime: startT,
98
- endTime: endT
99
- });
93
+ const startT = Math.max(0, (timestamps[start] ?? 0) - firstWordOffsetMs) / 1e3;
94
+ const endT = Math.max(0, (timestamps[nextStart] ?? 0) - firstWordOffsetMs) / 1e3;
95
+ timedWords.push(
96
+ (0, import_agents.createTimedString)({
97
+ text: text.slice(start, nextStart),
98
+ startTime: startT,
99
+ endTime: endT
100
+ })
101
+ );
100
102
  }
101
103
  if (flush && words.length > 0) {
102
104
  const lastWordStart = startIndices[startIndices.length - 1];
103
- const startT = (timestamps[lastWordStart] ?? 0) / 1e3;
104
- const endT = (timestamps[timestamps.length - 1] ?? 0) / 1e3;
105
- timedWords.push({
106
- text: text.slice(lastWordStart),
107
- startTime: startT,
108
- endTime: endT
109
- });
105
+ const startT = Math.max(0, (timestamps[lastWordStart] ?? 0) - firstWordOffsetMs) / 1e3;
106
+ const endT = Math.max(0, (timestamps[timestamps.length - 1] ?? 0) - firstWordOffsetMs) / 1e3;
107
+ timedWords.push(
108
+ (0, import_agents.createTimedString)({
109
+ text: text.slice(lastWordStart),
110
+ startTime: startT,
111
+ endTime: endT
112
+ })
113
+ );
110
114
  end = text.length;
111
115
  } else if (words.length > 0) {
112
116
  end = startIndices[startIndices.length - 1];
@@ -166,7 +170,8 @@ class Connection {
166
170
  waiter,
167
171
  textBuffer: "",
168
172
  startTimesMs: [],
169
- durationsMs: []
173
+ durationsMs: [],
174
+ firstWordOffsetMs: null
170
175
  });
171
176
  }
172
177
  sendContent(content) {
@@ -321,6 +326,9 @@ class Connection {
321
326
  const char = chars[i];
322
327
  const start = starts[i];
323
328
  const dur = durs[i];
329
+ if (ctx.firstWordOffsetMs === null && start > 0) {
330
+ ctx.firstWordOffsetMs = start;
331
+ }
324
332
  if (char.length > 1) {
325
333
  for (let j = 0; j < char.length - 1; j++) {
326
334
  ctx.startTimesMs.push(start);
@@ -333,7 +341,9 @@ class Connection {
333
341
  const [timedWords, remainingText] = toTimedWords(
334
342
  ctx.textBuffer,
335
343
  ctx.startTimesMs,
336
- ctx.durationsMs
344
+ ctx.durationsMs,
345
+ false,
346
+ ctx.firstWordOffsetMs ?? 0
337
347
  );
338
348
  if (timedWords.length > 0) {
339
349
  stream2.pushTimedTranscript(timedWords);
@@ -353,7 +363,8 @@ class Connection {
353
363
  ctx.textBuffer,
354
364
  ctx.startTimesMs,
355
365
  ctx.durationsMs,
356
- true
366
+ true,
367
+ ctx.firstWordOffsetMs ?? 0
357
368
  );
358
369
  if (timedWords.length > 0) {
359
370
  stream2.pushTimedTranscript(timedWords);
@@ -428,8 +439,10 @@ class TTS extends import_agents.tts.TTS {
428
439
  const autoMode = opts.autoMode ?? true;
429
440
  const encoding = opts.encoding ?? DEFAULT_ENCODING;
430
441
  const sampleRate = sampleRateFromFormat(encoding);
442
+ const syncAlignment = opts.syncAlignment ?? true;
431
443
  super(sampleRate, 1, {
432
- streaming: true
444
+ streaming: true,
445
+ alignedTranscript: syncAlignment
433
446
  });
434
447
  const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;
435
448
  if (!apiKey) {
@@ -720,13 +733,28 @@ class SynthesizeStream extends import_agents.tts.SynthesizeStream {
720
733
  };
721
734
  const audioProcessTask = async () => {
722
735
  let lastFrame;
736
+ let pendingTimedTranscripts = [];
737
+ const sendLastFrame = (final) => {
738
+ if (lastFrame) {
739
+ this.queue.put({
740
+ requestId,
741
+ segmentId,
742
+ frame: lastFrame,
743
+ final,
744
+ timedTranscripts: pendingTimedTranscripts.length > 0 ? pendingTimedTranscripts : void 0
745
+ });
746
+ lastFrame = void 0;
747
+ pendingTimedTranscripts = [];
748
+ }
749
+ };
723
750
  while (!this.abortController.signal.aborted) {
751
+ while (this.#timedTranscriptQueue.length > 0) {
752
+ pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift());
753
+ }
724
754
  while (this.#audioQueue.length > 0) {
725
755
  const audioData = this.#audioQueue.shift();
726
756
  for (const frame of bstream.write(audioData.buffer)) {
727
- if (lastFrame) {
728
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });
729
- }
757
+ sendLastFrame(false);
730
758
  lastFrame = frame;
731
759
  }
732
760
  }
@@ -735,15 +763,14 @@ class SynthesizeStream extends import_agents.tts.SynthesizeStream {
735
763
  }
736
764
  await new Promise((resolve) => setTimeout(resolve, 10));
737
765
  }
766
+ while (this.#timedTranscriptQueue.length > 0) {
767
+ pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift());
768
+ }
738
769
  for (const frame of bstream.flush()) {
739
- if (lastFrame) {
740
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });
741
- }
770
+ sendLastFrame(false);
742
771
  lastFrame = frame;
743
772
  }
744
- if (lastFrame) {
745
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: true });
746
- }
773
+ sendLastFrame(true);
747
774
  };
748
775
  try {
749
776
  await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);
package/dist/tts.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n APIConnectionError,\n APIError,\n APIStatusError,\n APITimeoutError,\n AudioByteStream,\n Future,\n log,\n shortuuid,\n stream,\n tokenize,\n tts,\n type voice,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { TTSEncoding, TTSModels } from './models.js';\n\ntype TimedString = voice.TimedString;\n\nconst DEFAULT_VOICE_ID = 'bIHbv24MWmeRgasZH58o';\nconst API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1';\nconst AUTHORIZATION_HEADER = 'xi-api-key';\nconst WS_INACTIVITY_TIMEOUT = 180;\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_22050';\n\nexport interface VoiceSettings {\n stability: number; // [0.0 - 1.0]\n similarity_boost: number; // [0.0 - 1.0]\n style?: number; // [0.0 - 1.0]\n speed?: number; // [0.8 - 1.2]\n use_speaker_boost?: boolean;\n}\n\nexport interface Voice {\n id: string;\n name: string;\n category: string;\n settings?: VoiceSettings;\n}\n\nexport interface PronunciationDictionaryLocator {\n pronunciation_dictionary_id: string;\n version_id: string;\n}\n\nexport interface TTSOptions {\n apiKey?: string;\n // New interface\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n // Legacy interface (backward compatibility)\n voice?: Voice;\n modelID?: TTSModels | string;\n languageCode?: string;\n // Common options\n baseURL?: string;\n encoding?: TTSEncoding;\n streamingLatency?: number;\n wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing?: boolean;\n enableLogging?: boolean;\n inactivityTimeout?: number;\n syncAlignment?: boolean;\n applyTextNormalization?: 'auto' | 'on' | 'off';\n preferredAlignment?: 'normalized' | 'original';\n autoMode?: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal options type with resolved defaults\ninterface ResolvedTTSOptions {\n apiKey: string;\n voiceId: string;\n voiceSettings?: VoiceSettings;\n model: TTSModels | string;\n language?: string;\n baseURL: string;\n encoding: TTSEncoding;\n sampleRate: number;\n streamingLatency?: number;\n wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing: boolean;\n enableLogging: boolean;\n inactivityTimeout: number;\n syncAlignment: boolean;\n applyTextNormalization: 'auto' | 'on' | 'off';\n preferredAlignment: 'normalized' | 'original';\n autoMode: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal types for connection management\ninterface SynthesizeContent {\n contextId: string;\n text: string;\n flush: boolean;\n}\n\ninterface CloseContext {\n contextId: string;\n}\n\ninterface StreamData {\n stream: SynthesizeStream;\n waiter: {\n resolve: (value: void) => void;\n reject: (error: Error) => void;\n };\n textBuffer: string;\n startTimesMs: number[];\n durationsMs: number[];\n}\n\ntype ConnectionMessage = SynthesizeContent | CloseContext;\n\n// Helper Functions\n\nfunction sampleRateFromFormat(encoding: TTSEncoding): number {\n const split = encoding.split('_');\n return parseInt(split[1]!, 10);\n}\n\nfunction synthesizeUrl(opts: ResolvedTTSOptions): string {\n const { baseURL, voiceId, model, encoding, streamingLatency } = opts;\n let url = `${baseURL}/text-to-speech/${voiceId}/stream?model_id=${model}&output_format=${encoding}`;\n if (streamingLatency !== undefined) {\n url += `&optimize_streaming_latency=${streamingLatency}`;\n }\n return url;\n}\n\nfunction multiStreamUrl(opts: ResolvedTTSOptions): string {\n const baseURL = opts.baseURL.replace('https://', 'wss://').replace('http://', 'ws://');\n const params: string[] = [];\n params.push(`model_id=${opts.model}`);\n params.push(`output_format=${opts.encoding}`);\n if (opts.language) {\n params.push(`language_code=${opts.language}`);\n }\n params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);\n params.push(`enable_logging=${opts.enableLogging}`);\n params.push(`inactivity_timeout=${opts.inactivityTimeout}`);\n params.push(`apply_text_normalization=${opts.applyTextNormalization}`);\n if (opts.syncAlignment) {\n params.push('sync_alignment=true');\n }\n if (opts.autoMode !== undefined) {\n params.push(`auto_mode=${opts.autoMode}`);\n }\n return `${baseURL}/text-to-speech/${opts.voiceId}/multi-stream-input?${params.join('&')}`;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const result: Partial<T> = {};\n for (const key in obj) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Convert alignment data to timed words.\n * Returns the timed words and remaining text buffer.\n */\nfunction toTimedWords(\n text: string,\n startTimesMs: number[],\n durationsMs: number[],\n flush: boolean = false,\n): [TimedString[], string] {\n if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {\n return [[], text || ''];\n }\n\n const lastStartTime = startTimesMs[startTimesMs.length - 1]!;\n const lastDuration = durationsMs[durationsMs.length - 1]!;\n const timestamps = [...startTimesMs, lastStartTime + lastDuration];\n\n const words = tokenize.basic.splitWords(text, false);\n const timedWords: TimedString[] = [];\n\n if (words.length === 0) {\n return [[], text];\n }\n\n const startIndices = words.map((w) => w[1]);\n let end = 0;\n\n // We don't know if the last word is complete, always leave it as remaining\n for (let i = 0; i < startIndices.length - 1; i++) {\n const start = startIndices[i]!;\n const nextStart = startIndices[i + 1]!;\n end = nextStart;\n const startT = (timestamps[start] ?? 0) / 1000;\n const endT = (timestamps[nextStart] ?? 0) / 1000;\n timedWords.push({\n text: text.slice(start, nextStart),\n startTime: startT,\n endTime: endT,\n });\n }\n\n if (flush && words.length > 0) {\n const lastWordStart = startIndices[startIndices.length - 1]!;\n const startT = (timestamps[lastWordStart] ?? 0) / 1000;\n const endT = (timestamps[timestamps.length - 1] ?? 0) / 1000;\n timedWords.push({\n text: text.slice(lastWordStart),\n startTime: startT,\n endTime: endT,\n });\n end = text.length;\n } else if (words.length > 0) {\n end = startIndices[startIndices.length - 1]!;\n }\n\n return [timedWords, text.slice(end)];\n}\n\nclass Connection {\n #opts: ResolvedTTSOptions;\n #ws: WebSocket | null = null;\n #isCurrent = true;\n #activeContexts = new Set<string>();\n #inputQueue: ConnectionMessage[] = [];\n #contextData = new Map<string, StreamData>();\n #sendTask: Promise<void> | null = null;\n #recvTask: Promise<void> | null = null;\n #closed = false;\n #logger = log();\n #inputQueueResolver: (() => void) | null = null;\n\n constructor(opts: ResolvedTTSOptions) {\n this.#opts = opts;\n }\n\n get voiceId(): string {\n return this.#opts.voiceId;\n }\n\n get isCurrent(): boolean {\n return this.#isCurrent;\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n markNonCurrent(): void {\n this.#isCurrent = false;\n }\n\n async connect(): Promise<void> {\n if (this.#ws || this.#closed) {\n return;\n }\n\n const url = multiStreamUrl(this.#opts);\n const headers = { [AUTHORIZATION_HEADER]: this.#opts.apiKey };\n\n return new Promise((resolve, reject) => {\n this.#ws = new WebSocket(url, { headers });\n\n this.#ws.on('open', () => {\n this.#sendTask = this.#sendLoop();\n this.#recvTask = this.#recvLoop();\n resolve();\n });\n\n this.#ws.on('error', (error) => {\n this.#logger.error({ error }, 'WebSocket connection error');\n reject(new APIConnectionError({ message: `WebSocket error: ${error.message}` }));\n });\n });\n }\n\n registerStream(\n stream: SynthesizeStream,\n waiter: { resolve: (value: void) => void; reject: (error: Error) => void },\n ): void {\n const contextId = stream.contextId;\n this.#contextData.set(contextId, {\n stream,\n waiter,\n textBuffer: '',\n startTimesMs: [],\n durationsMs: [],\n });\n }\n\n sendContent(content: SynthesizeContent): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push(content);\n this.#inputQueueResolver?.();\n }\n\n closeContext(contextId: string): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push({ contextId });\n this.#inputQueueResolver?.();\n }\n\n async #sendLoop(): Promise<void> {\n try {\n while (!this.#closed) {\n // Wait for messages in queue\n if (this.#inputQueue.length === 0) {\n await new Promise<void>((resolve) => {\n this.#inputQueueResolver = resolve;\n });\n this.#inputQueueResolver = null;\n }\n\n if (this.#closed) break;\n\n const msg = this.#inputQueue.shift();\n if (!msg) continue;\n\n if (!this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n break;\n }\n\n if ('text' in msg) {\n // SynthesizeContent\n const content = msg as SynthesizeContent;\n const isNewContext = !this.#activeContexts.has(content.contextId);\n\n // If not current and this is a new context, ignore it\n if (!this.#isCurrent && isNewContext) {\n continue;\n }\n\n if (isNewContext) {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : {};\n\n const initPkt: Record<string, unknown> = {\n text: ' ',\n voice_settings: voiceSettings,\n context_id: content.contextId,\n };\n\n if (this.#opts.pronunciationDictionaryLocators) {\n initPkt.pronunciation_dictionary_locators =\n this.#opts.pronunciationDictionaryLocators.map((locator) => ({\n pronunciation_dictionary_id: locator.pronunciation_dictionary_id,\n version_id: locator.version_id,\n }));\n }\n\n const initPktStr = JSON.stringify(initPkt);\n this.#ws.send(initPktStr);\n this.#activeContexts.add(content.contextId);\n }\n\n const pkt: Record<string, unknown> = {\n text: content.text,\n context_id: content.contextId,\n };\n if (content.flush) {\n pkt.flush = true;\n }\n\n const pktStr = JSON.stringify(pkt);\n this.#ws.send(pktStr);\n } else {\n // CloseContext\n const closeMsg = msg as CloseContext;\n if (this.#activeContexts.has(closeMsg.contextId)) {\n const closePkt = {\n context_id: closeMsg.contextId,\n close_context: true,\n };\n const closePktStr = JSON.stringify(closePkt);\n this.#ws.send(closePktStr);\n }\n }\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'send loop error');\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n async #recvLoop(): Promise<void> {\n try {\n const messageChannel = stream.createStreamChannel<Record<string, unknown>>();\n const errorFuture = new Future<Error>();\n\n const onMessage = (rawData: Buffer) => {\n try {\n const parsed = JSON.parse(rawData.toString());\n messageChannel.write(parsed);\n } catch (e) {\n this.#logger.warn({ error: e }, 'failed to parse WebSocket message');\n }\n };\n\n const onClose = () => {\n if (!this.#closed && this.#contextData.size > 0) {\n this.#logger.warn('websocket closed unexpectedly');\n }\n messageChannel.close();\n };\n\n const onError = (error: Error) => {\n errorFuture.resolve(error);\n messageChannel.close();\n };\n\n // Set up persistent listeners\n if (!this.#ws) return;\n this.#ws.on('message', onMessage);\n this.#ws.on('close', onClose);\n this.#ws.on('error', onError);\n\n const cleanup = () => {\n this.#ws?.off('message', onMessage);\n this.#ws?.off('close', onClose);\n this.#ws?.off('error', onError);\n };\n\n const reader = messageChannel.stream().getReader();\n try {\n while (!this.#closed) {\n const result = await reader.read();\n if (result.done || this.#closed) break;\n\n const data = result.value;\n const contextId = data.contextId as string | undefined;\n const ctx = contextId ? this.#contextData.get(contextId) : undefined;\n\n if (data.error) {\n this.#logger.error(\n { context_id: contextId, error: data.error, data },\n 'elevenlabs tts returned error',\n );\n if (contextId) {\n if (ctx) {\n ctx.waiter.reject(new APIError(data.error as string));\n }\n this.#cleanupContext(contextId);\n }\n continue;\n }\n\n if (!ctx) {\n this.#logger.warn({ data }, 'unexpected message received from elevenlabs tts');\n continue;\n }\n\n const stream = ctx.stream;\n\n // Process alignment data\n const alignment =\n this.#opts.preferredAlignment === 'normalized'\n ? (data.normalizedAlignment as Record<string, unknown>)\n : (data.alignment as Record<string, unknown>);\n\n if (alignment && stream) {\n const chars = alignment.chars as string[] | undefined;\n const starts = (alignment.charStartTimesMs || alignment.charsStartTimesMs) as\n | number[]\n | undefined;\n const durs = (alignment.charDurationsMs || alignment.charsDurationsMs) as\n | number[]\n | undefined;\n\n if (\n chars &&\n starts &&\n durs &&\n chars.length === durs.length &&\n starts.length === durs.length\n ) {\n ctx.textBuffer += chars.join('');\n\n // Handle chars with multiple characters\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const start = starts[i]!;\n const dur = durs[i]!;\n\n if (char.length > 1) {\n for (let j = 0; j < char.length - 1; j++) {\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(0);\n }\n }\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(dur);\n }\n\n const [timedWords, remainingText] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n );\n\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n\n ctx.textBuffer = remainingText;\n ctx.startTimesMs = ctx.startTimesMs.slice(-remainingText.length);\n ctx.durationsMs = ctx.durationsMs.slice(-remainingText.length);\n }\n }\n\n if (data.audio) {\n const audioData = Buffer.from(data.audio as string, 'base64');\n stream.pushAudio(audioData);\n }\n\n if (data.isFinal) {\n // Flush remaining alignment data\n if (ctx.textBuffer) {\n const [timedWords] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n true,\n );\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n }\n\n stream.markDone();\n ctx.waiter.resolve();\n this.#cleanupContext(contextId!);\n\n if (!this.#isCurrent && this.#activeContexts.size === 0) {\n this.#logger.debug('no active contexts, shutting down connection');\n break;\n }\n }\n }\n\n // Throw any error that occurred\n if (errorFuture.done) {\n throw await errorFuture.await;\n }\n } finally {\n reader.releaseLock();\n cleanup();\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'recv loop error');\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(e instanceof Error ? e : new Error(String(e)));\n }\n this.#contextData.clear();\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n #cleanupContext(contextId: string): void {\n this.#contextData.delete(contextId);\n this.#activeContexts.delete(contextId);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#inputQueueResolver?.();\n\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(new APIStatusError({ message: 'connection closed' }));\n }\n this.#contextData.clear();\n\n if (this.#ws) {\n this.#ws.close();\n this.#ws = null;\n }\n\n if (this.#sendTask) {\n await this.#sendTask.catch(() => {});\n }\n if (this.#recvTask) {\n await this.#recvTask.catch(() => {});\n }\n }\n}\n\nexport class TTS extends tts.TTS {\n #opts: ResolvedTTSOptions;\n #streams = new Set<SynthesizeStream>();\n #currentConnection: Connection | null = null;\n #connectionLock = new Mutex();\n #logger = log();\n\n label = 'elevenlabs.TTS';\n\n constructor(opts: TTSOptions = {}) {\n const autoMode = opts.autoMode ?? true;\n const encoding = opts.encoding ?? DEFAULT_ENCODING;\n const sampleRate = sampleRateFromFormat(encoding);\n\n super(sampleRate, 1, {\n streaming: true,\n });\n\n const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'ElevenLabs API key is required, either as argument or set ELEVEN_API_KEY environmental variable',\n );\n }\n\n let wordTokenizer = opts.wordTokenizer;\n if (!wordTokenizer) {\n wordTokenizer = autoMode\n ? new tokenize.basic.SentenceTokenizer()\n : new tokenize.basic.WordTokenizer(false);\n } else if (autoMode && !(wordTokenizer instanceof tokenize.SentenceTokenizer)) {\n this.#logger.warn(\n 'autoMode is enabled, it expects full sentences or phrases, ' +\n 'please provide a SentenceTokenizer instead of a WordTokenizer.',\n );\n }\n\n // Handle legacy options for backward compatibility\n const voiceId = opts.voiceId ?? opts.voice?.id ?? DEFAULT_VOICE_ID;\n const voiceSettings = opts.voiceSettings ?? opts.voice?.settings;\n const model = opts.model ?? opts.modelID ?? 'eleven_turbo_v2_5';\n const language = opts.language ?? opts.languageCode;\n\n this.#opts = {\n apiKey,\n voiceId,\n voiceSettings,\n model,\n language,\n baseURL: opts.baseURL ?? API_BASE_URL_V1,\n encoding,\n sampleRate,\n streamingLatency: opts.streamingLatency,\n wordTokenizer,\n chunkLengthSchedule: opts.chunkLengthSchedule,\n enableSsmlParsing: opts.enableSsmlParsing ?? false,\n enableLogging: opts.enableLogging ?? true,\n inactivityTimeout: opts.inactivityTimeout ?? WS_INACTIVITY_TIMEOUT,\n syncAlignment: opts.syncAlignment ?? true,\n applyTextNormalization: opts.applyTextNormalization ?? 'auto',\n preferredAlignment: opts.preferredAlignment ?? 'normalized',\n autoMode,\n pronunciationDictionaryLocators: opts.pronunciationDictionaryLocators,\n };\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'ElevenLabs';\n }\n\n async listVoices(): Promise<Voice[]> {\n const response = await fetch(`${this.#opts.baseURL}/voices`, {\n headers: { [AUTHORIZATION_HEADER]: this.#opts.apiKey },\n });\n const data = (await response.json()) as {\n voices: { voice_id: string; name: string; category: string }[];\n };\n return data.voices.map((v) => ({\n id: v.voice_id,\n name: v.name,\n category: v.category,\n }));\n }\n\n updateOptions(opts: {\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n }): void {\n let changed = false;\n\n if (opts.model !== undefined && opts.model !== this.#opts.model) {\n this.#opts.model = opts.model;\n changed = true;\n }\n\n if (opts.voiceId !== undefined && opts.voiceId !== this.#opts.voiceId) {\n this.#opts.voiceId = opts.voiceId;\n changed = true;\n }\n\n if (opts.voiceSettings !== undefined) {\n this.#opts.voiceSettings = opts.voiceSettings;\n changed = true;\n }\n\n if (opts.language !== undefined && opts.language !== this.#opts.language) {\n this.#opts.language = opts.language;\n changed = true;\n }\n\n if (opts.pronunciationDictionaryLocators !== undefined) {\n this.#opts.pronunciationDictionaryLocators = opts.pronunciationDictionaryLocators;\n changed = true;\n }\n\n if (changed && this.#currentConnection) {\n this.#currentConnection.markNonCurrent();\n this.#currentConnection = null;\n }\n }\n\n async currentConnection(): Promise<Connection> {\n const unlock = await this.#connectionLock.lock();\n try {\n if (\n this.#currentConnection &&\n this.#currentConnection.isCurrent &&\n !this.#currentConnection.closed\n ) {\n return this.#currentConnection;\n }\n\n const conn = new Connection({ ...this.#opts });\n await conn.connect();\n this.#currentConnection = conn;\n return conn;\n } finally {\n unlock();\n }\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(this, text, { ...this.#opts });\n }\n\n stream(): SynthesizeStream {\n const stream = new SynthesizeStream(this, { ...this.#opts });\n this.#streams.add(stream);\n return stream;\n }\n\n async close(): Promise<void> {\n for (const stream of this.#streams) {\n stream.close();\n }\n this.#streams.clear();\n\n if (this.#currentConnection) {\n await this.#currentConnection.close();\n this.#currentConnection = null;\n }\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #logger = log();\n\n label = 'elevenlabs.ChunkedStream';\n\n constructor(tts: TTS, text: string, opts: ResolvedTTSOptions) {\n super(text, tts);\n this.#tts = tts;\n this.#opts = opts;\n }\n\n protected async run(): Promise<void> {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : undefined;\n\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n try {\n const response = await fetch(synthesizeUrl(this.#opts), {\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n text: this.inputText,\n model_id: this.#opts.model,\n voice_settings: voiceSettings,\n }),\n signal: this.abortSignal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new APIStatusError({\n message: `ElevenLabs API error: ${errorText}`,\n options: { statusCode: response.status },\n });\n }\n\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.startsWith('audio/')) {\n const content = await response.text();\n throw new APIError(`ElevenLabs returned non-audio data: ${content}`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new APIError('No response body');\n }\n\n let lastFrame: AudioFrame | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n for (const frame of bstream.write(value.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Flush remaining data\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: true });\n }\n } catch (e) {\n if (e instanceof APIError) {\n throw e;\n }\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw new APIConnectionError({ message: `Connection error: ${e}` });\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #contextId: string;\n #sentTokenizerStream: tokenize.SentenceStream | tokenize.WordStream;\n #logger = log();\n #audioQueue: Buffer[] = [];\n #timedTranscriptQueue: TimedString[] = [];\n #streamDone = false;\n\n label = 'elevenlabs.SynthesizeStream';\n\n constructor(tts: TTS, opts: ResolvedTTSOptions) {\n super(tts);\n this.#tts = tts;\n this.#opts = opts;\n this.#contextId = shortuuid();\n this.#sentTokenizerStream = this.#opts.wordTokenizer.stream();\n }\n\n get contextId(): string {\n return this.#contextId;\n }\n\n pushAudio(data: Buffer): void {\n // Don't push if stream is closed/aborted\n if (this.closed || this.abortController.signal.aborted) {\n return;\n }\n this.#audioQueue.push(data);\n }\n\n pushTimedTranscript(timedWords: TimedString[]): void {\n this.#timedTranscriptQueue.push(...timedWords);\n }\n\n markDone(): void {\n this.#streamDone = true;\n }\n\n protected async run(): Promise<void> {\n const requestId = this.#contextId;\n const segmentId = this.#contextId;\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n let connection: Connection;\n try {\n connection = await this.#tts.currentConnection();\n } catch (e) {\n throw new APIConnectionError({ message: 'could not connect to ElevenLabs' });\n }\n\n let waiterReject: ((reason: Error) => void) | undefined;\n const waiterPromise = new Promise<void>((resolve, reject) => {\n waiterReject = reject;\n connection.registerStream(this, { resolve, reject });\n });\n\n // Handle abort - reject the waiter so Promise.all can complete\n const abortHandler = () => {\n if (waiterReject) {\n waiterReject(new Error('Stream aborted'));\n }\n };\n this.abortController.signal.addEventListener('abort', abortHandler, { once: true });\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#sentTokenizerStream.flush();\n continue;\n }\n this.#sentTokenizerStream.pushText(data);\n }\n this.#sentTokenizerStream.endInput();\n };\n\n const sentenceStreamTask = async () => {\n const flushOnChunk =\n this.#opts.wordTokenizer instanceof tokenize.SentenceTokenizer && this.#opts.autoMode;\n\n let xmlContent: string[] = [];\n\n for await (const data of this.#sentTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n let text = data.token;\n const xmlStartTokens = ['<phoneme', '<break'];\n const xmlEndTokens = ['</phoneme>', '/>'];\n\n if (\n (this.#opts.enableSsmlParsing &&\n xmlStartTokens.some((start) => text.startsWith(start))) ||\n xmlContent.length > 0\n ) {\n xmlContent.push(text);\n\n if (xmlEndTokens.some((end) => text.includes(end))) {\n text = xmlContent.join(' ');\n xmlContent = [];\n } else {\n continue;\n }\n }\n\n const formattedText = `${text} `; // must always end with a space\n connection.sendContent({\n contextId: this.#contextId,\n text: formattedText,\n flush: flushOnChunk,\n });\n }\n\n if (xmlContent.length > 0) {\n this.#logger.warn('ElevenLabs stream ended with incomplete xml content');\n }\n\n // Send final empty text to signal end of input\n connection.sendContent({ contextId: this.#contextId, text: '', flush: true });\n connection.closeContext(this.#contextId);\n };\n\n const audioProcessTask = async () => {\n let lastFrame: AudioFrame | undefined;\n\n while (!this.abortController.signal.aborted) {\n // Process audio queue\n while (this.#audioQueue.length > 0) {\n const audioData = this.#audioQueue.shift()!;\n for (const frame of bstream.write(audioData.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Exit when stream is done and queue is empty\n if (this.#streamDone && this.#audioQueue.length === 0) {\n break;\n }\n\n // Small delay to avoid busy waiting\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n // Flush remaining\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: true });\n }\n };\n\n try {\n await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);\n } catch (e) {\n // If aborted, this is a normal termination - don't throw\n if (this.abortController.signal.aborted) {\n return;\n }\n\n if (e instanceof APITimeoutError) {\n throw e;\n }\n if (e instanceof APIStatusError) {\n throw e;\n }\n throw new APIStatusError({ message: 'Could not synthesize' });\n } finally {\n // Clean up abort listener\n this.abortController.signal.removeEventListener('abort', abortHandler);\n }\n }\n\n close(): void {\n // Clear audio buffers to prevent memory leak\n this.#audioQueue.length = 0;\n this.#timedTranscriptQueue.length = 0;\n this.#streamDone = true;\n this.#sentTokenizerStream.close();\n super.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAaO;AACP,mBAAsB;AAEtB,gBAA0B;AAK1B,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AACxB,MAAM,uBAAuB;AAC7B,MAAM,wBAAwB;AAC9B,MAAM,mBAAgC;AAkGtC,SAAS,qBAAqB,UAA+B;AAC3D,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,SAAO,SAAS,MAAM,CAAC,GAAI,EAAE;AAC/B;AAEA,SAAS,cAAc,MAAkC;AACvD,QAAM,EAAE,SAAS,SAAS,OAAO,UAAU,iBAAiB,IAAI;AAChE,MAAI,MAAM,GAAG,OAAO,mBAAmB,OAAO,oBAAoB,KAAK,kBAAkB,QAAQ;AACjG,MAAI,qBAAqB,QAAW;AAClC,WAAO,+BAA+B,gBAAgB;AAAA,EACxD;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAkC;AACxD,QAAM,UAAU,KAAK,QAAQ,QAAQ,YAAY,QAAQ,EAAE,QAAQ,WAAW,OAAO;AACrF,QAAM,SAAmB,CAAC;AAC1B,SAAO,KAAK,YAAY,KAAK,KAAK,EAAE;AACpC,SAAO,KAAK,iBAAiB,KAAK,QAAQ,EAAE;AAC5C,MAAI,KAAK,UAAU;AACjB,WAAO,KAAK,iBAAiB,KAAK,QAAQ,EAAE;AAAA,EAC9C;AACA,SAAO,KAAK,uBAAuB,KAAK,iBAAiB,EAAE;AAC3D,SAAO,KAAK,kBAAkB,KAAK,aAAa,EAAE;AAClD,SAAO,KAAK,sBAAsB,KAAK,iBAAiB,EAAE;AAC1D,SAAO,KAAK,4BAA4B,KAAK,sBAAsB,EAAE;AACrE,MAAI,KAAK,eAAe;AACtB,WAAO,KAAK,qBAAqB;AAAA,EACnC;AACA,MAAI,KAAK,aAAa,QAAW;AAC/B,WAAO,KAAK,aAAa,KAAK,QAAQ,EAAE;AAAA,EAC1C;AACA,SAAO,GAAG,OAAO,mBAAmB,KAAK,OAAO,uBAAuB,OAAO,KAAK,GAAG,CAAC;AACzF;AAEA,SAAS,eAAiC,KAAoB;AAC5D,QAAM,SAAqB,CAAC;AAC5B,aAAW,OAAO,KAAK;AACrB,QAAI,IAAI,GAAG,MAAM,QAAW;AAC1B,aAAO,GAAG,IAAI,IAAI,GAAG;AAAA,IACvB;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,aACP,MACA,cACA,aACA,QAAiB,OACQ;AACzB,MAAI,CAAC,QAAQ,aAAa,WAAW,KAAK,YAAY,WAAW,GAAG;AAClE,WAAO,CAAC,CAAC,GAAG,QAAQ,EAAE;AAAA,EACxB;AAEA,QAAM,gBAAgB,aAAa,aAAa,SAAS,CAAC;AAC1D,QAAM,eAAe,YAAY,YAAY,SAAS,CAAC;AACvD,QAAM,aAAa,CAAC,GAAG,cAAc,gBAAgB,YAAY;AAEjE,QAAM,QAAQ,uBAAS,MAAM,WAAW,MAAM,KAAK;AACnD,QAAM,aAA4B,CAAC;AAEnC,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,CAAC,CAAC,GAAG,IAAI;AAAA,EAClB;AAEA,QAAM,eAAe,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAC1C,MAAI,MAAM;AAGV,WAAS,IAAI,GAAG,IAAI,aAAa,SAAS,GAAG,KAAK;AAChD,UAAM,QAAQ,aAAa,CAAC;AAC5B,UAAM,YAAY,aAAa,IAAI,CAAC;AACpC,UAAM;AACN,UAAM,UAAU,WAAW,KAAK,KAAK,KAAK;AAC1C,UAAM,QAAQ,WAAW,SAAS,KAAK,KAAK;AAC5C,eAAW,KAAK;AAAA,MACd,MAAM,KAAK,MAAM,OAAO,SAAS;AAAA,MACjC,WAAW;AAAA,MACX,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,UAAM,gBAAgB,aAAa,aAAa,SAAS,CAAC;AAC1D,UAAM,UAAU,WAAW,aAAa,KAAK,KAAK;AAClD,UAAM,QAAQ,WAAW,WAAW,SAAS,CAAC,KAAK,KAAK;AACxD,eAAW,KAAK;AAAA,MACd,MAAM,KAAK,MAAM,aAAa;AAAA,MAC9B,WAAW;AAAA,MACX,SAAS;AAAA,IACX,CAAC;AACD,UAAM,KAAK;AAAA,EACb,WAAW,MAAM,SAAS,GAAG;AAC3B,UAAM,aAAa,aAAa,SAAS,CAAC;AAAA,EAC5C;AAEA,SAAO,CAAC,YAAY,KAAK,MAAM,GAAG,CAAC;AACrC;AAEA,MAAM,WAAW;AAAA,EACf;AAAA,EACA,MAAwB;AAAA,EACxB,aAAa;AAAA,EACb,kBAAkB,oBAAI,IAAY;AAAA,EAClC,cAAmC,CAAC;AAAA,EACpC,eAAe,oBAAI,IAAwB;AAAA,EAC3C,YAAkC;AAAA,EAClC,YAAkC;AAAA,EAClC,UAAU;AAAA,EACV,cAAU,mBAAI;AAAA,EACd,sBAA2C;AAAA,EAE3C,YAAY,MAA0B;AACpC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,iBAAuB;AACrB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,OAAO,KAAK,SAAS;AAC5B;AAAA,IACF;AAEA,UAAM,MAAM,eAAe,KAAK,KAAK;AACrC,UAAM,UAAU,EAAE,CAAC,oBAAoB,GAAG,KAAK,MAAM,OAAO;AAE5D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAK,MAAM,IAAI,oBAAU,KAAK,EAAE,QAAQ,CAAC;AAEzC,WAAK,IAAI,GAAG,QAAQ,MAAM;AACxB,aAAK,YAAY,KAAK,UAAU;AAChC,aAAK,YAAY,KAAK,UAAU;AAChC,gBAAQ;AAAA,MACV,CAAC;AAED,WAAK,IAAI,GAAG,SAAS,CAAC,UAAU;AAC9B,aAAK,QAAQ,MAAM,EAAE,MAAM,GAAG,4BAA4B;AAC1D,eAAO,IAAI,iCAAmB,EAAE,SAAS,oBAAoB,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MACjF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,eACEA,SACA,QACM;AACN,UAAM,YAAYA,QAAO;AACzB,SAAK,aAAa,IAAI,WAAW;AAAA,MAC/B,QAAAA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,aAAa,CAAC;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,YAAY,SAAkC;AA7ShD;AA8SI,QAAI,KAAK,WAAW,CAAC,KAAK,OAAO,KAAK,IAAI,eAAe,oBAAU,MAAM;AACvE,YAAM,IAAI,iCAAmB,EAAE,SAAS,iCAAiC,CAAC;AAAA,IAC5E;AACA,SAAK,YAAY,KAAK,OAAO;AAC7B,eAAK,wBAAL;AAAA,EACF;AAAA,EAEA,aAAa,WAAyB;AArTxC;AAsTI,QAAI,KAAK,WAAW,CAAC,KAAK,OAAO,KAAK,IAAI,eAAe,oBAAU,MAAM;AACvE,YAAM,IAAI,iCAAmB,EAAE,SAAS,iCAAiC,CAAC;AAAA,IAC5E;AACA,SAAK,YAAY,KAAK,EAAE,UAAU,CAAC;AACnC,eAAK,wBAAL;AAAA,EACF;AAAA,EAEA,MAAM,YAA2B;AAC/B,QAAI;AACF,aAAO,CAAC,KAAK,SAAS;AAEpB,YAAI,KAAK,YAAY,WAAW,GAAG;AACjC,gBAAM,IAAI,QAAc,CAAC,YAAY;AACnC,iBAAK,sBAAsB;AAAA,UAC7B,CAAC;AACD,eAAK,sBAAsB;AAAA,QAC7B;AAEA,YAAI,KAAK,QAAS;AAElB,cAAM,MAAM,KAAK,YAAY,MAAM;AACnC,YAAI,CAAC,IAAK;AAEV,YAAI,CAAC,KAAK,OAAO,KAAK,IAAI,eAAe,oBAAU,MAAM;AACvD;AAAA,QACF;AAEA,YAAI,UAAU,KAAK;AAEjB,gBAAM,UAAU;AAChB,gBAAM,eAAe,CAAC,KAAK,gBAAgB,IAAI,QAAQ,SAAS;AAGhE,cAAI,CAAC,KAAK,cAAc,cAAc;AACpC;AAAA,UACF;AAEA,cAAI,cAAc;AAChB,kBAAM,gBAAgB,KAAK,MAAM,gBAC7B,eAAe,KAAK,MAAM,aAAa,IACvC,CAAC;AAEL,kBAAM,UAAmC;AAAA,cACvC,MAAM;AAAA,cACN,gBAAgB;AAAA,cAChB,YAAY,QAAQ;AAAA,YACtB;AAEA,gBAAI,KAAK,MAAM,iCAAiC;AAC9C,sBAAQ,oCACN,KAAK,MAAM,gCAAgC,IAAI,CAAC,aAAa;AAAA,gBAC3D,6BAA6B,QAAQ;AAAA,gBACrC,YAAY,QAAQ;AAAA,cACtB,EAAE;AAAA,YACN;AAEA,kBAAM,aAAa,KAAK,UAAU,OAAO;AACzC,iBAAK,IAAI,KAAK,UAAU;AACxB,iBAAK,gBAAgB,IAAI,QAAQ,SAAS;AAAA,UAC5C;AAEA,gBAAM,MAA+B;AAAA,YACnC,MAAM,QAAQ;AAAA,YACd,YAAY,QAAQ;AAAA,UACtB;AACA,cAAI,QAAQ,OAAO;AACjB,gBAAI,QAAQ;AAAA,UACd;AAEA,gBAAM,SAAS,KAAK,UAAU,GAAG;AACjC,eAAK,IAAI,KAAK,MAAM;AAAA,QACtB,OAAO;AAEL,gBAAM,WAAW;AACjB,cAAI,KAAK,gBAAgB,IAAI,SAAS,SAAS,GAAG;AAChD,kBAAM,WAAW;AAAA,cACf,YAAY,SAAS;AAAA,cACrB,eAAe;AAAA,YACjB;AACA,kBAAM,cAAc,KAAK,UAAU,QAAQ;AAC3C,iBAAK,IAAI,KAAK,WAAW;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,GAAG;AACV,WAAK,QAAQ,KAAK,EAAE,OAAO,EAAE,GAAG,iBAAiB;AAAA,IACnD,UAAE;AACA,UAAI,CAAC,KAAK,SAAS;AACjB,cAAM,KAAK,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,YAA2B;AAC/B,QAAI;AACF,YAAM,iBAAiB,qBAAO,oBAA6C;AAC3E,YAAM,cAAc,IAAI,qBAAc;AAEtC,YAAM,YAAY,CAAC,YAAoB;AACrC,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,QAAQ,SAAS,CAAC;AAC5C,yBAAe,MAAM,MAAM;AAAA,QAC7B,SAAS,GAAG;AACV,eAAK,QAAQ,KAAK,EAAE,OAAO,EAAE,GAAG,mCAAmC;AAAA,QACrE;AAAA,MACF;AAEA,YAAM,UAAU,MAAM;AACpB,YAAI,CAAC,KAAK,WAAW,KAAK,aAAa,OAAO,GAAG;AAC/C,eAAK,QAAQ,KAAK,+BAA+B;AAAA,QACnD;AACA,uBAAe,MAAM;AAAA,MACvB;AAEA,YAAM,UAAU,CAAC,UAAiB;AAChC,oBAAY,QAAQ,KAAK;AACzB,uBAAe,MAAM;AAAA,MACvB;AAGA,UAAI,CAAC,KAAK,IAAK;AACf,WAAK,IAAI,GAAG,WAAW,SAAS;AAChC,WAAK,IAAI,GAAG,SAAS,OAAO;AAC5B,WAAK,IAAI,GAAG,SAAS,OAAO;AAE5B,YAAM,UAAU,MAAM;AAnb5B;AAobQ,mBAAK,QAAL,mBAAU,IAAI,WAAW;AACzB,mBAAK,QAAL,mBAAU,IAAI,SAAS;AACvB,mBAAK,QAAL,mBAAU,IAAI,SAAS;AAAA,MACzB;AAEA,YAAM,SAAS,eAAe,OAAO,EAAE,UAAU;AACjD,UAAI;AACF,eAAO,CAAC,KAAK,SAAS;AACpB,gBAAM,SAAS,MAAM,OAAO,KAAK;AACjC,cAAI,OAAO,QAAQ,KAAK,QAAS;AAEjC,gBAAM,OAAO,OAAO;AACpB,gBAAM,YAAY,KAAK;AACvB,gBAAM,MAAM,YAAY,KAAK,aAAa,IAAI,SAAS,IAAI;AAE3D,cAAI,KAAK,OAAO;AACd,iBAAK,QAAQ;AAAA,cACX,EAAE,YAAY,WAAW,OAAO,KAAK,OAAO,KAAK;AAAA,cACjD;AAAA,YACF;AACA,gBAAI,WAAW;AACb,kBAAI,KAAK;AACP,oBAAI,OAAO,OAAO,IAAI,uBAAS,KAAK,KAAe,CAAC;AAAA,cACtD;AACA,mBAAK,gBAAgB,SAAS;AAAA,YAChC;AACA;AAAA,UACF;AAEA,cAAI,CAAC,KAAK;AACR,iBAAK,QAAQ,KAAK,EAAE,KAAK,GAAG,iDAAiD;AAC7E;AAAA,UACF;AAEA,gBAAMA,UAAS,IAAI;AAGnB,gBAAM,YACJ,KAAK,MAAM,uBAAuB,eAC7B,KAAK,sBACL,KAAK;AAEZ,cAAI,aAAaA,SAAQ;AACvB,kBAAM,QAAQ,UAAU;AACxB,kBAAM,SAAU,UAAU,oBAAoB,UAAU;AAGxD,kBAAM,OAAQ,UAAU,mBAAmB,UAAU;AAIrD,gBACE,SACA,UACA,QACA,MAAM,WAAW,KAAK,UACtB,OAAO,WAAW,KAAK,QACvB;AACA,kBAAI,cAAc,MAAM,KAAK,EAAE;AAG/B,uBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,sBAAM,OAAO,MAAM,CAAC;AACpB,sBAAM,QAAQ,OAAO,CAAC;AACtB,sBAAM,MAAM,KAAK,CAAC;AAElB,oBAAI,KAAK,SAAS,GAAG;AACnB,2BAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,wBAAI,aAAa,KAAK,KAAK;AAC3B,wBAAI,YAAY,KAAK,CAAC;AAAA,kBACxB;AAAA,gBACF;AACA,oBAAI,aAAa,KAAK,KAAK;AAC3B,oBAAI,YAAY,KAAK,GAAG;AAAA,cAC1B;AAEA,oBAAM,CAAC,YAAY,aAAa,IAAI;AAAA,gBAClC,IAAI;AAAA,gBACJ,IAAI;AAAA,gBACJ,IAAI;AAAA,cACN;AAEA,kBAAI,WAAW,SAAS,GAAG;AACzB,gBAAAA,QAAO,oBAAoB,UAAU;AAAA,cACvC;AAEA,kBAAI,aAAa;AACjB,kBAAI,eAAe,IAAI,aAAa,MAAM,CAAC,cAAc,MAAM;AAC/D,kBAAI,cAAc,IAAI,YAAY,MAAM,CAAC,cAAc,MAAM;AAAA,YAC/D;AAAA,UACF;AAEA,cAAI,KAAK,OAAO;AACd,kBAAM,YAAY,OAAO,KAAK,KAAK,OAAiB,QAAQ;AAC5D,YAAAA,QAAO,UAAU,SAAS;AAAA,UAC5B;AAEA,cAAI,KAAK,SAAS;AAEhB,gBAAI,IAAI,YAAY;AAClB,oBAAM,CAAC,UAAU,IAAI;AAAA,gBACnB,IAAI;AAAA,gBACJ,IAAI;AAAA,gBACJ,IAAI;AAAA,gBACJ;AAAA,cACF;AACA,kBAAI,WAAW,SAAS,GAAG;AACzB,gBAAAA,QAAO,oBAAoB,UAAU;AAAA,cACvC;AAAA,YACF;AAEA,YAAAA,QAAO,SAAS;AAChB,gBAAI,OAAO,QAAQ;AACnB,iBAAK,gBAAgB,SAAU;AAE/B,gBAAI,CAAC,KAAK,cAAc,KAAK,gBAAgB,SAAS,GAAG;AACvD,mBAAK,QAAQ,MAAM,8CAA8C;AACjE;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,YAAI,YAAY,MAAM;AACpB,gBAAM,MAAM,YAAY;AAAA,QAC1B;AAAA,MACF,UAAE;AACA,eAAO,YAAY;AACnB,gBAAQ;AAAA,MACV;AAAA,IACF,SAAS,GAAG;AACV,WAAK,QAAQ,KAAK,EAAE,OAAO,EAAE,GAAG,iBAAiB;AACjD,iBAAW,OAAO,KAAK,aAAa,OAAO,GAAG;AAC5C,YAAI,OAAO,OAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MACjE;AACA,WAAK,aAAa,MAAM;AAAA,IAC1B,UAAE;AACA,UAAI,CAAC,KAAK,SAAS;AACjB,cAAM,KAAK,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,gBAAgB,WAAyB;AACvC,SAAK,aAAa,OAAO,SAAS;AAClC,SAAK,gBAAgB,OAAO,SAAS;AAAA,EACvC;AAAA,EAEA,MAAM,QAAuB;AAxkB/B;AAykBI,QAAI,KAAK,SAAS;AAChB;AAAA,IACF;AAEA,SAAK,UAAU;AACf,eAAK,wBAAL;AAEA,eAAW,OAAO,KAAK,aAAa,OAAO,GAAG;AAC5C,UAAI,OAAO,OAAO,IAAI,6BAAe,EAAE,SAAS,oBAAoB,CAAC,CAAC;AAAA,IACxE;AACA,SAAK,aAAa,MAAM;AAExB,QAAI,KAAK,KAAK;AACZ,WAAK,IAAI,MAAM;AACf,WAAK,MAAM;AAAA,IACb;AAEA,QAAI,KAAK,WAAW;AAClB,YAAM,KAAK,UAAU,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACrC;AACA,QAAI,KAAK,WAAW;AAClB,YAAM,KAAK,UAAU,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACrC;AAAA,EACF;AACF;AAEO,MAAM,YAAY,kBAAI,IAAI;AAAA,EAC/B;AAAA,EACA,WAAW,oBAAI,IAAsB;AAAA,EACrC,qBAAwC;AAAA,EACxC,kBAAkB,IAAI,mBAAM;AAAA,EAC5B,cAAU,mBAAI;AAAA,EAEd,QAAQ;AAAA,EAER,YAAY,OAAmB,CAAC,GAAG;AA5mBrC;AA6mBI,UAAM,WAAW,KAAK,YAAY;AAClC,UAAM,WAAW,KAAK,YAAY;AAClC,UAAM,aAAa,qBAAqB,QAAQ;AAEhD,UAAM,YAAY,GAAG;AAAA,MACnB,WAAW;AAAA,IACb,CAAC;AAED,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,gBAAgB,KAAK;AACzB,QAAI,CAAC,eAAe;AAClB,sBAAgB,WACZ,IAAI,uBAAS,MAAM,kBAAkB,IACrC,IAAI,uBAAS,MAAM,cAAc,KAAK;AAAA,IAC5C,WAAW,YAAY,EAAE,yBAAyB,uBAAS,oBAAoB;AAC7E,WAAK,QAAQ;AAAA,QACX;AAAA,MAEF;AAAA,IACF;AAGA,UAAM,UAAU,KAAK,aAAW,UAAK,UAAL,mBAAY,OAAM;AAClD,UAAM,gBAAgB,KAAK,mBAAiB,UAAK,UAAL,mBAAY;AACxD,UAAM,QAAQ,KAAK,SAAS,KAAK,WAAW;AAC5C,UAAM,WAAW,KAAK,YAAY,KAAK;AAEvC,SAAK,QAAQ;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,KAAK,WAAW;AAAA,MACzB;AAAA,MACA;AAAA,MACA,kBAAkB,KAAK;AAAA,MACvB;AAAA,MACA,qBAAqB,KAAK;AAAA,MAC1B,mBAAmB,KAAK,qBAAqB;AAAA,MAC7C,eAAe,KAAK,iBAAiB;AAAA,MACrC,mBAAmB,KAAK,qBAAqB;AAAA,MAC7C,eAAe,KAAK,iBAAiB;AAAA,MACrC,wBAAwB,KAAK,0BAA0B;AAAA,MACvD,oBAAoB,KAAK,sBAAsB;AAAA,MAC/C;AAAA,MACA,iCAAiC,KAAK;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,IAAI,WAAmB;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAA+B;AACnC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,MAAM,OAAO,WAAW;AAAA,MAC3D,SAAS,EAAE,CAAC,oBAAoB,GAAG,KAAK,MAAM,OAAO;AAAA,IACvD,CAAC;AACD,UAAM,OAAQ,MAAM,SAAS,KAAK;AAGlC,WAAO,KAAK,OAAO,IAAI,CAAC,OAAO;AAAA,MAC7B,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,IACd,EAAE;AAAA,EACJ;AAAA,EAEA,cAAc,MAML;AACP,QAAI,UAAU;AAEd,QAAI,KAAK,UAAU,UAAa,KAAK,UAAU,KAAK,MAAM,OAAO;AAC/D,WAAK,MAAM,QAAQ,KAAK;AACxB,gBAAU;AAAA,IACZ;AAEA,QAAI,KAAK,YAAY,UAAa,KAAK,YAAY,KAAK,MAAM,SAAS;AACrE,WAAK,MAAM,UAAU,KAAK;AAC1B,gBAAU;AAAA,IACZ;AAEA,QAAI,KAAK,kBAAkB,QAAW;AACpC,WAAK,MAAM,gBAAgB,KAAK;AAChC,gBAAU;AAAA,IACZ;AAEA,QAAI,KAAK,aAAa,UAAa,KAAK,aAAa,KAAK,MAAM,UAAU;AACxE,WAAK,MAAM,WAAW,KAAK;AAC3B,gBAAU;AAAA,IACZ;AAEA,QAAI,KAAK,oCAAoC,QAAW;AACtD,WAAK,MAAM,kCAAkC,KAAK;AAClD,gBAAU;AAAA,IACZ;AAEA,QAAI,WAAW,KAAK,oBAAoB;AACtC,WAAK,mBAAmB,eAAe;AACvC,WAAK,qBAAqB;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAM,oBAAyC;AAC7C,UAAM,SAAS,MAAM,KAAK,gBAAgB,KAAK;AAC/C,QAAI;AACF,UACE,KAAK,sBACL,KAAK,mBAAmB,aACxB,CAAC,KAAK,mBAAmB,QACzB;AACA,eAAO,KAAK;AAAA,MACd;AAEA,YAAM,OAAO,IAAI,WAAW,EAAE,GAAG,KAAK,MAAM,CAAC;AAC7C,YAAM,KAAK,QAAQ;AACnB,WAAK,qBAAqB;AAC1B,aAAO;AAAA,IACT,UAAE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,WAAW,MAA6B;AACtC,WAAO,IAAI,cAAc,MAAM,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC;AAAA,EACxD;AAAA,EAEA,SAA2B;AACzB,UAAMA,UAAS,IAAI,iBAAiB,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC;AAC3D,SAAK,SAAS,IAAIA,OAAM;AACxB,WAAOA;AAAA,EACT;AAAA,EAEA,MAAM,QAAuB;AAC3B,eAAWA,WAAU,KAAK,UAAU;AAClC,MAAAA,QAAO,MAAM;AAAA,IACf;AACA,SAAK,SAAS,MAAM;AAEpB,QAAI,KAAK,oBAAoB;AAC3B,YAAM,KAAK,mBAAmB,MAAM;AACpC,WAAK,qBAAqB;AAAA,IAC5B;AAAA,EACF;AACF;AAEO,MAAM,sBAAsB,kBAAI,cAAc;AAAA,EACnD;AAAA,EACA;AAAA,EACA,cAAU,mBAAI;AAAA,EAEd,QAAQ;AAAA,EAER,YAAYC,MAAU,MAAc,MAA0B;AAC5D,UAAM,MAAMA,IAAG;AACf,SAAK,OAAOA;AACZ,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAgB,MAAqB;AA3xBvC;AA4xBI,UAAM,gBAAgB,KAAK,MAAM,gBAC7B,eAAe,KAAK,MAAM,aAAa,IACvC;AAEJ,UAAM,gBAAY,yBAAU;AAC5B,UAAM,UAAU,IAAI,8BAAgB,KAAK,MAAM,YAAY,CAAC;AAE5D,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,cAAc,KAAK,KAAK,GAAG;AAAA,QACtD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,CAAC,oBAAoB,GAAG,KAAK,MAAM;AAAA,UACnC,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,MAAM,KAAK;AAAA,UACX,UAAU,KAAK,MAAM;AAAA,UACrB,gBAAgB;AAAA,QAClB,CAAC;AAAA,QACD,QAAQ,KAAK;AAAA,MACf,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI,6BAAe;AAAA,UACvB,SAAS,yBAAyB,SAAS;AAAA,UAC3C,SAAS,EAAE,YAAY,SAAS,OAAO;AAAA,QACzC,CAAC;AAAA,MACH;AAEA,YAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,UAAI,CAAC,YAAY,WAAW,QAAQ,GAAG;AACrC,cAAM,UAAU,MAAM,SAAS,KAAK;AACpC,cAAM,IAAI,uBAAS,uCAAuC,OAAO,EAAE;AAAA,MACrE;AAEA,YAAM,UAAS,cAAS,SAAT,mBAAe;AAC9B,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,uBAAS,kBAAkB;AAAA,MACvC;AAEA,UAAI;AAEJ,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,mBAAW,SAAS,QAAQ,MAAM,MAAM,MAAM,GAAG;AAC/C,cAAI,WAAW;AACb,iBAAK,MAAM,IAAI,EAAE,WAAW,WAAW,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC;AAAA,UACpF;AACA,sBAAY;AAAA,QACd;AAAA,MACF;AAGA,iBAAW,SAAS,QAAQ,MAAM,GAAG;AACnC,YAAI,WAAW;AACb,eAAK,MAAM,IAAI,EAAE,WAAW,WAAW,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC;AAAA,QACpF;AACA,oBAAY;AAAA,MACd;AAEA,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,WAAW,OAAO,WAAW,OAAO,KAAK,CAAC;AAAA,MACnF;AAAA,IACF,SAAS,GAAG;AACV,UAAI,aAAa,wBAAU;AACzB,cAAM;AAAA,MACR;AACA,UAAI,aAAa,SAAS,EAAE,SAAS,cAAc;AACjD;AAAA,MACF;AACA,YAAM,IAAI,iCAAmB,EAAE,SAAS,qBAAqB,CAAC,GAAG,CAAC;AAAA,IACpE;AAAA,EACF;AACF;AAEO,MAAM,yBAAyB,kBAAI,iBAAiB;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAU,mBAAI;AAAA,EACd,cAAwB,CAAC;AAAA,EACzB,wBAAuC,CAAC;AAAA,EACxC,cAAc;AAAA,EAEd,QAAQ;AAAA,EAER,YAAYA,MAAU,MAA0B;AAC9C,UAAMA,IAAG;AACT,SAAK,OAAOA;AACZ,SAAK,QAAQ;AACb,SAAK,iBAAa,yBAAU;AAC5B,SAAK,uBAAuB,KAAK,MAAM,cAAc,OAAO;AAAA,EAC9D;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAU,MAAoB;AAE5B,QAAI,KAAK,UAAU,KAAK,gBAAgB,OAAO,SAAS;AACtD;AAAA,IACF;AACA,SAAK,YAAY,KAAK,IAAI;AAAA,EAC5B;AAAA,EAEA,oBAAoB,YAAiC;AACnD,SAAK,sBAAsB,KAAK,GAAG,UAAU;AAAA,EAC/C;AAAA,EAEA,WAAiB;AACf,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAgB,MAAqB;AACnC,UAAM,YAAY,KAAK;AACvB,UAAM,YAAY,KAAK;AACvB,UAAM,UAAU,IAAI,8BAAgB,KAAK,MAAM,YAAY,CAAC;AAE5D,QAAI;AACJ,QAAI;AACF,mBAAa,MAAM,KAAK,KAAK,kBAAkB;AAAA,IACjD,SAAS,GAAG;AACV,YAAM,IAAI,iCAAmB,EAAE,SAAS,kCAAkC,CAAC;AAAA,IAC7E;AAEA,QAAI;AACJ,UAAM,gBAAgB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,qBAAe;AACf,iBAAW,eAAe,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,IACrD,CAAC;AAGD,UAAM,eAAe,MAAM;AACzB,UAAI,cAAc;AAChB,qBAAa,IAAI,MAAM,gBAAgB,CAAC;AAAA,MAC1C;AAAA,IACF;AACA,SAAK,gBAAgB,OAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;AAElF,UAAM,YAAY,YAAY;AAC5B,uBAAiB,QAAQ,KAAK,OAAO;AACnC,YAAI,KAAK,gBAAgB,OAAO,QAAS;AACzC,YAAI,SAAS,iBAAiB,gBAAgB;AAC5C,eAAK,qBAAqB,MAAM;AAChC;AAAA,QACF;AACA,aAAK,qBAAqB,SAAS,IAAI;AAAA,MACzC;AACA,WAAK,qBAAqB,SAAS;AAAA,IACrC;AAEA,UAAM,qBAAqB,YAAY;AACrC,YAAM,eACJ,KAAK,MAAM,yBAAyB,uBAAS,qBAAqB,KAAK,MAAM;AAE/E,UAAI,aAAuB,CAAC;AAE5B,uBAAiB,QAAQ,KAAK,sBAAsB;AAClD,YAAI,KAAK,gBAAgB,OAAO,QAAS;AAEzC,YAAI,OAAO,KAAK;AAChB,cAAM,iBAAiB,CAAC,YAAY,QAAQ;AAC5C,cAAM,eAAe,CAAC,cAAc,IAAI;AAExC,YACG,KAAK,MAAM,qBACV,eAAe,KAAK,CAAC,UAAU,KAAK,WAAW,KAAK,CAAC,KACvD,WAAW,SAAS,GACpB;AACA,qBAAW,KAAK,IAAI;AAEpB,cAAI,aAAa,KAAK,CAAC,QAAQ,KAAK,SAAS,GAAG,CAAC,GAAG;AAClD,mBAAO,WAAW,KAAK,GAAG;AAC1B,yBAAa,CAAC;AAAA,UAChB,OAAO;AACL;AAAA,UACF;AAAA,QACF;AAEA,cAAM,gBAAgB,GAAG,IAAI;AAC7B,mBAAW,YAAY;AAAA,UACrB,WAAW,KAAK;AAAA,UAChB,MAAM;AAAA,UACN,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,UAAI,WAAW,SAAS,GAAG;AACzB,aAAK,QAAQ,KAAK,qDAAqD;AAAA,MACzE;AAGA,iBAAW,YAAY,EAAE,WAAW,KAAK,YAAY,MAAM,IAAI,OAAO,KAAK,CAAC;AAC5E,iBAAW,aAAa,KAAK,UAAU;AAAA,IACzC;AAEA,UAAM,mBAAmB,YAAY;AACnC,UAAI;AAEJ,aAAO,CAAC,KAAK,gBAAgB,OAAO,SAAS;AAE3C,eAAO,KAAK,YAAY,SAAS,GAAG;AAClC,gBAAM,YAAY,KAAK,YAAY,MAAM;AACzC,qBAAW,SAAS,QAAQ,MAAM,UAAU,MAAM,GAAG;AACnD,gBAAI,WAAW;AACb,mBAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC;AAAA,YACzE;AACA,wBAAY;AAAA,UACd;AAAA,QACF;AAGA,YAAI,KAAK,eAAe,KAAK,YAAY,WAAW,GAAG;AACrD;AAAA,QACF;AAGA,cAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,MACxD;AAGA,iBAAW,SAAS,QAAQ,MAAM,GAAG;AACnC,YAAI,WAAW;AACb,eAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC;AAAA,QACzE;AACA,oBAAY;AAAA,MACd;AAEA,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,OAAO,KAAK,CAAC;AAAA,MACxE;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,IAAI,CAAC,UAAU,GAAG,mBAAmB,GAAG,iBAAiB,GAAG,aAAa,CAAC;AAAA,IAC1F,SAAS,GAAG;AAEV,UAAI,KAAK,gBAAgB,OAAO,SAAS;AACvC;AAAA,MACF;AAEA,UAAI,aAAa,+BAAiB;AAChC,cAAM;AAAA,MACR;AACA,UAAI,aAAa,8BAAgB;AAC/B,cAAM;AAAA,MACR;AACA,YAAM,IAAI,6BAAe,EAAE,SAAS,uBAAuB,CAAC;AAAA,IAC9D,UAAE;AAEA,WAAK,gBAAgB,OAAO,oBAAoB,SAAS,YAAY;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,QAAc;AAEZ,SAAK,YAAY,SAAS;AAC1B,SAAK,sBAAsB,SAAS;AACpC,SAAK,cAAc;AACnB,SAAK,qBAAqB,MAAM;AAChC,UAAM,MAAM;AAAA,EACd;AACF;","names":["stream","tts"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n APIConnectionError,\n APIError,\n APIStatusError,\n APITimeoutError,\n AudioByteStream,\n Future,\n type TimedString,\n createTimedString,\n log,\n shortuuid,\n stream,\n tokenize,\n tts,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { TTSEncoding, TTSModels } from './models.js';\n\nconst DEFAULT_VOICE_ID = 'bIHbv24MWmeRgasZH58o';\nconst API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1';\nconst AUTHORIZATION_HEADER = 'xi-api-key';\nconst WS_INACTIVITY_TIMEOUT = 180;\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_22050';\n\nexport interface VoiceSettings {\n stability: number; // [0.0 - 1.0]\n similarity_boost: number; // [0.0 - 1.0]\n style?: number; // [0.0 - 1.0]\n speed?: number; // [0.8 - 1.2]\n use_speaker_boost?: boolean;\n}\n\nexport interface Voice {\n id: string;\n name: string;\n category: string;\n settings?: VoiceSettings;\n}\n\nexport interface PronunciationDictionaryLocator {\n pronunciation_dictionary_id: string;\n version_id: string;\n}\n\nexport interface TTSOptions {\n apiKey?: string;\n // New interface\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n // Legacy interface (backward compatibility)\n voice?: Voice;\n modelID?: TTSModels | string;\n languageCode?: string;\n // Common options\n baseURL?: string;\n encoding?: TTSEncoding;\n streamingLatency?: number;\n wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing?: boolean;\n enableLogging?: boolean;\n inactivityTimeout?: number;\n syncAlignment?: boolean;\n applyTextNormalization?: 'auto' | 'on' | 'off';\n preferredAlignment?: 'normalized' | 'original';\n autoMode?: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal options type with resolved defaults\ninterface ResolvedTTSOptions {\n apiKey: string;\n voiceId: string;\n voiceSettings?: VoiceSettings;\n model: TTSModels | string;\n language?: string;\n baseURL: string;\n encoding: TTSEncoding;\n sampleRate: number;\n streamingLatency?: number;\n wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing: boolean;\n enableLogging: boolean;\n inactivityTimeout: number;\n syncAlignment: boolean;\n applyTextNormalization: 'auto' | 'on' | 'off';\n preferredAlignment: 'normalized' | 'original';\n autoMode: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal types for connection management\ninterface SynthesizeContent {\n contextId: string;\n text: string;\n flush: boolean;\n}\n\ninterface CloseContext {\n contextId: string;\n}\n\ninterface StreamData {\n stream: SynthesizeStream;\n waiter: {\n resolve: (value: void) => void;\n reject: (error: Error) => void;\n };\n textBuffer: string;\n startTimesMs: number[];\n durationsMs: number[];\n /** First word offset for timestamp normalization (removes leading silence) */\n firstWordOffsetMs: number | null;\n}\n\ntype ConnectionMessage = SynthesizeContent | CloseContext;\n\n// Helper Functions\n\nfunction sampleRateFromFormat(encoding: TTSEncoding): number {\n const split = encoding.split('_');\n return parseInt(split[1]!, 10);\n}\n\nfunction synthesizeUrl(opts: ResolvedTTSOptions): string {\n const { baseURL, voiceId, model, encoding, streamingLatency } = opts;\n let url = `${baseURL}/text-to-speech/${voiceId}/stream?model_id=${model}&output_format=${encoding}`;\n if (streamingLatency !== undefined) {\n url += `&optimize_streaming_latency=${streamingLatency}`;\n }\n return url;\n}\n\nfunction multiStreamUrl(opts: ResolvedTTSOptions): string {\n const baseURL = opts.baseURL.replace('https://', 'wss://').replace('http://', 'ws://');\n const params: string[] = [];\n params.push(`model_id=${opts.model}`);\n params.push(`output_format=${opts.encoding}`);\n if (opts.language) {\n params.push(`language_code=${opts.language}`);\n }\n params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);\n params.push(`enable_logging=${opts.enableLogging}`);\n params.push(`inactivity_timeout=${opts.inactivityTimeout}`);\n params.push(`apply_text_normalization=${opts.applyTextNormalization}`);\n if (opts.syncAlignment) {\n params.push('sync_alignment=true');\n }\n if (opts.autoMode !== undefined) {\n params.push(`auto_mode=${opts.autoMode}`);\n }\n return `${baseURL}/text-to-speech/${opts.voiceId}/multi-stream-input?${params.join('&')}`;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const result: Partial<T> = {};\n for (const key in obj) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Convert alignment data to timed words.\n * Returns the timed words and remaining text buffer.\n *\n * @param firstWordOffsetMs - Optional offset to normalize timestamps (subtract from all).\n * ElevenLabs returns absolute timestamps from the start of TTS audio, which may include\n * leading silence. By normalizing to 0, we ensure proper sync with the synchronizer.\n */\nfunction toTimedWords(\n text: string,\n startTimesMs: number[],\n durationsMs: number[],\n flush: boolean = false,\n firstWordOffsetMs: number = 0,\n): [TimedString[], string] {\n if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {\n return [[], text || ''];\n }\n\n const lastStartTime = startTimesMs[startTimesMs.length - 1]!;\n const lastDuration = durationsMs[durationsMs.length - 1]!;\n const timestamps = [...startTimesMs, lastStartTime + lastDuration];\n\n const words = tokenize.basic.splitWords(text, false);\n const timedWords: TimedString[] = [];\n\n if (words.length === 0) {\n return [[], text];\n }\n\n const startIndices = words.map((w) => w[1]);\n let end = 0;\n\n // We don't know if the last word is complete, always leave it as remaining\n for (let i = 0; i < startIndices.length - 1; i++) {\n const start = startIndices[i]!;\n const nextStart = startIndices[i + 1]!;\n end = nextStart;\n // Normalize timestamps by subtracting the first word offset\n const startT = Math.max(0, (timestamps[start] ?? 0) - firstWordOffsetMs) / 1000;\n const endT = Math.max(0, (timestamps[nextStart] ?? 0) - firstWordOffsetMs) / 1000;\n timedWords.push(\n createTimedString({\n text: text.slice(start, nextStart),\n startTime: startT,\n endTime: endT,\n }),\n );\n }\n\n if (flush && words.length > 0) {\n const lastWordStart = startIndices[startIndices.length - 1]!;\n const startT = Math.max(0, (timestamps[lastWordStart] ?? 0) - firstWordOffsetMs) / 1000;\n const endT = Math.max(0, (timestamps[timestamps.length - 1] ?? 0) - firstWordOffsetMs) / 1000;\n timedWords.push(\n createTimedString({\n text: text.slice(lastWordStart),\n startTime: startT,\n endTime: endT,\n }),\n );\n end = text.length;\n } else if (words.length > 0) {\n end = startIndices[startIndices.length - 1]!;\n }\n\n return [timedWords, text.slice(end)];\n}\n\nclass Connection {\n #opts: ResolvedTTSOptions;\n #ws: WebSocket | null = null;\n #isCurrent = true;\n #activeContexts = new Set<string>();\n #inputQueue: ConnectionMessage[] = [];\n #contextData = new Map<string, StreamData>();\n #sendTask: Promise<void> | null = null;\n #recvTask: Promise<void> | null = null;\n #closed = false;\n #logger = log();\n #inputQueueResolver: (() => void) | null = null;\n\n constructor(opts: ResolvedTTSOptions) {\n this.#opts = opts;\n }\n\n get voiceId(): string {\n return this.#opts.voiceId;\n }\n\n get isCurrent(): boolean {\n return this.#isCurrent;\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n markNonCurrent(): void {\n this.#isCurrent = false;\n }\n\n async connect(): Promise<void> {\n if (this.#ws || this.#closed) {\n return;\n }\n\n const url = multiStreamUrl(this.#opts);\n const headers = { [AUTHORIZATION_HEADER]: this.#opts.apiKey };\n\n return new Promise((resolve, reject) => {\n this.#ws = new WebSocket(url, { headers });\n\n this.#ws.on('open', () => {\n this.#sendTask = this.#sendLoop();\n this.#recvTask = this.#recvLoop();\n resolve();\n });\n\n this.#ws.on('error', (error) => {\n this.#logger.error({ error }, 'WebSocket connection error');\n reject(new APIConnectionError({ message: `WebSocket error: ${error.message}` }));\n });\n });\n }\n\n registerStream(\n stream: SynthesizeStream,\n waiter: { resolve: (value: void) => void; reject: (error: Error) => void },\n ): void {\n const contextId = stream.contextId;\n this.#contextData.set(contextId, {\n stream,\n waiter,\n textBuffer: '',\n startTimesMs: [],\n durationsMs: [],\n firstWordOffsetMs: null,\n });\n }\n\n sendContent(content: SynthesizeContent): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push(content);\n this.#inputQueueResolver?.();\n }\n\n closeContext(contextId: string): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push({ contextId });\n this.#inputQueueResolver?.();\n }\n\n async #sendLoop(): Promise<void> {\n try {\n while (!this.#closed) {\n // Wait for messages in queue\n if (this.#inputQueue.length === 0) {\n await new Promise<void>((resolve) => {\n this.#inputQueueResolver = resolve;\n });\n this.#inputQueueResolver = null;\n }\n\n if (this.#closed) break;\n\n const msg = this.#inputQueue.shift();\n if (!msg) continue;\n\n if (!this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n break;\n }\n\n if ('text' in msg) {\n // SynthesizeContent\n const content = msg as SynthesizeContent;\n const isNewContext = !this.#activeContexts.has(content.contextId);\n\n // If not current and this is a new context, ignore it\n if (!this.#isCurrent && isNewContext) {\n continue;\n }\n\n if (isNewContext) {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : {};\n\n const initPkt: Record<string, unknown> = {\n text: ' ',\n voice_settings: voiceSettings,\n context_id: content.contextId,\n };\n\n if (this.#opts.pronunciationDictionaryLocators) {\n initPkt.pronunciation_dictionary_locators =\n this.#opts.pronunciationDictionaryLocators.map((locator) => ({\n pronunciation_dictionary_id: locator.pronunciation_dictionary_id,\n version_id: locator.version_id,\n }));\n }\n\n const initPktStr = JSON.stringify(initPkt);\n this.#ws.send(initPktStr);\n this.#activeContexts.add(content.contextId);\n }\n\n const pkt: Record<string, unknown> = {\n text: content.text,\n context_id: content.contextId,\n };\n if (content.flush) {\n pkt.flush = true;\n }\n\n const pktStr = JSON.stringify(pkt);\n this.#ws.send(pktStr);\n } else {\n // CloseContext\n const closeMsg = msg as CloseContext;\n if (this.#activeContexts.has(closeMsg.contextId)) {\n const closePkt = {\n context_id: closeMsg.contextId,\n close_context: true,\n };\n const closePktStr = JSON.stringify(closePkt);\n this.#ws.send(closePktStr);\n }\n }\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'send loop error');\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n async #recvLoop(): Promise<void> {\n try {\n const messageChannel = stream.createStreamChannel<Record<string, unknown>>();\n const errorFuture = new Future<Error>();\n\n const onMessage = (rawData: Buffer) => {\n try {\n const parsed = JSON.parse(rawData.toString());\n messageChannel.write(parsed);\n } catch (e) {\n this.#logger.warn({ error: e }, 'failed to parse WebSocket message');\n }\n };\n\n const onClose = () => {\n if (!this.#closed && this.#contextData.size > 0) {\n this.#logger.warn('websocket closed unexpectedly');\n }\n messageChannel.close();\n };\n\n const onError = (error: Error) => {\n errorFuture.resolve(error);\n messageChannel.close();\n };\n\n // Set up persistent listeners\n if (!this.#ws) return;\n this.#ws.on('message', onMessage);\n this.#ws.on('close', onClose);\n this.#ws.on('error', onError);\n\n const cleanup = () => {\n this.#ws?.off('message', onMessage);\n this.#ws?.off('close', onClose);\n this.#ws?.off('error', onError);\n };\n\n const reader = messageChannel.stream().getReader();\n try {\n while (!this.#closed) {\n const result = await reader.read();\n if (result.done || this.#closed) break;\n\n const data = result.value;\n const contextId = data.contextId as string | undefined;\n const ctx = contextId ? this.#contextData.get(contextId) : undefined;\n\n if (data.error) {\n this.#logger.error(\n { context_id: contextId, error: data.error, data },\n 'elevenlabs tts returned error',\n );\n if (contextId) {\n if (ctx) {\n ctx.waiter.reject(new APIError(data.error as string));\n }\n this.#cleanupContext(contextId);\n }\n continue;\n }\n\n if (!ctx) {\n this.#logger.warn({ data }, 'unexpected message received from elevenlabs tts');\n continue;\n }\n\n const stream = ctx.stream;\n\n // Process alignment data\n const alignment =\n this.#opts.preferredAlignment === 'normalized'\n ? (data.normalizedAlignment as Record<string, unknown>)\n : (data.alignment as Record<string, unknown>);\n\n if (alignment && stream) {\n const chars = alignment.chars as string[] | undefined;\n const starts = (alignment.charStartTimesMs || alignment.charsStartTimesMs) as\n | number[]\n | undefined;\n const durs = (alignment.charDurationsMs || alignment.charsDurationsMs) as\n | number[]\n | undefined;\n\n if (\n chars &&\n starts &&\n durs &&\n chars.length === durs.length &&\n starts.length === durs.length\n ) {\n ctx.textBuffer += chars.join('');\n\n // Handle chars with multiple characters\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const start = starts[i]!;\n const dur = durs[i]!;\n\n // Capture the first word's start time for normalization\n // This removes leading silence from timestamps\n if (ctx.firstWordOffsetMs === null && start > 0) {\n ctx.firstWordOffsetMs = start;\n }\n\n if (char.length > 1) {\n for (let j = 0; j < char.length - 1; j++) {\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(0);\n }\n }\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(dur);\n }\n\n const [timedWords, remainingText] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n false,\n ctx.firstWordOffsetMs ?? 0,\n );\n\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n\n ctx.textBuffer = remainingText;\n ctx.startTimesMs = ctx.startTimesMs.slice(-remainingText.length);\n ctx.durationsMs = ctx.durationsMs.slice(-remainingText.length);\n }\n }\n\n if (data.audio) {\n const audioData = Buffer.from(data.audio as string, 'base64');\n stream.pushAudio(audioData);\n }\n\n if (data.isFinal) {\n // Flush remaining alignment data\n if (ctx.textBuffer) {\n const [timedWords] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n true,\n ctx.firstWordOffsetMs ?? 0,\n );\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n }\n\n stream.markDone();\n ctx.waiter.resolve();\n this.#cleanupContext(contextId!);\n\n if (!this.#isCurrent && this.#activeContexts.size === 0) {\n this.#logger.debug('no active contexts, shutting down connection');\n break;\n }\n }\n }\n\n // Throw any error that occurred\n if (errorFuture.done) {\n throw await errorFuture.await;\n }\n } finally {\n reader.releaseLock();\n cleanup();\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'recv loop error');\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(e instanceof Error ? e : new Error(String(e)));\n }\n this.#contextData.clear();\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n #cleanupContext(contextId: string): void {\n this.#contextData.delete(contextId);\n this.#activeContexts.delete(contextId);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#inputQueueResolver?.();\n\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(new APIStatusError({ message: 'connection closed' }));\n }\n this.#contextData.clear();\n\n if (this.#ws) {\n this.#ws.close();\n this.#ws = null;\n }\n\n if (this.#sendTask) {\n await this.#sendTask.catch(() => {});\n }\n if (this.#recvTask) {\n await this.#recvTask.catch(() => {});\n }\n }\n}\n\nexport class TTS extends tts.TTS {\n #opts: ResolvedTTSOptions;\n #streams = new Set<SynthesizeStream>();\n #currentConnection: Connection | null = null;\n #connectionLock = new Mutex();\n #logger = log();\n\n label = 'elevenlabs.TTS';\n\n constructor(opts: TTSOptions = {}) {\n const autoMode = opts.autoMode ?? true;\n const encoding = opts.encoding ?? DEFAULT_ENCODING;\n const sampleRate = sampleRateFromFormat(encoding);\n const syncAlignment = opts.syncAlignment ?? true;\n\n super(sampleRate, 1, {\n streaming: true,\n alignedTranscript: syncAlignment,\n });\n\n const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'ElevenLabs API key is required, either as argument or set ELEVEN_API_KEY environmental variable',\n );\n }\n\n let wordTokenizer = opts.wordTokenizer;\n if (!wordTokenizer) {\n wordTokenizer = autoMode\n ? new tokenize.basic.SentenceTokenizer()\n : new tokenize.basic.WordTokenizer(false);\n } else if (autoMode && !(wordTokenizer instanceof tokenize.SentenceTokenizer)) {\n this.#logger.warn(\n 'autoMode is enabled, it expects full sentences or phrases, ' +\n 'please provide a SentenceTokenizer instead of a WordTokenizer.',\n );\n }\n\n // Handle legacy options for backward compatibility\n const voiceId = opts.voiceId ?? opts.voice?.id ?? DEFAULT_VOICE_ID;\n const voiceSettings = opts.voiceSettings ?? opts.voice?.settings;\n const model = opts.model ?? opts.modelID ?? 'eleven_turbo_v2_5';\n const language = opts.language ?? opts.languageCode;\n\n this.#opts = {\n apiKey,\n voiceId,\n voiceSettings,\n model,\n language,\n baseURL: opts.baseURL ?? API_BASE_URL_V1,\n encoding,\n sampleRate,\n streamingLatency: opts.streamingLatency,\n wordTokenizer,\n chunkLengthSchedule: opts.chunkLengthSchedule,\n enableSsmlParsing: opts.enableSsmlParsing ?? false,\n enableLogging: opts.enableLogging ?? true,\n inactivityTimeout: opts.inactivityTimeout ?? WS_INACTIVITY_TIMEOUT,\n syncAlignment: opts.syncAlignment ?? true,\n applyTextNormalization: opts.applyTextNormalization ?? 'auto',\n preferredAlignment: opts.preferredAlignment ?? 'normalized',\n autoMode,\n pronunciationDictionaryLocators: opts.pronunciationDictionaryLocators,\n };\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'ElevenLabs';\n }\n\n async listVoices(): Promise<Voice[]> {\n const response = await fetch(`${this.#opts.baseURL}/voices`, {\n headers: { [AUTHORIZATION_HEADER]: this.#opts.apiKey },\n });\n const data = (await response.json()) as {\n voices: { voice_id: string; name: string; category: string }[];\n };\n return data.voices.map((v) => ({\n id: v.voice_id,\n name: v.name,\n category: v.category,\n }));\n }\n\n updateOptions(opts: {\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n }): void {\n let changed = false;\n\n if (opts.model !== undefined && opts.model !== this.#opts.model) {\n this.#opts.model = opts.model;\n changed = true;\n }\n\n if (opts.voiceId !== undefined && opts.voiceId !== this.#opts.voiceId) {\n this.#opts.voiceId = opts.voiceId;\n changed = true;\n }\n\n if (opts.voiceSettings !== undefined) {\n this.#opts.voiceSettings = opts.voiceSettings;\n changed = true;\n }\n\n if (opts.language !== undefined && opts.language !== this.#opts.language) {\n this.#opts.language = opts.language;\n changed = true;\n }\n\n if (opts.pronunciationDictionaryLocators !== undefined) {\n this.#opts.pronunciationDictionaryLocators = opts.pronunciationDictionaryLocators;\n changed = true;\n }\n\n if (changed && this.#currentConnection) {\n this.#currentConnection.markNonCurrent();\n this.#currentConnection = null;\n }\n }\n\n async currentConnection(): Promise<Connection> {\n const unlock = await this.#connectionLock.lock();\n try {\n if (\n this.#currentConnection &&\n this.#currentConnection.isCurrent &&\n !this.#currentConnection.closed\n ) {\n return this.#currentConnection;\n }\n\n const conn = new Connection({ ...this.#opts });\n await conn.connect();\n this.#currentConnection = conn;\n return conn;\n } finally {\n unlock();\n }\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(this, text, { ...this.#opts });\n }\n\n stream(): SynthesizeStream {\n const stream = new SynthesizeStream(this, { ...this.#opts });\n this.#streams.add(stream);\n return stream;\n }\n\n async close(): Promise<void> {\n for (const stream of this.#streams) {\n stream.close();\n }\n this.#streams.clear();\n\n if (this.#currentConnection) {\n await this.#currentConnection.close();\n this.#currentConnection = null;\n }\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #logger = log();\n\n label = 'elevenlabs.ChunkedStream';\n\n constructor(tts: TTS, text: string, opts: ResolvedTTSOptions) {\n super(text, tts);\n this.#tts = tts;\n this.#opts = opts;\n }\n\n protected async run(): Promise<void> {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : undefined;\n\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n try {\n const response = await fetch(synthesizeUrl(this.#opts), {\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n text: this.inputText,\n model_id: this.#opts.model,\n voice_settings: voiceSettings,\n }),\n signal: this.abortSignal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new APIStatusError({\n message: `ElevenLabs API error: ${errorText}`,\n options: { statusCode: response.status },\n });\n }\n\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.startsWith('audio/')) {\n const content = await response.text();\n throw new APIError(`ElevenLabs returned non-audio data: ${content}`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new APIError('No response body');\n }\n\n let lastFrame: AudioFrame | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n for (const frame of bstream.write(value.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Flush remaining data\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: true });\n }\n } catch (e) {\n if (e instanceof APIError) {\n throw e;\n }\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw new APIConnectionError({ message: `Connection error: ${e}` });\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #contextId: string;\n #sentTokenizerStream: tokenize.SentenceStream | tokenize.WordStream;\n #logger = log();\n #audioQueue: Buffer[] = [];\n #timedTranscriptQueue: TimedString[] = [];\n #streamDone = false;\n\n label = 'elevenlabs.SynthesizeStream';\n\n constructor(tts: TTS, opts: ResolvedTTSOptions) {\n super(tts);\n this.#tts = tts;\n this.#opts = opts;\n this.#contextId = shortuuid();\n this.#sentTokenizerStream = this.#opts.wordTokenizer.stream();\n }\n\n get contextId(): string {\n return this.#contextId;\n }\n\n pushAudio(data: Buffer): void {\n // Don't push if stream is closed/aborted\n if (this.closed || this.abortController.signal.aborted) {\n return;\n }\n this.#audioQueue.push(data);\n }\n\n pushTimedTranscript(timedWords: TimedString[]): void {\n this.#timedTranscriptQueue.push(...timedWords);\n }\n\n markDone(): void {\n this.#streamDone = true;\n }\n\n protected async run(): Promise<void> {\n const requestId = this.#contextId;\n const segmentId = this.#contextId;\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n let connection: Connection;\n try {\n connection = await this.#tts.currentConnection();\n } catch (e) {\n throw new APIConnectionError({ message: 'could not connect to ElevenLabs' });\n }\n\n let waiterReject: ((reason: Error) => void) | undefined;\n const waiterPromise = new Promise<void>((resolve, reject) => {\n waiterReject = reject;\n connection.registerStream(this, { resolve, reject });\n });\n\n // Handle abort - reject the waiter so Promise.all can complete\n const abortHandler = () => {\n if (waiterReject) {\n waiterReject(new Error('Stream aborted'));\n }\n };\n this.abortController.signal.addEventListener('abort', abortHandler, { once: true });\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#sentTokenizerStream.flush();\n continue;\n }\n this.#sentTokenizerStream.pushText(data);\n }\n this.#sentTokenizerStream.endInput();\n };\n\n const sentenceStreamTask = async () => {\n const flushOnChunk =\n this.#opts.wordTokenizer instanceof tokenize.SentenceTokenizer && this.#opts.autoMode;\n\n let xmlContent: string[] = [];\n\n for await (const data of this.#sentTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n let text = data.token;\n const xmlStartTokens = ['<phoneme', '<break'];\n const xmlEndTokens = ['</phoneme>', '/>'];\n\n if (\n (this.#opts.enableSsmlParsing &&\n xmlStartTokens.some((start) => text.startsWith(start))) ||\n xmlContent.length > 0\n ) {\n xmlContent.push(text);\n\n if (xmlEndTokens.some((end) => text.includes(end))) {\n text = xmlContent.join(' ');\n xmlContent = [];\n } else {\n continue;\n }\n }\n\n const formattedText = `${text} `; // must always end with a space\n connection.sendContent({\n contextId: this.#contextId,\n text: formattedText,\n flush: flushOnChunk,\n });\n }\n\n if (xmlContent.length > 0) {\n this.#logger.warn('ElevenLabs stream ended with incomplete xml content');\n }\n\n // Send final empty text to signal end of input\n connection.sendContent({ contextId: this.#contextId, text: '', flush: true });\n connection.closeContext(this.#contextId);\n };\n\n const audioProcessTask = async () => {\n let lastFrame: AudioFrame | undefined;\n let pendingTimedTranscripts: TimedString[] = [];\n\n const sendLastFrame = (final: boolean) => {\n if (lastFrame) {\n // Include timedTranscripts with the audio frame\n this.queue.put({\n requestId,\n segmentId,\n frame: lastFrame,\n final,\n timedTranscripts:\n pendingTimedTranscripts.length > 0 ? pendingTimedTranscripts : undefined,\n });\n lastFrame = undefined;\n pendingTimedTranscripts = [];\n }\n };\n\n while (!this.abortController.signal.aborted) {\n // Drain timed transcript queue\n while (this.#timedTranscriptQueue.length > 0) {\n pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift()!);\n }\n\n // Process audio queue\n while (this.#audioQueue.length > 0) {\n const audioData = this.#audioQueue.shift()!;\n for (const frame of bstream.write(audioData.buffer)) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n }\n\n // Exit when stream is done and queue is empty\n if (this.#streamDone && this.#audioQueue.length === 0) {\n break;\n }\n\n // Small delay to avoid busy waiting\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n // Drain any remaining timed transcripts\n while (this.#timedTranscriptQueue.length > 0) {\n pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift()!);\n }\n\n // Flush remaining\n for (const frame of bstream.flush()) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n\n sendLastFrame(true);\n };\n\n try {\n await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);\n } catch (e) {\n // If aborted, this is a normal termination - don't throw\n if (this.abortController.signal.aborted) {\n return;\n }\n\n if (e instanceof APITimeoutError) {\n throw e;\n }\n if (e instanceof APIStatusError) {\n throw e;\n }\n throw new APIStatusError({ message: 'Could not synthesize' });\n } finally {\n // Clean up abort listener\n this.abortController.signal.removeEventListener('abort', abortHandler);\n }\n }\n\n close(): void {\n // Clear audio buffers to prevent memory leak\n this.#audioQueue.length = 0;\n this.#timedTranscriptQueue.length = 0;\n this.#streamDone = true;\n this.#sentTokenizerStream.close();\n super.close();\n }\n}\n"],"mappings":"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","names":["stream","tts"]}
package/dist/tts.d.cts CHANGED
@@ -1,8 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
- import { tokenize, tts, type voice } from '@livekit/agents';
3
+ import { type TimedString, tokenize, tts } from '@livekit/agents';
4
4
  import type { TTSEncoding, TTSModels } from './models.js';
5
- type TimedString = voice.TimedString;
6
5
  export interface VoiceSettings {
7
6
  stability: number;
8
7
  similarity_boost: number;
package/dist/tts.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
- import { tokenize, tts, type voice } from '@livekit/agents';
3
+ import { type TimedString, tokenize, tts } from '@livekit/agents';
4
4
  import type { TTSEncoding, TTSModels } from './models.js';
5
- type TimedString = voice.TimedString;
6
5
  export interface VoiceSettings {
7
6
  stability: number;
8
7
  similarity_boost: number;
package/dist/tts.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../src/tts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../src/tts.ts"],"names":[],"mappings":";;AAGA,OAAO,EAOL,KAAK,WAAW,EAKhB,QAAQ,EACR,GAAG,EACJ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQ1D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,8BAA8B;IAC7C,2BAA2B,EAAE,MAAM,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,iBAAiB,CAAC;IACpE,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;IAC/C,kBAAkB,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B,CAAC,EAAE,8BAA8B,EAAE,CAAC;CACpE;AAGD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,iBAAiB,CAAC;IACnE,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;IAC9C,kBAAkB,EAAE,YAAY,GAAG,UAAU,CAAC;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,+BAA+B,CAAC,EAAE,8BAA8B,EAAE,CAAC;CACpE;AAGD,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAyID,cAAM,UAAU;;gBAaF,IAAI,EAAE,kBAAkB;IAIpC,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,cAAc,IAAI,IAAI;IAIhB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB9B,cAAc,CACZ,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;KAAE,GACzE,IAAI;IAYP,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAQ7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA4R/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAyB7B;AAED,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAO9B,KAAK,SAAoB;gBAEb,IAAI,GAAE,UAAe;IA2DjC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAEK,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAcpC,aAAa,CAAC,IAAI,EAAE;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,aAAa,CAAC;QAC9B,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;QAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,+BAA+B,CAAC,EAAE,8BAA8B,EAAE,CAAC;KACpE,GAAG,IAAI;IAkCF,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC;IAoB9C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IAIvC,MAAM,IAAI,gBAAgB;IAMpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAW7B;AAED,qBAAa,aAAc,SAAQ,GAAG,CAAC,aAAa;;IAKlD,KAAK,SAA8B;gBAEvB,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB;cAM5C,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA6ErC;AAED,qBAAa,gBAAiB,SAAQ,GAAG,CAAC,gBAAgB;;IAUxD,KAAK,SAAiC;gBAE1B,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,kBAAkB;IAQ9C,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ7B,mBAAmB,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI;IAIpD,QAAQ,IAAI,IAAI;cAIA,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAkKpC,KAAK,IAAI,IAAI;CAQd"}
package/dist/tts.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  APITimeoutError,
6
6
  AudioByteStream,
7
7
  Future,
8
+ createTimedString,
8
9
  log,
9
10
  shortuuid,
10
11
  stream,
@@ -59,7 +60,7 @@ function stripUndefined(obj) {
59
60
  }
60
61
  return result;
61
62
  }
62
- function toTimedWords(text, startTimesMs, durationsMs, flush = false) {
63
+ function toTimedWords(text, startTimesMs, durationsMs, flush = false, firstWordOffsetMs = 0) {
63
64
  if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {
64
65
  return [[], text || ""];
65
66
  }
@@ -77,23 +78,27 @@ function toTimedWords(text, startTimesMs, durationsMs, flush = false) {
77
78
  const start = startIndices[i];
78
79
  const nextStart = startIndices[i + 1];
79
80
  end = nextStart;
80
- const startT = (timestamps[start] ?? 0) / 1e3;
81
- const endT = (timestamps[nextStart] ?? 0) / 1e3;
82
- timedWords.push({
83
- text: text.slice(start, nextStart),
84
- startTime: startT,
85
- endTime: endT
86
- });
81
+ const startT = Math.max(0, (timestamps[start] ?? 0) - firstWordOffsetMs) / 1e3;
82
+ const endT = Math.max(0, (timestamps[nextStart] ?? 0) - firstWordOffsetMs) / 1e3;
83
+ timedWords.push(
84
+ createTimedString({
85
+ text: text.slice(start, nextStart),
86
+ startTime: startT,
87
+ endTime: endT
88
+ })
89
+ );
87
90
  }
88
91
  if (flush && words.length > 0) {
89
92
  const lastWordStart = startIndices[startIndices.length - 1];
90
- const startT = (timestamps[lastWordStart] ?? 0) / 1e3;
91
- const endT = (timestamps[timestamps.length - 1] ?? 0) / 1e3;
92
- timedWords.push({
93
- text: text.slice(lastWordStart),
94
- startTime: startT,
95
- endTime: endT
96
- });
93
+ const startT = Math.max(0, (timestamps[lastWordStart] ?? 0) - firstWordOffsetMs) / 1e3;
94
+ const endT = Math.max(0, (timestamps[timestamps.length - 1] ?? 0) - firstWordOffsetMs) / 1e3;
95
+ timedWords.push(
96
+ createTimedString({
97
+ text: text.slice(lastWordStart),
98
+ startTime: startT,
99
+ endTime: endT
100
+ })
101
+ );
97
102
  end = text.length;
98
103
  } else if (words.length > 0) {
99
104
  end = startIndices[startIndices.length - 1];
@@ -153,7 +158,8 @@ class Connection {
153
158
  waiter,
154
159
  textBuffer: "",
155
160
  startTimesMs: [],
156
- durationsMs: []
161
+ durationsMs: [],
162
+ firstWordOffsetMs: null
157
163
  });
158
164
  }
159
165
  sendContent(content) {
@@ -308,6 +314,9 @@ class Connection {
308
314
  const char = chars[i];
309
315
  const start = starts[i];
310
316
  const dur = durs[i];
317
+ if (ctx.firstWordOffsetMs === null && start > 0) {
318
+ ctx.firstWordOffsetMs = start;
319
+ }
311
320
  if (char.length > 1) {
312
321
  for (let j = 0; j < char.length - 1; j++) {
313
322
  ctx.startTimesMs.push(start);
@@ -320,7 +329,9 @@ class Connection {
320
329
  const [timedWords, remainingText] = toTimedWords(
321
330
  ctx.textBuffer,
322
331
  ctx.startTimesMs,
323
- ctx.durationsMs
332
+ ctx.durationsMs,
333
+ false,
334
+ ctx.firstWordOffsetMs ?? 0
324
335
  );
325
336
  if (timedWords.length > 0) {
326
337
  stream2.pushTimedTranscript(timedWords);
@@ -340,7 +351,8 @@ class Connection {
340
351
  ctx.textBuffer,
341
352
  ctx.startTimesMs,
342
353
  ctx.durationsMs,
343
- true
354
+ true,
355
+ ctx.firstWordOffsetMs ?? 0
344
356
  );
345
357
  if (timedWords.length > 0) {
346
358
  stream2.pushTimedTranscript(timedWords);
@@ -415,8 +427,10 @@ class TTS extends tts.TTS {
415
427
  const autoMode = opts.autoMode ?? true;
416
428
  const encoding = opts.encoding ?? DEFAULT_ENCODING;
417
429
  const sampleRate = sampleRateFromFormat(encoding);
430
+ const syncAlignment = opts.syncAlignment ?? true;
418
431
  super(sampleRate, 1, {
419
- streaming: true
432
+ streaming: true,
433
+ alignedTranscript: syncAlignment
420
434
  });
421
435
  const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;
422
436
  if (!apiKey) {
@@ -707,13 +721,28 @@ class SynthesizeStream extends tts.SynthesizeStream {
707
721
  };
708
722
  const audioProcessTask = async () => {
709
723
  let lastFrame;
724
+ let pendingTimedTranscripts = [];
725
+ const sendLastFrame = (final) => {
726
+ if (lastFrame) {
727
+ this.queue.put({
728
+ requestId,
729
+ segmentId,
730
+ frame: lastFrame,
731
+ final,
732
+ timedTranscripts: pendingTimedTranscripts.length > 0 ? pendingTimedTranscripts : void 0
733
+ });
734
+ lastFrame = void 0;
735
+ pendingTimedTranscripts = [];
736
+ }
737
+ };
710
738
  while (!this.abortController.signal.aborted) {
739
+ while (this.#timedTranscriptQueue.length > 0) {
740
+ pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift());
741
+ }
711
742
  while (this.#audioQueue.length > 0) {
712
743
  const audioData = this.#audioQueue.shift();
713
744
  for (const frame of bstream.write(audioData.buffer)) {
714
- if (lastFrame) {
715
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });
716
- }
745
+ sendLastFrame(false);
717
746
  lastFrame = frame;
718
747
  }
719
748
  }
@@ -722,15 +751,14 @@ class SynthesizeStream extends tts.SynthesizeStream {
722
751
  }
723
752
  await new Promise((resolve) => setTimeout(resolve, 10));
724
753
  }
754
+ while (this.#timedTranscriptQueue.length > 0) {
755
+ pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift());
756
+ }
725
757
  for (const frame of bstream.flush()) {
726
- if (lastFrame) {
727
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });
728
- }
758
+ sendLastFrame(false);
729
759
  lastFrame = frame;
730
760
  }
731
- if (lastFrame) {
732
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: true });
733
- }
761
+ sendLastFrame(true);
734
762
  };
735
763
  try {
736
764
  await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);
package/dist/tts.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n APIConnectionError,\n APIError,\n APIStatusError,\n APITimeoutError,\n AudioByteStream,\n Future,\n log,\n shortuuid,\n stream,\n tokenize,\n tts,\n type voice,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { TTSEncoding, TTSModels } from './models.js';\n\ntype TimedString = voice.TimedString;\n\nconst DEFAULT_VOICE_ID = 'bIHbv24MWmeRgasZH58o';\nconst API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1';\nconst AUTHORIZATION_HEADER = 'xi-api-key';\nconst WS_INACTIVITY_TIMEOUT = 180;\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_22050';\n\nexport interface VoiceSettings {\n stability: number; // [0.0 - 1.0]\n similarity_boost: number; // [0.0 - 1.0]\n style?: number; // [0.0 - 1.0]\n speed?: number; // [0.8 - 1.2]\n use_speaker_boost?: boolean;\n}\n\nexport interface Voice {\n id: string;\n name: string;\n category: string;\n settings?: VoiceSettings;\n}\n\nexport interface PronunciationDictionaryLocator {\n pronunciation_dictionary_id: string;\n version_id: string;\n}\n\nexport interface TTSOptions {\n apiKey?: string;\n // New interface\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n // Legacy interface (backward compatibility)\n voice?: Voice;\n modelID?: TTSModels | string;\n languageCode?: string;\n // Common options\n baseURL?: string;\n encoding?: TTSEncoding;\n streamingLatency?: number;\n wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing?: boolean;\n enableLogging?: boolean;\n inactivityTimeout?: number;\n syncAlignment?: boolean;\n applyTextNormalization?: 'auto' | 'on' | 'off';\n preferredAlignment?: 'normalized' | 'original';\n autoMode?: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal options type with resolved defaults\ninterface ResolvedTTSOptions {\n apiKey: string;\n voiceId: string;\n voiceSettings?: VoiceSettings;\n model: TTSModels | string;\n language?: string;\n baseURL: string;\n encoding: TTSEncoding;\n sampleRate: number;\n streamingLatency?: number;\n wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing: boolean;\n enableLogging: boolean;\n inactivityTimeout: number;\n syncAlignment: boolean;\n applyTextNormalization: 'auto' | 'on' | 'off';\n preferredAlignment: 'normalized' | 'original';\n autoMode: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal types for connection management\ninterface SynthesizeContent {\n contextId: string;\n text: string;\n flush: boolean;\n}\n\ninterface CloseContext {\n contextId: string;\n}\n\ninterface StreamData {\n stream: SynthesizeStream;\n waiter: {\n resolve: (value: void) => void;\n reject: (error: Error) => void;\n };\n textBuffer: string;\n startTimesMs: number[];\n durationsMs: number[];\n}\n\ntype ConnectionMessage = SynthesizeContent | CloseContext;\n\n// Helper Functions\n\nfunction sampleRateFromFormat(encoding: TTSEncoding): number {\n const split = encoding.split('_');\n return parseInt(split[1]!, 10);\n}\n\nfunction synthesizeUrl(opts: ResolvedTTSOptions): string {\n const { baseURL, voiceId, model, encoding, streamingLatency } = opts;\n let url = `${baseURL}/text-to-speech/${voiceId}/stream?model_id=${model}&output_format=${encoding}`;\n if (streamingLatency !== undefined) {\n url += `&optimize_streaming_latency=${streamingLatency}`;\n }\n return url;\n}\n\nfunction multiStreamUrl(opts: ResolvedTTSOptions): string {\n const baseURL = opts.baseURL.replace('https://', 'wss://').replace('http://', 'ws://');\n const params: string[] = [];\n params.push(`model_id=${opts.model}`);\n params.push(`output_format=${opts.encoding}`);\n if (opts.language) {\n params.push(`language_code=${opts.language}`);\n }\n params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);\n params.push(`enable_logging=${opts.enableLogging}`);\n params.push(`inactivity_timeout=${opts.inactivityTimeout}`);\n params.push(`apply_text_normalization=${opts.applyTextNormalization}`);\n if (opts.syncAlignment) {\n params.push('sync_alignment=true');\n }\n if (opts.autoMode !== undefined) {\n params.push(`auto_mode=${opts.autoMode}`);\n }\n return `${baseURL}/text-to-speech/${opts.voiceId}/multi-stream-input?${params.join('&')}`;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const result: Partial<T> = {};\n for (const key in obj) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Convert alignment data to timed words.\n * Returns the timed words and remaining text buffer.\n */\nfunction toTimedWords(\n text: string,\n startTimesMs: number[],\n durationsMs: number[],\n flush: boolean = false,\n): [TimedString[], string] {\n if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {\n return [[], text || ''];\n }\n\n const lastStartTime = startTimesMs[startTimesMs.length - 1]!;\n const lastDuration = durationsMs[durationsMs.length - 1]!;\n const timestamps = [...startTimesMs, lastStartTime + lastDuration];\n\n const words = tokenize.basic.splitWords(text, false);\n const timedWords: TimedString[] = [];\n\n if (words.length === 0) {\n return [[], text];\n }\n\n const startIndices = words.map((w) => w[1]);\n let end = 0;\n\n // We don't know if the last word is complete, always leave it as remaining\n for (let i = 0; i < startIndices.length - 1; i++) {\n const start = startIndices[i]!;\n const nextStart = startIndices[i + 1]!;\n end = nextStart;\n const startT = (timestamps[start] ?? 0) / 1000;\n const endT = (timestamps[nextStart] ?? 0) / 1000;\n timedWords.push({\n text: text.slice(start, nextStart),\n startTime: startT,\n endTime: endT,\n });\n }\n\n if (flush && words.length > 0) {\n const lastWordStart = startIndices[startIndices.length - 1]!;\n const startT = (timestamps[lastWordStart] ?? 0) / 1000;\n const endT = (timestamps[timestamps.length - 1] ?? 0) / 1000;\n timedWords.push({\n text: text.slice(lastWordStart),\n startTime: startT,\n endTime: endT,\n });\n end = text.length;\n } else if (words.length > 0) {\n end = startIndices[startIndices.length - 1]!;\n }\n\n return [timedWords, text.slice(end)];\n}\n\nclass Connection {\n #opts: ResolvedTTSOptions;\n #ws: WebSocket | null = null;\n #isCurrent = true;\n #activeContexts = new Set<string>();\n #inputQueue: ConnectionMessage[] = [];\n #contextData = new Map<string, StreamData>();\n #sendTask: Promise<void> | null = null;\n #recvTask: Promise<void> | null = null;\n #closed = false;\n #logger = log();\n #inputQueueResolver: (() => void) | null = null;\n\n constructor(opts: ResolvedTTSOptions) {\n this.#opts = opts;\n }\n\n get voiceId(): string {\n return this.#opts.voiceId;\n }\n\n get isCurrent(): boolean {\n return this.#isCurrent;\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n markNonCurrent(): void {\n this.#isCurrent = false;\n }\n\n async connect(): Promise<void> {\n if (this.#ws || this.#closed) {\n return;\n }\n\n const url = multiStreamUrl(this.#opts);\n const headers = { [AUTHORIZATION_HEADER]: this.#opts.apiKey };\n\n return new Promise((resolve, reject) => {\n this.#ws = new WebSocket(url, { headers });\n\n this.#ws.on('open', () => {\n this.#sendTask = this.#sendLoop();\n this.#recvTask = this.#recvLoop();\n resolve();\n });\n\n this.#ws.on('error', (error) => {\n this.#logger.error({ error }, 'WebSocket connection error');\n reject(new APIConnectionError({ message: `WebSocket error: ${error.message}` }));\n });\n });\n }\n\n registerStream(\n stream: SynthesizeStream,\n waiter: { resolve: (value: void) => void; reject: (error: Error) => void },\n ): void {\n const contextId = stream.contextId;\n this.#contextData.set(contextId, {\n stream,\n waiter,\n textBuffer: '',\n startTimesMs: [],\n durationsMs: [],\n });\n }\n\n sendContent(content: SynthesizeContent): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push(content);\n this.#inputQueueResolver?.();\n }\n\n closeContext(contextId: string): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push({ contextId });\n this.#inputQueueResolver?.();\n }\n\n async #sendLoop(): Promise<void> {\n try {\n while (!this.#closed) {\n // Wait for messages in queue\n if (this.#inputQueue.length === 0) {\n await new Promise<void>((resolve) => {\n this.#inputQueueResolver = resolve;\n });\n this.#inputQueueResolver = null;\n }\n\n if (this.#closed) break;\n\n const msg = this.#inputQueue.shift();\n if (!msg) continue;\n\n if (!this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n break;\n }\n\n if ('text' in msg) {\n // SynthesizeContent\n const content = msg as SynthesizeContent;\n const isNewContext = !this.#activeContexts.has(content.contextId);\n\n // If not current and this is a new context, ignore it\n if (!this.#isCurrent && isNewContext) {\n continue;\n }\n\n if (isNewContext) {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : {};\n\n const initPkt: Record<string, unknown> = {\n text: ' ',\n voice_settings: voiceSettings,\n context_id: content.contextId,\n };\n\n if (this.#opts.pronunciationDictionaryLocators) {\n initPkt.pronunciation_dictionary_locators =\n this.#opts.pronunciationDictionaryLocators.map((locator) => ({\n pronunciation_dictionary_id: locator.pronunciation_dictionary_id,\n version_id: locator.version_id,\n }));\n }\n\n const initPktStr = JSON.stringify(initPkt);\n this.#ws.send(initPktStr);\n this.#activeContexts.add(content.contextId);\n }\n\n const pkt: Record<string, unknown> = {\n text: content.text,\n context_id: content.contextId,\n };\n if (content.flush) {\n pkt.flush = true;\n }\n\n const pktStr = JSON.stringify(pkt);\n this.#ws.send(pktStr);\n } else {\n // CloseContext\n const closeMsg = msg as CloseContext;\n if (this.#activeContexts.has(closeMsg.contextId)) {\n const closePkt = {\n context_id: closeMsg.contextId,\n close_context: true,\n };\n const closePktStr = JSON.stringify(closePkt);\n this.#ws.send(closePktStr);\n }\n }\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'send loop error');\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n async #recvLoop(): Promise<void> {\n try {\n const messageChannel = stream.createStreamChannel<Record<string, unknown>>();\n const errorFuture = new Future<Error>();\n\n const onMessage = (rawData: Buffer) => {\n try {\n const parsed = JSON.parse(rawData.toString());\n messageChannel.write(parsed);\n } catch (e) {\n this.#logger.warn({ error: e }, 'failed to parse WebSocket message');\n }\n };\n\n const onClose = () => {\n if (!this.#closed && this.#contextData.size > 0) {\n this.#logger.warn('websocket closed unexpectedly');\n }\n messageChannel.close();\n };\n\n const onError = (error: Error) => {\n errorFuture.resolve(error);\n messageChannel.close();\n };\n\n // Set up persistent listeners\n if (!this.#ws) return;\n this.#ws.on('message', onMessage);\n this.#ws.on('close', onClose);\n this.#ws.on('error', onError);\n\n const cleanup = () => {\n this.#ws?.off('message', onMessage);\n this.#ws?.off('close', onClose);\n this.#ws?.off('error', onError);\n };\n\n const reader = messageChannel.stream().getReader();\n try {\n while (!this.#closed) {\n const result = await reader.read();\n if (result.done || this.#closed) break;\n\n const data = result.value;\n const contextId = data.contextId as string | undefined;\n const ctx = contextId ? this.#contextData.get(contextId) : undefined;\n\n if (data.error) {\n this.#logger.error(\n { context_id: contextId, error: data.error, data },\n 'elevenlabs tts returned error',\n );\n if (contextId) {\n if (ctx) {\n ctx.waiter.reject(new APIError(data.error as string));\n }\n this.#cleanupContext(contextId);\n }\n continue;\n }\n\n if (!ctx) {\n this.#logger.warn({ data }, 'unexpected message received from elevenlabs tts');\n continue;\n }\n\n const stream = ctx.stream;\n\n // Process alignment data\n const alignment =\n this.#opts.preferredAlignment === 'normalized'\n ? (data.normalizedAlignment as Record<string, unknown>)\n : (data.alignment as Record<string, unknown>);\n\n if (alignment && stream) {\n const chars = alignment.chars as string[] | undefined;\n const starts = (alignment.charStartTimesMs || alignment.charsStartTimesMs) as\n | number[]\n | undefined;\n const durs = (alignment.charDurationsMs || alignment.charsDurationsMs) as\n | number[]\n | undefined;\n\n if (\n chars &&\n starts &&\n durs &&\n chars.length === durs.length &&\n starts.length === durs.length\n ) {\n ctx.textBuffer += chars.join('');\n\n // Handle chars with multiple characters\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const start = starts[i]!;\n const dur = durs[i]!;\n\n if (char.length > 1) {\n for (let j = 0; j < char.length - 1; j++) {\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(0);\n }\n }\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(dur);\n }\n\n const [timedWords, remainingText] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n );\n\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n\n ctx.textBuffer = remainingText;\n ctx.startTimesMs = ctx.startTimesMs.slice(-remainingText.length);\n ctx.durationsMs = ctx.durationsMs.slice(-remainingText.length);\n }\n }\n\n if (data.audio) {\n const audioData = Buffer.from(data.audio as string, 'base64');\n stream.pushAudio(audioData);\n }\n\n if (data.isFinal) {\n // Flush remaining alignment data\n if (ctx.textBuffer) {\n const [timedWords] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n true,\n );\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n }\n\n stream.markDone();\n ctx.waiter.resolve();\n this.#cleanupContext(contextId!);\n\n if (!this.#isCurrent && this.#activeContexts.size === 0) {\n this.#logger.debug('no active contexts, shutting down connection');\n break;\n }\n }\n }\n\n // Throw any error that occurred\n if (errorFuture.done) {\n throw await errorFuture.await;\n }\n } finally {\n reader.releaseLock();\n cleanup();\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'recv loop error');\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(e instanceof Error ? e : new Error(String(e)));\n }\n this.#contextData.clear();\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n #cleanupContext(contextId: string): void {\n this.#contextData.delete(contextId);\n this.#activeContexts.delete(contextId);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#inputQueueResolver?.();\n\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(new APIStatusError({ message: 'connection closed' }));\n }\n this.#contextData.clear();\n\n if (this.#ws) {\n this.#ws.close();\n this.#ws = null;\n }\n\n if (this.#sendTask) {\n await this.#sendTask.catch(() => {});\n }\n if (this.#recvTask) {\n await this.#recvTask.catch(() => {});\n }\n }\n}\n\nexport class TTS extends tts.TTS {\n #opts: ResolvedTTSOptions;\n #streams = new Set<SynthesizeStream>();\n #currentConnection: Connection | null = null;\n #connectionLock = new Mutex();\n #logger = log();\n\n label = 'elevenlabs.TTS';\n\n constructor(opts: TTSOptions = {}) {\n const autoMode = opts.autoMode ?? true;\n const encoding = opts.encoding ?? DEFAULT_ENCODING;\n const sampleRate = sampleRateFromFormat(encoding);\n\n super(sampleRate, 1, {\n streaming: true,\n });\n\n const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'ElevenLabs API key is required, either as argument or set ELEVEN_API_KEY environmental variable',\n );\n }\n\n let wordTokenizer = opts.wordTokenizer;\n if (!wordTokenizer) {\n wordTokenizer = autoMode\n ? new tokenize.basic.SentenceTokenizer()\n : new tokenize.basic.WordTokenizer(false);\n } else if (autoMode && !(wordTokenizer instanceof tokenize.SentenceTokenizer)) {\n this.#logger.warn(\n 'autoMode is enabled, it expects full sentences or phrases, ' +\n 'please provide a SentenceTokenizer instead of a WordTokenizer.',\n );\n }\n\n // Handle legacy options for backward compatibility\n const voiceId = opts.voiceId ?? opts.voice?.id ?? DEFAULT_VOICE_ID;\n const voiceSettings = opts.voiceSettings ?? opts.voice?.settings;\n const model = opts.model ?? opts.modelID ?? 'eleven_turbo_v2_5';\n const language = opts.language ?? opts.languageCode;\n\n this.#opts = {\n apiKey,\n voiceId,\n voiceSettings,\n model,\n language,\n baseURL: opts.baseURL ?? API_BASE_URL_V1,\n encoding,\n sampleRate,\n streamingLatency: opts.streamingLatency,\n wordTokenizer,\n chunkLengthSchedule: opts.chunkLengthSchedule,\n enableSsmlParsing: opts.enableSsmlParsing ?? false,\n enableLogging: opts.enableLogging ?? true,\n inactivityTimeout: opts.inactivityTimeout ?? WS_INACTIVITY_TIMEOUT,\n syncAlignment: opts.syncAlignment ?? true,\n applyTextNormalization: opts.applyTextNormalization ?? 'auto',\n preferredAlignment: opts.preferredAlignment ?? 'normalized',\n autoMode,\n pronunciationDictionaryLocators: opts.pronunciationDictionaryLocators,\n };\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'ElevenLabs';\n }\n\n async listVoices(): Promise<Voice[]> {\n const response = await fetch(`${this.#opts.baseURL}/voices`, {\n headers: { [AUTHORIZATION_HEADER]: this.#opts.apiKey },\n });\n const data = (await response.json()) as {\n voices: { voice_id: string; name: string; category: string }[];\n };\n return data.voices.map((v) => ({\n id: v.voice_id,\n name: v.name,\n category: v.category,\n }));\n }\n\n updateOptions(opts: {\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n }): void {\n let changed = false;\n\n if (opts.model !== undefined && opts.model !== this.#opts.model) {\n this.#opts.model = opts.model;\n changed = true;\n }\n\n if (opts.voiceId !== undefined && opts.voiceId !== this.#opts.voiceId) {\n this.#opts.voiceId = opts.voiceId;\n changed = true;\n }\n\n if (opts.voiceSettings !== undefined) {\n this.#opts.voiceSettings = opts.voiceSettings;\n changed = true;\n }\n\n if (opts.language !== undefined && opts.language !== this.#opts.language) {\n this.#opts.language = opts.language;\n changed = true;\n }\n\n if (opts.pronunciationDictionaryLocators !== undefined) {\n this.#opts.pronunciationDictionaryLocators = opts.pronunciationDictionaryLocators;\n changed = true;\n }\n\n if (changed && this.#currentConnection) {\n this.#currentConnection.markNonCurrent();\n this.#currentConnection = null;\n }\n }\n\n async currentConnection(): Promise<Connection> {\n const unlock = await this.#connectionLock.lock();\n try {\n if (\n this.#currentConnection &&\n this.#currentConnection.isCurrent &&\n !this.#currentConnection.closed\n ) {\n return this.#currentConnection;\n }\n\n const conn = new Connection({ ...this.#opts });\n await conn.connect();\n this.#currentConnection = conn;\n return conn;\n } finally {\n unlock();\n }\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(this, text, { ...this.#opts });\n }\n\n stream(): SynthesizeStream {\n const stream = new SynthesizeStream(this, { ...this.#opts });\n this.#streams.add(stream);\n return stream;\n }\n\n async close(): Promise<void> {\n for (const stream of this.#streams) {\n stream.close();\n }\n this.#streams.clear();\n\n if (this.#currentConnection) {\n await this.#currentConnection.close();\n this.#currentConnection = null;\n }\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #logger = log();\n\n label = 'elevenlabs.ChunkedStream';\n\n constructor(tts: TTS, text: string, opts: ResolvedTTSOptions) {\n super(text, tts);\n this.#tts = tts;\n this.#opts = opts;\n }\n\n protected async run(): Promise<void> {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : undefined;\n\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n try {\n const response = await fetch(synthesizeUrl(this.#opts), {\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n text: this.inputText,\n model_id: this.#opts.model,\n voice_settings: voiceSettings,\n }),\n signal: this.abortSignal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new APIStatusError({\n message: `ElevenLabs API error: ${errorText}`,\n options: { statusCode: response.status },\n });\n }\n\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.startsWith('audio/')) {\n const content = await response.text();\n throw new APIError(`ElevenLabs returned non-audio data: ${content}`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new APIError('No response body');\n }\n\n let lastFrame: AudioFrame | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n for (const frame of bstream.write(value.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Flush remaining data\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: true });\n }\n } catch (e) {\n if (e instanceof APIError) {\n throw e;\n }\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw new APIConnectionError({ message: `Connection error: ${e}` });\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #contextId: string;\n #sentTokenizerStream: tokenize.SentenceStream | tokenize.WordStream;\n #logger = log();\n #audioQueue: Buffer[] = [];\n #timedTranscriptQueue: TimedString[] = [];\n #streamDone = false;\n\n label = 'elevenlabs.SynthesizeStream';\n\n constructor(tts: TTS, opts: ResolvedTTSOptions) {\n super(tts);\n this.#tts = tts;\n this.#opts = opts;\n this.#contextId = shortuuid();\n this.#sentTokenizerStream = this.#opts.wordTokenizer.stream();\n }\n\n get contextId(): string {\n return this.#contextId;\n }\n\n pushAudio(data: Buffer): void {\n // Don't push if stream is closed/aborted\n if (this.closed || this.abortController.signal.aborted) {\n return;\n }\n this.#audioQueue.push(data);\n }\n\n pushTimedTranscript(timedWords: TimedString[]): void {\n this.#timedTranscriptQueue.push(...timedWords);\n }\n\n markDone(): void {\n this.#streamDone = true;\n }\n\n protected async run(): Promise<void> {\n const requestId = this.#contextId;\n const segmentId = this.#contextId;\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n let connection: Connection;\n try {\n connection = await this.#tts.currentConnection();\n } catch (e) {\n throw new APIConnectionError({ message: 'could not connect to ElevenLabs' });\n }\n\n let waiterReject: ((reason: Error) => void) | undefined;\n const waiterPromise = new Promise<void>((resolve, reject) => {\n waiterReject = reject;\n connection.registerStream(this, { resolve, reject });\n });\n\n // Handle abort - reject the waiter so Promise.all can complete\n const abortHandler = () => {\n if (waiterReject) {\n waiterReject(new Error('Stream aborted'));\n }\n };\n this.abortController.signal.addEventListener('abort', abortHandler, { once: true });\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#sentTokenizerStream.flush();\n continue;\n }\n this.#sentTokenizerStream.pushText(data);\n }\n this.#sentTokenizerStream.endInput();\n };\n\n const sentenceStreamTask = async () => {\n const flushOnChunk =\n this.#opts.wordTokenizer instanceof tokenize.SentenceTokenizer && this.#opts.autoMode;\n\n let xmlContent: string[] = [];\n\n for await (const data of this.#sentTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n let text = data.token;\n const xmlStartTokens = ['<phoneme', '<break'];\n const xmlEndTokens = ['</phoneme>', '/>'];\n\n if (\n (this.#opts.enableSsmlParsing &&\n xmlStartTokens.some((start) => text.startsWith(start))) ||\n xmlContent.length > 0\n ) {\n xmlContent.push(text);\n\n if (xmlEndTokens.some((end) => text.includes(end))) {\n text = xmlContent.join(' ');\n xmlContent = [];\n } else {\n continue;\n }\n }\n\n const formattedText = `${text} `; // must always end with a space\n connection.sendContent({\n contextId: this.#contextId,\n text: formattedText,\n flush: flushOnChunk,\n });\n }\n\n if (xmlContent.length > 0) {\n this.#logger.warn('ElevenLabs stream ended with incomplete xml content');\n }\n\n // Send final empty text to signal end of input\n connection.sendContent({ contextId: this.#contextId, text: '', flush: true });\n connection.closeContext(this.#contextId);\n };\n\n const audioProcessTask = async () => {\n let lastFrame: AudioFrame | undefined;\n\n while (!this.abortController.signal.aborted) {\n // Process audio queue\n while (this.#audioQueue.length > 0) {\n const audioData = this.#audioQueue.shift()!;\n for (const frame of bstream.write(audioData.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Exit when stream is done and queue is empty\n if (this.#streamDone && this.#audioQueue.length === 0) {\n break;\n }\n\n // Small delay to avoid busy waiting\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n // Flush remaining\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: true });\n }\n };\n\n try {\n await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);\n } catch (e) {\n // If aborted, this is a normal termination - don't throw\n if (this.abortController.signal.aborted) {\n return;\n }\n\n if (e instanceof APITimeoutError) {\n throw e;\n }\n if (e instanceof APIStatusError) {\n throw e;\n }\n throw new APIStatusError({ message: 'Could not synthesize' });\n } finally {\n // Clean up abort listener\n this.abortController.signal.removeEventListener('abort', abortHandler);\n }\n }\n\n close(): void {\n // Clear audio buffers to prevent memory leak\n this.#audioQueue.length = 0;\n this.#timedTranscriptQueue.length = 0;\n this.#streamDone = true;\n this.#sentTokenizerStream.close();\n super.close();\n }\n}\n"],"mappings":"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","names":["stream","tts"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n APIConnectionError,\n APIError,\n APIStatusError,\n APITimeoutError,\n AudioByteStream,\n Future,\n type TimedString,\n createTimedString,\n log,\n shortuuid,\n stream,\n tokenize,\n tts,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { TTSEncoding, TTSModels } from './models.js';\n\nconst DEFAULT_VOICE_ID = 'bIHbv24MWmeRgasZH58o';\nconst API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1';\nconst AUTHORIZATION_HEADER = 'xi-api-key';\nconst WS_INACTIVITY_TIMEOUT = 180;\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_22050';\n\nexport interface VoiceSettings {\n stability: number; // [0.0 - 1.0]\n similarity_boost: number; // [0.0 - 1.0]\n style?: number; // [0.0 - 1.0]\n speed?: number; // [0.8 - 1.2]\n use_speaker_boost?: boolean;\n}\n\nexport interface Voice {\n id: string;\n name: string;\n category: string;\n settings?: VoiceSettings;\n}\n\nexport interface PronunciationDictionaryLocator {\n pronunciation_dictionary_id: string;\n version_id: string;\n}\n\nexport interface TTSOptions {\n apiKey?: string;\n // New interface\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n // Legacy interface (backward compatibility)\n voice?: Voice;\n modelID?: TTSModels | string;\n languageCode?: string;\n // Common options\n baseURL?: string;\n encoding?: TTSEncoding;\n streamingLatency?: number;\n wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing?: boolean;\n enableLogging?: boolean;\n inactivityTimeout?: number;\n syncAlignment?: boolean;\n applyTextNormalization?: 'auto' | 'on' | 'off';\n preferredAlignment?: 'normalized' | 'original';\n autoMode?: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal options type with resolved defaults\ninterface ResolvedTTSOptions {\n apiKey: string;\n voiceId: string;\n voiceSettings?: VoiceSettings;\n model: TTSModels | string;\n language?: string;\n baseURL: string;\n encoding: TTSEncoding;\n sampleRate: number;\n streamingLatency?: number;\n wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing: boolean;\n enableLogging: boolean;\n inactivityTimeout: number;\n syncAlignment: boolean;\n applyTextNormalization: 'auto' | 'on' | 'off';\n preferredAlignment: 'normalized' | 'original';\n autoMode: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal types for connection management\ninterface SynthesizeContent {\n contextId: string;\n text: string;\n flush: boolean;\n}\n\ninterface CloseContext {\n contextId: string;\n}\n\ninterface StreamData {\n stream: SynthesizeStream;\n waiter: {\n resolve: (value: void) => void;\n reject: (error: Error) => void;\n };\n textBuffer: string;\n startTimesMs: number[];\n durationsMs: number[];\n /** First word offset for timestamp normalization (removes leading silence) */\n firstWordOffsetMs: number | null;\n}\n\ntype ConnectionMessage = SynthesizeContent | CloseContext;\n\n// Helper Functions\n\nfunction sampleRateFromFormat(encoding: TTSEncoding): number {\n const split = encoding.split('_');\n return parseInt(split[1]!, 10);\n}\n\nfunction synthesizeUrl(opts: ResolvedTTSOptions): string {\n const { baseURL, voiceId, model, encoding, streamingLatency } = opts;\n let url = `${baseURL}/text-to-speech/${voiceId}/stream?model_id=${model}&output_format=${encoding}`;\n if (streamingLatency !== undefined) {\n url += `&optimize_streaming_latency=${streamingLatency}`;\n }\n return url;\n}\n\nfunction multiStreamUrl(opts: ResolvedTTSOptions): string {\n const baseURL = opts.baseURL.replace('https://', 'wss://').replace('http://', 'ws://');\n const params: string[] = [];\n params.push(`model_id=${opts.model}`);\n params.push(`output_format=${opts.encoding}`);\n if (opts.language) {\n params.push(`language_code=${opts.language}`);\n }\n params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);\n params.push(`enable_logging=${opts.enableLogging}`);\n params.push(`inactivity_timeout=${opts.inactivityTimeout}`);\n params.push(`apply_text_normalization=${opts.applyTextNormalization}`);\n if (opts.syncAlignment) {\n params.push('sync_alignment=true');\n }\n if (opts.autoMode !== undefined) {\n params.push(`auto_mode=${opts.autoMode}`);\n }\n return `${baseURL}/text-to-speech/${opts.voiceId}/multi-stream-input?${params.join('&')}`;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const result: Partial<T> = {};\n for (const key in obj) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Convert alignment data to timed words.\n * Returns the timed words and remaining text buffer.\n *\n * @param firstWordOffsetMs - Optional offset to normalize timestamps (subtract from all).\n * ElevenLabs returns absolute timestamps from the start of TTS audio, which may include\n * leading silence. By normalizing to 0, we ensure proper sync with the synchronizer.\n */\nfunction toTimedWords(\n text: string,\n startTimesMs: number[],\n durationsMs: number[],\n flush: boolean = false,\n firstWordOffsetMs: number = 0,\n): [TimedString[], string] {\n if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {\n return [[], text || ''];\n }\n\n const lastStartTime = startTimesMs[startTimesMs.length - 1]!;\n const lastDuration = durationsMs[durationsMs.length - 1]!;\n const timestamps = [...startTimesMs, lastStartTime + lastDuration];\n\n const words = tokenize.basic.splitWords(text, false);\n const timedWords: TimedString[] = [];\n\n if (words.length === 0) {\n return [[], text];\n }\n\n const startIndices = words.map((w) => w[1]);\n let end = 0;\n\n // We don't know if the last word is complete, always leave it as remaining\n for (let i = 0; i < startIndices.length - 1; i++) {\n const start = startIndices[i]!;\n const nextStart = startIndices[i + 1]!;\n end = nextStart;\n // Normalize timestamps by subtracting the first word offset\n const startT = Math.max(0, (timestamps[start] ?? 0) - firstWordOffsetMs) / 1000;\n const endT = Math.max(0, (timestamps[nextStart] ?? 0) - firstWordOffsetMs) / 1000;\n timedWords.push(\n createTimedString({\n text: text.slice(start, nextStart),\n startTime: startT,\n endTime: endT,\n }),\n );\n }\n\n if (flush && words.length > 0) {\n const lastWordStart = startIndices[startIndices.length - 1]!;\n const startT = Math.max(0, (timestamps[lastWordStart] ?? 0) - firstWordOffsetMs) / 1000;\n const endT = Math.max(0, (timestamps[timestamps.length - 1] ?? 0) - firstWordOffsetMs) / 1000;\n timedWords.push(\n createTimedString({\n text: text.slice(lastWordStart),\n startTime: startT,\n endTime: endT,\n }),\n );\n end = text.length;\n } else if (words.length > 0) {\n end = startIndices[startIndices.length - 1]!;\n }\n\n return [timedWords, text.slice(end)];\n}\n\nclass Connection {\n #opts: ResolvedTTSOptions;\n #ws: WebSocket | null = null;\n #isCurrent = true;\n #activeContexts = new Set<string>();\n #inputQueue: ConnectionMessage[] = [];\n #contextData = new Map<string, StreamData>();\n #sendTask: Promise<void> | null = null;\n #recvTask: Promise<void> | null = null;\n #closed = false;\n #logger = log();\n #inputQueueResolver: (() => void) | null = null;\n\n constructor(opts: ResolvedTTSOptions) {\n this.#opts = opts;\n }\n\n get voiceId(): string {\n return this.#opts.voiceId;\n }\n\n get isCurrent(): boolean {\n return this.#isCurrent;\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n markNonCurrent(): void {\n this.#isCurrent = false;\n }\n\n async connect(): Promise<void> {\n if (this.#ws || this.#closed) {\n return;\n }\n\n const url = multiStreamUrl(this.#opts);\n const headers = { [AUTHORIZATION_HEADER]: this.#opts.apiKey };\n\n return new Promise((resolve, reject) => {\n this.#ws = new WebSocket(url, { headers });\n\n this.#ws.on('open', () => {\n this.#sendTask = this.#sendLoop();\n this.#recvTask = this.#recvLoop();\n resolve();\n });\n\n this.#ws.on('error', (error) => {\n this.#logger.error({ error }, 'WebSocket connection error');\n reject(new APIConnectionError({ message: `WebSocket error: ${error.message}` }));\n });\n });\n }\n\n registerStream(\n stream: SynthesizeStream,\n waiter: { resolve: (value: void) => void; reject: (error: Error) => void },\n ): void {\n const contextId = stream.contextId;\n this.#contextData.set(contextId, {\n stream,\n waiter,\n textBuffer: '',\n startTimesMs: [],\n durationsMs: [],\n firstWordOffsetMs: null,\n });\n }\n\n sendContent(content: SynthesizeContent): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push(content);\n this.#inputQueueResolver?.();\n }\n\n closeContext(contextId: string): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push({ contextId });\n this.#inputQueueResolver?.();\n }\n\n async #sendLoop(): Promise<void> {\n try {\n while (!this.#closed) {\n // Wait for messages in queue\n if (this.#inputQueue.length === 0) {\n await new Promise<void>((resolve) => {\n this.#inputQueueResolver = resolve;\n });\n this.#inputQueueResolver = null;\n }\n\n if (this.#closed) break;\n\n const msg = this.#inputQueue.shift();\n if (!msg) continue;\n\n if (!this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n break;\n }\n\n if ('text' in msg) {\n // SynthesizeContent\n const content = msg as SynthesizeContent;\n const isNewContext = !this.#activeContexts.has(content.contextId);\n\n // If not current and this is a new context, ignore it\n if (!this.#isCurrent && isNewContext) {\n continue;\n }\n\n if (isNewContext) {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : {};\n\n const initPkt: Record<string, unknown> = {\n text: ' ',\n voice_settings: voiceSettings,\n context_id: content.contextId,\n };\n\n if (this.#opts.pronunciationDictionaryLocators) {\n initPkt.pronunciation_dictionary_locators =\n this.#opts.pronunciationDictionaryLocators.map((locator) => ({\n pronunciation_dictionary_id: locator.pronunciation_dictionary_id,\n version_id: locator.version_id,\n }));\n }\n\n const initPktStr = JSON.stringify(initPkt);\n this.#ws.send(initPktStr);\n this.#activeContexts.add(content.contextId);\n }\n\n const pkt: Record<string, unknown> = {\n text: content.text,\n context_id: content.contextId,\n };\n if (content.flush) {\n pkt.flush = true;\n }\n\n const pktStr = JSON.stringify(pkt);\n this.#ws.send(pktStr);\n } else {\n // CloseContext\n const closeMsg = msg as CloseContext;\n if (this.#activeContexts.has(closeMsg.contextId)) {\n const closePkt = {\n context_id: closeMsg.contextId,\n close_context: true,\n };\n const closePktStr = JSON.stringify(closePkt);\n this.#ws.send(closePktStr);\n }\n }\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'send loop error');\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n async #recvLoop(): Promise<void> {\n try {\n const messageChannel = stream.createStreamChannel<Record<string, unknown>>();\n const errorFuture = new Future<Error>();\n\n const onMessage = (rawData: Buffer) => {\n try {\n const parsed = JSON.parse(rawData.toString());\n messageChannel.write(parsed);\n } catch (e) {\n this.#logger.warn({ error: e }, 'failed to parse WebSocket message');\n }\n };\n\n const onClose = () => {\n if (!this.#closed && this.#contextData.size > 0) {\n this.#logger.warn('websocket closed unexpectedly');\n }\n messageChannel.close();\n };\n\n const onError = (error: Error) => {\n errorFuture.resolve(error);\n messageChannel.close();\n };\n\n // Set up persistent listeners\n if (!this.#ws) return;\n this.#ws.on('message', onMessage);\n this.#ws.on('close', onClose);\n this.#ws.on('error', onError);\n\n const cleanup = () => {\n this.#ws?.off('message', onMessage);\n this.#ws?.off('close', onClose);\n this.#ws?.off('error', onError);\n };\n\n const reader = messageChannel.stream().getReader();\n try {\n while (!this.#closed) {\n const result = await reader.read();\n if (result.done || this.#closed) break;\n\n const data = result.value;\n const contextId = data.contextId as string | undefined;\n const ctx = contextId ? this.#contextData.get(contextId) : undefined;\n\n if (data.error) {\n this.#logger.error(\n { context_id: contextId, error: data.error, data },\n 'elevenlabs tts returned error',\n );\n if (contextId) {\n if (ctx) {\n ctx.waiter.reject(new APIError(data.error as string));\n }\n this.#cleanupContext(contextId);\n }\n continue;\n }\n\n if (!ctx) {\n this.#logger.warn({ data }, 'unexpected message received from elevenlabs tts');\n continue;\n }\n\n const stream = ctx.stream;\n\n // Process alignment data\n const alignment =\n this.#opts.preferredAlignment === 'normalized'\n ? (data.normalizedAlignment as Record<string, unknown>)\n : (data.alignment as Record<string, unknown>);\n\n if (alignment && stream) {\n const chars = alignment.chars as string[] | undefined;\n const starts = (alignment.charStartTimesMs || alignment.charsStartTimesMs) as\n | number[]\n | undefined;\n const durs = (alignment.charDurationsMs || alignment.charsDurationsMs) as\n | number[]\n | undefined;\n\n if (\n chars &&\n starts &&\n durs &&\n chars.length === durs.length &&\n starts.length === durs.length\n ) {\n ctx.textBuffer += chars.join('');\n\n // Handle chars with multiple characters\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const start = starts[i]!;\n const dur = durs[i]!;\n\n // Capture the first word's start time for normalization\n // This removes leading silence from timestamps\n if (ctx.firstWordOffsetMs === null && start > 0) {\n ctx.firstWordOffsetMs = start;\n }\n\n if (char.length > 1) {\n for (let j = 0; j < char.length - 1; j++) {\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(0);\n }\n }\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(dur);\n }\n\n const [timedWords, remainingText] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n false,\n ctx.firstWordOffsetMs ?? 0,\n );\n\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n\n ctx.textBuffer = remainingText;\n ctx.startTimesMs = ctx.startTimesMs.slice(-remainingText.length);\n ctx.durationsMs = ctx.durationsMs.slice(-remainingText.length);\n }\n }\n\n if (data.audio) {\n const audioData = Buffer.from(data.audio as string, 'base64');\n stream.pushAudio(audioData);\n }\n\n if (data.isFinal) {\n // Flush remaining alignment data\n if (ctx.textBuffer) {\n const [timedWords] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n true,\n ctx.firstWordOffsetMs ?? 0,\n );\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n }\n\n stream.markDone();\n ctx.waiter.resolve();\n this.#cleanupContext(contextId!);\n\n if (!this.#isCurrent && this.#activeContexts.size === 0) {\n this.#logger.debug('no active contexts, shutting down connection');\n break;\n }\n }\n }\n\n // Throw any error that occurred\n if (errorFuture.done) {\n throw await errorFuture.await;\n }\n } finally {\n reader.releaseLock();\n cleanup();\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'recv loop error');\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(e instanceof Error ? e : new Error(String(e)));\n }\n this.#contextData.clear();\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n #cleanupContext(contextId: string): void {\n this.#contextData.delete(contextId);\n this.#activeContexts.delete(contextId);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#inputQueueResolver?.();\n\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(new APIStatusError({ message: 'connection closed' }));\n }\n this.#contextData.clear();\n\n if (this.#ws) {\n this.#ws.close();\n this.#ws = null;\n }\n\n if (this.#sendTask) {\n await this.#sendTask.catch(() => {});\n }\n if (this.#recvTask) {\n await this.#recvTask.catch(() => {});\n }\n }\n}\n\nexport class TTS extends tts.TTS {\n #opts: ResolvedTTSOptions;\n #streams = new Set<SynthesizeStream>();\n #currentConnection: Connection | null = null;\n #connectionLock = new Mutex();\n #logger = log();\n\n label = 'elevenlabs.TTS';\n\n constructor(opts: TTSOptions = {}) {\n const autoMode = opts.autoMode ?? true;\n const encoding = opts.encoding ?? DEFAULT_ENCODING;\n const sampleRate = sampleRateFromFormat(encoding);\n const syncAlignment = opts.syncAlignment ?? true;\n\n super(sampleRate, 1, {\n streaming: true,\n alignedTranscript: syncAlignment,\n });\n\n const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'ElevenLabs API key is required, either as argument or set ELEVEN_API_KEY environmental variable',\n );\n }\n\n let wordTokenizer = opts.wordTokenizer;\n if (!wordTokenizer) {\n wordTokenizer = autoMode\n ? new tokenize.basic.SentenceTokenizer()\n : new tokenize.basic.WordTokenizer(false);\n } else if (autoMode && !(wordTokenizer instanceof tokenize.SentenceTokenizer)) {\n this.#logger.warn(\n 'autoMode is enabled, it expects full sentences or phrases, ' +\n 'please provide a SentenceTokenizer instead of a WordTokenizer.',\n );\n }\n\n // Handle legacy options for backward compatibility\n const voiceId = opts.voiceId ?? opts.voice?.id ?? DEFAULT_VOICE_ID;\n const voiceSettings = opts.voiceSettings ?? opts.voice?.settings;\n const model = opts.model ?? opts.modelID ?? 'eleven_turbo_v2_5';\n const language = opts.language ?? opts.languageCode;\n\n this.#opts = {\n apiKey,\n voiceId,\n voiceSettings,\n model,\n language,\n baseURL: opts.baseURL ?? API_BASE_URL_V1,\n encoding,\n sampleRate,\n streamingLatency: opts.streamingLatency,\n wordTokenizer,\n chunkLengthSchedule: opts.chunkLengthSchedule,\n enableSsmlParsing: opts.enableSsmlParsing ?? false,\n enableLogging: opts.enableLogging ?? true,\n inactivityTimeout: opts.inactivityTimeout ?? WS_INACTIVITY_TIMEOUT,\n syncAlignment: opts.syncAlignment ?? true,\n applyTextNormalization: opts.applyTextNormalization ?? 'auto',\n preferredAlignment: opts.preferredAlignment ?? 'normalized',\n autoMode,\n pronunciationDictionaryLocators: opts.pronunciationDictionaryLocators,\n };\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'ElevenLabs';\n }\n\n async listVoices(): Promise<Voice[]> {\n const response = await fetch(`${this.#opts.baseURL}/voices`, {\n headers: { [AUTHORIZATION_HEADER]: this.#opts.apiKey },\n });\n const data = (await response.json()) as {\n voices: { voice_id: string; name: string; category: string }[];\n };\n return data.voices.map((v) => ({\n id: v.voice_id,\n name: v.name,\n category: v.category,\n }));\n }\n\n updateOptions(opts: {\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n }): void {\n let changed = false;\n\n if (opts.model !== undefined && opts.model !== this.#opts.model) {\n this.#opts.model = opts.model;\n changed = true;\n }\n\n if (opts.voiceId !== undefined && opts.voiceId !== this.#opts.voiceId) {\n this.#opts.voiceId = opts.voiceId;\n changed = true;\n }\n\n if (opts.voiceSettings !== undefined) {\n this.#opts.voiceSettings = opts.voiceSettings;\n changed = true;\n }\n\n if (opts.language !== undefined && opts.language !== this.#opts.language) {\n this.#opts.language = opts.language;\n changed = true;\n }\n\n if (opts.pronunciationDictionaryLocators !== undefined) {\n this.#opts.pronunciationDictionaryLocators = opts.pronunciationDictionaryLocators;\n changed = true;\n }\n\n if (changed && this.#currentConnection) {\n this.#currentConnection.markNonCurrent();\n this.#currentConnection = null;\n }\n }\n\n async currentConnection(): Promise<Connection> {\n const unlock = await this.#connectionLock.lock();\n try {\n if (\n this.#currentConnection &&\n this.#currentConnection.isCurrent &&\n !this.#currentConnection.closed\n ) {\n return this.#currentConnection;\n }\n\n const conn = new Connection({ ...this.#opts });\n await conn.connect();\n this.#currentConnection = conn;\n return conn;\n } finally {\n unlock();\n }\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(this, text, { ...this.#opts });\n }\n\n stream(): SynthesizeStream {\n const stream = new SynthesizeStream(this, { ...this.#opts });\n this.#streams.add(stream);\n return stream;\n }\n\n async close(): Promise<void> {\n for (const stream of this.#streams) {\n stream.close();\n }\n this.#streams.clear();\n\n if (this.#currentConnection) {\n await this.#currentConnection.close();\n this.#currentConnection = null;\n }\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #logger = log();\n\n label = 'elevenlabs.ChunkedStream';\n\n constructor(tts: TTS, text: string, opts: ResolvedTTSOptions) {\n super(text, tts);\n this.#tts = tts;\n this.#opts = opts;\n }\n\n protected async run(): Promise<void> {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : undefined;\n\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n try {\n const response = await fetch(synthesizeUrl(this.#opts), {\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n text: this.inputText,\n model_id: this.#opts.model,\n voice_settings: voiceSettings,\n }),\n signal: this.abortSignal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new APIStatusError({\n message: `ElevenLabs API error: ${errorText}`,\n options: { statusCode: response.status },\n });\n }\n\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.startsWith('audio/')) {\n const content = await response.text();\n throw new APIError(`ElevenLabs returned non-audio data: ${content}`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new APIError('No response body');\n }\n\n let lastFrame: AudioFrame | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n for (const frame of bstream.write(value.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Flush remaining data\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: true });\n }\n } catch (e) {\n if (e instanceof APIError) {\n throw e;\n }\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw new APIConnectionError({ message: `Connection error: ${e}` });\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #contextId: string;\n #sentTokenizerStream: tokenize.SentenceStream | tokenize.WordStream;\n #logger = log();\n #audioQueue: Buffer[] = [];\n #timedTranscriptQueue: TimedString[] = [];\n #streamDone = false;\n\n label = 'elevenlabs.SynthesizeStream';\n\n constructor(tts: TTS, opts: ResolvedTTSOptions) {\n super(tts);\n this.#tts = tts;\n this.#opts = opts;\n this.#contextId = shortuuid();\n this.#sentTokenizerStream = this.#opts.wordTokenizer.stream();\n }\n\n get contextId(): string {\n return this.#contextId;\n }\n\n pushAudio(data: Buffer): void {\n // Don't push if stream is closed/aborted\n if (this.closed || this.abortController.signal.aborted) {\n return;\n }\n this.#audioQueue.push(data);\n }\n\n pushTimedTranscript(timedWords: TimedString[]): void {\n this.#timedTranscriptQueue.push(...timedWords);\n }\n\n markDone(): void {\n this.#streamDone = true;\n }\n\n protected async run(): Promise<void> {\n const requestId = this.#contextId;\n const segmentId = this.#contextId;\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n let connection: Connection;\n try {\n connection = await this.#tts.currentConnection();\n } catch (e) {\n throw new APIConnectionError({ message: 'could not connect to ElevenLabs' });\n }\n\n let waiterReject: ((reason: Error) => void) | undefined;\n const waiterPromise = new Promise<void>((resolve, reject) => {\n waiterReject = reject;\n connection.registerStream(this, { resolve, reject });\n });\n\n // Handle abort - reject the waiter so Promise.all can complete\n const abortHandler = () => {\n if (waiterReject) {\n waiterReject(new Error('Stream aborted'));\n }\n };\n this.abortController.signal.addEventListener('abort', abortHandler, { once: true });\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#sentTokenizerStream.flush();\n continue;\n }\n this.#sentTokenizerStream.pushText(data);\n }\n this.#sentTokenizerStream.endInput();\n };\n\n const sentenceStreamTask = async () => {\n const flushOnChunk =\n this.#opts.wordTokenizer instanceof tokenize.SentenceTokenizer && this.#opts.autoMode;\n\n let xmlContent: string[] = [];\n\n for await (const data of this.#sentTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n let text = data.token;\n const xmlStartTokens = ['<phoneme', '<break'];\n const xmlEndTokens = ['</phoneme>', '/>'];\n\n if (\n (this.#opts.enableSsmlParsing &&\n xmlStartTokens.some((start) => text.startsWith(start))) ||\n xmlContent.length > 0\n ) {\n xmlContent.push(text);\n\n if (xmlEndTokens.some((end) => text.includes(end))) {\n text = xmlContent.join(' ');\n xmlContent = [];\n } else {\n continue;\n }\n }\n\n const formattedText = `${text} `; // must always end with a space\n connection.sendContent({\n contextId: this.#contextId,\n text: formattedText,\n flush: flushOnChunk,\n });\n }\n\n if (xmlContent.length > 0) {\n this.#logger.warn('ElevenLabs stream ended with incomplete xml content');\n }\n\n // Send final empty text to signal end of input\n connection.sendContent({ contextId: this.#contextId, text: '', flush: true });\n connection.closeContext(this.#contextId);\n };\n\n const audioProcessTask = async () => {\n let lastFrame: AudioFrame | undefined;\n let pendingTimedTranscripts: TimedString[] = [];\n\n const sendLastFrame = (final: boolean) => {\n if (lastFrame) {\n // Include timedTranscripts with the audio frame\n this.queue.put({\n requestId,\n segmentId,\n frame: lastFrame,\n final,\n timedTranscripts:\n pendingTimedTranscripts.length > 0 ? pendingTimedTranscripts : undefined,\n });\n lastFrame = undefined;\n pendingTimedTranscripts = [];\n }\n };\n\n while (!this.abortController.signal.aborted) {\n // Drain timed transcript queue\n while (this.#timedTranscriptQueue.length > 0) {\n pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift()!);\n }\n\n // Process audio queue\n while (this.#audioQueue.length > 0) {\n const audioData = this.#audioQueue.shift()!;\n for (const frame of bstream.write(audioData.buffer)) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n }\n\n // Exit when stream is done and queue is empty\n if (this.#streamDone && this.#audioQueue.length === 0) {\n break;\n }\n\n // Small delay to avoid busy waiting\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n // Drain any remaining timed transcripts\n while (this.#timedTranscriptQueue.length > 0) {\n pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift()!);\n }\n\n // Flush remaining\n for (const frame of bstream.flush()) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n\n sendLastFrame(true);\n };\n\n try {\n await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);\n } catch (e) {\n // If aborted, this is a normal termination - don't throw\n if (this.abortController.signal.aborted) {\n return;\n }\n\n if (e instanceof APITimeoutError) {\n throw e;\n }\n if (e instanceof APIStatusError) {\n throw e;\n }\n throw new APIStatusError({ message: 'Could not synthesize' });\n } finally {\n // Clean up abort listener\n this.abortController.signal.removeEventListener('abort', abortHandler);\n }\n }\n\n close(): void {\n // Clear audio buffers to prevent memory leak\n this.#audioQueue.length = 0;\n this.#timedTranscriptQueue.length = 0;\n this.#streamDone = true;\n this.#sentTokenizerStream.close();\n super.close();\n }\n}\n"],"mappings":"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","names":["stream","tts"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-elevenlabs",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "ElevenLabs plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -30,9 +30,9 @@
30
30
  "@types/ws": "^8.5.10",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.0.0",
33
- "@livekit/agents-plugin-openai": "1.0.40",
34
- "@livekit/agents": "1.0.40",
35
- "@livekit/agents-plugins-test": "1.0.40"
33
+ "@livekit/agents": "1.0.41",
34
+ "@livekit/agents-plugin-openai": "1.0.41",
35
+ "@livekit/agents-plugins-test": "1.0.41"
36
36
  },
37
37
  "dependencies": {
38
38
  "@livekit/mutex": "^1.1.1",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@livekit/rtc-node": "^0.13.24",
43
- "@livekit/agents": "1.0.40"
43
+ "@livekit/agents": "1.0.41"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup --onSuccess \"pnpm build:types\"",
package/src/tts.ts CHANGED
@@ -8,20 +8,19 @@ import {
8
8
  APITimeoutError,
9
9
  AudioByteStream,
10
10
  Future,
11
+ type TimedString,
12
+ createTimedString,
11
13
  log,
12
14
  shortuuid,
13
15
  stream,
14
16
  tokenize,
15
17
  tts,
16
- type voice,
17
18
  } from '@livekit/agents';
18
19
  import { Mutex } from '@livekit/mutex';
19
20
  import type { AudioFrame } from '@livekit/rtc-node';
20
21
  import { WebSocket } from 'ws';
21
22
  import type { TTSEncoding, TTSModels } from './models.js';
22
23
 
23
- type TimedString = voice.TimedString;
24
-
25
24
  const DEFAULT_VOICE_ID = 'bIHbv24MWmeRgasZH58o';
26
25
  const API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1';
27
26
  const AUTHORIZATION_HEADER = 'xi-api-key';
@@ -118,6 +117,8 @@ interface StreamData {
118
117
  textBuffer: string;
119
118
  startTimesMs: number[];
120
119
  durationsMs: number[];
120
+ /** First word offset for timestamp normalization (removes leading silence) */
121
+ firstWordOffsetMs: number | null;
121
122
  }
122
123
 
123
124
  type ConnectionMessage = SynthesizeContent | CloseContext;
@@ -172,12 +173,17 @@ function stripUndefined<T extends object>(obj: T): Partial<T> {
172
173
  /**
173
174
  * Convert alignment data to timed words.
174
175
  * Returns the timed words and remaining text buffer.
176
+ *
177
+ * @param firstWordOffsetMs - Optional offset to normalize timestamps (subtract from all).
178
+ * ElevenLabs returns absolute timestamps from the start of TTS audio, which may include
179
+ * leading silence. By normalizing to 0, we ensure proper sync with the synchronizer.
175
180
  */
176
181
  function toTimedWords(
177
182
  text: string,
178
183
  startTimesMs: number[],
179
184
  durationsMs: number[],
180
185
  flush: boolean = false,
186
+ firstWordOffsetMs: number = 0,
181
187
  ): [TimedString[], string] {
182
188
  if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {
183
189
  return [[], text || ''];
@@ -202,24 +208,29 @@ function toTimedWords(
202
208
  const start = startIndices[i]!;
203
209
  const nextStart = startIndices[i + 1]!;
204
210
  end = nextStart;
205
- const startT = (timestamps[start] ?? 0) / 1000;
206
- const endT = (timestamps[nextStart] ?? 0) / 1000;
207
- timedWords.push({
208
- text: text.slice(start, nextStart),
209
- startTime: startT,
210
- endTime: endT,
211
- });
211
+ // Normalize timestamps by subtracting the first word offset
212
+ const startT = Math.max(0, (timestamps[start] ?? 0) - firstWordOffsetMs) / 1000;
213
+ const endT = Math.max(0, (timestamps[nextStart] ?? 0) - firstWordOffsetMs) / 1000;
214
+ timedWords.push(
215
+ createTimedString({
216
+ text: text.slice(start, nextStart),
217
+ startTime: startT,
218
+ endTime: endT,
219
+ }),
220
+ );
212
221
  }
213
222
 
214
223
  if (flush && words.length > 0) {
215
224
  const lastWordStart = startIndices[startIndices.length - 1]!;
216
- const startT = (timestamps[lastWordStart] ?? 0) / 1000;
217
- const endT = (timestamps[timestamps.length - 1] ?? 0) / 1000;
218
- timedWords.push({
219
- text: text.slice(lastWordStart),
220
- startTime: startT,
221
- endTime: endT,
222
- });
225
+ const startT = Math.max(0, (timestamps[lastWordStart] ?? 0) - firstWordOffsetMs) / 1000;
226
+ const endT = Math.max(0, (timestamps[timestamps.length - 1] ?? 0) - firstWordOffsetMs) / 1000;
227
+ timedWords.push(
228
+ createTimedString({
229
+ text: text.slice(lastWordStart),
230
+ startTime: startT,
231
+ endTime: endT,
232
+ }),
233
+ );
223
234
  end = text.length;
224
235
  } else if (words.length > 0) {
225
236
  end = startIndices[startIndices.length - 1]!;
@@ -296,6 +307,7 @@ class Connection {
296
307
  textBuffer: '',
297
308
  startTimesMs: [],
298
309
  durationsMs: [],
310
+ firstWordOffsetMs: null,
299
311
  });
300
312
  }
301
313
 
@@ -500,6 +512,12 @@ class Connection {
500
512
  const start = starts[i]!;
501
513
  const dur = durs[i]!;
502
514
 
515
+ // Capture the first word's start time for normalization
516
+ // This removes leading silence from timestamps
517
+ if (ctx.firstWordOffsetMs === null && start > 0) {
518
+ ctx.firstWordOffsetMs = start;
519
+ }
520
+
503
521
  if (char.length > 1) {
504
522
  for (let j = 0; j < char.length - 1; j++) {
505
523
  ctx.startTimesMs.push(start);
@@ -514,6 +532,8 @@ class Connection {
514
532
  ctx.textBuffer,
515
533
  ctx.startTimesMs,
516
534
  ctx.durationsMs,
535
+ false,
536
+ ctx.firstWordOffsetMs ?? 0,
517
537
  );
518
538
 
519
539
  if (timedWords.length > 0) {
@@ -539,6 +559,7 @@ class Connection {
539
559
  ctx.startTimesMs,
540
560
  ctx.durationsMs,
541
561
  true,
562
+ ctx.firstWordOffsetMs ?? 0,
542
563
  );
543
564
  if (timedWords.length > 0) {
544
565
  stream.pushTimedTranscript(timedWords);
@@ -622,9 +643,11 @@ export class TTS extends tts.TTS {
622
643
  const autoMode = opts.autoMode ?? true;
623
644
  const encoding = opts.encoding ?? DEFAULT_ENCODING;
624
645
  const sampleRate = sampleRateFromFormat(encoding);
646
+ const syncAlignment = opts.syncAlignment ?? true;
625
647
 
626
648
  super(sampleRate, 1, {
627
649
  streaming: true,
650
+ alignedTranscript: syncAlignment,
628
651
  });
629
652
 
630
653
  const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;
@@ -997,15 +1020,35 @@ export class SynthesizeStream extends tts.SynthesizeStream {
997
1020
 
998
1021
  const audioProcessTask = async () => {
999
1022
  let lastFrame: AudioFrame | undefined;
1023
+ let pendingTimedTranscripts: TimedString[] = [];
1024
+
1025
+ const sendLastFrame = (final: boolean) => {
1026
+ if (lastFrame) {
1027
+ // Include timedTranscripts with the audio frame
1028
+ this.queue.put({
1029
+ requestId,
1030
+ segmentId,
1031
+ frame: lastFrame,
1032
+ final,
1033
+ timedTranscripts:
1034
+ pendingTimedTranscripts.length > 0 ? pendingTimedTranscripts : undefined,
1035
+ });
1036
+ lastFrame = undefined;
1037
+ pendingTimedTranscripts = [];
1038
+ }
1039
+ };
1000
1040
 
1001
1041
  while (!this.abortController.signal.aborted) {
1042
+ // Drain timed transcript queue
1043
+ while (this.#timedTranscriptQueue.length > 0) {
1044
+ pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift()!);
1045
+ }
1046
+
1002
1047
  // Process audio queue
1003
1048
  while (this.#audioQueue.length > 0) {
1004
1049
  const audioData = this.#audioQueue.shift()!;
1005
1050
  for (const frame of bstream.write(audioData.buffer)) {
1006
- if (lastFrame) {
1007
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });
1008
- }
1051
+ sendLastFrame(false);
1009
1052
  lastFrame = frame;
1010
1053
  }
1011
1054
  }
@@ -1019,17 +1062,18 @@ export class SynthesizeStream extends tts.SynthesizeStream {
1019
1062
  await new Promise((resolve) => setTimeout(resolve, 10));
1020
1063
  }
1021
1064
 
1065
+ // Drain any remaining timed transcripts
1066
+ while (this.#timedTranscriptQueue.length > 0) {
1067
+ pendingTimedTranscripts.push(this.#timedTranscriptQueue.shift()!);
1068
+ }
1069
+
1022
1070
  // Flush remaining
1023
1071
  for (const frame of bstream.flush()) {
1024
- if (lastFrame) {
1025
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });
1026
- }
1072
+ sendLastFrame(false);
1027
1073
  lastFrame = frame;
1028
1074
  }
1029
1075
 
1030
- if (lastFrame) {
1031
- this.queue.put({ requestId, segmentId, frame: lastFrame, final: true });
1032
- }
1076
+ sendLastFrame(true);
1033
1077
  };
1034
1078
 
1035
1079
  try {