@livekit/agents-plugin-cartesia 1.2.6 → 1.2.8

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
@@ -21,7 +21,7 @@ class CartesiaPlugin extends import_agents.Plugin {
21
21
  constructor() {
22
22
  super({
23
23
  title: "cartesia",
24
- version: "1.2.6",
24
+ version: "1.2.8",
25
25
  package: "@livekit/agents-plugin-cartesia"
26
26
  });
27
27
  }
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ class CartesiaPlugin extends Plugin {
4
4
  constructor() {
5
5
  super({
6
6
  title: "cartesia",
7
- version: "1.2.6",
7
+ version: "1.2.8",
8
8
  package: "@livekit/agents-plugin-cartesia"
9
9
  });
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/models.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n// Ref: python livekit-plugins/livekit-plugins-cartesia/livekit/plugins/cartesia/models.py - 11 lines\nexport type TTSModels =\n | 'sonic'\n | 'sonic-2'\n | 'sonic-3'\n | 'sonic-lite'\n | 'sonic-preview'\n | 'sonic-turbo';\n\nexport type TTSLanguages = 'en' | 'es' | 'fr' | 'de' | 'pt' | 'zh' | 'ja';\n\nexport const TTSDefaultVoiceId = 'f786b574-daa5-4673-aa0c-cbe3e8534c02';\n\nexport const isSonic3 = (model: string): boolean => model.startsWith('sonic-3');\n\nexport type TTSVoiceSpeed = 'fastest' | 'fast' | 'normal' | 'slow' | 'slowest';\n\nexport type TTSVoiceEmotion =\n | 'anger:lowest'\n | 'anger:low'\n | 'anger'\n | 'anger:high'\n | 'anger:highest'\n | 'positivity:lowest'\n | 'positivity:low'\n | 'positivity'\n | 'positivity:high'\n | 'positivity:highest'\n | 'surprise:lowest'\n | 'surprise:low'\n | 'surprise'\n | 'surprise:high'\n | 'surprise:highest'\n | 'sadness:lowest'\n | 'sadness:low'\n | 'sadness'\n | 'sadness:high'\n | 'sadness:highest'\n | 'curiosity:lowest'\n | 'curiosity:low'\n | 'curiosity'\n | 'curiosity:high'\n | 'curiosity:highest';\n\nexport type TTSEncoding =\n // XXX(nbsp): not yet supported\n // | 'pcm_f32le'\n // | 'pcm_mulaw'\n // | 'pcm_alaw'\n 'pcm_s16le';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeO,MAAM,oBAAoB;AAE1B,MAAM,WAAW,CAAC,UAA2B,MAAM,WAAW,SAAS;","names":[]}
1
+ {"version":3,"sources":["../src/models.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport type TTSModels =\n | 'sonic'\n | 'sonic-2'\n | 'sonic-3'\n | 'sonic-lite'\n | 'sonic-preview'\n | 'sonic-turbo';\n\nexport type TTSLanguages = 'en' | 'es' | 'fr' | 'de' | 'pt' | 'zh' | 'ja';\n\nexport const TTSDefaultVoiceId = 'f786b574-daa5-4673-aa0c-cbe3e8534c02';\n\nexport const isSonic3 = (model: string): boolean => model.startsWith('sonic-3');\n\nexport type TTSVoiceSpeed = 'fastest' | 'fast' | 'normal' | 'slow' | 'slowest';\n\nexport type TTSVoiceEmotion =\n | 'anger:lowest'\n | 'anger:low'\n | 'anger'\n | 'anger:high'\n | 'anger:highest'\n | 'positivity:lowest'\n | 'positivity:low'\n | 'positivity'\n | 'positivity:high'\n | 'positivity:highest'\n | 'surprise:lowest'\n | 'surprise:low'\n | 'surprise'\n | 'surprise:high'\n | 'surprise:highest'\n | 'sadness:lowest'\n | 'sadness:low'\n | 'sadness'\n | 'sadness:high'\n | 'sadness:highest'\n | 'curiosity:lowest'\n | 'curiosity:low'\n | 'curiosity'\n | 'curiosity:high'\n | 'curiosity:highest';\n\nexport type TTSEncoding =\n // XXX(nbsp): not yet supported\n // | 'pcm_f32le'\n // | 'pcm_mulaw'\n // | 'pcm_alaw'\n 'pcm_s16le';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcO,MAAM,oBAAoB;AAE1B,MAAM,WAAW,CAAC,UAA2B,MAAM,WAAW,SAAS;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,SAAS,GACT,SAAS,GACT,YAAY,GACZ,eAAe,GACf,aAAa,CAAC;AAElB,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1E,eAAO,MAAM,iBAAiB,yCAAyC,CAAC;AAExE,eAAO,MAAM,QAAQ,UAAW,MAAM,KAAG,OAAsC,CAAC;AAEhF,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/E,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,WAAW,GACX,OAAO,GACP,YAAY,GACZ,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,iBAAiB,GACjB,oBAAoB,GACpB,iBAAiB,GACjB,cAAc,GACd,UAAU,GACV,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,SAAS,GACT,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,eAAe,GACf,WAAW,GACX,gBAAgB,GAChB,mBAAmB,CAAC;AAExB,MAAM,MAAM,WAAW,GAKrB,WAAW,CAAC"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,SAAS,GACT,SAAS,GACT,YAAY,GACZ,eAAe,GACf,aAAa,CAAC;AAElB,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1E,eAAO,MAAM,iBAAiB,yCAAyC,CAAC;AAExE,eAAO,MAAM,QAAQ,UAAW,MAAM,KAAG,OAAsC,CAAC;AAEhF,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/E,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,WAAW,GACX,OAAO,GACP,YAAY,GACZ,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,iBAAiB,GACjB,oBAAoB,GACpB,iBAAiB,GACjB,cAAc,GACd,UAAU,GACV,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,SAAS,GACT,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,eAAe,GACf,WAAW,GACX,gBAAgB,GAChB,mBAAmB,CAAC;AAExB,MAAM,MAAM,WAAW,GAKrB,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/models.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n// Ref: python livekit-plugins/livekit-plugins-cartesia/livekit/plugins/cartesia/models.py - 11 lines\nexport type TTSModels =\n | 'sonic'\n | 'sonic-2'\n | 'sonic-3'\n | 'sonic-lite'\n | 'sonic-preview'\n | 'sonic-turbo';\n\nexport type TTSLanguages = 'en' | 'es' | 'fr' | 'de' | 'pt' | 'zh' | 'ja';\n\nexport const TTSDefaultVoiceId = 'f786b574-daa5-4673-aa0c-cbe3e8534c02';\n\nexport const isSonic3 = (model: string): boolean => model.startsWith('sonic-3');\n\nexport type TTSVoiceSpeed = 'fastest' | 'fast' | 'normal' | 'slow' | 'slowest';\n\nexport type TTSVoiceEmotion =\n | 'anger:lowest'\n | 'anger:low'\n | 'anger'\n | 'anger:high'\n | 'anger:highest'\n | 'positivity:lowest'\n | 'positivity:low'\n | 'positivity'\n | 'positivity:high'\n | 'positivity:highest'\n | 'surprise:lowest'\n | 'surprise:low'\n | 'surprise'\n | 'surprise:high'\n | 'surprise:highest'\n | 'sadness:lowest'\n | 'sadness:low'\n | 'sadness'\n | 'sadness:high'\n | 'sadness:highest'\n | 'curiosity:lowest'\n | 'curiosity:low'\n | 'curiosity'\n | 'curiosity:high'\n | 'curiosity:highest';\n\nexport type TTSEncoding =\n // XXX(nbsp): not yet supported\n // | 'pcm_f32le'\n // | 'pcm_mulaw'\n // | 'pcm_alaw'\n 'pcm_s16le';\n"],"mappings":"AAeO,MAAM,oBAAoB;AAE1B,MAAM,WAAW,CAAC,UAA2B,MAAM,WAAW,SAAS;","names":[]}
1
+ {"version":3,"sources":["../src/models.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport type TTSModels =\n | 'sonic'\n | 'sonic-2'\n | 'sonic-3'\n | 'sonic-lite'\n | 'sonic-preview'\n | 'sonic-turbo';\n\nexport type TTSLanguages = 'en' | 'es' | 'fr' | 'de' | 'pt' | 'zh' | 'ja';\n\nexport const TTSDefaultVoiceId = 'f786b574-daa5-4673-aa0c-cbe3e8534c02';\n\nexport const isSonic3 = (model: string): boolean => model.startsWith('sonic-3');\n\nexport type TTSVoiceSpeed = 'fastest' | 'fast' | 'normal' | 'slow' | 'slowest';\n\nexport type TTSVoiceEmotion =\n | 'anger:lowest'\n | 'anger:low'\n | 'anger'\n | 'anger:high'\n | 'anger:highest'\n | 'positivity:lowest'\n | 'positivity:low'\n | 'positivity'\n | 'positivity:high'\n | 'positivity:highest'\n | 'surprise:lowest'\n | 'surprise:low'\n | 'surprise'\n | 'surprise:high'\n | 'surprise:highest'\n | 'sadness:lowest'\n | 'sadness:low'\n | 'sadness'\n | 'sadness:high'\n | 'sadness:highest'\n | 'curiosity:lowest'\n | 'curiosity:low'\n | 'curiosity'\n | 'curiosity:high'\n | 'curiosity:highest';\n\nexport type TTSEncoding =\n // XXX(nbsp): not yet supported\n // | 'pcm_f32le'\n // | 'pcm_mulaw'\n // | 'pcm_alaw'\n 'pcm_s16le';\n"],"mappings":"AAcO,MAAM,oBAAoB;AAE1B,MAAM,WAAW,CAAC,UAA2B,MAAM,WAAW,SAAS;","names":[]}
package/dist/tts.cjs CHANGED
@@ -405,7 +405,6 @@ class SynthesizeStream extends import_agents.tts.SynthesizeStream {
405
405
  }
406
406
  }
407
407
  }
408
- const asError = (e) => e instanceof Error ? e : new Error(String(e));
409
408
  const transientNetworkCodes = /* @__PURE__ */ new Set([
410
409
  "ETIMEDOUT",
411
410
  "ECONNRESET",
@@ -438,7 +437,7 @@ const hasAnyTransientCode = (e) => {
438
437
  return false;
439
438
  };
440
439
  const toRetryableConnectionError = (e) => {
441
- const err = asError(e);
440
+ const err = (0, import_agents.asError)(e);
442
441
  const isTimeout = hasErrorCode(e, "ETIMEDOUT") || typeof err.message === "string" && err.message.includes("ETIMEDOUT");
443
442
  const message = isTimeout ? `Cartesia connection timed out` : `Cartesia connection failed: ${err.message || "unknown error"}`;
444
443
  return isTimeout ? new import_agents.APITimeoutError({ message }) : new import_agents.APIConnectionError({ message });
@@ -461,7 +460,7 @@ const waitForWsOpen = async ({
461
460
  abortSignal.removeEventListener("abort", onAbort);
462
461
  };
463
462
  const onOpen = () => fut.resolve();
464
- const onError = (err) => fut.reject(asError(err));
463
+ const onError = (err) => fut.reject((0, import_agents.asError)(err));
465
464
  const onClose = (code, reason) => fut.reject(
466
465
  new Error(`WebSocket closed before open (code=${code}, reason=${reason.toString()})`)
467
466
  );
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 type APIConnectOptions,\n APIConnectionError,\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 type { AudioFrame } from '@livekit/rtc-node';\nimport { request } from 'node:https';\nimport { type RawData, WebSocket } from 'ws';\nimport {\n TTSDefaultVoiceId,\n type TTSEncoding,\n type TTSModels,\n type TTSVoiceEmotion,\n type TTSVoiceSpeed,\n isSonic3,\n} from './models.js';\nimport {\n type CartesiaServerMessage,\n cartesiaMessageSchema,\n hasWordTimestamps,\n isChunkMessage,\n isDoneMessage,\n isErrorMessage,\n} from './types.js';\n\nconst AUTHORIZATION_HEADER = 'X-API-Key';\nconst VERSION_HEADER = 'Cartesia-Version';\nconst API_VERSION = '2025-04-16';\nconst API_VERSION_WITH_EXPERIMENTAL_CONTROLS = '2024-11-13';\nconst MODEL_WITH_EXPERIMENTAL_CONTROLS = 'sonic-2-2025-03-07';\nconst NUM_CHANNELS = 1;\nconst BUFFERED_WORDS_COUNT = 8;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n encoding: TTSEncoding;\n sampleRate: number;\n voice: string | number[];\n speed?: TTSVoiceSpeed | number;\n emotion?: (TTSVoiceEmotion | string)[];\n /**\n * Volume of the speech. For sonic-3, the value is valid between 0.5 and 2.0.\n * @see https://docs.cartesia.ai/api-reference/tts/bytes#body-generation-config-volume\n */\n volume?: number;\n apiKey?: string;\n language: string;\n baseUrl: string;\n apiVersion: string;\n\n /**\n * The timeout for the next chunk to be received from the Cartesia API.\n */\n chunkTimeout: number;\n\n /**\n * Whether to add word timestamps to the output. When enabled, the TTS will return\n * timing information for each word in the transcript.\n * @defaultValue true\n */\n wordTimestamps?: boolean;\n\n pronunciationDictId?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n model: 'sonic-3',\n encoding: 'pcm_s16le',\n sampleRate: 24000,\n voice: TTSDefaultVoiceId,\n apiKey: process.env.CARTESIA_API_KEY,\n language: 'en',\n baseUrl: 'https://api.cartesia.ai',\n apiVersion: API_VERSION,\n chunkTimeout: 5000,\n wordTimestamps: true,\n};\n\nconst checkGenerationConfig = (opts: TTSOptions) => {\n const logger = log();\n if (isSonic3(opts.model)) {\n if (opts.speed !== undefined && typeof opts.speed === 'number') {\n if (opts.speed < 0.6 || opts.speed > 2.0) {\n logger.warn('speed must be between 0.6 and 2.0 for sonic-3');\n }\n }\n if (opts.volume !== undefined && (opts.volume < 0.5 || opts.volume > 2.0)) {\n logger.warn('volume must be between 0.5 and 2.0 for sonic-3');\n }\n } else if (\n opts.apiVersion !== API_VERSION_WITH_EXPERIMENTAL_CONTROLS ||\n opts.model !== MODEL_WITH_EXPERIMENTAL_CONTROLS\n ) {\n if (opts.speed || opts.emotion) {\n logger.warn(\n { model: opts.model, speed: opts.speed, emotion: opts.emotion },\n `speed and emotion controls are only supported for model '${MODEL_WITH_EXPERIMENTAL_CONTROLS}' ` +\n `or sonic-3 models, see https://docs.cartesia.ai/developer-tools/changelog for details`,\n );\n }\n }\n\n if (opts.pronunciationDictId && !isSonic3(opts.model)) {\n logger.warn(\n { model: opts.model, pronunciationDictId: opts.pronunciationDictId },\n 'pronunciationDictId is only supported for sonic-3 models',\n );\n }\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n label = 'cartesia.TTS';\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Cartesia';\n }\n\n constructor(opts: Partial<TTSOptions> = {}) {\n const resolvedOpts = {\n ...defaultTTSOptions,\n ...opts,\n };\n\n super(resolvedOpts.sampleRate || defaultTTSOptions.sampleRate, NUM_CHANNELS, {\n streaming: true,\n alignedTranscript: resolvedOpts.wordTimestamps ?? true,\n });\n\n this.#opts = resolvedOpts;\n this.#opts.language = normalizeLanguage(this.#opts.language);\n\n if (this.#opts.apiKey === undefined) {\n throw new Error(\n 'Cartesia API key is required, whether as an argument or as $CARTESIA_API_KEY',\n );\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.language !== undefined) {\n this.#opts.language = normalizeLanguage(opts.language);\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): tts.ChunkedStream {\n return new ChunkedStream(this, text, this.#opts, connOptions, abortSignal);\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream {\n return new SynthesizeStream(this, this.#opts, options?.connOptions);\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'cartesia.ChunkedStream';\n #logger = log();\n #opts: TTSOptions;\n #text: string;\n\n constructor(\n tts: TTS,\n text: string,\n opts: TTSOptions,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(text, tts, connOptions, abortSignal);\n this.#text = text;\n this.#opts = opts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n const json = toCartesiaOptions(this.#opts);\n json.transcript = this.#text;\n\n const baseUrl = new URL(this.#opts.baseUrl);\n const doneFut = new Future<void>();\n\n const req = request(\n {\n hostname: baseUrl.hostname,\n port: parseInt(baseUrl.port) || (baseUrl.protocol === 'https:' ? 443 : 80),\n path: '/tts/bytes',\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey!,\n [VERSION_HEADER]: this.#opts.apiVersion,\n },\n signal: this.abortSignal,\n },\n (res) => {\n res.on('data', (chunk) => {\n for (const frame of bstream.write(chunk)) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n });\n res.on('close', () => {\n for (const frame of bstream.flush()) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n this.queue.close();\n if (!doneFut.done) doneFut.resolve();\n });\n res.on('error', (err) => {\n if (err.message === 'aborted') return;\n this.#logger.error({ err }, 'Cartesia TTS response error');\n if (!doneFut.done) doneFut.reject(err);\n });\n },\n );\n\n req.on('error', (err) => {\n if (err.name === 'AbortError') return;\n this.#logger.error({ err }, 'Cartesia TTS request error');\n if (!doneFut.done) doneFut.reject(err);\n });\n req.on('close', () => {\n if (!doneFut.done) doneFut.resolve();\n });\n req.write(JSON.stringify(json));\n req.end();\n\n try {\n await doneFut.await;\n } catch (e) {\n if (this.abortSignal.aborted) return;\n if (!this.queue.closed) this.queue.close();\n throw toRetryableConnectionError(e);\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #opts: TTSOptions;\n #logger = log();\n #tokenizer = new tokenize.basic.SentenceTokenizer({\n minSentenceLength: BUFFERED_WORDS_COUNT,\n }).stream();\n label = 'cartesia.SynthesizeStream';\n\n constructor(tts: TTS, opts: TTSOptions, connOptions?: APIConnectOptions) {\n super(tts, connOptions);\n this.#opts = opts;\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n protected async run() {\n const requestId = shortuuid();\n let closing = false;\n // Only close WebSocket when both: 1) Cartesia returns done, AND 2) all sentences have been sent\n let sentenceStreamClosed = false;\n\n const sentenceStreamTask = async (ws: WebSocket) => {\n const packet = toCartesiaOptions(this.#opts, true);\n for await (const event of this.#tokenizer) {\n const msg = {\n ...packet,\n context_id: requestId,\n transcript: event.token + ' ',\n continue: true,\n };\n ws.send(JSON.stringify(msg));\n }\n\n const endMsg = {\n ...packet,\n context_id: requestId,\n transcript: ' ',\n continue: false,\n };\n ws.send(JSON.stringify(endMsg));\n // Mark sentence stream as closed\n sentenceStreamClosed = true;\n };\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#tokenizer.flush();\n continue;\n }\n this.#tokenizer.pushText(data);\n }\n this.#tokenizer.endInput();\n this.#tokenizer.close();\n };\n\n // Use event channel and set up listeners ONCE to avoid missing messages during listener re-registration\n const recvTask = async (ws: WebSocket) => {\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n\n // Create event channel to buffer incoming messages\n // This prevents message loss between listener re-registrations\n const eventChannel = stream.createStreamChannel<RawData>();\n\n let lastFrame: AudioFrame | undefined;\n let pendingTimedTranscripts: TimedString[] = [];\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame && !this.queue.closed) {\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 let timeout: NodeJS.Timeout | null = null;\n\n const clearTTSChunkTimeout = () => {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n };\n\n // Set up WebSocket listeners ONCE (not in a loop)\n const onMessage = (data: RawData) => {\n void eventChannel.write(data).catch((error: unknown) => {\n this.#logger.debug({ error }, 'Failed writing Cartesia event to channel (likely closed)');\n });\n };\n\n const onClose = (code: number, reason: Buffer) => {\n if (!closing) {\n this.#logger.debug(`WebSocket closed with code ${code}: ${reason.toString()}`);\n }\n clearTTSChunkTimeout();\n void eventChannel.close();\n };\n\n const onError = (err: Error) => {\n this.#logger.error({ err }, 'Cartesia WebSocket error');\n void eventChannel.close();\n };\n\n // Attach listeners ONCE\n ws.on('message', onMessage);\n ws.on('close', onClose);\n ws.on('error', onError);\n\n try {\n // Process messages from the channel\n const reader = eventChannel.stream().getReader();\n\n while (!this.closed && !this.abortController.signal.aborted) {\n const result = await reader.read();\n if (result.done) break;\n\n const rawMsg = result.value;\n\n // Parse message with Zod schema for type safety\n let serverMsg: CartesiaServerMessage;\n try {\n const json = JSON.parse(rawMsg.toString());\n serverMsg = cartesiaMessageSchema.parse(json);\n } catch (parseErr) {\n this.#logger.warn({ parseErr }, 'Failed to parse Cartesia message');\n continue;\n }\n\n // Handle error messages\n if (isErrorMessage(serverMsg)) {\n this.#logger.error({ error: serverMsg.error }, 'Cartesia returned error');\n continue;\n }\n\n const segmentId = serverMsg.context_id;\n\n // Process word timestamps if present (typed via Zod schema)\n if (this.#opts.wordTimestamps !== false && hasWordTimestamps(serverMsg)) {\n const wordTimestamps = serverMsg.word_timestamps;\n for (let i = 0; i < wordTimestamps.words.length; i++) {\n const word = wordTimestamps.words[i];\n const startTime = wordTimestamps.start[i];\n const endTime = wordTimestamps.end[i];\n if (word !== undefined && startTime !== undefined && endTime !== undefined) {\n pendingTimedTranscripts.push(\n createTimedString({\n text: word + ' ', // Add space after word for consistency\n startTime,\n endTime,\n }),\n );\n }\n }\n }\n\n // Handle audio chunk messages\n if (isChunkMessage(serverMsg)) {\n const audioBuffer = Buffer.from(serverMsg.data, 'base64');\n // Extract ArrayBuffer from Buffer for AudioByteStream compatibility\n const audioData = audioBuffer.buffer.slice(\n audioBuffer.byteOffset,\n audioBuffer.byteOffset + audioBuffer.byteLength,\n );\n for (const frame of bstream.write(audioData)) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n\n // IMPORTANT: close WS if TTS chunk stream been stuck too long\n // this allows unblock the current \"broken\" TTS node so that any future TTS nodes\n // can continue to process the stream without been blocked by the stuck node\n clearTTSChunkTimeout();\n timeout = setTimeout(() => {\n // cartesia chunk timeout quite often, so we make it a debug log\n this.#logger.debug(\n `Cartesia WebSocket TTS chunk stream timeout after ${this.#opts.chunkTimeout}ms`,\n );\n ws.close();\n }, this.#opts.chunkTimeout);\n } else if (isDoneMessage(serverMsg)) {\n // This ensures all sentences have been sent before closing\n if (sentenceStreamClosed) {\n for (const frame of bstream.flush()) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n sendLastFrame(segmentId, true);\n if (!this.queue.closed) {\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n }\n\n if (segmentId === requestId) {\n closing = true;\n clearTTSChunkTimeout();\n ws.close();\n break; // Exit the loop\n }\n }\n // If sentenceStreamClosed is false, continue receiving - more done messages will come\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 (\n err.message.includes('Queue is closed') ||\n err.message.includes('Channel is closed')\n ) {\n this.#logger.warn(\n { err },\n 'Channel closed during transcript processing (expected during disconnect)',\n );\n } else {\n this.#logger.error({ err }, 'Error in recvTask from Cartesia WebSocket');\n }\n }\n } finally {\n // IMPORTANT: Remove listeners so connection can be reused\n ws.off('message', onMessage);\n ws.off('close', onClose);\n ws.off('error', onError);\n clearTTSChunkTimeout();\n }\n };\n\n const wsUrl = this.#opts.baseUrl.replace(/^http/, 'ws');\n const url = `${wsUrl}/tts/websocket?api_key=${this.#opts.apiKey}&cartesia_version=${this.#opts.apiVersion}`;\n\n let ws: WebSocket | undefined;\n try {\n ws = await connectCartesiaWebSocket({\n url,\n timeoutMs: this.connOptions.timeoutMs,\n abortSignal: this.abortSignal,\n });\n await Promise.all([inputTask(), sentenceStreamTask(ws), recvTask(ws)]);\n } catch (e) {\n if (this.abortSignal.aborted) {\n return;\n }\n throw toRetryableConnectionError(e);\n } finally {\n // Ensure we don't leak sockets/tasks across retry attempts.\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n safeTerminateWebSocket(ws);\n }\n }\n }\n}\n\nconst asError = (e: unknown): Error => (e instanceof Error ? e : new Error(String(e)));\n\nconst transientNetworkCodes = new Set([\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'EAI_AGAIN',\n 'ENETUNREACH',\n 'ECONNREFUSED',\n 'EHOSTUNREACH',\n]);\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => {\n return v !== null && typeof v === 'object';\n};\n\nconst isAggregateErrorLike = (e: unknown): e is { errors: unknown[]; name?: string } => {\n if (!isRecord(e)) return false;\n return e.name === 'AggregateError' && Array.isArray(e.errors);\n};\n\nconst hasErrorCode = (e: unknown, code: string): boolean => {\n if (isRecord(e) && e.code === code) return true;\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasErrorCode(inner, code));\n }\n return false;\n};\n\nconst hasAnyTransientCode = (e: unknown): boolean => {\n if (isRecord(e) && typeof e.code === 'string') {\n return transientNetworkCodes.has(e.code);\n }\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasAnyTransientCode(inner));\n }\n return false;\n};\n\nconst toRetryableConnectionError = (e: unknown): APIConnectionError => {\n const err = asError(e);\n const isTimeout =\n hasErrorCode(e, 'ETIMEDOUT') ||\n (typeof err.message === 'string' && err.message.includes('ETIMEDOUT'));\n const message = isTimeout\n ? `Cartesia connection timed out`\n : `Cartesia connection failed: ${err.message || 'unknown error'}`;\n return isTimeout ? new APITimeoutError({ message }) : new APIConnectionError({ message });\n};\n\nconst waitForWsOpen = async ({\n ws,\n timeoutMs,\n abortSignal,\n}: {\n ws: WebSocket;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}) => {\n if (abortSignal.aborted) {\n throw new Error('aborted');\n }\n\n const fut = new Future<void>();\n let timeout: NodeJS.Timeout | undefined;\n\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n ws.off('open', onOpen);\n ws.off('error', onError);\n ws.off('close', onClose);\n abortSignal.removeEventListener('abort', onAbort);\n };\n\n const onOpen = () => fut.resolve();\n const onError = (err: Error) => fut.reject(asError(err));\n const onClose = (code: number, reason: Buffer) =>\n fut.reject(\n new Error(`WebSocket closed before open (code=${code}, reason=${reason.toString()})`),\n );\n const onAbort = () => fut.reject(new Error('aborted'));\n\n ws.on('open', onOpen);\n ws.on('error', onError);\n ws.on('close', onClose);\n abortSignal.addEventListener('abort', onAbort, { once: true });\n\n if (timeoutMs > 0) {\n timeout = setTimeout(() => fut.reject(new Error('connect timeout')), timeoutMs);\n }\n\n try {\n await fut.await;\n } finally {\n cleanup();\n }\n};\n\nconst safeTerminateWebSocket = (ws: WebSocket) => {\n // `ws` can emit an 'error' event during teardown (especially if CONNECTING).\n // If there is no error listener at that moment, Node will treat it as unhandled and crash the process.\n try {\n ws.on('error', () => {});\n } catch {\n // ignore\n }\n\n try {\n // `terminate()` can throw if the socket was never established; `close()` is safer in CONNECTING.\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close();\n } else {\n ws.terminate();\n }\n } catch {\n // ignore\n }\n};\n\nconst connectCartesiaWebSocket = async ({\n url,\n timeoutMs,\n abortSignal,\n}: {\n url: string;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}): Promise<WebSocket> => {\n const connectOnce = async (family?: number): Promise<WebSocket> => {\n const ws = new WebSocket(url, { handshakeTimeout: timeoutMs, family });\n try {\n await waitForWsOpen({ ws, timeoutMs, abortSignal });\n return ws;\n } catch (e) {\n safeTerminateWebSocket(ws);\n throw e;\n }\n };\n\n try {\n return await connectOnce();\n } catch (e) {\n // Mitigation for Node.js dual-stack (IPv6/IPv4) connect flakiness (\"happy eyeballs\"):\n // some environments surface `AggregateError` with nested `ETIMEDOUT` during the initial\n // WebSocket open. In that case we do a one-off retry forcing IPv4 (`family: 4`) before\n // letting the outer framework retry loop handle further attempts.\n //\n // If you still see `AggregateError`/`ETIMEDOUT`:\n // - Increase the session TTS connect timeout (`connOptions.ttsConnOptions.timeoutMs`)\n // - Or adjust Node's family autoselection behavior via `NODE_OPTIONS`, e.g.\n // `--network-family-autoselection-attempt-timeout=5000` (or disable it entirely).\n if (hasAnyTransientCode(e) || isAggregateErrorLike(e)) {\n return await connectOnce(4);\n }\n throw e;\n }\n};\n\nconst toCartesiaOptions = (\n opts: TTSOptions,\n streaming: boolean = false,\n): { [id: string]: unknown } => {\n const voice: { [id: string]: unknown } = {};\n if (typeof opts.voice === 'string') {\n voice.mode = 'id';\n voice.id = opts.voice;\n } else {\n voice.mode = 'embedding';\n voice.embedding = opts.voice;\n }\n\n if (opts.apiVersion === API_VERSION_WITH_EXPERIMENTAL_CONTROLS) {\n const voiceControls: { [id: string]: unknown } = {};\n if (opts.speed) {\n voiceControls.speed = opts.speed;\n }\n if (opts.emotion) {\n voiceControls.emotion = opts.emotion;\n }\n if (Object.keys(voiceControls).length) {\n voice.__experimental_controls = voiceControls;\n }\n }\n\n const result: { [id: string]: unknown } = {\n model_id: opts.model,\n voice,\n output_format: {\n container: 'raw',\n encoding: opts.encoding,\n sample_rate: opts.sampleRate,\n },\n language: getBaseLanguage(opts.language),\n max_buffer_delay_ms: 0,\n };\n\n if (opts.pronunciationDictId) {\n result.pronunciation_dict_id = opts.pronunciationDictId;\n }\n\n if (opts.apiVersion > API_VERSION_WITH_EXPERIMENTAL_CONTROLS && isSonic3(opts.model)) {\n const generationConfig: { [id: string]: unknown } = {};\n if (opts.speed) {\n generationConfig.speed = opts.speed;\n }\n if (opts.emotion) {\n generationConfig.emotion = opts.emotion[0];\n }\n if (opts.volume) {\n generationConfig.volume = opts.volume;\n }\n if (Object.keys(generationConfig).length) {\n result.generation_config = generationConfig;\n }\n }\n\n if (streaming && opts.wordTimestamps !== false) {\n result.add_timestamps = true;\n }\n\n return result;\n};\n"],"mappings":"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","names":["tts","ws"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APITimeoutError,\n AudioByteStream,\n Future,\n type TimedString,\n asError,\n createTimedString,\n getBaseLanguage,\n log,\n normalizeLanguage,\n shortuuid,\n stream,\n tokenize,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { request } from 'node:https';\nimport { type RawData, WebSocket } from 'ws';\nimport {\n TTSDefaultVoiceId,\n type TTSEncoding,\n type TTSModels,\n type TTSVoiceEmotion,\n type TTSVoiceSpeed,\n isSonic3,\n} from './models.js';\nimport {\n type CartesiaServerMessage,\n cartesiaMessageSchema,\n hasWordTimestamps,\n isChunkMessage,\n isDoneMessage,\n isErrorMessage,\n} from './types.js';\n\nconst AUTHORIZATION_HEADER = 'X-API-Key';\nconst VERSION_HEADER = 'Cartesia-Version';\nconst API_VERSION = '2025-04-16';\nconst API_VERSION_WITH_EXPERIMENTAL_CONTROLS = '2024-11-13';\nconst MODEL_WITH_EXPERIMENTAL_CONTROLS = 'sonic-2-2025-03-07';\nconst NUM_CHANNELS = 1;\nconst BUFFERED_WORDS_COUNT = 8;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n encoding: TTSEncoding;\n sampleRate: number;\n voice: string | number[];\n speed?: TTSVoiceSpeed | number;\n emotion?: (TTSVoiceEmotion | string)[];\n /**\n * Volume of the speech. For sonic-3, the value is valid between 0.5 and 2.0.\n * @see https://docs.cartesia.ai/api-reference/tts/bytes#body-generation-config-volume\n */\n volume?: number;\n apiKey?: string;\n language: string;\n baseUrl: string;\n apiVersion: string;\n\n /**\n * The timeout for the next chunk to be received from the Cartesia API.\n */\n chunkTimeout: number;\n\n /**\n * Whether to add word timestamps to the output. When enabled, the TTS will return\n * timing information for each word in the transcript.\n * @defaultValue true\n */\n wordTimestamps?: boolean;\n\n pronunciationDictId?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n model: 'sonic-3',\n encoding: 'pcm_s16le',\n sampleRate: 24000,\n voice: TTSDefaultVoiceId,\n apiKey: process.env.CARTESIA_API_KEY,\n language: 'en',\n baseUrl: 'https://api.cartesia.ai',\n apiVersion: API_VERSION,\n chunkTimeout: 5000,\n wordTimestamps: true,\n};\n\nconst checkGenerationConfig = (opts: TTSOptions) => {\n const logger = log();\n if (isSonic3(opts.model)) {\n if (opts.speed !== undefined && typeof opts.speed === 'number') {\n if (opts.speed < 0.6 || opts.speed > 2.0) {\n logger.warn('speed must be between 0.6 and 2.0 for sonic-3');\n }\n }\n if (opts.volume !== undefined && (opts.volume < 0.5 || opts.volume > 2.0)) {\n logger.warn('volume must be between 0.5 and 2.0 for sonic-3');\n }\n } else if (\n opts.apiVersion !== API_VERSION_WITH_EXPERIMENTAL_CONTROLS ||\n opts.model !== MODEL_WITH_EXPERIMENTAL_CONTROLS\n ) {\n if (opts.speed || opts.emotion) {\n logger.warn(\n { model: opts.model, speed: opts.speed, emotion: opts.emotion },\n `speed and emotion controls are only supported for model '${MODEL_WITH_EXPERIMENTAL_CONTROLS}' ` +\n `or sonic-3 models, see https://docs.cartesia.ai/developer-tools/changelog for details`,\n );\n }\n }\n\n if (opts.pronunciationDictId && !isSonic3(opts.model)) {\n logger.warn(\n { model: opts.model, pronunciationDictId: opts.pronunciationDictId },\n 'pronunciationDictId is only supported for sonic-3 models',\n );\n }\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n label = 'cartesia.TTS';\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Cartesia';\n }\n\n constructor(opts: Partial<TTSOptions> = {}) {\n const resolvedOpts = {\n ...defaultTTSOptions,\n ...opts,\n };\n\n super(resolvedOpts.sampleRate || defaultTTSOptions.sampleRate, NUM_CHANNELS, {\n streaming: true,\n alignedTranscript: resolvedOpts.wordTimestamps ?? true,\n });\n\n this.#opts = resolvedOpts;\n this.#opts.language = normalizeLanguage(this.#opts.language);\n\n if (this.#opts.apiKey === undefined) {\n throw new Error(\n 'Cartesia API key is required, whether as an argument or as $CARTESIA_API_KEY',\n );\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.language !== undefined) {\n this.#opts.language = normalizeLanguage(opts.language);\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): tts.ChunkedStream {\n return new ChunkedStream(this, text, this.#opts, connOptions, abortSignal);\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream {\n return new SynthesizeStream(this, this.#opts, options?.connOptions);\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'cartesia.ChunkedStream';\n #logger = log();\n #opts: TTSOptions;\n #text: string;\n\n constructor(\n tts: TTS,\n text: string,\n opts: TTSOptions,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(text, tts, connOptions, abortSignal);\n this.#text = text;\n this.#opts = opts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n const json = toCartesiaOptions(this.#opts);\n json.transcript = this.#text;\n\n const baseUrl = new URL(this.#opts.baseUrl);\n const doneFut = new Future<void>();\n\n const req = request(\n {\n hostname: baseUrl.hostname,\n port: parseInt(baseUrl.port) || (baseUrl.protocol === 'https:' ? 443 : 80),\n path: '/tts/bytes',\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey!,\n [VERSION_HEADER]: this.#opts.apiVersion,\n },\n signal: this.abortSignal,\n },\n (res) => {\n res.on('data', (chunk) => {\n for (const frame of bstream.write(chunk)) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n });\n res.on('close', () => {\n for (const frame of bstream.flush()) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n this.queue.close();\n if (!doneFut.done) doneFut.resolve();\n });\n res.on('error', (err) => {\n if (err.message === 'aborted') return;\n this.#logger.error({ err }, 'Cartesia TTS response error');\n if (!doneFut.done) doneFut.reject(err);\n });\n },\n );\n\n req.on('error', (err) => {\n if (err.name === 'AbortError') return;\n this.#logger.error({ err }, 'Cartesia TTS request error');\n if (!doneFut.done) doneFut.reject(err);\n });\n req.on('close', () => {\n if (!doneFut.done) doneFut.resolve();\n });\n req.write(JSON.stringify(json));\n req.end();\n\n try {\n await doneFut.await;\n } catch (e) {\n if (this.abortSignal.aborted) return;\n if (!this.queue.closed) this.queue.close();\n throw toRetryableConnectionError(e);\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #opts: TTSOptions;\n #logger = log();\n #tokenizer = new tokenize.basic.SentenceTokenizer({\n minSentenceLength: BUFFERED_WORDS_COUNT,\n }).stream();\n label = 'cartesia.SynthesizeStream';\n\n constructor(tts: TTS, opts: TTSOptions, connOptions?: APIConnectOptions) {\n super(tts, connOptions);\n this.#opts = opts;\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n protected async run() {\n const requestId = shortuuid();\n let closing = false;\n // Only close WebSocket when both: 1) Cartesia returns done, AND 2) all sentences have been sent\n let sentenceStreamClosed = false;\n\n const sentenceStreamTask = async (ws: WebSocket) => {\n const packet = toCartesiaOptions(this.#opts, true);\n for await (const event of this.#tokenizer) {\n const msg = {\n ...packet,\n context_id: requestId,\n transcript: event.token + ' ',\n continue: true,\n };\n ws.send(JSON.stringify(msg));\n }\n\n const endMsg = {\n ...packet,\n context_id: requestId,\n transcript: ' ',\n continue: false,\n };\n ws.send(JSON.stringify(endMsg));\n // Mark sentence stream as closed\n sentenceStreamClosed = true;\n };\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#tokenizer.flush();\n continue;\n }\n this.#tokenizer.pushText(data);\n }\n this.#tokenizer.endInput();\n this.#tokenizer.close();\n };\n\n // Use event channel and set up listeners ONCE to avoid missing messages during listener re-registration\n const recvTask = async (ws: WebSocket) => {\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n\n // Create event channel to buffer incoming messages\n // This prevents message loss between listener re-registrations\n const eventChannel = stream.createStreamChannel<RawData>();\n\n let lastFrame: AudioFrame | undefined;\n let pendingTimedTranscripts: TimedString[] = [];\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame && !this.queue.closed) {\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 let timeout: NodeJS.Timeout | null = null;\n\n const clearTTSChunkTimeout = () => {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n };\n\n // Set up WebSocket listeners ONCE (not in a loop)\n const onMessage = (data: RawData) => {\n void eventChannel.write(data).catch((error: unknown) => {\n this.#logger.debug({ error }, 'Failed writing Cartesia event to channel (likely closed)');\n });\n };\n\n const onClose = (code: number, reason: Buffer) => {\n if (!closing) {\n this.#logger.debug(`WebSocket closed with code ${code}: ${reason.toString()}`);\n }\n clearTTSChunkTimeout();\n void eventChannel.close();\n };\n\n const onError = (err: Error) => {\n this.#logger.error({ err }, 'Cartesia WebSocket error');\n void eventChannel.close();\n };\n\n // Attach listeners ONCE\n ws.on('message', onMessage);\n ws.on('close', onClose);\n ws.on('error', onError);\n\n try {\n // Process messages from the channel\n const reader = eventChannel.stream().getReader();\n\n while (!this.closed && !this.abortController.signal.aborted) {\n const result = await reader.read();\n if (result.done) break;\n\n const rawMsg = result.value;\n\n // Parse message with Zod schema for type safety\n let serverMsg: CartesiaServerMessage;\n try {\n const json = JSON.parse(rawMsg.toString());\n serverMsg = cartesiaMessageSchema.parse(json);\n } catch (parseErr) {\n this.#logger.warn({ parseErr }, 'Failed to parse Cartesia message');\n continue;\n }\n\n // Handle error messages\n if (isErrorMessage(serverMsg)) {\n this.#logger.error({ error: serverMsg.error }, 'Cartesia returned error');\n continue;\n }\n\n const segmentId = serverMsg.context_id;\n\n // Process word timestamps if present (typed via Zod schema)\n if (this.#opts.wordTimestamps !== false && hasWordTimestamps(serverMsg)) {\n const wordTimestamps = serverMsg.word_timestamps;\n for (let i = 0; i < wordTimestamps.words.length; i++) {\n const word = wordTimestamps.words[i];\n const startTime = wordTimestamps.start[i];\n const endTime = wordTimestamps.end[i];\n if (word !== undefined && startTime !== undefined && endTime !== undefined) {\n pendingTimedTranscripts.push(\n createTimedString({\n text: word + ' ', // Add space after word for consistency\n startTime,\n endTime,\n }),\n );\n }\n }\n }\n\n // Handle audio chunk messages\n if (isChunkMessage(serverMsg)) {\n const audioBuffer = Buffer.from(serverMsg.data, 'base64');\n // Extract ArrayBuffer from Buffer for AudioByteStream compatibility\n const audioData = audioBuffer.buffer.slice(\n audioBuffer.byteOffset,\n audioBuffer.byteOffset + audioBuffer.byteLength,\n );\n for (const frame of bstream.write(audioData)) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n\n // IMPORTANT: close WS if TTS chunk stream been stuck too long\n // this allows unblock the current \"broken\" TTS node so that any future TTS nodes\n // can continue to process the stream without been blocked by the stuck node\n clearTTSChunkTimeout();\n timeout = setTimeout(() => {\n // cartesia chunk timeout quite often, so we make it a debug log\n this.#logger.debug(\n `Cartesia WebSocket TTS chunk stream timeout after ${this.#opts.chunkTimeout}ms`,\n );\n ws.close();\n }, this.#opts.chunkTimeout);\n } else if (isDoneMessage(serverMsg)) {\n // This ensures all sentences have been sent before closing\n if (sentenceStreamClosed) {\n for (const frame of bstream.flush()) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n sendLastFrame(segmentId, true);\n if (!this.queue.closed) {\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n }\n\n if (segmentId === requestId) {\n closing = true;\n clearTTSChunkTimeout();\n ws.close();\n break; // Exit the loop\n }\n }\n // If sentenceStreamClosed is false, continue receiving - more done messages will come\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 (\n err.message.includes('Queue is closed') ||\n err.message.includes('Channel is closed')\n ) {\n this.#logger.warn(\n { err },\n 'Channel closed during transcript processing (expected during disconnect)',\n );\n } else {\n this.#logger.error({ err }, 'Error in recvTask from Cartesia WebSocket');\n }\n }\n } finally {\n // IMPORTANT: Remove listeners so connection can be reused\n ws.off('message', onMessage);\n ws.off('close', onClose);\n ws.off('error', onError);\n clearTTSChunkTimeout();\n }\n };\n\n const wsUrl = this.#opts.baseUrl.replace(/^http/, 'ws');\n const url = `${wsUrl}/tts/websocket?api_key=${this.#opts.apiKey}&cartesia_version=${this.#opts.apiVersion}`;\n\n let ws: WebSocket | undefined;\n try {\n ws = await connectCartesiaWebSocket({\n url,\n timeoutMs: this.connOptions.timeoutMs,\n abortSignal: this.abortSignal,\n });\n await Promise.all([inputTask(), sentenceStreamTask(ws), recvTask(ws)]);\n } catch (e) {\n if (this.abortSignal.aborted) {\n return;\n }\n throw toRetryableConnectionError(e);\n } finally {\n // Ensure we don't leak sockets/tasks across retry attempts.\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n safeTerminateWebSocket(ws);\n }\n }\n }\n}\n\nconst transientNetworkCodes = new Set([\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'EAI_AGAIN',\n 'ENETUNREACH',\n 'ECONNREFUSED',\n 'EHOSTUNREACH',\n]);\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => {\n return v !== null && typeof v === 'object';\n};\n\nconst isAggregateErrorLike = (e: unknown): e is { errors: unknown[]; name?: string } => {\n if (!isRecord(e)) return false;\n return e.name === 'AggregateError' && Array.isArray(e.errors);\n};\n\nconst hasErrorCode = (e: unknown, code: string): boolean => {\n if (isRecord(e) && e.code === code) return true;\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasErrorCode(inner, code));\n }\n return false;\n};\n\nconst hasAnyTransientCode = (e: unknown): boolean => {\n if (isRecord(e) && typeof e.code === 'string') {\n return transientNetworkCodes.has(e.code);\n }\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasAnyTransientCode(inner));\n }\n return false;\n};\n\nconst toRetryableConnectionError = (e: unknown): APIConnectionError => {\n const err = asError(e);\n const isTimeout =\n hasErrorCode(e, 'ETIMEDOUT') ||\n (typeof err.message === 'string' && err.message.includes('ETIMEDOUT'));\n const message = isTimeout\n ? `Cartesia connection timed out`\n : `Cartesia connection failed: ${err.message || 'unknown error'}`;\n return isTimeout ? new APITimeoutError({ message }) : new APIConnectionError({ message });\n};\n\nconst waitForWsOpen = async ({\n ws,\n timeoutMs,\n abortSignal,\n}: {\n ws: WebSocket;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}) => {\n if (abortSignal.aborted) {\n throw new Error('aborted');\n }\n\n const fut = new Future<void>();\n let timeout: NodeJS.Timeout | undefined;\n\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n ws.off('open', onOpen);\n ws.off('error', onError);\n ws.off('close', onClose);\n abortSignal.removeEventListener('abort', onAbort);\n };\n\n const onOpen = () => fut.resolve();\n const onError = (err: Error) => fut.reject(asError(err));\n const onClose = (code: number, reason: Buffer) =>\n fut.reject(\n new Error(`WebSocket closed before open (code=${code}, reason=${reason.toString()})`),\n );\n const onAbort = () => fut.reject(new Error('aborted'));\n\n ws.on('open', onOpen);\n ws.on('error', onError);\n ws.on('close', onClose);\n abortSignal.addEventListener('abort', onAbort, { once: true });\n\n if (timeoutMs > 0) {\n timeout = setTimeout(() => fut.reject(new Error('connect timeout')), timeoutMs);\n }\n\n try {\n await fut.await;\n } finally {\n cleanup();\n }\n};\n\nconst safeTerminateWebSocket = (ws: WebSocket) => {\n // `ws` can emit an 'error' event during teardown (especially if CONNECTING).\n // If there is no error listener at that moment, Node will treat it as unhandled and crash the process.\n try {\n ws.on('error', () => {});\n } catch {\n // ignore\n }\n\n try {\n // `terminate()` can throw if the socket was never established; `close()` is safer in CONNECTING.\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close();\n } else {\n ws.terminate();\n }\n } catch {\n // ignore\n }\n};\n\nconst connectCartesiaWebSocket = async ({\n url,\n timeoutMs,\n abortSignal,\n}: {\n url: string;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}): Promise<WebSocket> => {\n const connectOnce = async (family?: number): Promise<WebSocket> => {\n const ws = new WebSocket(url, { handshakeTimeout: timeoutMs, family });\n try {\n await waitForWsOpen({ ws, timeoutMs, abortSignal });\n return ws;\n } catch (e) {\n safeTerminateWebSocket(ws);\n throw e;\n }\n };\n\n try {\n return await connectOnce();\n } catch (e) {\n // Mitigation for Node.js dual-stack (IPv6/IPv4) connect flakiness (\"happy eyeballs\"):\n // some environments surface `AggregateError` with nested `ETIMEDOUT` during the initial\n // WebSocket open. In that case we do a one-off retry forcing IPv4 (`family: 4`) before\n // letting the outer framework retry loop handle further attempts.\n //\n // If you still see `AggregateError`/`ETIMEDOUT`:\n // - Increase the session TTS connect timeout (`connOptions.ttsConnOptions.timeoutMs`)\n // - Or adjust Node's family autoselection behavior via `NODE_OPTIONS`, e.g.\n // `--network-family-autoselection-attempt-timeout=5000` (or disable it entirely).\n if (hasAnyTransientCode(e) || isAggregateErrorLike(e)) {\n return await connectOnce(4);\n }\n throw e;\n }\n};\n\nconst toCartesiaOptions = (\n opts: TTSOptions,\n streaming: boolean = false,\n): { [id: string]: unknown } => {\n const voice: { [id: string]: unknown } = {};\n if (typeof opts.voice === 'string') {\n voice.mode = 'id';\n voice.id = opts.voice;\n } else {\n voice.mode = 'embedding';\n voice.embedding = opts.voice;\n }\n\n if (opts.apiVersion === API_VERSION_WITH_EXPERIMENTAL_CONTROLS) {\n const voiceControls: { [id: string]: unknown } = {};\n if (opts.speed) {\n voiceControls.speed = opts.speed;\n }\n if (opts.emotion) {\n voiceControls.emotion = opts.emotion;\n }\n if (Object.keys(voiceControls).length) {\n voice.__experimental_controls = voiceControls;\n }\n }\n\n const result: { [id: string]: unknown } = {\n model_id: opts.model,\n voice,\n output_format: {\n container: 'raw',\n encoding: opts.encoding,\n sample_rate: opts.sampleRate,\n },\n language: getBaseLanguage(opts.language),\n max_buffer_delay_ms: 0,\n };\n\n if (opts.pronunciationDictId) {\n result.pronunciation_dict_id = opts.pronunciationDictId;\n }\n\n if (opts.apiVersion > API_VERSION_WITH_EXPERIMENTAL_CONTROLS && isSonic3(opts.model)) {\n const generationConfig: { [id: string]: unknown } = {};\n if (opts.speed) {\n generationConfig.speed = opts.speed;\n }\n if (opts.emotion) {\n generationConfig.emotion = opts.emotion[0];\n }\n if (opts.volume) {\n generationConfig.volume = opts.volume;\n }\n if (Object.keys(generationConfig).length) {\n result.generation_config = generationConfig;\n }\n }\n\n if (streaming && opts.wordTimestamps !== false) {\n result.add_timestamps = true;\n }\n\n return result;\n};\n"],"mappings":"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","names":["tts","ws"]}
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,EACL,KAAK,iBAAiB,EAatB,GAAG,EACJ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,aAAa,CAAC;AAkBrB,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC;IACvC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AA+CD,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAE9B,KAAK,SAAkB;IAEvB,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAEW,IAAI,GAAE,OAAO,CAAC,UAAU,CAAM;IA8B1C,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;IAgBvC,UAAU,CACR,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,iBAAiB,EAC/B,WAAW,CAAC,EAAE,WAAW,GACxB,GAAG,CAAC,aAAa;IAIpB,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,gBAAgB;CAGxE;AAED,qBAAa,aAAc,SAAQ,GAAG,CAAC,aAAa;;IAClD,KAAK,SAA4B;gBAM/B,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,WAAW,CAAC,EAAE,WAAW;cAOX,GAAG;CAuEpB;AAED,qBAAa,gBAAiB,SAAQ,GAAG,CAAC,gBAAgB;;IAMxD,KAAK,SAA+B;gBAExB,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,iBAAiB;IAKvE,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;cAavB,GAAG;CAkPpB"}
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../src/tts.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,iBAAiB,EActB,GAAG,EACJ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,aAAa,CAAC;AAkBrB,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC;IACvC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AA+CD,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAE9B,KAAK,SAAkB;IAEvB,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAEW,IAAI,GAAE,OAAO,CAAC,UAAU,CAAM;IA8B1C,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;IAgBvC,UAAU,CACR,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,iBAAiB,EAC/B,WAAW,CAAC,EAAE,WAAW,GACxB,GAAG,CAAC,aAAa;IAIpB,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,gBAAgB;CAGxE;AAED,qBAAa,aAAc,SAAQ,GAAG,CAAC,aAAa;;IAClD,KAAK,SAA4B;gBAM/B,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,WAAW,CAAC,EAAE,WAAW;cAOX,GAAG;CAuEpB;AAED,qBAAa,gBAAiB,SAAQ,GAAG,CAAC,gBAAgB;;IAMxD,KAAK,SAA+B;gBAExB,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,iBAAiB;IAKvE,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC;cAavB,GAAG;CAkPpB"}
package/dist/tts.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  APITimeoutError,
4
4
  AudioByteStream,
5
5
  Future,
6
+ asError,
6
7
  createTimedString,
7
8
  getBaseLanguage,
8
9
  log,
@@ -402,7 +403,6 @@ class SynthesizeStream extends tts.SynthesizeStream {
402
403
  }
403
404
  }
404
405
  }
405
- const asError = (e) => e instanceof Error ? e : new Error(String(e));
406
406
  const transientNetworkCodes = /* @__PURE__ */ new Set([
407
407
  "ETIMEDOUT",
408
408
  "ECONNRESET",
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 type APIConnectOptions,\n APIConnectionError,\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 type { AudioFrame } from '@livekit/rtc-node';\nimport { request } from 'node:https';\nimport { type RawData, WebSocket } from 'ws';\nimport {\n TTSDefaultVoiceId,\n type TTSEncoding,\n type TTSModels,\n type TTSVoiceEmotion,\n type TTSVoiceSpeed,\n isSonic3,\n} from './models.js';\nimport {\n type CartesiaServerMessage,\n cartesiaMessageSchema,\n hasWordTimestamps,\n isChunkMessage,\n isDoneMessage,\n isErrorMessage,\n} from './types.js';\n\nconst AUTHORIZATION_HEADER = 'X-API-Key';\nconst VERSION_HEADER = 'Cartesia-Version';\nconst API_VERSION = '2025-04-16';\nconst API_VERSION_WITH_EXPERIMENTAL_CONTROLS = '2024-11-13';\nconst MODEL_WITH_EXPERIMENTAL_CONTROLS = 'sonic-2-2025-03-07';\nconst NUM_CHANNELS = 1;\nconst BUFFERED_WORDS_COUNT = 8;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n encoding: TTSEncoding;\n sampleRate: number;\n voice: string | number[];\n speed?: TTSVoiceSpeed | number;\n emotion?: (TTSVoiceEmotion | string)[];\n /**\n * Volume of the speech. For sonic-3, the value is valid between 0.5 and 2.0.\n * @see https://docs.cartesia.ai/api-reference/tts/bytes#body-generation-config-volume\n */\n volume?: number;\n apiKey?: string;\n language: string;\n baseUrl: string;\n apiVersion: string;\n\n /**\n * The timeout for the next chunk to be received from the Cartesia API.\n */\n chunkTimeout: number;\n\n /**\n * Whether to add word timestamps to the output. When enabled, the TTS will return\n * timing information for each word in the transcript.\n * @defaultValue true\n */\n wordTimestamps?: boolean;\n\n pronunciationDictId?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n model: 'sonic-3',\n encoding: 'pcm_s16le',\n sampleRate: 24000,\n voice: TTSDefaultVoiceId,\n apiKey: process.env.CARTESIA_API_KEY,\n language: 'en',\n baseUrl: 'https://api.cartesia.ai',\n apiVersion: API_VERSION,\n chunkTimeout: 5000,\n wordTimestamps: true,\n};\n\nconst checkGenerationConfig = (opts: TTSOptions) => {\n const logger = log();\n if (isSonic3(opts.model)) {\n if (opts.speed !== undefined && typeof opts.speed === 'number') {\n if (opts.speed < 0.6 || opts.speed > 2.0) {\n logger.warn('speed must be between 0.6 and 2.0 for sonic-3');\n }\n }\n if (opts.volume !== undefined && (opts.volume < 0.5 || opts.volume > 2.0)) {\n logger.warn('volume must be between 0.5 and 2.0 for sonic-3');\n }\n } else if (\n opts.apiVersion !== API_VERSION_WITH_EXPERIMENTAL_CONTROLS ||\n opts.model !== MODEL_WITH_EXPERIMENTAL_CONTROLS\n ) {\n if (opts.speed || opts.emotion) {\n logger.warn(\n { model: opts.model, speed: opts.speed, emotion: opts.emotion },\n `speed and emotion controls are only supported for model '${MODEL_WITH_EXPERIMENTAL_CONTROLS}' ` +\n `or sonic-3 models, see https://docs.cartesia.ai/developer-tools/changelog for details`,\n );\n }\n }\n\n if (opts.pronunciationDictId && !isSonic3(opts.model)) {\n logger.warn(\n { model: opts.model, pronunciationDictId: opts.pronunciationDictId },\n 'pronunciationDictId is only supported for sonic-3 models',\n );\n }\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n label = 'cartesia.TTS';\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Cartesia';\n }\n\n constructor(opts: Partial<TTSOptions> = {}) {\n const resolvedOpts = {\n ...defaultTTSOptions,\n ...opts,\n };\n\n super(resolvedOpts.sampleRate || defaultTTSOptions.sampleRate, NUM_CHANNELS, {\n streaming: true,\n alignedTranscript: resolvedOpts.wordTimestamps ?? true,\n });\n\n this.#opts = resolvedOpts;\n this.#opts.language = normalizeLanguage(this.#opts.language);\n\n if (this.#opts.apiKey === undefined) {\n throw new Error(\n 'Cartesia API key is required, whether as an argument or as $CARTESIA_API_KEY',\n );\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.language !== undefined) {\n this.#opts.language = normalizeLanguage(opts.language);\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): tts.ChunkedStream {\n return new ChunkedStream(this, text, this.#opts, connOptions, abortSignal);\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream {\n return new SynthesizeStream(this, this.#opts, options?.connOptions);\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'cartesia.ChunkedStream';\n #logger = log();\n #opts: TTSOptions;\n #text: string;\n\n constructor(\n tts: TTS,\n text: string,\n opts: TTSOptions,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(text, tts, connOptions, abortSignal);\n this.#text = text;\n this.#opts = opts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n const json = toCartesiaOptions(this.#opts);\n json.transcript = this.#text;\n\n const baseUrl = new URL(this.#opts.baseUrl);\n const doneFut = new Future<void>();\n\n const req = request(\n {\n hostname: baseUrl.hostname,\n port: parseInt(baseUrl.port) || (baseUrl.protocol === 'https:' ? 443 : 80),\n path: '/tts/bytes',\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey!,\n [VERSION_HEADER]: this.#opts.apiVersion,\n },\n signal: this.abortSignal,\n },\n (res) => {\n res.on('data', (chunk) => {\n for (const frame of bstream.write(chunk)) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n });\n res.on('close', () => {\n for (const frame of bstream.flush()) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n this.queue.close();\n if (!doneFut.done) doneFut.resolve();\n });\n res.on('error', (err) => {\n if (err.message === 'aborted') return;\n this.#logger.error({ err }, 'Cartesia TTS response error');\n if (!doneFut.done) doneFut.reject(err);\n });\n },\n );\n\n req.on('error', (err) => {\n if (err.name === 'AbortError') return;\n this.#logger.error({ err }, 'Cartesia TTS request error');\n if (!doneFut.done) doneFut.reject(err);\n });\n req.on('close', () => {\n if (!doneFut.done) doneFut.resolve();\n });\n req.write(JSON.stringify(json));\n req.end();\n\n try {\n await doneFut.await;\n } catch (e) {\n if (this.abortSignal.aborted) return;\n if (!this.queue.closed) this.queue.close();\n throw toRetryableConnectionError(e);\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #opts: TTSOptions;\n #logger = log();\n #tokenizer = new tokenize.basic.SentenceTokenizer({\n minSentenceLength: BUFFERED_WORDS_COUNT,\n }).stream();\n label = 'cartesia.SynthesizeStream';\n\n constructor(tts: TTS, opts: TTSOptions, connOptions?: APIConnectOptions) {\n super(tts, connOptions);\n this.#opts = opts;\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n protected async run() {\n const requestId = shortuuid();\n let closing = false;\n // Only close WebSocket when both: 1) Cartesia returns done, AND 2) all sentences have been sent\n let sentenceStreamClosed = false;\n\n const sentenceStreamTask = async (ws: WebSocket) => {\n const packet = toCartesiaOptions(this.#opts, true);\n for await (const event of this.#tokenizer) {\n const msg = {\n ...packet,\n context_id: requestId,\n transcript: event.token + ' ',\n continue: true,\n };\n ws.send(JSON.stringify(msg));\n }\n\n const endMsg = {\n ...packet,\n context_id: requestId,\n transcript: ' ',\n continue: false,\n };\n ws.send(JSON.stringify(endMsg));\n // Mark sentence stream as closed\n sentenceStreamClosed = true;\n };\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#tokenizer.flush();\n continue;\n }\n this.#tokenizer.pushText(data);\n }\n this.#tokenizer.endInput();\n this.#tokenizer.close();\n };\n\n // Use event channel and set up listeners ONCE to avoid missing messages during listener re-registration\n const recvTask = async (ws: WebSocket) => {\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n\n // Create event channel to buffer incoming messages\n // This prevents message loss between listener re-registrations\n const eventChannel = stream.createStreamChannel<RawData>();\n\n let lastFrame: AudioFrame | undefined;\n let pendingTimedTranscripts: TimedString[] = [];\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame && !this.queue.closed) {\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 let timeout: NodeJS.Timeout | null = null;\n\n const clearTTSChunkTimeout = () => {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n };\n\n // Set up WebSocket listeners ONCE (not in a loop)\n const onMessage = (data: RawData) => {\n void eventChannel.write(data).catch((error: unknown) => {\n this.#logger.debug({ error }, 'Failed writing Cartesia event to channel (likely closed)');\n });\n };\n\n const onClose = (code: number, reason: Buffer) => {\n if (!closing) {\n this.#logger.debug(`WebSocket closed with code ${code}: ${reason.toString()}`);\n }\n clearTTSChunkTimeout();\n void eventChannel.close();\n };\n\n const onError = (err: Error) => {\n this.#logger.error({ err }, 'Cartesia WebSocket error');\n void eventChannel.close();\n };\n\n // Attach listeners ONCE\n ws.on('message', onMessage);\n ws.on('close', onClose);\n ws.on('error', onError);\n\n try {\n // Process messages from the channel\n const reader = eventChannel.stream().getReader();\n\n while (!this.closed && !this.abortController.signal.aborted) {\n const result = await reader.read();\n if (result.done) break;\n\n const rawMsg = result.value;\n\n // Parse message with Zod schema for type safety\n let serverMsg: CartesiaServerMessage;\n try {\n const json = JSON.parse(rawMsg.toString());\n serverMsg = cartesiaMessageSchema.parse(json);\n } catch (parseErr) {\n this.#logger.warn({ parseErr }, 'Failed to parse Cartesia message');\n continue;\n }\n\n // Handle error messages\n if (isErrorMessage(serverMsg)) {\n this.#logger.error({ error: serverMsg.error }, 'Cartesia returned error');\n continue;\n }\n\n const segmentId = serverMsg.context_id;\n\n // Process word timestamps if present (typed via Zod schema)\n if (this.#opts.wordTimestamps !== false && hasWordTimestamps(serverMsg)) {\n const wordTimestamps = serverMsg.word_timestamps;\n for (let i = 0; i < wordTimestamps.words.length; i++) {\n const word = wordTimestamps.words[i];\n const startTime = wordTimestamps.start[i];\n const endTime = wordTimestamps.end[i];\n if (word !== undefined && startTime !== undefined && endTime !== undefined) {\n pendingTimedTranscripts.push(\n createTimedString({\n text: word + ' ', // Add space after word for consistency\n startTime,\n endTime,\n }),\n );\n }\n }\n }\n\n // Handle audio chunk messages\n if (isChunkMessage(serverMsg)) {\n const audioBuffer = Buffer.from(serverMsg.data, 'base64');\n // Extract ArrayBuffer from Buffer for AudioByteStream compatibility\n const audioData = audioBuffer.buffer.slice(\n audioBuffer.byteOffset,\n audioBuffer.byteOffset + audioBuffer.byteLength,\n );\n for (const frame of bstream.write(audioData)) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n\n // IMPORTANT: close WS if TTS chunk stream been stuck too long\n // this allows unblock the current \"broken\" TTS node so that any future TTS nodes\n // can continue to process the stream without been blocked by the stuck node\n clearTTSChunkTimeout();\n timeout = setTimeout(() => {\n // cartesia chunk timeout quite often, so we make it a debug log\n this.#logger.debug(\n `Cartesia WebSocket TTS chunk stream timeout after ${this.#opts.chunkTimeout}ms`,\n );\n ws.close();\n }, this.#opts.chunkTimeout);\n } else if (isDoneMessage(serverMsg)) {\n // This ensures all sentences have been sent before closing\n if (sentenceStreamClosed) {\n for (const frame of bstream.flush()) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n sendLastFrame(segmentId, true);\n if (!this.queue.closed) {\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n }\n\n if (segmentId === requestId) {\n closing = true;\n clearTTSChunkTimeout();\n ws.close();\n break; // Exit the loop\n }\n }\n // If sentenceStreamClosed is false, continue receiving - more done messages will come\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 (\n err.message.includes('Queue is closed') ||\n err.message.includes('Channel is closed')\n ) {\n this.#logger.warn(\n { err },\n 'Channel closed during transcript processing (expected during disconnect)',\n );\n } else {\n this.#logger.error({ err }, 'Error in recvTask from Cartesia WebSocket');\n }\n }\n } finally {\n // IMPORTANT: Remove listeners so connection can be reused\n ws.off('message', onMessage);\n ws.off('close', onClose);\n ws.off('error', onError);\n clearTTSChunkTimeout();\n }\n };\n\n const wsUrl = this.#opts.baseUrl.replace(/^http/, 'ws');\n const url = `${wsUrl}/tts/websocket?api_key=${this.#opts.apiKey}&cartesia_version=${this.#opts.apiVersion}`;\n\n let ws: WebSocket | undefined;\n try {\n ws = await connectCartesiaWebSocket({\n url,\n timeoutMs: this.connOptions.timeoutMs,\n abortSignal: this.abortSignal,\n });\n await Promise.all([inputTask(), sentenceStreamTask(ws), recvTask(ws)]);\n } catch (e) {\n if (this.abortSignal.aborted) {\n return;\n }\n throw toRetryableConnectionError(e);\n } finally {\n // Ensure we don't leak sockets/tasks across retry attempts.\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n safeTerminateWebSocket(ws);\n }\n }\n }\n}\n\nconst asError = (e: unknown): Error => (e instanceof Error ? e : new Error(String(e)));\n\nconst transientNetworkCodes = new Set([\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'EAI_AGAIN',\n 'ENETUNREACH',\n 'ECONNREFUSED',\n 'EHOSTUNREACH',\n]);\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => {\n return v !== null && typeof v === 'object';\n};\n\nconst isAggregateErrorLike = (e: unknown): e is { errors: unknown[]; name?: string } => {\n if (!isRecord(e)) return false;\n return e.name === 'AggregateError' && Array.isArray(e.errors);\n};\n\nconst hasErrorCode = (e: unknown, code: string): boolean => {\n if (isRecord(e) && e.code === code) return true;\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasErrorCode(inner, code));\n }\n return false;\n};\n\nconst hasAnyTransientCode = (e: unknown): boolean => {\n if (isRecord(e) && typeof e.code === 'string') {\n return transientNetworkCodes.has(e.code);\n }\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasAnyTransientCode(inner));\n }\n return false;\n};\n\nconst toRetryableConnectionError = (e: unknown): APIConnectionError => {\n const err = asError(e);\n const isTimeout =\n hasErrorCode(e, 'ETIMEDOUT') ||\n (typeof err.message === 'string' && err.message.includes('ETIMEDOUT'));\n const message = isTimeout\n ? `Cartesia connection timed out`\n : `Cartesia connection failed: ${err.message || 'unknown error'}`;\n return isTimeout ? new APITimeoutError({ message }) : new APIConnectionError({ message });\n};\n\nconst waitForWsOpen = async ({\n ws,\n timeoutMs,\n abortSignal,\n}: {\n ws: WebSocket;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}) => {\n if (abortSignal.aborted) {\n throw new Error('aborted');\n }\n\n const fut = new Future<void>();\n let timeout: NodeJS.Timeout | undefined;\n\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n ws.off('open', onOpen);\n ws.off('error', onError);\n ws.off('close', onClose);\n abortSignal.removeEventListener('abort', onAbort);\n };\n\n const onOpen = () => fut.resolve();\n const onError = (err: Error) => fut.reject(asError(err));\n const onClose = (code: number, reason: Buffer) =>\n fut.reject(\n new Error(`WebSocket closed before open (code=${code}, reason=${reason.toString()})`),\n );\n const onAbort = () => fut.reject(new Error('aborted'));\n\n ws.on('open', onOpen);\n ws.on('error', onError);\n ws.on('close', onClose);\n abortSignal.addEventListener('abort', onAbort, { once: true });\n\n if (timeoutMs > 0) {\n timeout = setTimeout(() => fut.reject(new Error('connect timeout')), timeoutMs);\n }\n\n try {\n await fut.await;\n } finally {\n cleanup();\n }\n};\n\nconst safeTerminateWebSocket = (ws: WebSocket) => {\n // `ws` can emit an 'error' event during teardown (especially if CONNECTING).\n // If there is no error listener at that moment, Node will treat it as unhandled and crash the process.\n try {\n ws.on('error', () => {});\n } catch {\n // ignore\n }\n\n try {\n // `terminate()` can throw if the socket was never established; `close()` is safer in CONNECTING.\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close();\n } else {\n ws.terminate();\n }\n } catch {\n // ignore\n }\n};\n\nconst connectCartesiaWebSocket = async ({\n url,\n timeoutMs,\n abortSignal,\n}: {\n url: string;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}): Promise<WebSocket> => {\n const connectOnce = async (family?: number): Promise<WebSocket> => {\n const ws = new WebSocket(url, { handshakeTimeout: timeoutMs, family });\n try {\n await waitForWsOpen({ ws, timeoutMs, abortSignal });\n return ws;\n } catch (e) {\n safeTerminateWebSocket(ws);\n throw e;\n }\n };\n\n try {\n return await connectOnce();\n } catch (e) {\n // Mitigation for Node.js dual-stack (IPv6/IPv4) connect flakiness (\"happy eyeballs\"):\n // some environments surface `AggregateError` with nested `ETIMEDOUT` during the initial\n // WebSocket open. In that case we do a one-off retry forcing IPv4 (`family: 4`) before\n // letting the outer framework retry loop handle further attempts.\n //\n // If you still see `AggregateError`/`ETIMEDOUT`:\n // - Increase the session TTS connect timeout (`connOptions.ttsConnOptions.timeoutMs`)\n // - Or adjust Node's family autoselection behavior via `NODE_OPTIONS`, e.g.\n // `--network-family-autoselection-attempt-timeout=5000` (or disable it entirely).\n if (hasAnyTransientCode(e) || isAggregateErrorLike(e)) {\n return await connectOnce(4);\n }\n throw e;\n }\n};\n\nconst toCartesiaOptions = (\n opts: TTSOptions,\n streaming: boolean = false,\n): { [id: string]: unknown } => {\n const voice: { [id: string]: unknown } = {};\n if (typeof opts.voice === 'string') {\n voice.mode = 'id';\n voice.id = opts.voice;\n } else {\n voice.mode = 'embedding';\n voice.embedding = opts.voice;\n }\n\n if (opts.apiVersion === API_VERSION_WITH_EXPERIMENTAL_CONTROLS) {\n const voiceControls: { [id: string]: unknown } = {};\n if (opts.speed) {\n voiceControls.speed = opts.speed;\n }\n if (opts.emotion) {\n voiceControls.emotion = opts.emotion;\n }\n if (Object.keys(voiceControls).length) {\n voice.__experimental_controls = voiceControls;\n }\n }\n\n const result: { [id: string]: unknown } = {\n model_id: opts.model,\n voice,\n output_format: {\n container: 'raw',\n encoding: opts.encoding,\n sample_rate: opts.sampleRate,\n },\n language: getBaseLanguage(opts.language),\n max_buffer_delay_ms: 0,\n };\n\n if (opts.pronunciationDictId) {\n result.pronunciation_dict_id = opts.pronunciationDictId;\n }\n\n if (opts.apiVersion > API_VERSION_WITH_EXPERIMENTAL_CONTROLS && isSonic3(opts.model)) {\n const generationConfig: { [id: string]: unknown } = {};\n if (opts.speed) {\n generationConfig.speed = opts.speed;\n }\n if (opts.emotion) {\n generationConfig.emotion = opts.emotion[0];\n }\n if (opts.volume) {\n generationConfig.volume = opts.volume;\n }\n if (Object.keys(generationConfig).length) {\n result.generation_config = generationConfig;\n }\n }\n\n if (streaming && opts.wordTimestamps !== false) {\n result.add_timestamps = true;\n }\n\n return result;\n};\n"],"mappings":"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","names":["tts","ws"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APITimeoutError,\n AudioByteStream,\n Future,\n type TimedString,\n asError,\n createTimedString,\n getBaseLanguage,\n log,\n normalizeLanguage,\n shortuuid,\n stream,\n tokenize,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { request } from 'node:https';\nimport { type RawData, WebSocket } from 'ws';\nimport {\n TTSDefaultVoiceId,\n type TTSEncoding,\n type TTSModels,\n type TTSVoiceEmotion,\n type TTSVoiceSpeed,\n isSonic3,\n} from './models.js';\nimport {\n type CartesiaServerMessage,\n cartesiaMessageSchema,\n hasWordTimestamps,\n isChunkMessage,\n isDoneMessage,\n isErrorMessage,\n} from './types.js';\n\nconst AUTHORIZATION_HEADER = 'X-API-Key';\nconst VERSION_HEADER = 'Cartesia-Version';\nconst API_VERSION = '2025-04-16';\nconst API_VERSION_WITH_EXPERIMENTAL_CONTROLS = '2024-11-13';\nconst MODEL_WITH_EXPERIMENTAL_CONTROLS = 'sonic-2-2025-03-07';\nconst NUM_CHANNELS = 1;\nconst BUFFERED_WORDS_COUNT = 8;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n encoding: TTSEncoding;\n sampleRate: number;\n voice: string | number[];\n speed?: TTSVoiceSpeed | number;\n emotion?: (TTSVoiceEmotion | string)[];\n /**\n * Volume of the speech. For sonic-3, the value is valid between 0.5 and 2.0.\n * @see https://docs.cartesia.ai/api-reference/tts/bytes#body-generation-config-volume\n */\n volume?: number;\n apiKey?: string;\n language: string;\n baseUrl: string;\n apiVersion: string;\n\n /**\n * The timeout for the next chunk to be received from the Cartesia API.\n */\n chunkTimeout: number;\n\n /**\n * Whether to add word timestamps to the output. When enabled, the TTS will return\n * timing information for each word in the transcript.\n * @defaultValue true\n */\n wordTimestamps?: boolean;\n\n pronunciationDictId?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n model: 'sonic-3',\n encoding: 'pcm_s16le',\n sampleRate: 24000,\n voice: TTSDefaultVoiceId,\n apiKey: process.env.CARTESIA_API_KEY,\n language: 'en',\n baseUrl: 'https://api.cartesia.ai',\n apiVersion: API_VERSION,\n chunkTimeout: 5000,\n wordTimestamps: true,\n};\n\nconst checkGenerationConfig = (opts: TTSOptions) => {\n const logger = log();\n if (isSonic3(opts.model)) {\n if (opts.speed !== undefined && typeof opts.speed === 'number') {\n if (opts.speed < 0.6 || opts.speed > 2.0) {\n logger.warn('speed must be between 0.6 and 2.0 for sonic-3');\n }\n }\n if (opts.volume !== undefined && (opts.volume < 0.5 || opts.volume > 2.0)) {\n logger.warn('volume must be between 0.5 and 2.0 for sonic-3');\n }\n } else if (\n opts.apiVersion !== API_VERSION_WITH_EXPERIMENTAL_CONTROLS ||\n opts.model !== MODEL_WITH_EXPERIMENTAL_CONTROLS\n ) {\n if (opts.speed || opts.emotion) {\n logger.warn(\n { model: opts.model, speed: opts.speed, emotion: opts.emotion },\n `speed and emotion controls are only supported for model '${MODEL_WITH_EXPERIMENTAL_CONTROLS}' ` +\n `or sonic-3 models, see https://docs.cartesia.ai/developer-tools/changelog for details`,\n );\n }\n }\n\n if (opts.pronunciationDictId && !isSonic3(opts.model)) {\n logger.warn(\n { model: opts.model, pronunciationDictId: opts.pronunciationDictId },\n 'pronunciationDictId is only supported for sonic-3 models',\n );\n }\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n label = 'cartesia.TTS';\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n return 'Cartesia';\n }\n\n constructor(opts: Partial<TTSOptions> = {}) {\n const resolvedOpts = {\n ...defaultTTSOptions,\n ...opts,\n };\n\n super(resolvedOpts.sampleRate || defaultTTSOptions.sampleRate, NUM_CHANNELS, {\n streaming: true,\n alignedTranscript: resolvedOpts.wordTimestamps ?? true,\n });\n\n this.#opts = resolvedOpts;\n this.#opts.language = normalizeLanguage(this.#opts.language);\n\n if (this.#opts.apiKey === undefined) {\n throw new Error(\n 'Cartesia API key is required, whether as an argument or as $CARTESIA_API_KEY',\n );\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n if (opts.language !== undefined) {\n this.#opts.language = normalizeLanguage(opts.language);\n }\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): tts.ChunkedStream {\n return new ChunkedStream(this, text, this.#opts, connOptions, abortSignal);\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream {\n return new SynthesizeStream(this, this.#opts, options?.connOptions);\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'cartesia.ChunkedStream';\n #logger = log();\n #opts: TTSOptions;\n #text: string;\n\n constructor(\n tts: TTS,\n text: string,\n opts: TTSOptions,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(text, tts, connOptions, abortSignal);\n this.#text = text;\n this.#opts = opts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n const json = toCartesiaOptions(this.#opts);\n json.transcript = this.#text;\n\n const baseUrl = new URL(this.#opts.baseUrl);\n const doneFut = new Future<void>();\n\n const req = request(\n {\n hostname: baseUrl.hostname,\n port: parseInt(baseUrl.port) || (baseUrl.protocol === 'https:' ? 443 : 80),\n path: '/tts/bytes',\n method: 'POST',\n headers: {\n [AUTHORIZATION_HEADER]: this.#opts.apiKey!,\n [VERSION_HEADER]: this.#opts.apiVersion,\n },\n signal: this.abortSignal,\n },\n (res) => {\n res.on('data', (chunk) => {\n for (const frame of bstream.write(chunk)) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n });\n res.on('close', () => {\n for (const frame of bstream.flush()) {\n this.queue.put({\n requestId,\n frame,\n final: false,\n segmentId: requestId,\n });\n }\n this.queue.close();\n if (!doneFut.done) doneFut.resolve();\n });\n res.on('error', (err) => {\n if (err.message === 'aborted') return;\n this.#logger.error({ err }, 'Cartesia TTS response error');\n if (!doneFut.done) doneFut.reject(err);\n });\n },\n );\n\n req.on('error', (err) => {\n if (err.name === 'AbortError') return;\n this.#logger.error({ err }, 'Cartesia TTS request error');\n if (!doneFut.done) doneFut.reject(err);\n });\n req.on('close', () => {\n if (!doneFut.done) doneFut.resolve();\n });\n req.write(JSON.stringify(json));\n req.end();\n\n try {\n await doneFut.await;\n } catch (e) {\n if (this.abortSignal.aborted) return;\n if (!this.queue.closed) this.queue.close();\n throw toRetryableConnectionError(e);\n }\n }\n}\n\nexport class SynthesizeStream extends tts.SynthesizeStream {\n #opts: TTSOptions;\n #logger = log();\n #tokenizer = new tokenize.basic.SentenceTokenizer({\n minSentenceLength: BUFFERED_WORDS_COUNT,\n }).stream();\n label = 'cartesia.SynthesizeStream';\n\n constructor(tts: TTS, opts: TTSOptions, connOptions?: APIConnectOptions) {\n super(tts, connOptions);\n this.#opts = opts;\n }\n\n updateOptions(opts: Partial<TTSOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n\n if (\n this.#opts.speed ||\n this.#opts.emotion ||\n this.#opts.volume ||\n this.#opts.pronunciationDictId\n ) {\n checkGenerationConfig(this.#opts);\n }\n }\n\n protected async run() {\n const requestId = shortuuid();\n let closing = false;\n // Only close WebSocket when both: 1) Cartesia returns done, AND 2) all sentences have been sent\n let sentenceStreamClosed = false;\n\n const sentenceStreamTask = async (ws: WebSocket) => {\n const packet = toCartesiaOptions(this.#opts, true);\n for await (const event of this.#tokenizer) {\n const msg = {\n ...packet,\n context_id: requestId,\n transcript: event.token + ' ',\n continue: true,\n };\n ws.send(JSON.stringify(msg));\n }\n\n const endMsg = {\n ...packet,\n context_id: requestId,\n transcript: ' ',\n continue: false,\n };\n ws.send(JSON.stringify(endMsg));\n // Mark sentence stream as closed\n sentenceStreamClosed = true;\n };\n\n const inputTask = async () => {\n for await (const data of this.input) {\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n this.#tokenizer.flush();\n continue;\n }\n this.#tokenizer.pushText(data);\n }\n this.#tokenizer.endInput();\n this.#tokenizer.close();\n };\n\n // Use event channel and set up listeners ONCE to avoid missing messages during listener re-registration\n const recvTask = async (ws: WebSocket) => {\n const bstream = new AudioByteStream(this.#opts.sampleRate, NUM_CHANNELS);\n\n // Create event channel to buffer incoming messages\n // This prevents message loss between listener re-registrations\n const eventChannel = stream.createStreamChannel<RawData>();\n\n let lastFrame: AudioFrame | undefined;\n let pendingTimedTranscripts: TimedString[] = [];\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame && !this.queue.closed) {\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 let timeout: NodeJS.Timeout | null = null;\n\n const clearTTSChunkTimeout = () => {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n };\n\n // Set up WebSocket listeners ONCE (not in a loop)\n const onMessage = (data: RawData) => {\n void eventChannel.write(data).catch((error: unknown) => {\n this.#logger.debug({ error }, 'Failed writing Cartesia event to channel (likely closed)');\n });\n };\n\n const onClose = (code: number, reason: Buffer) => {\n if (!closing) {\n this.#logger.debug(`WebSocket closed with code ${code}: ${reason.toString()}`);\n }\n clearTTSChunkTimeout();\n void eventChannel.close();\n };\n\n const onError = (err: Error) => {\n this.#logger.error({ err }, 'Cartesia WebSocket error');\n void eventChannel.close();\n };\n\n // Attach listeners ONCE\n ws.on('message', onMessage);\n ws.on('close', onClose);\n ws.on('error', onError);\n\n try {\n // Process messages from the channel\n const reader = eventChannel.stream().getReader();\n\n while (!this.closed && !this.abortController.signal.aborted) {\n const result = await reader.read();\n if (result.done) break;\n\n const rawMsg = result.value;\n\n // Parse message with Zod schema for type safety\n let serverMsg: CartesiaServerMessage;\n try {\n const json = JSON.parse(rawMsg.toString());\n serverMsg = cartesiaMessageSchema.parse(json);\n } catch (parseErr) {\n this.#logger.warn({ parseErr }, 'Failed to parse Cartesia message');\n continue;\n }\n\n // Handle error messages\n if (isErrorMessage(serverMsg)) {\n this.#logger.error({ error: serverMsg.error }, 'Cartesia returned error');\n continue;\n }\n\n const segmentId = serverMsg.context_id;\n\n // Process word timestamps if present (typed via Zod schema)\n if (this.#opts.wordTimestamps !== false && hasWordTimestamps(serverMsg)) {\n const wordTimestamps = serverMsg.word_timestamps;\n for (let i = 0; i < wordTimestamps.words.length; i++) {\n const word = wordTimestamps.words[i];\n const startTime = wordTimestamps.start[i];\n const endTime = wordTimestamps.end[i];\n if (word !== undefined && startTime !== undefined && endTime !== undefined) {\n pendingTimedTranscripts.push(\n createTimedString({\n text: word + ' ', // Add space after word for consistency\n startTime,\n endTime,\n }),\n );\n }\n }\n }\n\n // Handle audio chunk messages\n if (isChunkMessage(serverMsg)) {\n const audioBuffer = Buffer.from(serverMsg.data, 'base64');\n // Extract ArrayBuffer from Buffer for AudioByteStream compatibility\n const audioData = audioBuffer.buffer.slice(\n audioBuffer.byteOffset,\n audioBuffer.byteOffset + audioBuffer.byteLength,\n );\n for (const frame of bstream.write(audioData)) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n\n // IMPORTANT: close WS if TTS chunk stream been stuck too long\n // this allows unblock the current \"broken\" TTS node so that any future TTS nodes\n // can continue to process the stream without been blocked by the stuck node\n clearTTSChunkTimeout();\n timeout = setTimeout(() => {\n // cartesia chunk timeout quite often, so we make it a debug log\n this.#logger.debug(\n `Cartesia WebSocket TTS chunk stream timeout after ${this.#opts.chunkTimeout}ms`,\n );\n ws.close();\n }, this.#opts.chunkTimeout);\n } else if (isDoneMessage(serverMsg)) {\n // This ensures all sentences have been sent before closing\n if (sentenceStreamClosed) {\n for (const frame of bstream.flush()) {\n sendLastFrame(segmentId, false);\n lastFrame = frame;\n }\n sendLastFrame(segmentId, true);\n if (!this.queue.closed) {\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n }\n\n if (segmentId === requestId) {\n closing = true;\n clearTTSChunkTimeout();\n ws.close();\n break; // Exit the loop\n }\n }\n // If sentenceStreamClosed is false, continue receiving - more done messages will come\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 (\n err.message.includes('Queue is closed') ||\n err.message.includes('Channel is closed')\n ) {\n this.#logger.warn(\n { err },\n 'Channel closed during transcript processing (expected during disconnect)',\n );\n } else {\n this.#logger.error({ err }, 'Error in recvTask from Cartesia WebSocket');\n }\n }\n } finally {\n // IMPORTANT: Remove listeners so connection can be reused\n ws.off('message', onMessage);\n ws.off('close', onClose);\n ws.off('error', onError);\n clearTTSChunkTimeout();\n }\n };\n\n const wsUrl = this.#opts.baseUrl.replace(/^http/, 'ws');\n const url = `${wsUrl}/tts/websocket?api_key=${this.#opts.apiKey}&cartesia_version=${this.#opts.apiVersion}`;\n\n let ws: WebSocket | undefined;\n try {\n ws = await connectCartesiaWebSocket({\n url,\n timeoutMs: this.connOptions.timeoutMs,\n abortSignal: this.abortSignal,\n });\n await Promise.all([inputTask(), sentenceStreamTask(ws), recvTask(ws)]);\n } catch (e) {\n if (this.abortSignal.aborted) {\n return;\n }\n throw toRetryableConnectionError(e);\n } finally {\n // Ensure we don't leak sockets/tasks across retry attempts.\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n safeTerminateWebSocket(ws);\n }\n }\n }\n}\n\nconst transientNetworkCodes = new Set([\n 'ETIMEDOUT',\n 'ECONNRESET',\n 'EAI_AGAIN',\n 'ENETUNREACH',\n 'ECONNREFUSED',\n 'EHOSTUNREACH',\n]);\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => {\n return v !== null && typeof v === 'object';\n};\n\nconst isAggregateErrorLike = (e: unknown): e is { errors: unknown[]; name?: string } => {\n if (!isRecord(e)) return false;\n return e.name === 'AggregateError' && Array.isArray(e.errors);\n};\n\nconst hasErrorCode = (e: unknown, code: string): boolean => {\n if (isRecord(e) && e.code === code) return true;\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasErrorCode(inner, code));\n }\n return false;\n};\n\nconst hasAnyTransientCode = (e: unknown): boolean => {\n if (isRecord(e) && typeof e.code === 'string') {\n return transientNetworkCodes.has(e.code);\n }\n if (isAggregateErrorLike(e)) {\n return e.errors.some((inner) => hasAnyTransientCode(inner));\n }\n return false;\n};\n\nconst toRetryableConnectionError = (e: unknown): APIConnectionError => {\n const err = asError(e);\n const isTimeout =\n hasErrorCode(e, 'ETIMEDOUT') ||\n (typeof err.message === 'string' && err.message.includes('ETIMEDOUT'));\n const message = isTimeout\n ? `Cartesia connection timed out`\n : `Cartesia connection failed: ${err.message || 'unknown error'}`;\n return isTimeout ? new APITimeoutError({ message }) : new APIConnectionError({ message });\n};\n\nconst waitForWsOpen = async ({\n ws,\n timeoutMs,\n abortSignal,\n}: {\n ws: WebSocket;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}) => {\n if (abortSignal.aborted) {\n throw new Error('aborted');\n }\n\n const fut = new Future<void>();\n let timeout: NodeJS.Timeout | undefined;\n\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n ws.off('open', onOpen);\n ws.off('error', onError);\n ws.off('close', onClose);\n abortSignal.removeEventListener('abort', onAbort);\n };\n\n const onOpen = () => fut.resolve();\n const onError = (err: Error) => fut.reject(asError(err));\n const onClose = (code: number, reason: Buffer) =>\n fut.reject(\n new Error(`WebSocket closed before open (code=${code}, reason=${reason.toString()})`),\n );\n const onAbort = () => fut.reject(new Error('aborted'));\n\n ws.on('open', onOpen);\n ws.on('error', onError);\n ws.on('close', onClose);\n abortSignal.addEventListener('abort', onAbort, { once: true });\n\n if (timeoutMs > 0) {\n timeout = setTimeout(() => fut.reject(new Error('connect timeout')), timeoutMs);\n }\n\n try {\n await fut.await;\n } finally {\n cleanup();\n }\n};\n\nconst safeTerminateWebSocket = (ws: WebSocket) => {\n // `ws` can emit an 'error' event during teardown (especially if CONNECTING).\n // If there is no error listener at that moment, Node will treat it as unhandled and crash the process.\n try {\n ws.on('error', () => {});\n } catch {\n // ignore\n }\n\n try {\n // `terminate()` can throw if the socket was never established; `close()` is safer in CONNECTING.\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close();\n } else {\n ws.terminate();\n }\n } catch {\n // ignore\n }\n};\n\nconst connectCartesiaWebSocket = async ({\n url,\n timeoutMs,\n abortSignal,\n}: {\n url: string;\n timeoutMs: number;\n abortSignal: AbortSignal;\n}): Promise<WebSocket> => {\n const connectOnce = async (family?: number): Promise<WebSocket> => {\n const ws = new WebSocket(url, { handshakeTimeout: timeoutMs, family });\n try {\n await waitForWsOpen({ ws, timeoutMs, abortSignal });\n return ws;\n } catch (e) {\n safeTerminateWebSocket(ws);\n throw e;\n }\n };\n\n try {\n return await connectOnce();\n } catch (e) {\n // Mitigation for Node.js dual-stack (IPv6/IPv4) connect flakiness (\"happy eyeballs\"):\n // some environments surface `AggregateError` with nested `ETIMEDOUT` during the initial\n // WebSocket open. In that case we do a one-off retry forcing IPv4 (`family: 4`) before\n // letting the outer framework retry loop handle further attempts.\n //\n // If you still see `AggregateError`/`ETIMEDOUT`:\n // - Increase the session TTS connect timeout (`connOptions.ttsConnOptions.timeoutMs`)\n // - Or adjust Node's family autoselection behavior via `NODE_OPTIONS`, e.g.\n // `--network-family-autoselection-attempt-timeout=5000` (or disable it entirely).\n if (hasAnyTransientCode(e) || isAggregateErrorLike(e)) {\n return await connectOnce(4);\n }\n throw e;\n }\n};\n\nconst toCartesiaOptions = (\n opts: TTSOptions,\n streaming: boolean = false,\n): { [id: string]: unknown } => {\n const voice: { [id: string]: unknown } = {};\n if (typeof opts.voice === 'string') {\n voice.mode = 'id';\n voice.id = opts.voice;\n } else {\n voice.mode = 'embedding';\n voice.embedding = opts.voice;\n }\n\n if (opts.apiVersion === API_VERSION_WITH_EXPERIMENTAL_CONTROLS) {\n const voiceControls: { [id: string]: unknown } = {};\n if (opts.speed) {\n voiceControls.speed = opts.speed;\n }\n if (opts.emotion) {\n voiceControls.emotion = opts.emotion;\n }\n if (Object.keys(voiceControls).length) {\n voice.__experimental_controls = voiceControls;\n }\n }\n\n const result: { [id: string]: unknown } = {\n model_id: opts.model,\n voice,\n output_format: {\n container: 'raw',\n encoding: opts.encoding,\n sample_rate: opts.sampleRate,\n },\n language: getBaseLanguage(opts.language),\n max_buffer_delay_ms: 0,\n };\n\n if (opts.pronunciationDictId) {\n result.pronunciation_dict_id = opts.pronunciationDictId;\n }\n\n if (opts.apiVersion > API_VERSION_WITH_EXPERIMENTAL_CONTROLS && isSonic3(opts.model)) {\n const generationConfig: { [id: string]: unknown } = {};\n if (opts.speed) {\n generationConfig.speed = opts.speed;\n }\n if (opts.emotion) {\n generationConfig.emotion = opts.emotion[0];\n }\n if (opts.volume) {\n generationConfig.volume = opts.volume;\n }\n if (Object.keys(generationConfig).length) {\n result.generation_config = generationConfig;\n }\n }\n\n if (streaming && opts.wordTimestamps !== false) {\n result.add_timestamps = true;\n }\n\n return result;\n};\n"],"mappings":"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","names":["tts","ws"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-cartesia",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Cartesia plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -30,17 +30,17 @@
30
30
  "@types/ws": "^8.5.10",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.0.0",
33
- "@livekit/agents": "1.2.6",
34
- "@livekit/agents-plugin-openai": "1.2.6",
35
- "@livekit/agents-plugins-test": "1.2.6"
33
+ "@livekit/agents": "1.2.8",
34
+ "@livekit/agents-plugin-openai": "1.2.8",
35
+ "@livekit/agents-plugins-test": "1.2.8"
36
36
  },
37
37
  "dependencies": {
38
- "ws": "^8.16.0"
38
+ "ws": "^8.18.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@livekit/rtc-node": "^0.13.25",
42
42
  "zod": "^3.25.76 || ^4.1.8",
43
- "@livekit/agents": "1.2.6"
43
+ "@livekit/agents": "1.2.8"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup --onSuccess \"pnpm build:types\"",
package/src/models.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
 
5
- // Ref: python livekit-plugins/livekit-plugins-cartesia/livekit/plugins/cartesia/models.py - 11 lines
6
5
  export type TTSModels =
7
6
  | 'sonic'
8
7
  | 'sonic-2'
package/src/tts.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  AudioByteStream,
9
9
  Future,
10
10
  type TimedString,
11
+ asError,
11
12
  createTimedString,
12
13
  getBaseLanguage,
13
14
  log,
@@ -554,8 +555,6 @@ export class SynthesizeStream extends tts.SynthesizeStream {
554
555
  }
555
556
  }
556
557
 
557
- const asError = (e: unknown): Error => (e instanceof Error ? e : new Error(String(e)));
558
-
559
558
  const transientNetworkCodes = new Set([
560
559
  'ETIMEDOUT',
561
560
  'ECONNRESET',