@livekit/agents-plugin-elevenlabs 1.0.27 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 AsyncIterableQueue,\n AudioByteStream,\n log,\n shortuuid,\n tokenize,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { URL } from 'node:url';\nimport { type RawData, WebSocket } from 'ws';\nimport type { TTSEncoding, TTSModels } from './models.js';\n\nconst DEFAULT_INACTIVITY_TIMEOUT = 300;\n\ntype Voice = {\n id: string;\n name: string;\n category: string;\n settings?: VoiceSettings;\n};\n\ntype VoiceSettings = {\n stability: number; // 0..1\n similarity_boost: number; // 0..1\n style?: number; // 0..1\n use_speaker_boost: boolean;\n};\n\nconst DEFAULT_VOICE: Voice = {\n id: 'bIHbv24MWmeRgasZH58o',\n name: 'Bella',\n category: 'premade',\n settings: {\n stability: 0.71,\n similarity_boost: 0.5,\n style: 0.0,\n use_speaker_boost: true,\n },\n};\n\nconst API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1/';\nconst AUTHORIZATION_HEADER = 'xi-api-key';\n\nexport interface TTSOptions {\n apiKey?: string;\n voice: Voice;\n modelID: TTSModels | string;\n languageCode?: string;\n baseURL: string;\n encoding: TTSEncoding;\n streamingLatency?: number;\n wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing: boolean;\n inactivityTimeout: number;\n syncAlignment: boolean;\n autoMode?: boolean;\n}\n\nconst defaultTTSOptionsBase = {\n apiKey: process.env.ELEVEN_API_KEY,\n voice: DEFAULT_VOICE,\n modelID: 'eleven_turbo_v2_5',\n baseURL: API_BASE_URL_V1,\n encoding: 'pcm_22050' as TTSEncoding,\n enableSsmlParsing: false,\n inactivityTimeout: DEFAULT_INACTIVITY_TIMEOUT,\n syncAlignment: true,\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n label = 'elevenlabs.TTS';\n\n constructor(opts: Partial<TTSOptions> = {}) {\n super(sampleRateFromFormat(opts.encoding || defaultTTSOptionsBase.encoding), 1, {\n streaming: true,\n });\n\n // Set autoMode to true by default if not provided is Python behavior,\n // but to make it non-breaking, we keep false as default in typescript\n const autoMode = opts.autoMode !== undefined ? opts.autoMode : false;\n\n // Set default tokenizer based on autoMode if not provided\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 // Warn if autoMode is enabled but a WordTokenizer was provided\n log().warn(\n 'autoMode is enabled, it expects full sentences or phrases. ' +\n 'Please provide a SentenceTokenizer instead of a WordTokenizer.',\n );\n }\n\n this.#opts = {\n ...defaultTTSOptionsBase,\n ...opts,\n autoMode,\n wordTokenizer,\n };\n\n if (this.#opts.apiKey === undefined) {\n throw new Error(\n 'ElevenLabs API key is required, whether as an argument or as $ELEVEN_API_KEY',\n );\n }\n }\n\n async listVoices(): Promise<Voice[]> {\n return fetch(this.#opts.baseURL + '/voices', {\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey!,\n },\n })\n .then((data) => data.json())\n .then((data) => {\n const voices: Voice[] = [];\n for (const voice of (\n data as { voices: { voice_id: string; name: string; category: string }[] }\n ).voices) {\n voices.push({\n id: voice.voice_id,\n name: voice.name,\n category: voice.category,\n settings: undefined,\n });\n }\n return voices;\n });\n }\n\n synthesize(): tts.ChunkedStream {\n throw new Error('Chunked responses are not supported on ElevenLabs TTS');\n }\n\n stream(): tts.SynthesizeStream {\n return new SynthesizeStream(this, this.#opts);\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #opts: TTSOptions;\n #logger = log();\n label = 'elevenlabs.SynthesizeStream';\n readonly streamURL: URL;\n\n constructor(tts: TTS, opts: TTSOptions) {\n super(tts);\n this.#opts = opts;\n this.closed = false;\n\n // add trailing slash to URL if needed\n const baseURL = opts.baseURL + (opts.baseURL.endsWith('/') ? '' : '/');\n\n this.streamURL = new URL(`text-to-speech/${opts.voice.id}/stream-input`, baseURL);\n const params = {\n model_id: opts.modelID,\n output_format: opts.encoding,\n enable_ssml_parsing: `${opts.enableSsmlParsing}`,\n sync_alignment: `${opts.syncAlignment}`,\n ...(opts.autoMode !== undefined && { auto_mode: `${opts.autoMode}` }),\n ...(opts.languageCode && { language_code: opts.languageCode }),\n ...(opts.inactivityTimeout && { inactivity_timeout: `${opts.inactivityTimeout}` }),\n ...(opts.streamingLatency && { optimize_streaming_latency: `${opts.streamingLatency}` }),\n };\n Object.entries(params).forEach(([k, v]) => this.streamURL.searchParams.append(k, v));\n this.streamURL.protocol = this.streamURL.protocol.replace('http', 'ws');\n }\n\n protected async run() {\n const segments = new AsyncIterableQueue<tokenize.WordStream | tokenize.SentenceStream>();\n\n const tokenizeInput = async () => {\n let stream: tokenize.WordStream | tokenize.SentenceStream | null = null;\n for await (const text of this.input) {\n if (this.abortController.signal.aborted) {\n break;\n }\n if (text === SynthesizeStream.FLUSH_SENTINEL) {\n stream?.endInput();\n stream = null;\n } else {\n if (!stream) {\n stream = this.#opts.wordTokenizer.stream();\n segments.put(stream);\n }\n stream.pushText(text);\n }\n }\n segments.close();\n };\n\n const runStream = async () => {\n for await (const stream of segments) {\n if (this.abortController.signal.aborted) {\n break;\n }\n await this.#runWS(stream);\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n }\n };\n\n await Promise.all([tokenizeInput(), runStream()]);\n }\n\n async #runWS(stream: tokenize.WordStream | tokenize.SentenceStream, maxRetry = 3) {\n let retries = 0;\n let ws: WebSocket;\n while (true) {\n ws = new WebSocket(this.streamURL, {\n headers: { [AUTHORIZATION_HEADER]: this.#opts.apiKey },\n });\n\n ws.on('error', (error) => {\n this.abortController.abort();\n this.#logger.error({ error }, 'Error connecting to ElevenLabs');\n });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n break;\n } catch (e) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to ElevenLabs after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 5);\n retries++;\n\n this.#logger.warn(\n `failed to connect to ElevenLabs, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n }\n }\n\n const requestId = shortuuid();\n const segmentId = shortuuid();\n\n // simple helper to make sure what we send to ws.send\n const wsSend = (data: {\n // (SynthesizeContent from python)\n text: string;\n // setting flush somehow never finishes the current speech generation\n // https://github.com/livekit/agents-js/pull/820#issuecomment-3517138706\n // flush?: boolean;\n // initialization\n voice_settings?: VoiceSettings;\n generation_config?: {\n chunk_length_schedule: number[];\n };\n }) => {\n ws.send(JSON.stringify(data));\n };\n\n wsSend({\n text: ' ',\n voice_settings: this.#opts.voice.settings,\n ...(this.#opts.chunkLengthSchedule && {\n generation_config: {\n chunk_length_schedule: this.#opts.chunkLengthSchedule,\n },\n }),\n });\n let eosSent = false;\n\n const sendTask = async () => {\n // Determine if we should flush on each chunk (sentence)\n /*const flushOnChunk =\n this.#opts.wordTokenizer instanceof tokenize.SentenceTokenizer &&\n this.#opts.autoMode !== undefined &&\n this.#opts.autoMode;*/\n\n let xmlContent: string[] = [];\n for await (const data of stream) {\n if (this.abortController.signal.aborted) {\n break;\n }\n let text = data.token;\n\n if ((this.#opts.enableSsmlParsing && text.startsWith('<phoneme')) || xmlContent.length) {\n xmlContent.push(text);\n if (text.indexOf('</phoneme>') !== -1) {\n text = xmlContent.join(' ');\n xmlContent = [];\n } else {\n continue;\n }\n }\n\n wsSend({\n text: text + ' ', // must always end with a space\n // ...(flushOnChunk && { flush: true }),\n });\n }\n\n if (xmlContent.length) {\n this.#logger.warn('ElevenLabs stream ended with incomplete XML content');\n }\n\n // no more tokens, mark eos with flush\n // setting flush somehow never finishes the current speech generation\n // wsSend({ text: '', flush: true });\n wsSend({ text: '' });\n eosSent = true;\n };\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n const listenTask = async () => {\n let finalReceived = false;\n const bstream = new AudioByteStream(sampleRateFromFormat(this.#opts.encoding), 1);\n while (!this.closed && !this.abortController.signal.aborted) {\n try {\n await new Promise<RawData>((resolve, reject) => {\n ws.removeAllListeners();\n ws.on('message', (data) => resolve(data));\n ws.on('close', (code, reason) => {\n if (!eosSent) {\n this.#logger.error(`WebSocket closed with code ${code}: ${reason}`);\n }\n if (!finalReceived) {\n reject(new Error('WebSocket closed'));\n }\n });\n }).then((msg) => {\n const json = JSON.parse(msg.toString());\n // remove the \"audio\" field from the json object when printing\n if ('audio' in json && json.audio !== null) {\n const data = Buffer.from(json.audio, 'base64');\n for (const frame of bstream.write(\n data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength),\n )) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n } else if (json.isFinal) {\n finalReceived = true;\n for (const frame of bstream.flush()) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n sendLastFrame(segmentId, true);\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n\n if (segmentId === requestId || this.abortController.signal.aborted) {\n ws.close();\n return;\n }\n }\n });\n } catch (err) {\n // skip log error for normal websocket close\n if (err instanceof Error && !err.message.includes('WebSocket closed')) {\n if (err.message.includes('Queue is closed')) {\n this.#logger.warn(\n { err },\n 'Queue closed during transcript processing (expected during disconnect)',\n );\n } else {\n this.#logger.error({ err }, 'Error in listenTask from ElevenLabs WebSocket');\n }\n }\n break;\n }\n }\n };\n\n await Promise.all([sendTask(), listenTask()]);\n }\n}\n\nconst sampleRateFromFormat = (encoding: TTSEncoding): number => {\n return Number(encoding.split('_')[1]);\n};\n"],"mappings":"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","names":["tts","segmentId"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n APIConnectionError,\n APIError,\n APIStatusError,\n APITimeoutError,\n AudioByteStream,\n Future,\n log,\n shortuuid,\n stream,\n tokenize,\n tts,\n type voice,\n} from '@livekit/agents';\nimport { Mutex } from '@livekit/mutex';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { TTSEncoding, TTSModels } from './models.js';\n\ntype TimedString = voice.TimedString;\n\nconst DEFAULT_VOICE_ID = 'bIHbv24MWmeRgasZH58o';\nconst API_BASE_URL_V1 = 'https://api.elevenlabs.io/v1';\nconst AUTHORIZATION_HEADER = 'xi-api-key';\nconst WS_INACTIVITY_TIMEOUT = 180;\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_22050';\n\nexport interface VoiceSettings {\n stability: number; // [0.0 - 1.0]\n similarity_boost: number; // [0.0 - 1.0]\n style?: number; // [0.0 - 1.0]\n speed?: number; // [0.8 - 1.2]\n use_speaker_boost?: boolean;\n}\n\nexport interface Voice {\n id: string;\n name: string;\n category: string;\n settings?: VoiceSettings;\n}\n\nexport interface PronunciationDictionaryLocator {\n pronunciation_dictionary_id: string;\n version_id: string;\n}\n\nexport interface TTSOptions {\n apiKey?: string;\n // New interface\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n // Legacy interface (backward compatibility)\n voice?: Voice;\n modelID?: TTSModels | string;\n languageCode?: string;\n // Common options\n baseURL?: string;\n encoding?: TTSEncoding;\n streamingLatency?: number;\n wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing?: boolean;\n enableLogging?: boolean;\n inactivityTimeout?: number;\n syncAlignment?: boolean;\n applyTextNormalization?: 'auto' | 'on' | 'off';\n preferredAlignment?: 'normalized' | 'original';\n autoMode?: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal options type with resolved defaults\ninterface ResolvedTTSOptions {\n apiKey: string;\n voiceId: string;\n voiceSettings?: VoiceSettings;\n model: TTSModels | string;\n language?: string;\n baseURL: string;\n encoding: TTSEncoding;\n sampleRate: number;\n streamingLatency?: number;\n wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;\n chunkLengthSchedule?: number[];\n enableSsmlParsing: boolean;\n enableLogging: boolean;\n inactivityTimeout: number;\n syncAlignment: boolean;\n applyTextNormalization: 'auto' | 'on' | 'off';\n preferredAlignment: 'normalized' | 'original';\n autoMode: boolean;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n}\n\n// Internal types for connection management\ninterface SynthesizeContent {\n contextId: string;\n text: string;\n flush: boolean;\n}\n\ninterface CloseContext {\n contextId: string;\n}\n\ninterface StreamData {\n stream: SynthesizeStream;\n waiter: {\n resolve: (value: void) => void;\n reject: (error: Error) => void;\n };\n textBuffer: string;\n startTimesMs: number[];\n durationsMs: number[];\n}\n\ntype ConnectionMessage = SynthesizeContent | CloseContext;\n\n// Helper Functions\n\nfunction sampleRateFromFormat(encoding: TTSEncoding): number {\n const split = encoding.split('_');\n return parseInt(split[1]!, 10);\n}\n\nfunction synthesizeUrl(opts: ResolvedTTSOptions): string {\n const { baseURL, voiceId, model, encoding, streamingLatency } = opts;\n let url = `${baseURL}/text-to-speech/${voiceId}/stream?model_id=${model}&output_format=${encoding}`;\n if (streamingLatency !== undefined) {\n url += `&optimize_streaming_latency=${streamingLatency}`;\n }\n return url;\n}\n\nfunction multiStreamUrl(opts: ResolvedTTSOptions): string {\n const baseURL = opts.baseURL.replace('https://', 'wss://').replace('http://', 'ws://');\n const params: string[] = [];\n params.push(`model_id=${opts.model}`);\n params.push(`output_format=${opts.encoding}`);\n if (opts.language) {\n params.push(`language_code=${opts.language}`);\n }\n params.push(`enable_ssml_parsing=${opts.enableSsmlParsing}`);\n params.push(`enable_logging=${opts.enableLogging}`);\n params.push(`inactivity_timeout=${opts.inactivityTimeout}`);\n params.push(`apply_text_normalization=${opts.applyTextNormalization}`);\n if (opts.syncAlignment) {\n params.push('sync_alignment=true');\n }\n if (opts.autoMode !== undefined) {\n params.push(`auto_mode=${opts.autoMode}`);\n }\n return `${baseURL}/text-to-speech/${opts.voiceId}/multi-stream-input?${params.join('&')}`;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const result: Partial<T> = {};\n for (const key in obj) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Convert alignment data to timed words.\n * Returns the timed words and remaining text buffer.\n */\nfunction toTimedWords(\n text: string,\n startTimesMs: number[],\n durationsMs: number[],\n flush: boolean = false,\n): [TimedString[], string] {\n if (!text || startTimesMs.length === 0 || durationsMs.length === 0) {\n return [[], text || ''];\n }\n\n const lastStartTime = startTimesMs[startTimesMs.length - 1]!;\n const lastDuration = durationsMs[durationsMs.length - 1]!;\n const timestamps = [...startTimesMs, lastStartTime + lastDuration];\n\n const words = tokenize.basic.splitWords(text, false);\n const timedWords: TimedString[] = [];\n\n if (words.length === 0) {\n return [[], text];\n }\n\n const startIndices = words.map((w) => w[1]);\n let end = 0;\n\n // We don't know if the last word is complete, always leave it as remaining\n for (let i = 0; i < startIndices.length - 1; i++) {\n const start = startIndices[i]!;\n const nextStart = startIndices[i + 1]!;\n end = nextStart;\n const startT = (timestamps[start] ?? 0) / 1000;\n const endT = (timestamps[nextStart] ?? 0) / 1000;\n timedWords.push({\n text: text.slice(start, nextStart),\n startTime: startT,\n endTime: endT,\n });\n }\n\n if (flush && words.length > 0) {\n const lastWordStart = startIndices[startIndices.length - 1]!;\n const startT = (timestamps[lastWordStart] ?? 0) / 1000;\n const endT = (timestamps[timestamps.length - 1] ?? 0) / 1000;\n timedWords.push({\n text: text.slice(lastWordStart),\n startTime: startT,\n endTime: endT,\n });\n end = text.length;\n } else if (words.length > 0) {\n end = startIndices[startIndices.length - 1]!;\n }\n\n return [timedWords, text.slice(end)];\n}\n\nclass Connection {\n #opts: ResolvedTTSOptions;\n #ws: WebSocket | null = null;\n #isCurrent = true;\n #activeContexts = new Set<string>();\n #inputQueue: ConnectionMessage[] = [];\n #contextData = new Map<string, StreamData>();\n #sendTask: Promise<void> | null = null;\n #recvTask: Promise<void> | null = null;\n #closed = false;\n #logger = log();\n #inputQueueResolver: (() => void) | null = null;\n\n constructor(opts: ResolvedTTSOptions) {\n this.#opts = opts;\n }\n\n get voiceId(): string {\n return this.#opts.voiceId;\n }\n\n get isCurrent(): boolean {\n return this.#isCurrent;\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n markNonCurrent(): void {\n this.#isCurrent = false;\n }\n\n async connect(): Promise<void> {\n if (this.#ws || this.#closed) {\n return;\n }\n\n const url = multiStreamUrl(this.#opts);\n const headers = { [AUTHORIZATION_HEADER]: this.#opts.apiKey };\n\n return new Promise((resolve, reject) => {\n this.#ws = new WebSocket(url, { headers });\n\n this.#ws.on('open', () => {\n this.#sendTask = this.#sendLoop();\n this.#recvTask = this.#recvLoop();\n resolve();\n });\n\n this.#ws.on('error', (error) => {\n this.#logger.error({ error }, 'WebSocket connection error');\n reject(new APIConnectionError({ message: `WebSocket error: ${error.message}` }));\n });\n });\n }\n\n registerStream(\n stream: SynthesizeStream,\n waiter: { resolve: (value: void) => void; reject: (error: Error) => void },\n ): void {\n const contextId = stream.contextId;\n this.#contextData.set(contextId, {\n stream,\n waiter,\n textBuffer: '',\n startTimesMs: [],\n durationsMs: [],\n });\n }\n\n sendContent(content: SynthesizeContent): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push(content);\n this.#inputQueueResolver?.();\n }\n\n closeContext(contextId: string): void {\n if (this.#closed || !this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({ message: 'WebSocket connection is closed' });\n }\n this.#inputQueue.push({ contextId });\n this.#inputQueueResolver?.();\n }\n\n async #sendLoop(): Promise<void> {\n try {\n while (!this.#closed) {\n // Wait for messages in queue\n if (this.#inputQueue.length === 0) {\n await new Promise<void>((resolve) => {\n this.#inputQueueResolver = resolve;\n });\n this.#inputQueueResolver = null;\n }\n\n if (this.#closed) break;\n\n const msg = this.#inputQueue.shift();\n if (!msg) continue;\n\n if (!this.#ws || this.#ws.readyState !== WebSocket.OPEN) {\n break;\n }\n\n if ('text' in msg) {\n // SynthesizeContent\n const content = msg as SynthesizeContent;\n const isNewContext = !this.#activeContexts.has(content.contextId);\n\n // If not current and this is a new context, ignore it\n if (!this.#isCurrent && isNewContext) {\n continue;\n }\n\n if (isNewContext) {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : {};\n\n const initPkt: Record<string, unknown> = {\n text: ' ',\n voice_settings: voiceSettings,\n context_id: content.contextId,\n };\n\n if (this.#opts.pronunciationDictionaryLocators) {\n initPkt.pronunciation_dictionary_locators =\n this.#opts.pronunciationDictionaryLocators.map((locator) => ({\n pronunciation_dictionary_id: locator.pronunciation_dictionary_id,\n version_id: locator.version_id,\n }));\n }\n\n const initPktStr = JSON.stringify(initPkt);\n this.#ws.send(initPktStr);\n this.#activeContexts.add(content.contextId);\n }\n\n const pkt: Record<string, unknown> = {\n text: content.text,\n context_id: content.contextId,\n };\n if (content.flush) {\n pkt.flush = true;\n }\n\n const pktStr = JSON.stringify(pkt);\n this.#ws.send(pktStr);\n } else {\n // CloseContext\n const closeMsg = msg as CloseContext;\n if (this.#activeContexts.has(closeMsg.contextId)) {\n const closePkt = {\n context_id: closeMsg.contextId,\n close_context: true,\n };\n const closePktStr = JSON.stringify(closePkt);\n this.#ws.send(closePktStr);\n }\n }\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'send loop error');\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n async #recvLoop(): Promise<void> {\n try {\n const messageChannel = stream.createStreamChannel<Record<string, unknown>>();\n const errorFuture = new Future<Error>();\n\n const onMessage = (rawData: Buffer) => {\n try {\n const parsed = JSON.parse(rawData.toString());\n messageChannel.write(parsed);\n } catch (e) {\n this.#logger.warn({ error: e }, 'failed to parse WebSocket message');\n }\n };\n\n const onClose = () => {\n if (!this.#closed && this.#contextData.size > 0) {\n this.#logger.warn('websocket closed unexpectedly');\n }\n messageChannel.close();\n };\n\n const onError = (error: Error) => {\n errorFuture.resolve(error);\n messageChannel.close();\n };\n\n // Set up persistent listeners\n if (!this.#ws) return;\n this.#ws.on('message', onMessage);\n this.#ws.on('close', onClose);\n this.#ws.on('error', onError);\n\n const cleanup = () => {\n this.#ws?.off('message', onMessage);\n this.#ws?.off('close', onClose);\n this.#ws?.off('error', onError);\n };\n\n const reader = messageChannel.stream().getReader();\n try {\n while (!this.#closed) {\n const result = await reader.read();\n if (result.done || this.#closed) break;\n\n const data = result.value;\n const contextId = data.contextId as string | undefined;\n const ctx = contextId ? this.#contextData.get(contextId) : undefined;\n\n if (data.error) {\n this.#logger.error(\n { context_id: contextId, error: data.error, data },\n 'elevenlabs tts returned error',\n );\n if (contextId) {\n if (ctx) {\n ctx.waiter.reject(new APIError(data.error as string));\n }\n this.#cleanupContext(contextId);\n }\n continue;\n }\n\n if (!ctx) {\n this.#logger.warn({ data }, 'unexpected message received from elevenlabs tts');\n continue;\n }\n\n const stream = ctx.stream;\n\n // Process alignment data\n const alignment =\n this.#opts.preferredAlignment === 'normalized'\n ? (data.normalizedAlignment as Record<string, unknown>)\n : (data.alignment as Record<string, unknown>);\n\n if (alignment && stream) {\n const chars = alignment.chars as string[] | undefined;\n const starts = (alignment.charStartTimesMs || alignment.charsStartTimesMs) as\n | number[]\n | undefined;\n const durs = (alignment.charDurationsMs || alignment.charsDurationsMs) as\n | number[]\n | undefined;\n\n if (\n chars &&\n starts &&\n durs &&\n chars.length === durs.length &&\n starts.length === durs.length\n ) {\n ctx.textBuffer += chars.join('');\n\n // Handle chars with multiple characters\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const start = starts[i]!;\n const dur = durs[i]!;\n\n if (char.length > 1) {\n for (let j = 0; j < char.length - 1; j++) {\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(0);\n }\n }\n ctx.startTimesMs.push(start);\n ctx.durationsMs.push(dur);\n }\n\n const [timedWords, remainingText] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n );\n\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n\n ctx.textBuffer = remainingText;\n ctx.startTimesMs = ctx.startTimesMs.slice(-remainingText.length);\n ctx.durationsMs = ctx.durationsMs.slice(-remainingText.length);\n }\n }\n\n if (data.audio) {\n const audioData = Buffer.from(data.audio as string, 'base64');\n stream.pushAudio(audioData);\n }\n\n if (data.isFinal) {\n // Flush remaining alignment data\n if (ctx.textBuffer) {\n const [timedWords] = toTimedWords(\n ctx.textBuffer,\n ctx.startTimesMs,\n ctx.durationsMs,\n true,\n );\n if (timedWords.length > 0) {\n stream.pushTimedTranscript(timedWords);\n }\n }\n\n stream.markDone();\n ctx.waiter.resolve();\n this.#cleanupContext(contextId!);\n\n if (!this.#isCurrent && this.#activeContexts.size === 0) {\n this.#logger.debug('no active contexts, shutting down connection');\n break;\n }\n }\n }\n\n // Throw any error that occurred\n if (errorFuture.done) {\n throw await errorFuture.await;\n }\n } finally {\n reader.releaseLock();\n cleanup();\n }\n } catch (e) {\n this.#logger.warn({ error: e }, 'recv loop error');\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(e instanceof Error ? e : new Error(String(e)));\n }\n this.#contextData.clear();\n } finally {\n if (!this.#closed) {\n await this.close();\n }\n }\n }\n\n #cleanupContext(contextId: string): void {\n this.#contextData.delete(contextId);\n this.#activeContexts.delete(contextId);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#inputQueueResolver?.();\n\n for (const ctx of this.#contextData.values()) {\n ctx.waiter.reject(new APIStatusError({ message: 'connection closed' }));\n }\n this.#contextData.clear();\n\n if (this.#ws) {\n this.#ws.close();\n this.#ws = null;\n }\n\n if (this.#sendTask) {\n await this.#sendTask.catch(() => {});\n }\n if (this.#recvTask) {\n await this.#recvTask.catch(() => {});\n }\n }\n}\n\nexport class TTS extends tts.TTS {\n #opts: ResolvedTTSOptions;\n #streams = new Set<SynthesizeStream>();\n #currentConnection: Connection | null = null;\n #connectionLock = new Mutex();\n #logger = log();\n\n label = 'elevenlabs.TTS';\n\n constructor(opts: TTSOptions = {}) {\n const autoMode = opts.autoMode ?? true;\n const encoding = opts.encoding ?? DEFAULT_ENCODING;\n const sampleRate = sampleRateFromFormat(encoding);\n\n super(sampleRate, 1, {\n streaming: true,\n });\n\n const apiKey = opts.apiKey ?? process.env.ELEVEN_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'ElevenLabs API key is required, either as argument or set ELEVEN_API_KEY environmental variable',\n );\n }\n\n let wordTokenizer = opts.wordTokenizer;\n if (!wordTokenizer) {\n wordTokenizer = autoMode\n ? new tokenize.basic.SentenceTokenizer()\n : new tokenize.basic.WordTokenizer(false);\n } else if (autoMode && !(wordTokenizer instanceof tokenize.SentenceTokenizer)) {\n this.#logger.warn(\n 'autoMode is enabled, it expects full sentences or phrases, ' +\n 'please provide a SentenceTokenizer instead of a WordTokenizer.',\n );\n }\n\n // Handle legacy options for backward compatibility\n const voiceId = opts.voiceId ?? opts.voice?.id ?? DEFAULT_VOICE_ID;\n const voiceSettings = opts.voiceSettings ?? opts.voice?.settings;\n const model = opts.model ?? opts.modelID ?? 'eleven_turbo_v2_5';\n const language = opts.language ?? opts.languageCode;\n\n this.#opts = {\n apiKey,\n voiceId,\n voiceSettings,\n model,\n language,\n baseURL: opts.baseURL ?? API_BASE_URL_V1,\n encoding,\n sampleRate,\n streamingLatency: opts.streamingLatency,\n wordTokenizer,\n chunkLengthSchedule: opts.chunkLengthSchedule,\n enableSsmlParsing: opts.enableSsmlParsing ?? false,\n enableLogging: opts.enableLogging ?? true,\n inactivityTimeout: opts.inactivityTimeout ?? WS_INACTIVITY_TIMEOUT,\n syncAlignment: opts.syncAlignment ?? true,\n applyTextNormalization: opts.applyTextNormalization ?? 'auto',\n preferredAlignment: opts.preferredAlignment ?? 'normalized',\n autoMode,\n pronunciationDictionaryLocators: opts.pronunciationDictionaryLocators,\n };\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'ElevenLabs';\n }\n\n async listVoices(): Promise<Voice[]> {\n const response = await fetch(`${this.#opts.baseURL}/voices`, {\n headers: { [AUTHORIZATION_HEADER]: this.#opts.apiKey },\n });\n const data = (await response.json()) as {\n voices: { voice_id: string; name: string; category: string }[];\n };\n return data.voices.map((v) => ({\n id: v.voice_id,\n name: v.name,\n category: v.category,\n }));\n }\n\n updateOptions(opts: {\n voiceId?: string;\n voiceSettings?: VoiceSettings;\n model?: TTSModels | string;\n language?: string;\n pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];\n }): void {\n let changed = false;\n\n if (opts.model !== undefined && opts.model !== this.#opts.model) {\n this.#opts.model = opts.model;\n changed = true;\n }\n\n if (opts.voiceId !== undefined && opts.voiceId !== this.#opts.voiceId) {\n this.#opts.voiceId = opts.voiceId;\n changed = true;\n }\n\n if (opts.voiceSettings !== undefined) {\n this.#opts.voiceSettings = opts.voiceSettings;\n changed = true;\n }\n\n if (opts.language !== undefined && opts.language !== this.#opts.language) {\n this.#opts.language = opts.language;\n changed = true;\n }\n\n if (opts.pronunciationDictionaryLocators !== undefined) {\n this.#opts.pronunciationDictionaryLocators = opts.pronunciationDictionaryLocators;\n changed = true;\n }\n\n if (changed && this.#currentConnection) {\n this.#currentConnection.markNonCurrent();\n this.#currentConnection = null;\n }\n }\n\n async currentConnection(): Promise<Connection> {\n const unlock = await this.#connectionLock.lock();\n try {\n if (\n this.#currentConnection &&\n this.#currentConnection.isCurrent &&\n !this.#currentConnection.closed\n ) {\n return this.#currentConnection;\n }\n\n const conn = new Connection({ ...this.#opts });\n await conn.connect();\n this.#currentConnection = conn;\n return conn;\n } finally {\n unlock();\n }\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(this, text, { ...this.#opts });\n }\n\n stream(): SynthesizeStream {\n const stream = new SynthesizeStream(this, { ...this.#opts });\n this.#streams.add(stream);\n return stream;\n }\n\n async close(): Promise<void> {\n for (const stream of this.#streams) {\n stream.close();\n }\n this.#streams.clear();\n\n if (this.#currentConnection) {\n await this.#currentConnection.close();\n this.#currentConnection = null;\n }\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #logger = log();\n\n label = 'elevenlabs.ChunkedStream';\n\n constructor(tts: TTS, text: string, opts: ResolvedTTSOptions) {\n super(text, tts);\n this.#tts = tts;\n this.#opts = opts;\n }\n\n protected async run(): Promise<void> {\n const voiceSettings = this.#opts.voiceSettings\n ? stripUndefined(this.#opts.voiceSettings)\n : undefined;\n\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n try {\n const response = await fetch(synthesizeUrl(this.#opts), {\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n text: this.inputText,\n model_id: this.#opts.model,\n voice_settings: voiceSettings,\n }),\n signal: this.abortSignal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new APIStatusError({\n message: `ElevenLabs API error: ${errorText}`,\n options: { statusCode: response.status },\n });\n }\n\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.startsWith('audio/')) {\n const content = await response.text();\n throw new APIError(`ElevenLabs returned non-audio data: ${content}`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new APIError('No response body');\n }\n\n let lastFrame: AudioFrame | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n for (const frame of bstream.write(value.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Flush remaining data\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId: requestId, frame: lastFrame, final: true });\n }\n } catch (e) {\n if (e instanceof APIError) {\n throw e;\n }\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw new APIConnectionError({ message: `Connection error: ${e}` });\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #tts: TTS;\n #opts: ResolvedTTSOptions;\n #contextId: string;\n #sentTokenizerStream: tokenize.SentenceStream | tokenize.WordStream;\n #logger = log();\n #audioQueue: Buffer[] = [];\n #timedTranscriptQueue: TimedString[] = [];\n #streamDone = false;\n\n label = 'elevenlabs.SynthesizeStream';\n\n constructor(tts: TTS, opts: ResolvedTTSOptions) {\n super(tts);\n this.#tts = tts;\n this.#opts = opts;\n this.#contextId = shortuuid();\n this.#sentTokenizerStream = this.#opts.wordTokenizer.stream();\n }\n\n get contextId(): string {\n return this.#contextId;\n }\n\n pushAudio(data: Buffer): void {\n // Don't push if stream is closed/aborted\n if (this.closed || this.abortController.signal.aborted) {\n return;\n }\n this.#audioQueue.push(data);\n }\n\n pushTimedTranscript(timedWords: TimedString[]): void {\n this.#timedTranscriptQueue.push(...timedWords);\n }\n\n markDone(): void {\n this.#streamDone = true;\n }\n\n protected async run(): Promise<void> {\n const requestId = this.#contextId;\n const segmentId = this.#contextId;\n const bstream = new AudioByteStream(this.#opts.sampleRate, 1);\n\n let connection: Connection;\n try {\n connection = await this.#tts.currentConnection();\n } catch (e) {\n throw new APIConnectionError({ message: 'could not connect to ElevenLabs' });\n }\n\n let waiterReject: ((reason: Error) => void) | undefined;\n const waiterPromise = new Promise<void>((resolve, reject) => {\n waiterReject = reject;\n connection.registerStream(this, { resolve, reject });\n });\n\n // Handle abort - reject the waiter so Promise.all can complete\n const abortHandler = () => {\n if (waiterReject) {\n waiterReject(new Error('Stream aborted'));\n }\n };\n this.abortController.signal.addEventListener('abort', abortHandler, { once: true });\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#sentTokenizerStream.flush();\n continue;\n }\n this.#sentTokenizerStream.pushText(data);\n }\n this.#sentTokenizerStream.endInput();\n };\n\n const sentenceStreamTask = async () => {\n const flushOnChunk =\n this.#opts.wordTokenizer instanceof tokenize.SentenceTokenizer && this.#opts.autoMode;\n\n let xmlContent: string[] = [];\n\n for await (const data of this.#sentTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n let text = data.token;\n const xmlStartTokens = ['<phoneme', '<break'];\n const xmlEndTokens = ['</phoneme>', '/>'];\n\n if (\n (this.#opts.enableSsmlParsing &&\n xmlStartTokens.some((start) => text.startsWith(start))) ||\n xmlContent.length > 0\n ) {\n xmlContent.push(text);\n\n if (xmlEndTokens.some((end) => text.includes(end))) {\n text = xmlContent.join(' ');\n xmlContent = [];\n } else {\n continue;\n }\n }\n\n const formattedText = `${text} `; // must always end with a space\n connection.sendContent({\n contextId: this.#contextId,\n text: formattedText,\n flush: flushOnChunk,\n });\n }\n\n if (xmlContent.length > 0) {\n this.#logger.warn('ElevenLabs stream ended with incomplete xml content');\n }\n\n // Send final empty text to signal end of input\n connection.sendContent({ contextId: this.#contextId, text: '', flush: true });\n connection.closeContext(this.#contextId);\n };\n\n const audioProcessTask = async () => {\n let lastFrame: AudioFrame | undefined;\n\n while (!this.abortController.signal.aborted) {\n // Process audio queue\n while (this.#audioQueue.length > 0) {\n const audioData = this.#audioQueue.shift()!;\n for (const frame of bstream.write(audioData.buffer)) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n }\n\n // Exit when stream is done and queue is empty\n if (this.#streamDone && this.#audioQueue.length === 0) {\n break;\n }\n\n // Small delay to avoid busy waiting\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n // Flush remaining\n for (const frame of bstream.flush()) {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: false });\n }\n lastFrame = frame;\n }\n\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final: true });\n }\n };\n\n try {\n await Promise.all([inputTask(), sentenceStreamTask(), audioProcessTask(), waiterPromise]);\n } catch (e) {\n // If aborted, this is a normal termination - don't throw\n if (this.abortController.signal.aborted) {\n return;\n }\n\n if (e instanceof APITimeoutError) {\n throw e;\n }\n if (e instanceof APIStatusError) {\n throw e;\n }\n throw new APIStatusError({ message: 'Could not synthesize' });\n } finally {\n // Clean up abort listener\n this.abortController.signal.removeEventListener('abort', abortHandler);\n }\n }\n\n close(): void {\n // Clear audio buffers to prevent memory leak\n this.#audioQueue.length = 0;\n this.#timedTranscriptQueue.length = 0;\n this.#streamDone = true;\n this.#sentTokenizerStream.close();\n super.close();\n }\n}\n"],"mappings":"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","names":["stream","tts"]}
package/dist/tts.d.cts CHANGED
@@ -1,48 +1,125 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import { tokenize, tts } from '@livekit/agents';
3
- import { URL } from 'node:url';
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import { tokenize, tts, type voice } from '@livekit/agents';
4
4
  import type { TTSEncoding, TTSModels } from './models.js';
5
- type Voice = {
5
+ type TimedString = voice.TimedString;
6
+ export interface VoiceSettings {
7
+ stability: number;
8
+ similarity_boost: number;
9
+ style?: number;
10
+ speed?: number;
11
+ use_speaker_boost?: boolean;
12
+ }
13
+ export interface Voice {
6
14
  id: string;
7
15
  name: string;
8
16
  category: string;
9
17
  settings?: VoiceSettings;
10
- };
11
- type VoiceSettings = {
12
- stability: number;
13
- similarity_boost: number;
14
- style?: number;
15
- use_speaker_boost: boolean;
16
- };
18
+ }
19
+ export interface PronunciationDictionaryLocator {
20
+ pronunciation_dictionary_id: string;
21
+ version_id: string;
22
+ }
17
23
  export interface TTSOptions {
18
24
  apiKey?: string;
19
- voice: Voice;
20
- modelID: TTSModels | string;
25
+ voiceId?: string;
26
+ voiceSettings?: VoiceSettings;
27
+ model?: TTSModels | string;
28
+ language?: string;
29
+ voice?: Voice;
30
+ modelID?: TTSModels | string;
21
31
  languageCode?: string;
32
+ baseURL?: string;
33
+ encoding?: TTSEncoding;
34
+ streamingLatency?: number;
35
+ wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;
36
+ chunkLengthSchedule?: number[];
37
+ enableSsmlParsing?: boolean;
38
+ enableLogging?: boolean;
39
+ inactivityTimeout?: number;
40
+ syncAlignment?: boolean;
41
+ applyTextNormalization?: 'auto' | 'on' | 'off';
42
+ preferredAlignment?: 'normalized' | 'original';
43
+ autoMode?: boolean;
44
+ pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];
45
+ }
46
+ interface ResolvedTTSOptions {
47
+ apiKey: string;
48
+ voiceId: string;
49
+ voiceSettings?: VoiceSettings;
50
+ model: TTSModels | string;
51
+ language?: string;
22
52
  baseURL: string;
23
53
  encoding: TTSEncoding;
54
+ sampleRate: number;
24
55
  streamingLatency?: number;
25
56
  wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;
26
57
  chunkLengthSchedule?: number[];
27
58
  enableSsmlParsing: boolean;
59
+ enableLogging: boolean;
28
60
  inactivityTimeout: number;
29
61
  syncAlignment: boolean;
30
- autoMode?: boolean;
62
+ applyTextNormalization: 'auto' | 'on' | 'off';
63
+ preferredAlignment: 'normalized' | 'original';
64
+ autoMode: boolean;
65
+ pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];
66
+ }
67
+ interface SynthesizeContent {
68
+ contextId: string;
69
+ text: string;
70
+ flush: boolean;
71
+ }
72
+ declare class Connection {
73
+ #private;
74
+ constructor(opts: ResolvedTTSOptions);
75
+ get voiceId(): string;
76
+ get isCurrent(): boolean;
77
+ get closed(): boolean;
78
+ markNonCurrent(): void;
79
+ connect(): Promise<void>;
80
+ registerStream(stream: SynthesizeStream, waiter: {
81
+ resolve: (value: void) => void;
82
+ reject: (error: Error) => void;
83
+ }): void;
84
+ sendContent(content: SynthesizeContent): void;
85
+ closeContext(contextId: string): void;
86
+ close(): Promise<void>;
31
87
  }
32
88
  export declare class TTS extends tts.TTS {
33
89
  #private;
34
90
  label: string;
35
- constructor(opts?: Partial<TTSOptions>);
91
+ constructor(opts?: TTSOptions);
92
+ get model(): string;
93
+ get provider(): string;
36
94
  listVoices(): Promise<Voice[]>;
37
- synthesize(): tts.ChunkedStream;
38
- stream(): tts.SynthesizeStream;
95
+ updateOptions(opts: {
96
+ voiceId?: string;
97
+ voiceSettings?: VoiceSettings;
98
+ model?: TTSModels | string;
99
+ language?: string;
100
+ pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];
101
+ }): void;
102
+ currentConnection(): Promise<Connection>;
103
+ synthesize(text: string): ChunkedStream;
104
+ stream(): SynthesizeStream;
105
+ close(): Promise<void>;
106
+ }
107
+ export declare class ChunkedStream extends tts.ChunkedStream {
108
+ #private;
109
+ label: string;
110
+ constructor(tts: TTS, text: string, opts: ResolvedTTSOptions);
111
+ protected run(): Promise<void>;
39
112
  }
40
113
  export declare class SynthesizeStream extends tts.SynthesizeStream {
41
114
  #private;
42
115
  label: string;
43
- readonly streamURL: URL;
44
- constructor(tts: TTS, opts: TTSOptions);
116
+ constructor(tts: TTS, opts: ResolvedTTSOptions);
117
+ get contextId(): string;
118
+ pushAudio(data: Buffer): void;
119
+ pushTimedTranscript(timedWords: TimedString[]): void;
120
+ markDone(): void;
45
121
  protected run(): Promise<void>;
122
+ close(): void;
46
123
  }
47
124
  export {};
48
125
  //# sourceMappingURL=tts.d.ts.map
package/dist/tts.d.ts CHANGED
@@ -1,48 +1,125 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import { tokenize, tts } from '@livekit/agents';
3
- import { URL } from 'node:url';
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import { tokenize, tts, type voice } from '@livekit/agents';
4
4
  import type { TTSEncoding, TTSModels } from './models.js';
5
- type Voice = {
5
+ type TimedString = voice.TimedString;
6
+ export interface VoiceSettings {
7
+ stability: number;
8
+ similarity_boost: number;
9
+ style?: number;
10
+ speed?: number;
11
+ use_speaker_boost?: boolean;
12
+ }
13
+ export interface Voice {
6
14
  id: string;
7
15
  name: string;
8
16
  category: string;
9
17
  settings?: VoiceSettings;
10
- };
11
- type VoiceSettings = {
12
- stability: number;
13
- similarity_boost: number;
14
- style?: number;
15
- use_speaker_boost: boolean;
16
- };
18
+ }
19
+ export interface PronunciationDictionaryLocator {
20
+ pronunciation_dictionary_id: string;
21
+ version_id: string;
22
+ }
17
23
  export interface TTSOptions {
18
24
  apiKey?: string;
19
- voice: Voice;
20
- modelID: TTSModels | string;
25
+ voiceId?: string;
26
+ voiceSettings?: VoiceSettings;
27
+ model?: TTSModels | string;
28
+ language?: string;
29
+ voice?: Voice;
30
+ modelID?: TTSModels | string;
21
31
  languageCode?: string;
32
+ baseURL?: string;
33
+ encoding?: TTSEncoding;
34
+ streamingLatency?: number;
35
+ wordTokenizer?: tokenize.WordTokenizer | tokenize.SentenceTokenizer;
36
+ chunkLengthSchedule?: number[];
37
+ enableSsmlParsing?: boolean;
38
+ enableLogging?: boolean;
39
+ inactivityTimeout?: number;
40
+ syncAlignment?: boolean;
41
+ applyTextNormalization?: 'auto' | 'on' | 'off';
42
+ preferredAlignment?: 'normalized' | 'original';
43
+ autoMode?: boolean;
44
+ pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];
45
+ }
46
+ interface ResolvedTTSOptions {
47
+ apiKey: string;
48
+ voiceId: string;
49
+ voiceSettings?: VoiceSettings;
50
+ model: TTSModels | string;
51
+ language?: string;
22
52
  baseURL: string;
23
53
  encoding: TTSEncoding;
54
+ sampleRate: number;
24
55
  streamingLatency?: number;
25
56
  wordTokenizer: tokenize.WordTokenizer | tokenize.SentenceTokenizer;
26
57
  chunkLengthSchedule?: number[];
27
58
  enableSsmlParsing: boolean;
59
+ enableLogging: boolean;
28
60
  inactivityTimeout: number;
29
61
  syncAlignment: boolean;
30
- autoMode?: boolean;
62
+ applyTextNormalization: 'auto' | 'on' | 'off';
63
+ preferredAlignment: 'normalized' | 'original';
64
+ autoMode: boolean;
65
+ pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];
66
+ }
67
+ interface SynthesizeContent {
68
+ contextId: string;
69
+ text: string;
70
+ flush: boolean;
71
+ }
72
+ declare class Connection {
73
+ #private;
74
+ constructor(opts: ResolvedTTSOptions);
75
+ get voiceId(): string;
76
+ get isCurrent(): boolean;
77
+ get closed(): boolean;
78
+ markNonCurrent(): void;
79
+ connect(): Promise<void>;
80
+ registerStream(stream: SynthesizeStream, waiter: {
81
+ resolve: (value: void) => void;
82
+ reject: (error: Error) => void;
83
+ }): void;
84
+ sendContent(content: SynthesizeContent): void;
85
+ closeContext(contextId: string): void;
86
+ close(): Promise<void>;
31
87
  }
32
88
  export declare class TTS extends tts.TTS {
33
89
  #private;
34
90
  label: string;
35
- constructor(opts?: Partial<TTSOptions>);
91
+ constructor(opts?: TTSOptions);
92
+ get model(): string;
93
+ get provider(): string;
36
94
  listVoices(): Promise<Voice[]>;
37
- synthesize(): tts.ChunkedStream;
38
- stream(): tts.SynthesizeStream;
95
+ updateOptions(opts: {
96
+ voiceId?: string;
97
+ voiceSettings?: VoiceSettings;
98
+ model?: TTSModels | string;
99
+ language?: string;
100
+ pronunciationDictionaryLocators?: PronunciationDictionaryLocator[];
101
+ }): void;
102
+ currentConnection(): Promise<Connection>;
103
+ synthesize(text: string): ChunkedStream;
104
+ stream(): SynthesizeStream;
105
+ close(): Promise<void>;
106
+ }
107
+ export declare class ChunkedStream extends tts.ChunkedStream {
108
+ #private;
109
+ label: string;
110
+ constructor(tts: TTS, text: string, opts: ResolvedTTSOptions);
111
+ protected run(): Promise<void>;
39
112
  }
40
113
  export declare class SynthesizeStream extends tts.SynthesizeStream {
41
114
  #private;
42
115
  label: string;
43
- readonly streamURL: URL;
44
- constructor(tts: TTS, opts: TTSOptions);
116
+ constructor(tts: TTS, opts: ResolvedTTSOptions);
117
+ get contextId(): string;
118
+ pushAudio(data: Buffer): void;
119
+ pushTimedTranscript(timedWords: TimedString[]): void;
120
+ markDone(): void;
45
121
  protected run(): Promise<void>;
122
+ close(): void;
46
123
  }
47
124
  export {};
48
125
  //# sourceMappingURL=tts.d.ts.map
package/dist/tts.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../src/tts.ts"],"names":[],"mappings":";AAGA,OAAO,EAKL,QAAQ,EACR,GAAG,EACJ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI1D,KAAK,KAAK,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAiBF,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,iBAAiB,CAAC;IACnE,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAaD,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAE9B,KAAK,SAAoB;gBAEb,IAAI,GAAE,OAAO,CAAC,UAAU,CAAM;IAqCpC,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAuBpC,UAAU,IAAI,GAAG,CAAC,aAAa;IAI/B,MAAM,IAAI,GAAG,CAAC,gBAAgB;CAG/B;AAED,qBAAa,gBAAiB,SAAQ,GAAG,CAAC,gBAAgB;;IAGxD,KAAK,SAAiC;IACtC,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;gBAEZ,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU;cAuBtB,GAAG;CAmNpB"}
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../src/tts.ts"],"names":[],"mappings":"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"}