@livekit/agents-plugin-elevenlabs 1.0.50 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -22,7 +22,7 @@ class ElevenLabsPlugin extends import_agents.Plugin {
22
22
  constructor() {
23
23
  super({
24
24
  title: "elevenlabs",
25
- version: "1.0.50",
25
+ version: "1.1.0",
26
26
  package: "@livekit/agents-plugin-elevenlabs"
27
27
  });
28
28
  }
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ class ElevenLabsPlugin extends Plugin {
5
5
  constructor() {
6
6
  super({
7
7
  title: "elevenlabs",
8
- version: "1.0.50",
8
+ version: "1.1.0",
9
9
  package: "@livekit/agents-plugin-elevenlabs"
10
10
  });
11
11
  }
package/dist/tts.cjs CHANGED
@@ -49,7 +49,7 @@ function multiStreamUrl(opts) {
49
49
  params.push(`model_id=${opts.model}`);
50
50
  params.push(`output_format=${opts.encoding}`);
51
51
  if (opts.language) {
52
- params.push(`language_code=${opts.language}`);
52
+ params.push(`language_code=${(0, import_agents.getBaseLanguage)(opts.language)}`);
53
53
  }
54
54
  params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);
55
55
  params.push(`enable_logging=${opts.enableLogging}`);
@@ -461,7 +461,8 @@ class TTS extends import_agents.tts.TTS {
461
461
  const voiceId = opts.voiceId ?? ((_a = opts.voice) == null ? void 0 : _a.id) ?? DEFAULT_VOICE_ID;
462
462
  const voiceSettings = opts.voiceSettings ?? ((_b = opts.voice) == null ? void 0 : _b.settings);
463
463
  const model = opts.model ?? opts.modelID ?? "eleven_turbo_v2_5";
464
- const language = opts.language ?? opts.languageCode;
464
+ const rawLanguage = opts.language ?? opts.languageCode;
465
+ const language = rawLanguage ? (0, import_agents.normalizeLanguage)(rawLanguage) : void 0;
465
466
  this.#opts = {
466
467
  apiKey,
467
468
  voiceId,
@@ -516,7 +517,7 @@ class TTS extends import_agents.tts.TTS {
516
517
  changed = true;
517
518
  }
518
519
  if (opts.language !== void 0 && opts.language !== this.#opts.language) {
519
- this.#opts.language = opts.language;
520
+ this.#opts.language = (0, import_agents.normalizeLanguage)(opts.language);
520
521
  changed = true;
521
522
  }
522
523
  if (opts.pronunciationDictionaryLocators !== void 0) {
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 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"]}
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 getBaseLanguage,\n log,\n normalizeLanguage,\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=${getBaseLanguage(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 rawLanguage = opts.language ?? opts.languageCode;\n const language = rawLanguage ? normalizeLanguage(rawLanguage) : undefined;\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 = normalizeLanguage(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.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":"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"}
package/dist/tts.js CHANGED
@@ -6,7 +6,9 @@ import {
6
6
  AudioByteStream,
7
7
  Future,
8
8
  createTimedString,
9
+ getBaseLanguage,
9
10
  log,
11
+ normalizeLanguage,
10
12
  shortuuid,
11
13
  stream,
12
14
  tokenize,
@@ -37,7 +39,7 @@ function multiStreamUrl(opts) {
37
39
  params.push(`model_id=${opts.model}`);
38
40
  params.push(`output_format=${opts.encoding}`);
39
41
  if (opts.language) {
40
- params.push(`language_code=${opts.language}`);
42
+ params.push(`language_code=${getBaseLanguage(opts.language)}`);
41
43
  }
42
44
  params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);
43
45
  params.push(`enable_logging=${opts.enableLogging}`);
@@ -449,7 +451,8 @@ class TTS extends tts.TTS {
449
451
  const voiceId = opts.voiceId ?? ((_a = opts.voice) == null ? void 0 : _a.id) ?? DEFAULT_VOICE_ID;
450
452
  const voiceSettings = opts.voiceSettings ?? ((_b = opts.voice) == null ? void 0 : _b.settings);
451
453
  const model = opts.model ?? opts.modelID ?? "eleven_turbo_v2_5";
452
- const language = opts.language ?? opts.languageCode;
454
+ const rawLanguage = opts.language ?? opts.languageCode;
455
+ const language = rawLanguage ? normalizeLanguage(rawLanguage) : void 0;
453
456
  this.#opts = {
454
457
  apiKey,
455
458
  voiceId,
@@ -504,7 +507,7 @@ class TTS extends tts.TTS {
504
507
  changed = true;
505
508
  }
506
509
  if (opts.language !== void 0 && opts.language !== this.#opts.language) {
507
- this.#opts.language = opts.language;
510
+ this.#opts.language = normalizeLanguage(opts.language);
508
511
  changed = true;
509
512
  }
510
513
  if (opts.pronunciationDictionaryLocators !== void 0) {
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 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"]}
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 getBaseLanguage,\n log,\n normalizeLanguage,\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=${getBaseLanguage(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 rawLanguage = opts.language ?? opts.languageCode;\n const language = rawLanguage ? normalizeLanguage(rawLanguage) : undefined;\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 = normalizeLanguage(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.test.cjs CHANGED
@@ -3,7 +3,15 @@ var import_agents_plugin_openai = require("@livekit/agents-plugin-openai");
3
3
  var import_agents_plugins_test = require("@livekit/agents-plugins-test");
4
4
  var import_vitest = require("vitest");
5
5
  var import_tts = require("./tts.cjs");
6
- (0, import_vitest.describe)("ElevenLabs", async () => {
7
- await (0, import_agents_plugins_test.tts)(new import_tts.TTS(), new import_agents_plugin_openai.STT());
8
- });
6
+ const hasElevenlabsConfig = Boolean(process.env.ELEVEN_API_KEY && process.env.OPENAI_API_KEY);
7
+ if (hasElevenlabsConfig) {
8
+ (0, import_vitest.describe)("ElevenLabs", async () => {
9
+ await (0, import_agents_plugins_test.tts)(new import_tts.TTS(), new import_agents_plugin_openai.STT());
10
+ });
11
+ } else {
12
+ (0, import_vitest.describe)("ElevenLabs", () => {
13
+ import_vitest.it.skip("requires ELEVEN_API_KEY and OPENAI_API_KEY", () => {
14
+ });
15
+ });
16
+ }
9
17
  //# sourceMappingURL=tts.test.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { STT } from '@livekit/agents-plugin-openai';\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { TTS } from './tts.js';\n\ndescribe('ElevenLabs', async () => {\n await tts(new TTS(), new STT());\n});\n"],"mappings":";AAGA,kCAAoB;AACpB,iCAAoB;AACpB,oBAAyB;AACzB,iBAAoB;AAAA,IAEpB,wBAAS,cAAc,YAAY;AACjC,YAAM,gCAAI,IAAI,eAAI,GAAG,IAAI,gCAAI,CAAC;AAChC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { STT } from '@livekit/agents-plugin-openai';\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { TTS } from './tts.js';\n\nconst hasElevenlabsConfig = Boolean(process.env.ELEVEN_API_KEY && process.env.OPENAI_API_KEY);\n\nif (hasElevenlabsConfig) {\n describe('ElevenLabs', async () => {\n await tts(new TTS(), new STT());\n });\n} else {\n describe('ElevenLabs', () => {\n it.skip('requires ELEVEN_API_KEY and OPENAI_API_KEY', () => {});\n });\n}\n"],"mappings":";AAGA,kCAAoB;AACpB,iCAAoB;AACpB,oBAA6B;AAC7B,iBAAoB;AAEpB,MAAM,sBAAsB,QAAQ,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,cAAc;AAE5F,IAAI,qBAAqB;AACvB,8BAAS,cAAc,YAAY;AACjC,cAAM,gCAAI,IAAI,eAAI,GAAG,IAAI,gCAAI,CAAC;AAAA,EAChC,CAAC;AACH,OAAO;AACL,8BAAS,cAAc,MAAM;AAC3B,qBAAG,KAAK,8CAA8C,MAAM;AAAA,IAAC,CAAC;AAAA,EAChE,CAAC;AACH;","names":[]}
package/dist/tts.test.js CHANGED
@@ -1,8 +1,16 @@
1
1
  import { STT } from "@livekit/agents-plugin-openai";
2
2
  import { tts } from "@livekit/agents-plugins-test";
3
- import { describe } from "vitest";
3
+ import { describe, it } from "vitest";
4
4
  import { TTS } from "./tts.js";
5
- describe("ElevenLabs", async () => {
6
- await tts(new TTS(), new STT());
7
- });
5
+ const hasElevenlabsConfig = Boolean(process.env.ELEVEN_API_KEY && process.env.OPENAI_API_KEY);
6
+ if (hasElevenlabsConfig) {
7
+ describe("ElevenLabs", async () => {
8
+ await tts(new TTS(), new STT());
9
+ });
10
+ } else {
11
+ describe("ElevenLabs", () => {
12
+ it.skip("requires ELEVEN_API_KEY and OPENAI_API_KEY", () => {
13
+ });
14
+ });
15
+ }
8
16
  //# sourceMappingURL=tts.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { STT } from '@livekit/agents-plugin-openai';\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { TTS } from './tts.js';\n\ndescribe('ElevenLabs', async () => {\n await tts(new TTS(), new STT());\n});\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,WAAW;AACpB,SAAS,gBAAgB;AACzB,SAAS,WAAW;AAEpB,SAAS,cAAc,YAAY;AACjC,QAAM,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC;AAChC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { STT } from '@livekit/agents-plugin-openai';\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { TTS } from './tts.js';\n\nconst hasElevenlabsConfig = Boolean(process.env.ELEVEN_API_KEY && process.env.OPENAI_API_KEY);\n\nif (hasElevenlabsConfig) {\n describe('ElevenLabs', async () => {\n await tts(new TTS(), new STT());\n });\n} else {\n describe('ElevenLabs', () => {\n it.skip('requires ELEVEN_API_KEY and OPENAI_API_KEY', () => {});\n });\n}\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,WAAW;AACpB,SAAS,UAAU,UAAU;AAC7B,SAAS,WAAW;AAEpB,MAAM,sBAAsB,QAAQ,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,cAAc;AAE5F,IAAI,qBAAqB;AACvB,WAAS,cAAc,YAAY;AACjC,UAAM,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC;AAAA,EAChC,CAAC;AACH,OAAO;AACL,WAAS,cAAc,MAAM;AAC3B,OAAG,KAAK,8CAA8C,MAAM;AAAA,IAAC,CAAC;AAAA,EAChE,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-elevenlabs",
3
- "version": "1.0.50",
3
+ "version": "1.1.0",
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": "1.0.50",
34
- "@livekit/agents-plugin-openai": "1.0.50",
35
- "@livekit/agents-plugins-test": "1.0.50"
33
+ "@livekit/agents": "1.1.0",
34
+ "@livekit/agents-plugin-openai": "1.1.0",
35
+ "@livekit/agents-plugins-test": "1.1.0"
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.50"
43
+ "@livekit/agents": "1.1.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup --onSuccess \"pnpm build:types\"",
package/src/tts.test.ts CHANGED
@@ -3,9 +3,17 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { STT } from '@livekit/agents-plugin-openai';
5
5
  import { tts } from '@livekit/agents-plugins-test';
6
- import { describe } from 'vitest';
6
+ import { describe, it } from 'vitest';
7
7
  import { TTS } from './tts.js';
8
8
 
9
- describe('ElevenLabs', async () => {
10
- await tts(new TTS(), new STT());
11
- });
9
+ const hasElevenlabsConfig = Boolean(process.env.ELEVEN_API_KEY && process.env.OPENAI_API_KEY);
10
+
11
+ if (hasElevenlabsConfig) {
12
+ describe('ElevenLabs', async () => {
13
+ await tts(new TTS(), new STT());
14
+ });
15
+ } else {
16
+ describe('ElevenLabs', () => {
17
+ it.skip('requires ELEVEN_API_KEY and OPENAI_API_KEY', () => {});
18
+ });
19
+ }
package/src/tts.ts CHANGED
@@ -10,7 +10,9 @@ import {
10
10
  Future,
11
11
  type TimedString,
12
12
  createTimedString,
13
+ getBaseLanguage,
13
14
  log,
15
+ normalizeLanguage,
14
16
  shortuuid,
15
17
  stream,
16
18
  tokenize,
@@ -145,7 +147,7 @@ function multiStreamUrl(opts: ResolvedTTSOptions): string {
145
147
  params.push(`model_id=${opts.model}`);
146
148
  params.push(`output_format=${opts.encoding}`);
147
149
  if (opts.language) {
148
- params.push(`language_code=${opts.language}`);
150
+ params.push(`language_code=${getBaseLanguage(opts.language)}`);
149
151
  }
150
152
  params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);
151
153
  params.push(`enable_logging=${opts.enableLogging}`);
@@ -673,7 +675,8 @@ export class TTS extends tts.TTS {
673
675
  const voiceId = opts.voiceId ?? opts.voice?.id ?? DEFAULT_VOICE_ID;
674
676
  const voiceSettings = opts.voiceSettings ?? opts.voice?.settings;
675
677
  const model = opts.model ?? opts.modelID ?? 'eleven_turbo_v2_5';
676
- const language = opts.language ?? opts.languageCode;
678
+ const rawLanguage = opts.language ?? opts.languageCode;
679
+ const language = rawLanguage ? normalizeLanguage(rawLanguage) : undefined;
677
680
 
678
681
  this.#opts = {
679
682
  apiKey,
@@ -745,7 +748,7 @@ export class TTS extends tts.TTS {
745
748
  }
746
749
 
747
750
  if (opts.language !== undefined && opts.language !== this.#opts.language) {
748
- this.#opts.language = opts.language;
751
+ this.#opts.language = normalizeLanguage(opts.language);
749
752
  changed = true;
750
753
  }
751
754