@livekit/agents 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/index.cjs +3 -0
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +2 -1
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/inference/api_protos.cjs +104 -0
  9. package/dist/inference/api_protos.cjs.map +1 -0
  10. package/dist/inference/api_protos.d.cts +222 -0
  11. package/dist/inference/api_protos.d.ts +222 -0
  12. package/dist/inference/api_protos.d.ts.map +1 -0
  13. package/dist/inference/api_protos.js +70 -0
  14. package/dist/inference/api_protos.js.map +1 -0
  15. package/dist/inference/index.cjs +56 -0
  16. package/dist/inference/index.cjs.map +1 -0
  17. package/dist/inference/index.d.cts +9 -0
  18. package/dist/inference/index.d.ts +9 -0
  19. package/dist/inference/index.d.ts.map +1 -0
  20. package/dist/inference/index.js +16 -0
  21. package/dist/inference/index.js.map +1 -0
  22. package/dist/inference/llm.cjs +315 -0
  23. package/dist/inference/llm.cjs.map +1 -0
  24. package/dist/inference/llm.d.cts +92 -0
  25. package/dist/inference/llm.d.ts +92 -0
  26. package/dist/inference/llm.d.ts.map +1 -0
  27. package/dist/inference/llm.js +286 -0
  28. package/dist/inference/llm.js.map +1 -0
  29. package/dist/inference/stt.cjs +305 -0
  30. package/dist/inference/stt.cjs.map +1 -0
  31. package/dist/inference/stt.d.cts +79 -0
  32. package/dist/inference/stt.d.ts +79 -0
  33. package/dist/inference/stt.d.ts.map +1 -0
  34. package/dist/inference/stt.js +284 -0
  35. package/dist/inference/stt.js.map +1 -0
  36. package/dist/inference/tts.cjs +317 -0
  37. package/dist/inference/tts.cjs.map +1 -0
  38. package/dist/inference/tts.d.cts +75 -0
  39. package/dist/inference/tts.d.ts +75 -0
  40. package/dist/inference/tts.d.ts.map +1 -0
  41. package/dist/inference/tts.js +299 -0
  42. package/dist/inference/tts.js.map +1 -0
  43. package/dist/inference/utils.cjs +76 -0
  44. package/dist/inference/utils.cjs.map +1 -0
  45. package/dist/inference/utils.d.cts +5 -0
  46. package/dist/inference/utils.d.ts +5 -0
  47. package/dist/inference/utils.d.ts.map +1 -0
  48. package/dist/inference/utils.js +51 -0
  49. package/dist/inference/utils.js.map +1 -0
  50. package/dist/tts/tts.cjs +1 -1
  51. package/dist/tts/tts.cjs.map +1 -1
  52. package/dist/tts/tts.js +1 -1
  53. package/dist/tts/tts.js.map +1 -1
  54. package/dist/utils.cjs +11 -0
  55. package/dist/utils.cjs.map +1 -1
  56. package/dist/utils.d.cts +1 -0
  57. package/dist/utils.d.ts +1 -0
  58. package/dist/utils.d.ts.map +1 -1
  59. package/dist/utils.js +10 -0
  60. package/dist/utils.js.map +1 -1
  61. package/dist/voice/agent.cjs +16 -3
  62. package/dist/voice/agent.cjs.map +1 -1
  63. package/dist/voice/agent.d.cts +4 -3
  64. package/dist/voice/agent.d.ts +4 -3
  65. package/dist/voice/agent.d.ts.map +1 -1
  66. package/dist/voice/agent.js +20 -3
  67. package/dist/voice/agent.js.map +1 -1
  68. package/dist/voice/agent_session.cjs +16 -3
  69. package/dist/voice/agent_session.cjs.map +1 -1
  70. package/dist/voice/agent_session.d.cts +4 -3
  71. package/dist/voice/agent_session.d.ts +4 -3
  72. package/dist/voice/agent_session.d.ts.map +1 -1
  73. package/dist/voice/agent_session.js +20 -3
  74. package/dist/voice/agent_session.js.map +1 -1
  75. package/dist/voice/room_io/_input.cjs.map +1 -1
  76. package/dist/voice/room_io/_input.d.ts.map +1 -1
  77. package/dist/voice/room_io/_input.js +1 -0
  78. package/dist/voice/room_io/_input.js.map +1 -1
  79. package/dist/worker.cjs.map +1 -1
  80. package/dist/worker.d.ts.map +1 -1
  81. package/dist/worker.js +1 -1
  82. package/dist/worker.js.map +1 -1
  83. package/package.json +3 -2
  84. package/src/index.ts +2 -1
  85. package/src/inference/api_protos.ts +82 -0
  86. package/src/inference/index.ts +12 -0
  87. package/src/inference/llm.ts +485 -0
  88. package/src/inference/stt.ts +414 -0
  89. package/src/inference/tts.ts +421 -0
  90. package/src/inference/utils.ts +66 -0
  91. package/src/tts/tts.ts +1 -1
  92. package/src/utils.ts +11 -0
  93. package/src/voice/agent.ts +30 -6
  94. package/src/voice/agent_session.ts +29 -6
  95. package/src/voice/room_io/_input.ts +1 -1
  96. package/src/worker.ts +2 -7
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inference/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { APIError, APIStatusError } from '../_exceptions.js';\nimport { AudioByteStream } from '../audio.js';\nimport { log } from '../log.js';\nimport { createStreamChannel } from '../stream/stream_channel.js';\nimport { basic as tokenizeBasic } from '../tokenize/index.js';\nimport {\n SynthesizeStream as BaseSynthesizeStream,\n TTS as BaseTTS,\n ChunkedStream,\n} from '../tts/index.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { shortuuid } from '../utils.js';\nimport {\n type TtsClientEvent,\n type TtsServerEvent,\n type TtsSessionCreateEvent,\n ttsClientEventSchema,\n ttsServerEventSchema,\n} from './api_protos.js';\nimport { type AnyModels, connectWs, createAccessToken } from './utils.js';\n\ntype _CartesiaModels = 'cartesia' | 'cartesia/sonic' | 'cartesia/sonic-2' | 'cartesia/sonic-turbo';\n\nexport type CartesiaModels = _CartesiaModels | `${_CartesiaModels}:${string}`;\n\ntype _ElevenlabsModels =\n | 'elevenlabs'\n | 'elevenlabs/eleven_flash_v2'\n | 'elevenlabs/eleven_flash_v2_5'\n | 'elevenlabs/eleven_turbo_v2'\n | 'elevenlabs/eleven_turbo_v2_5'\n | 'elevenlabs/eleven_multilingual_v2';\n\nexport type ElevenlabsModels = _ElevenlabsModels | `${_ElevenlabsModels}:${string}`;\n\nexport type _RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';\n\nexport type RimeModels = _RimeModels | `${_RimeModels}:${string}`;\n\nexport type _InworldModels = 'inworld' | 'inworld/inworld-tts-1';\n\nexport type InworldModels = _InworldModels | `${_InworldModels}:${string}`;\n\nexport interface CartesiaOptions {\n duration?: number; // max duration of audio in seconds\n speed?: 'slow' | 'normal' | 'fast'; // default: not specified\n}\n\nexport interface ElevenlabsOptions {\n inactivity_timeout?: number; // default: 60\n apply_text_normalization?: 'auto' | 'off' | 'on'; // default: \"auto\"\n}\n\nexport interface RimeOptions {}\n\nexport interface InworldOptions {}\n\nexport type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyModels;\n\nexport type TTSOptions<TModel extends TTSModels> = TModel extends CartesiaModels\n ? CartesiaOptions\n : TModel extends ElevenlabsModels\n ? ElevenlabsOptions\n : TModel extends RimeOptions\n ? RimeOptions\n : TModel extends InworldOptions\n ? InworldOptions\n : Record<string, unknown>;\n\ntype TTSEncoding = 'pcm_s16le';\n\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_s16le';\nconst DEFAULT_SAMPLE_RATE = 16000;\nconst DEFAULT_BASE_URL = 'https://agent-gateway.livekit.cloud/v1';\nconst NUM_CHANNELS = 1;\nconst DEFAULT_LANGUAGE = 'en';\n\nexport interface InferenceTTSOptions<TModel extends TTSModels> {\n model?: TModel;\n voice?: string;\n language?: string;\n encoding: TTSEncoding;\n sampleRate: number;\n baseURL: string;\n apiKey: string;\n apiSecret: string;\n extraKwargs: TTSOptions<TModel>;\n}\n\nexport class TTS<TModel extends TTSModels> extends BaseTTS {\n private opts: InferenceTTSOptions<TModel>;\n private streams: Set<SynthesizeStream<TModel>> = new Set();\n\n #logger = log();\n\n constructor(opts: {\n model: TModel;\n voice?: string;\n language?: string;\n baseURL?: string;\n encoding?: TTSEncoding;\n sampleRate?: number;\n apiKey?: string;\n apiSecret?: string;\n extraKwargs?: TTSOptions<TModel>;\n }) {\n const sampleRate = opts?.sampleRate ?? DEFAULT_SAMPLE_RATE;\n super(sampleRate, 1, { streaming: true });\n\n const {\n model,\n voice,\n language = DEFAULT_LANGUAGE,\n baseURL,\n encoding = DEFAULT_ENCODING,\n apiKey,\n apiSecret,\n extraKwargs = {} as TTSOptions<TModel>,\n } = opts || {};\n\n const lkBaseURL = baseURL || process.env.LIVEKIT_INFERENCE_URL || DEFAULT_BASE_URL;\n const lkApiKey = apiKey || process.env.LIVEKIT_INFERENCE_API_KEY || process.env.LIVEKIT_API_KEY;\n if (!lkApiKey) {\n throw new Error('apiKey is required: pass apiKey or set LIVEKIT_API_KEY');\n }\n\n const lkApiSecret =\n apiSecret || process.env.LIVEKIT_INFERENCE_API_SECRET || process.env.LIVEKIT_API_SECRET;\n if (!lkApiSecret) {\n throw new Error('apiSecret is required: pass apiSecret or set LIVEKIT_API_SECRET');\n }\n\n // read voice id from the model if provided: \"provider/model:voice_id\"\n let nextModel = model;\n let nextVoice = voice;\n if (typeof nextModel === 'string') {\n const idx = nextModel.lastIndexOf(':');\n if (idx !== -1) {\n const voiceFromModel = nextModel.slice(idx + 1);\n if (nextVoice && nextVoice !== voiceFromModel) {\n this.#logger.warn(\n '`voice` is provided via both argument and model, using the one from the argument',\n { voice: nextVoice, model: nextModel },\n );\n } else {\n nextVoice = voiceFromModel;\n }\n nextModel = nextModel.slice(0, idx) as TModel;\n }\n }\n\n this.opts = {\n model: nextModel,\n voice: nextVoice,\n language,\n encoding,\n sampleRate,\n baseURL: lkBaseURL,\n apiKey: lkApiKey,\n apiSecret: lkApiSecret,\n extraKwargs,\n };\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = { ...this.opts, ...opts };\n for (const stream of this.streams) {\n stream.updateOptions(opts);\n }\n }\n\n synthesize(_: string): ChunkedStream {\n throw new Error('ChunkedStream is not implemented');\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream<TModel> {\n const { connOptions = DEFAULT_API_CONNECT_OPTIONS } = options || {};\n const stream = new SynthesizeStream(this, { ...this.opts }, connOptions);\n this.streams.add(stream);\n return stream;\n }\n\n async connectWs(timeout: number): Promise<WebSocket> {\n let baseURL = this.opts.baseURL;\n if (baseURL.startsWith('http://') || baseURL.startsWith('https://')) {\n baseURL = baseURL.replace('http', 'ws');\n }\n\n const token = await createAccessToken(this.opts.apiKey, this.opts.apiSecret);\n const url = `${baseURL}/tts`;\n const headers = { Authorization: `Bearer ${token}` } as Record<string, string>;\n\n const params = {\n type: 'session.create',\n sample_rate: String(this.opts.sampleRate),\n encoding: this.opts.encoding,\n extra: this.opts.extraKwargs,\n } as TtsSessionCreateEvent;\n\n if (this.opts.voice) params.voice = this.opts.voice;\n if (this.opts.model) params.model = this.opts.model;\n if (this.opts.language) params.language = this.opts.language;\n\n const socket = await connectWs(url, headers, timeout);\n socket.send(JSON.stringify(params));\n return socket;\n }\n\n async closeWs(ws: WebSocket) {\n await ws.close();\n }\n\n async close() {\n for (const stream of this.streams) {\n await stream.close();\n }\n this.streams.clear();\n }\n}\n\nexport class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeStream {\n private opts: InferenceTTSOptions<TModel>;\n private tts: TTS<TModel>;\n private connOptions: APIConnectOptions;\n\n #logger = log();\n\n constructor(tts: TTS<TModel>, opts: InferenceTTSOptions<TModel>, connOptions: APIConnectOptions) {\n super(tts, connOptions);\n this.opts = opts;\n this.tts = tts;\n this.connOptions = connOptions;\n }\n\n get label() {\n return 'inference.SynthesizeStream';\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = { ...this.opts, ...opts };\n }\n\n protected async run(): Promise<void> {\n let ws: WebSocket | null = null;\n let closing = false;\n let finalReceived = false;\n let lastFrame: AudioFrame | undefined;\n\n const sendTokenizerStream = new tokenizeBasic.SentenceTokenizer().stream();\n const eventChannel = createStreamChannel<TtsServerEvent>();\n const requestId = shortuuid('tts_request_');\n\n const resourceCleanup = () => {\n if (closing) return;\n closing = true;\n sendTokenizerStream.close();\n eventChannel.close();\n ws?.removeAllListeners();\n ws?.close();\n };\n\n const sendClientEvent = async (event: TtsClientEvent) => {\n const validatedEvent = await ttsClientEventSchema.parseAsync(event);\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n this.#logger.warn('Trying to send client TTS event to a closed WebSocket');\n return;\n }\n ws.send(JSON.stringify(validatedEvent));\n };\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n const createInputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n sendTokenizerStream.flush();\n continue;\n }\n sendTokenizerStream.pushText(data);\n }\n sendTokenizerStream.endInput();\n };\n\n const createSentenceStreamTask = async () => {\n for await (const ev of sendTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n sendClientEvent({\n type: 'input_transcript',\n transcript: ev.token + ' ',\n });\n }\n\n sendClientEvent({ type: 'session.flush' });\n };\n\n const createWsListenerTask = async (ws: WebSocket) => {\n return new Promise<void>((resolve, reject) => {\n this.abortController.signal.addEventListener('abort', () => {\n resourceCleanup();\n reject(new Error('WebSocket connection aborted'));\n });\n\n ws.on('message', async (data) => {\n const eventJson = JSON.parse(data.toString()) as Record<string, unknown>;\n const validatedEvent = ttsServerEventSchema.parse(eventJson);\n eventChannel.write(validatedEvent);\n });\n\n ws.on('error', (e) => {\n this.#logger.error({ error: e }, 'WebSocket error');\n resourceCleanup();\n reject(e);\n });\n\n ws.on('close', () => {\n resourceCleanup();\n\n if (!closing) return this.#logger.error('WebSocket closed unexpectedly');\n if (finalReceived) return resolve();\n\n reject(\n new APIStatusError({\n message: 'Gateway connection closed unexpectedly',\n options: { requestId },\n }),\n );\n });\n });\n };\n\n const createRecvTask = async () => {\n let currentSessionId: string | null = null;\n\n const bstream = new AudioByteStream(this.opts.sampleRate, NUM_CHANNELS);\n const serverEventStream = eventChannel.stream();\n const reader = serverEventStream.getReader();\n\n try {\n while (!this.closed && !this.abortController.signal.aborted) {\n const result = await reader.read();\n if (this.abortController.signal.aborted) return;\n if (result.done) return;\n\n const serverEvent = result.value;\n switch (serverEvent.type) {\n case 'session.created':\n currentSessionId = serverEvent.session_id;\n break;\n case 'output_audio':\n const base64Data = new Int8Array(Buffer.from(serverEvent.audio, 'base64'));\n for (const frame of bstream.write(base64Data.buffer)) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n break;\n case 'done':\n finalReceived = true;\n for (const frame of bstream.flush()) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n sendLastFrame(currentSessionId!, true);\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n break;\n case 'session.closed':\n resourceCleanup();\n break;\n case 'error':\n this.#logger.error(\n { serverEvent },\n 'Received error message from LiveKit TTS WebSocket',\n );\n resourceCleanup();\n throw new APIError(`LiveKit TTS returned error: ${serverEvent.message}`);\n default:\n this.#logger.warn('Unexpected message %s', serverEvent);\n break;\n }\n }\n } finally {\n reader.releaseLock();\n try {\n await serverEventStream.cancel();\n } catch (e) {\n this.#logger.debug('Error cancelling serverEventStream (may already be cancelled):', e);\n }\n }\n };\n\n try {\n ws = await this.tts.connectWs(this.connOptions.timeoutMs);\n\n await Promise.all([\n createInputTask(),\n createSentenceStreamTask(),\n createWsListenerTask(ws),\n createRecvTask(),\n ]);\n } catch (e) {\n this.#logger.error('Error in SynthesizeStream', { error: e });\n } finally {\n resourceCleanup();\n }\n }\n}\n"],"mappings":"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","names":["import_utils","BaseTTS","BaseSynthesizeStream","tokenizeBasic","ws"]}
@@ -0,0 +1,75 @@
1
+ import { WebSocket } from 'ws';
2
+ import { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS, ChunkedStream } from '../tts/index.js';
3
+ import { type APIConnectOptions } from '../types.js';
4
+ import { type AnyModels } from './utils.js';
5
+ type _CartesiaModels = 'cartesia' | 'cartesia/sonic' | 'cartesia/sonic-2' | 'cartesia/sonic-turbo';
6
+ export type CartesiaModels = _CartesiaModels | `${_CartesiaModels}:${string}`;
7
+ type _ElevenlabsModels = 'elevenlabs' | 'elevenlabs/eleven_flash_v2' | 'elevenlabs/eleven_flash_v2_5' | 'elevenlabs/eleven_turbo_v2' | 'elevenlabs/eleven_turbo_v2_5' | 'elevenlabs/eleven_multilingual_v2';
8
+ export type ElevenlabsModels = _ElevenlabsModels | `${_ElevenlabsModels}:${string}`;
9
+ export type _RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';
10
+ export type RimeModels = _RimeModels | `${_RimeModels}:${string}`;
11
+ export type _InworldModels = 'inworld' | 'inworld/inworld-tts-1';
12
+ export type InworldModels = _InworldModels | `${_InworldModels}:${string}`;
13
+ export interface CartesiaOptions {
14
+ duration?: number;
15
+ speed?: 'slow' | 'normal' | 'fast';
16
+ }
17
+ export interface ElevenlabsOptions {
18
+ inactivity_timeout?: number;
19
+ apply_text_normalization?: 'auto' | 'off' | 'on';
20
+ }
21
+ export interface RimeOptions {
22
+ }
23
+ export interface InworldOptions {
24
+ }
25
+ export type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyModels;
26
+ export type TTSOptions<TModel extends TTSModels> = TModel extends CartesiaModels ? CartesiaOptions : TModel extends ElevenlabsModels ? ElevenlabsOptions : TModel extends RimeOptions ? RimeOptions : TModel extends InworldOptions ? InworldOptions : Record<string, unknown>;
27
+ type TTSEncoding = 'pcm_s16le';
28
+ export interface InferenceTTSOptions<TModel extends TTSModels> {
29
+ model?: TModel;
30
+ voice?: string;
31
+ language?: string;
32
+ encoding: TTSEncoding;
33
+ sampleRate: number;
34
+ baseURL: string;
35
+ apiKey: string;
36
+ apiSecret: string;
37
+ extraKwargs: TTSOptions<TModel>;
38
+ }
39
+ export declare class TTS<TModel extends TTSModels> extends BaseTTS {
40
+ #private;
41
+ private opts;
42
+ private streams;
43
+ constructor(opts: {
44
+ model: TModel;
45
+ voice?: string;
46
+ language?: string;
47
+ baseURL?: string;
48
+ encoding?: TTSEncoding;
49
+ sampleRate?: number;
50
+ apiKey?: string;
51
+ apiSecret?: string;
52
+ extraKwargs?: TTSOptions<TModel>;
53
+ });
54
+ get label(): string;
55
+ updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>): void;
56
+ synthesize(_: string): ChunkedStream;
57
+ stream(options?: {
58
+ connOptions?: APIConnectOptions;
59
+ }): SynthesizeStream<TModel>;
60
+ connectWs(timeout: number): Promise<WebSocket>;
61
+ closeWs(ws: WebSocket): Promise<void>;
62
+ close(): Promise<void>;
63
+ }
64
+ export declare class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeStream {
65
+ #private;
66
+ private opts;
67
+ private tts;
68
+ private connOptions;
69
+ constructor(tts: TTS<TModel>, opts: InferenceTTSOptions<TModel>, connOptions: APIConnectOptions);
70
+ get label(): string;
71
+ updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>): void;
72
+ protected run(): Promise<void>;
73
+ }
74
+ export {};
75
+ //# sourceMappingURL=tts.d.ts.map
@@ -0,0 +1,75 @@
1
+ import { WebSocket } from 'ws';
2
+ import { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS, ChunkedStream } from '../tts/index.js';
3
+ import { type APIConnectOptions } from '../types.js';
4
+ import { type AnyModels } from './utils.js';
5
+ type _CartesiaModels = 'cartesia' | 'cartesia/sonic' | 'cartesia/sonic-2' | 'cartesia/sonic-turbo';
6
+ export type CartesiaModels = _CartesiaModels | `${_CartesiaModels}:${string}`;
7
+ type _ElevenlabsModels = 'elevenlabs' | 'elevenlabs/eleven_flash_v2' | 'elevenlabs/eleven_flash_v2_5' | 'elevenlabs/eleven_turbo_v2' | 'elevenlabs/eleven_turbo_v2_5' | 'elevenlabs/eleven_multilingual_v2';
8
+ export type ElevenlabsModels = _ElevenlabsModels | `${_ElevenlabsModels}:${string}`;
9
+ export type _RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';
10
+ export type RimeModels = _RimeModels | `${_RimeModels}:${string}`;
11
+ export type _InworldModels = 'inworld' | 'inworld/inworld-tts-1';
12
+ export type InworldModels = _InworldModels | `${_InworldModels}:${string}`;
13
+ export interface CartesiaOptions {
14
+ duration?: number;
15
+ speed?: 'slow' | 'normal' | 'fast';
16
+ }
17
+ export interface ElevenlabsOptions {
18
+ inactivity_timeout?: number;
19
+ apply_text_normalization?: 'auto' | 'off' | 'on';
20
+ }
21
+ export interface RimeOptions {
22
+ }
23
+ export interface InworldOptions {
24
+ }
25
+ export type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyModels;
26
+ export type TTSOptions<TModel extends TTSModels> = TModel extends CartesiaModels ? CartesiaOptions : TModel extends ElevenlabsModels ? ElevenlabsOptions : TModel extends RimeOptions ? RimeOptions : TModel extends InworldOptions ? InworldOptions : Record<string, unknown>;
27
+ type TTSEncoding = 'pcm_s16le';
28
+ export interface InferenceTTSOptions<TModel extends TTSModels> {
29
+ model?: TModel;
30
+ voice?: string;
31
+ language?: string;
32
+ encoding: TTSEncoding;
33
+ sampleRate: number;
34
+ baseURL: string;
35
+ apiKey: string;
36
+ apiSecret: string;
37
+ extraKwargs: TTSOptions<TModel>;
38
+ }
39
+ export declare class TTS<TModel extends TTSModels> extends BaseTTS {
40
+ #private;
41
+ private opts;
42
+ private streams;
43
+ constructor(opts: {
44
+ model: TModel;
45
+ voice?: string;
46
+ language?: string;
47
+ baseURL?: string;
48
+ encoding?: TTSEncoding;
49
+ sampleRate?: number;
50
+ apiKey?: string;
51
+ apiSecret?: string;
52
+ extraKwargs?: TTSOptions<TModel>;
53
+ });
54
+ get label(): string;
55
+ updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>): void;
56
+ synthesize(_: string): ChunkedStream;
57
+ stream(options?: {
58
+ connOptions?: APIConnectOptions;
59
+ }): SynthesizeStream<TModel>;
60
+ connectWs(timeout: number): Promise<WebSocket>;
61
+ closeWs(ws: WebSocket): Promise<void>;
62
+ close(): Promise<void>;
63
+ }
64
+ export declare class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeStream {
65
+ #private;
66
+ private opts;
67
+ private tts;
68
+ private connOptions;
69
+ constructor(tts: TTS<TModel>, opts: InferenceTTSOptions<TModel>, connOptions: APIConnectOptions);
70
+ get label(): string;
71
+ updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>): void;
72
+ protected run(): Promise<void>;
73
+ }
74
+ export {};
75
+ //# sourceMappingURL=tts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/inference/tts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,299 @@
1
+ import { WebSocket } from "ws";
2
+ import { APIError, APIStatusError } from "../_exceptions.js";
3
+ import { AudioByteStream } from "../audio.js";
4
+ import { log } from "../log.js";
5
+ import { createStreamChannel } from "../stream/stream_channel.js";
6
+ import { basic as tokenizeBasic } from "../tokenize/index.js";
7
+ import {
8
+ SynthesizeStream as BaseSynthesizeStream,
9
+ TTS as BaseTTS,
10
+ ChunkedStream
11
+ } from "../tts/index.js";
12
+ import { DEFAULT_API_CONNECT_OPTIONS } from "../types.js";
13
+ import { shortuuid } from "../utils.js";
14
+ import {
15
+ ttsClientEventSchema,
16
+ ttsServerEventSchema
17
+ } from "./api_protos.js";
18
+ import { connectWs, createAccessToken } from "./utils.js";
19
+ const DEFAULT_ENCODING = "pcm_s16le";
20
+ const DEFAULT_SAMPLE_RATE = 16e3;
21
+ const DEFAULT_BASE_URL = "https://agent-gateway.livekit.cloud/v1";
22
+ const NUM_CHANNELS = 1;
23
+ const DEFAULT_LANGUAGE = "en";
24
+ class TTS extends BaseTTS {
25
+ opts;
26
+ streams = /* @__PURE__ */ new Set();
27
+ #logger = log();
28
+ constructor(opts) {
29
+ const sampleRate = (opts == null ? void 0 : opts.sampleRate) ?? DEFAULT_SAMPLE_RATE;
30
+ super(sampleRate, 1, { streaming: true });
31
+ const {
32
+ model,
33
+ voice,
34
+ language = DEFAULT_LANGUAGE,
35
+ baseURL,
36
+ encoding = DEFAULT_ENCODING,
37
+ apiKey,
38
+ apiSecret,
39
+ extraKwargs = {}
40
+ } = opts || {};
41
+ const lkBaseURL = baseURL || process.env.LIVEKIT_INFERENCE_URL || DEFAULT_BASE_URL;
42
+ const lkApiKey = apiKey || process.env.LIVEKIT_INFERENCE_API_KEY || process.env.LIVEKIT_API_KEY;
43
+ if (!lkApiKey) {
44
+ throw new Error("apiKey is required: pass apiKey or set LIVEKIT_API_KEY");
45
+ }
46
+ const lkApiSecret = apiSecret || process.env.LIVEKIT_INFERENCE_API_SECRET || process.env.LIVEKIT_API_SECRET;
47
+ if (!lkApiSecret) {
48
+ throw new Error("apiSecret is required: pass apiSecret or set LIVEKIT_API_SECRET");
49
+ }
50
+ let nextModel = model;
51
+ let nextVoice = voice;
52
+ if (typeof nextModel === "string") {
53
+ const idx = nextModel.lastIndexOf(":");
54
+ if (idx !== -1) {
55
+ const voiceFromModel = nextModel.slice(idx + 1);
56
+ if (nextVoice && nextVoice !== voiceFromModel) {
57
+ this.#logger.warn(
58
+ "`voice` is provided via both argument and model, using the one from the argument",
59
+ { voice: nextVoice, model: nextModel }
60
+ );
61
+ } else {
62
+ nextVoice = voiceFromModel;
63
+ }
64
+ nextModel = nextModel.slice(0, idx);
65
+ }
66
+ }
67
+ this.opts = {
68
+ model: nextModel,
69
+ voice: nextVoice,
70
+ language,
71
+ encoding,
72
+ sampleRate,
73
+ baseURL: lkBaseURL,
74
+ apiKey: lkApiKey,
75
+ apiSecret: lkApiSecret,
76
+ extraKwargs
77
+ };
78
+ }
79
+ get label() {
80
+ return "inference.TTS";
81
+ }
82
+ updateOptions(opts) {
83
+ this.opts = { ...this.opts, ...opts };
84
+ for (const stream of this.streams) {
85
+ stream.updateOptions(opts);
86
+ }
87
+ }
88
+ synthesize(_) {
89
+ throw new Error("ChunkedStream is not implemented");
90
+ }
91
+ stream(options) {
92
+ const { connOptions = DEFAULT_API_CONNECT_OPTIONS } = options || {};
93
+ const stream = new SynthesizeStream(this, { ...this.opts }, connOptions);
94
+ this.streams.add(stream);
95
+ return stream;
96
+ }
97
+ async connectWs(timeout) {
98
+ let baseURL = this.opts.baseURL;
99
+ if (baseURL.startsWith("http://") || baseURL.startsWith("https://")) {
100
+ baseURL = baseURL.replace("http", "ws");
101
+ }
102
+ const token = await createAccessToken(this.opts.apiKey, this.opts.apiSecret);
103
+ const url = `${baseURL}/tts`;
104
+ const headers = { Authorization: `Bearer ${token}` };
105
+ const params = {
106
+ type: "session.create",
107
+ sample_rate: String(this.opts.sampleRate),
108
+ encoding: this.opts.encoding,
109
+ extra: this.opts.extraKwargs
110
+ };
111
+ if (this.opts.voice) params.voice = this.opts.voice;
112
+ if (this.opts.model) params.model = this.opts.model;
113
+ if (this.opts.language) params.language = this.opts.language;
114
+ const socket = await connectWs(url, headers, timeout);
115
+ socket.send(JSON.stringify(params));
116
+ return socket;
117
+ }
118
+ async closeWs(ws) {
119
+ await ws.close();
120
+ }
121
+ async close() {
122
+ for (const stream of this.streams) {
123
+ await stream.close();
124
+ }
125
+ this.streams.clear();
126
+ }
127
+ }
128
+ class SynthesizeStream extends BaseSynthesizeStream {
129
+ opts;
130
+ tts;
131
+ connOptions;
132
+ #logger = log();
133
+ constructor(tts, opts, connOptions) {
134
+ super(tts, connOptions);
135
+ this.opts = opts;
136
+ this.tts = tts;
137
+ this.connOptions = connOptions;
138
+ }
139
+ get label() {
140
+ return "inference.SynthesizeStream";
141
+ }
142
+ updateOptions(opts) {
143
+ this.opts = { ...this.opts, ...opts };
144
+ }
145
+ async run() {
146
+ let ws = null;
147
+ let closing = false;
148
+ let finalReceived = false;
149
+ let lastFrame;
150
+ const sendTokenizerStream = new tokenizeBasic.SentenceTokenizer().stream();
151
+ const eventChannel = createStreamChannel();
152
+ const requestId = shortuuid("tts_request_");
153
+ const resourceCleanup = () => {
154
+ if (closing) return;
155
+ closing = true;
156
+ sendTokenizerStream.close();
157
+ eventChannel.close();
158
+ ws == null ? void 0 : ws.removeAllListeners();
159
+ ws == null ? void 0 : ws.close();
160
+ };
161
+ const sendClientEvent = async (event) => {
162
+ const validatedEvent = await ttsClientEventSchema.parseAsync(event);
163
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
164
+ this.#logger.warn("Trying to send client TTS event to a closed WebSocket");
165
+ return;
166
+ }
167
+ ws.send(JSON.stringify(validatedEvent));
168
+ };
169
+ const sendLastFrame = (segmentId, final) => {
170
+ if (lastFrame) {
171
+ this.queue.put({ requestId, segmentId, frame: lastFrame, final });
172
+ lastFrame = void 0;
173
+ }
174
+ };
175
+ const createInputTask = async () => {
176
+ for await (const data of this.input) {
177
+ if (this.abortController.signal.aborted) break;
178
+ if (data === SynthesizeStream.FLUSH_SENTINEL) {
179
+ sendTokenizerStream.flush();
180
+ continue;
181
+ }
182
+ sendTokenizerStream.pushText(data);
183
+ }
184
+ sendTokenizerStream.endInput();
185
+ };
186
+ const createSentenceStreamTask = async () => {
187
+ for await (const ev of sendTokenizerStream) {
188
+ if (this.abortController.signal.aborted) break;
189
+ sendClientEvent({
190
+ type: "input_transcript",
191
+ transcript: ev.token + " "
192
+ });
193
+ }
194
+ sendClientEvent({ type: "session.flush" });
195
+ };
196
+ const createWsListenerTask = async (ws2) => {
197
+ return new Promise((resolve, reject) => {
198
+ this.abortController.signal.addEventListener("abort", () => {
199
+ resourceCleanup();
200
+ reject(new Error("WebSocket connection aborted"));
201
+ });
202
+ ws2.on("message", async (data) => {
203
+ const eventJson = JSON.parse(data.toString());
204
+ const validatedEvent = ttsServerEventSchema.parse(eventJson);
205
+ eventChannel.write(validatedEvent);
206
+ });
207
+ ws2.on("error", (e) => {
208
+ this.#logger.error({ error: e }, "WebSocket error");
209
+ resourceCleanup();
210
+ reject(e);
211
+ });
212
+ ws2.on("close", () => {
213
+ resourceCleanup();
214
+ if (!closing) return this.#logger.error("WebSocket closed unexpectedly");
215
+ if (finalReceived) return resolve();
216
+ reject(
217
+ new APIStatusError({
218
+ message: "Gateway connection closed unexpectedly",
219
+ options: { requestId }
220
+ })
221
+ );
222
+ });
223
+ });
224
+ };
225
+ const createRecvTask = async () => {
226
+ let currentSessionId = null;
227
+ const bstream = new AudioByteStream(this.opts.sampleRate, NUM_CHANNELS);
228
+ const serverEventStream = eventChannel.stream();
229
+ const reader = serverEventStream.getReader();
230
+ try {
231
+ while (!this.closed && !this.abortController.signal.aborted) {
232
+ const result = await reader.read();
233
+ if (this.abortController.signal.aborted) return;
234
+ if (result.done) return;
235
+ const serverEvent = result.value;
236
+ switch (serverEvent.type) {
237
+ case "session.created":
238
+ currentSessionId = serverEvent.session_id;
239
+ break;
240
+ case "output_audio":
241
+ const base64Data = new Int8Array(Buffer.from(serverEvent.audio, "base64"));
242
+ for (const frame of bstream.write(base64Data.buffer)) {
243
+ sendLastFrame(currentSessionId, false);
244
+ lastFrame = frame;
245
+ }
246
+ break;
247
+ case "done":
248
+ finalReceived = true;
249
+ for (const frame of bstream.flush()) {
250
+ sendLastFrame(currentSessionId, false);
251
+ lastFrame = frame;
252
+ }
253
+ sendLastFrame(currentSessionId, true);
254
+ this.queue.put(SynthesizeStream.END_OF_STREAM);
255
+ break;
256
+ case "session.closed":
257
+ resourceCleanup();
258
+ break;
259
+ case "error":
260
+ this.#logger.error(
261
+ { serverEvent },
262
+ "Received error message from LiveKit TTS WebSocket"
263
+ );
264
+ resourceCleanup();
265
+ throw new APIError(`LiveKit TTS returned error: ${serverEvent.message}`);
266
+ default:
267
+ this.#logger.warn("Unexpected message %s", serverEvent);
268
+ break;
269
+ }
270
+ }
271
+ } finally {
272
+ reader.releaseLock();
273
+ try {
274
+ await serverEventStream.cancel();
275
+ } catch (e) {
276
+ this.#logger.debug("Error cancelling serverEventStream (may already be cancelled):", e);
277
+ }
278
+ }
279
+ };
280
+ try {
281
+ ws = await this.tts.connectWs(this.connOptions.timeoutMs);
282
+ await Promise.all([
283
+ createInputTask(),
284
+ createSentenceStreamTask(),
285
+ createWsListenerTask(ws),
286
+ createRecvTask()
287
+ ]);
288
+ } catch (e) {
289
+ this.#logger.error("Error in SynthesizeStream", { error: e });
290
+ } finally {
291
+ resourceCleanup();
292
+ }
293
+ }
294
+ }
295
+ export {
296
+ SynthesizeStream,
297
+ TTS
298
+ };
299
+ //# sourceMappingURL=tts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inference/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { APIError, APIStatusError } from '../_exceptions.js';\nimport { AudioByteStream } from '../audio.js';\nimport { log } from '../log.js';\nimport { createStreamChannel } from '../stream/stream_channel.js';\nimport { basic as tokenizeBasic } from '../tokenize/index.js';\nimport {\n SynthesizeStream as BaseSynthesizeStream,\n TTS as BaseTTS,\n ChunkedStream,\n} from '../tts/index.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { shortuuid } from '../utils.js';\nimport {\n type TtsClientEvent,\n type TtsServerEvent,\n type TtsSessionCreateEvent,\n ttsClientEventSchema,\n ttsServerEventSchema,\n} from './api_protos.js';\nimport { type AnyModels, connectWs, createAccessToken } from './utils.js';\n\ntype _CartesiaModels = 'cartesia' | 'cartesia/sonic' | 'cartesia/sonic-2' | 'cartesia/sonic-turbo';\n\nexport type CartesiaModels = _CartesiaModels | `${_CartesiaModels}:${string}`;\n\ntype _ElevenlabsModels =\n | 'elevenlabs'\n | 'elevenlabs/eleven_flash_v2'\n | 'elevenlabs/eleven_flash_v2_5'\n | 'elevenlabs/eleven_turbo_v2'\n | 'elevenlabs/eleven_turbo_v2_5'\n | 'elevenlabs/eleven_multilingual_v2';\n\nexport type ElevenlabsModels = _ElevenlabsModels | `${_ElevenlabsModels}:${string}`;\n\nexport type _RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';\n\nexport type RimeModels = _RimeModels | `${_RimeModels}:${string}`;\n\nexport type _InworldModels = 'inworld' | 'inworld/inworld-tts-1';\n\nexport type InworldModels = _InworldModels | `${_InworldModels}:${string}`;\n\nexport interface CartesiaOptions {\n duration?: number; // max duration of audio in seconds\n speed?: 'slow' | 'normal' | 'fast'; // default: not specified\n}\n\nexport interface ElevenlabsOptions {\n inactivity_timeout?: number; // default: 60\n apply_text_normalization?: 'auto' | 'off' | 'on'; // default: \"auto\"\n}\n\nexport interface RimeOptions {}\n\nexport interface InworldOptions {}\n\nexport type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyModels;\n\nexport type TTSOptions<TModel extends TTSModels> = TModel extends CartesiaModels\n ? CartesiaOptions\n : TModel extends ElevenlabsModels\n ? ElevenlabsOptions\n : TModel extends RimeOptions\n ? RimeOptions\n : TModel extends InworldOptions\n ? InworldOptions\n : Record<string, unknown>;\n\ntype TTSEncoding = 'pcm_s16le';\n\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_s16le';\nconst DEFAULT_SAMPLE_RATE = 16000;\nconst DEFAULT_BASE_URL = 'https://agent-gateway.livekit.cloud/v1';\nconst NUM_CHANNELS = 1;\nconst DEFAULT_LANGUAGE = 'en';\n\nexport interface InferenceTTSOptions<TModel extends TTSModels> {\n model?: TModel;\n voice?: string;\n language?: string;\n encoding: TTSEncoding;\n sampleRate: number;\n baseURL: string;\n apiKey: string;\n apiSecret: string;\n extraKwargs: TTSOptions<TModel>;\n}\n\nexport class TTS<TModel extends TTSModels> extends BaseTTS {\n private opts: InferenceTTSOptions<TModel>;\n private streams: Set<SynthesizeStream<TModel>> = new Set();\n\n #logger = log();\n\n constructor(opts: {\n model: TModel;\n voice?: string;\n language?: string;\n baseURL?: string;\n encoding?: TTSEncoding;\n sampleRate?: number;\n apiKey?: string;\n apiSecret?: string;\n extraKwargs?: TTSOptions<TModel>;\n }) {\n const sampleRate = opts?.sampleRate ?? DEFAULT_SAMPLE_RATE;\n super(sampleRate, 1, { streaming: true });\n\n const {\n model,\n voice,\n language = DEFAULT_LANGUAGE,\n baseURL,\n encoding = DEFAULT_ENCODING,\n apiKey,\n apiSecret,\n extraKwargs = {} as TTSOptions<TModel>,\n } = opts || {};\n\n const lkBaseURL = baseURL || process.env.LIVEKIT_INFERENCE_URL || DEFAULT_BASE_URL;\n const lkApiKey = apiKey || process.env.LIVEKIT_INFERENCE_API_KEY || process.env.LIVEKIT_API_KEY;\n if (!lkApiKey) {\n throw new Error('apiKey is required: pass apiKey or set LIVEKIT_API_KEY');\n }\n\n const lkApiSecret =\n apiSecret || process.env.LIVEKIT_INFERENCE_API_SECRET || process.env.LIVEKIT_API_SECRET;\n if (!lkApiSecret) {\n throw new Error('apiSecret is required: pass apiSecret or set LIVEKIT_API_SECRET');\n }\n\n // read voice id from the model if provided: \"provider/model:voice_id\"\n let nextModel = model;\n let nextVoice = voice;\n if (typeof nextModel === 'string') {\n const idx = nextModel.lastIndexOf(':');\n if (idx !== -1) {\n const voiceFromModel = nextModel.slice(idx + 1);\n if (nextVoice && nextVoice !== voiceFromModel) {\n this.#logger.warn(\n '`voice` is provided via both argument and model, using the one from the argument',\n { voice: nextVoice, model: nextModel },\n );\n } else {\n nextVoice = voiceFromModel;\n }\n nextModel = nextModel.slice(0, idx) as TModel;\n }\n }\n\n this.opts = {\n model: nextModel,\n voice: nextVoice,\n language,\n encoding,\n sampleRate,\n baseURL: lkBaseURL,\n apiKey: lkApiKey,\n apiSecret: lkApiSecret,\n extraKwargs,\n };\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = { ...this.opts, ...opts };\n for (const stream of this.streams) {\n stream.updateOptions(opts);\n }\n }\n\n synthesize(_: string): ChunkedStream {\n throw new Error('ChunkedStream is not implemented');\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream<TModel> {\n const { connOptions = DEFAULT_API_CONNECT_OPTIONS } = options || {};\n const stream = new SynthesizeStream(this, { ...this.opts }, connOptions);\n this.streams.add(stream);\n return stream;\n }\n\n async connectWs(timeout: number): Promise<WebSocket> {\n let baseURL = this.opts.baseURL;\n if (baseURL.startsWith('http://') || baseURL.startsWith('https://')) {\n baseURL = baseURL.replace('http', 'ws');\n }\n\n const token = await createAccessToken(this.opts.apiKey, this.opts.apiSecret);\n const url = `${baseURL}/tts`;\n const headers = { Authorization: `Bearer ${token}` } as Record<string, string>;\n\n const params = {\n type: 'session.create',\n sample_rate: String(this.opts.sampleRate),\n encoding: this.opts.encoding,\n extra: this.opts.extraKwargs,\n } as TtsSessionCreateEvent;\n\n if (this.opts.voice) params.voice = this.opts.voice;\n if (this.opts.model) params.model = this.opts.model;\n if (this.opts.language) params.language = this.opts.language;\n\n const socket = await connectWs(url, headers, timeout);\n socket.send(JSON.stringify(params));\n return socket;\n }\n\n async closeWs(ws: WebSocket) {\n await ws.close();\n }\n\n async close() {\n for (const stream of this.streams) {\n await stream.close();\n }\n this.streams.clear();\n }\n}\n\nexport class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeStream {\n private opts: InferenceTTSOptions<TModel>;\n private tts: TTS<TModel>;\n private connOptions: APIConnectOptions;\n\n #logger = log();\n\n constructor(tts: TTS<TModel>, opts: InferenceTTSOptions<TModel>, connOptions: APIConnectOptions) {\n super(tts, connOptions);\n this.opts = opts;\n this.tts = tts;\n this.connOptions = connOptions;\n }\n\n get label() {\n return 'inference.SynthesizeStream';\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = { ...this.opts, ...opts };\n }\n\n protected async run(): Promise<void> {\n let ws: WebSocket | null = null;\n let closing = false;\n let finalReceived = false;\n let lastFrame: AudioFrame | undefined;\n\n const sendTokenizerStream = new tokenizeBasic.SentenceTokenizer().stream();\n const eventChannel = createStreamChannel<TtsServerEvent>();\n const requestId = shortuuid('tts_request_');\n\n const resourceCleanup = () => {\n if (closing) return;\n closing = true;\n sendTokenizerStream.close();\n eventChannel.close();\n ws?.removeAllListeners();\n ws?.close();\n };\n\n const sendClientEvent = async (event: TtsClientEvent) => {\n const validatedEvent = await ttsClientEventSchema.parseAsync(event);\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n this.#logger.warn('Trying to send client TTS event to a closed WebSocket');\n return;\n }\n ws.send(JSON.stringify(validatedEvent));\n };\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n const createInputTask = async () => {\n for await (const data of this.input) {\n if (this.abortController.signal.aborted) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n sendTokenizerStream.flush();\n continue;\n }\n sendTokenizerStream.pushText(data);\n }\n sendTokenizerStream.endInput();\n };\n\n const createSentenceStreamTask = async () => {\n for await (const ev of sendTokenizerStream) {\n if (this.abortController.signal.aborted) break;\n\n sendClientEvent({\n type: 'input_transcript',\n transcript: ev.token + ' ',\n });\n }\n\n sendClientEvent({ type: 'session.flush' });\n };\n\n const createWsListenerTask = async (ws: WebSocket) => {\n return new Promise<void>((resolve, reject) => {\n this.abortController.signal.addEventListener('abort', () => {\n resourceCleanup();\n reject(new Error('WebSocket connection aborted'));\n });\n\n ws.on('message', async (data) => {\n const eventJson = JSON.parse(data.toString()) as Record<string, unknown>;\n const validatedEvent = ttsServerEventSchema.parse(eventJson);\n eventChannel.write(validatedEvent);\n });\n\n ws.on('error', (e) => {\n this.#logger.error({ error: e }, 'WebSocket error');\n resourceCleanup();\n reject(e);\n });\n\n ws.on('close', () => {\n resourceCleanup();\n\n if (!closing) return this.#logger.error('WebSocket closed unexpectedly');\n if (finalReceived) return resolve();\n\n reject(\n new APIStatusError({\n message: 'Gateway connection closed unexpectedly',\n options: { requestId },\n }),\n );\n });\n });\n };\n\n const createRecvTask = async () => {\n let currentSessionId: string | null = null;\n\n const bstream = new AudioByteStream(this.opts.sampleRate, NUM_CHANNELS);\n const serverEventStream = eventChannel.stream();\n const reader = serverEventStream.getReader();\n\n try {\n while (!this.closed && !this.abortController.signal.aborted) {\n const result = await reader.read();\n if (this.abortController.signal.aborted) return;\n if (result.done) return;\n\n const serverEvent = result.value;\n switch (serverEvent.type) {\n case 'session.created':\n currentSessionId = serverEvent.session_id;\n break;\n case 'output_audio':\n const base64Data = new Int8Array(Buffer.from(serverEvent.audio, 'base64'));\n for (const frame of bstream.write(base64Data.buffer)) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n break;\n case 'done':\n finalReceived = true;\n for (const frame of bstream.flush()) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n sendLastFrame(currentSessionId!, true);\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n break;\n case 'session.closed':\n resourceCleanup();\n break;\n case 'error':\n this.#logger.error(\n { serverEvent },\n 'Received error message from LiveKit TTS WebSocket',\n );\n resourceCleanup();\n throw new APIError(`LiveKit TTS returned error: ${serverEvent.message}`);\n default:\n this.#logger.warn('Unexpected message %s', serverEvent);\n break;\n }\n }\n } finally {\n reader.releaseLock();\n try {\n await serverEventStream.cancel();\n } catch (e) {\n this.#logger.debug('Error cancelling serverEventStream (may already be cancelled):', e);\n }\n }\n };\n\n try {\n ws = await this.tts.connectWs(this.connOptions.timeoutMs);\n\n await Promise.all([\n createInputTask(),\n createSentenceStreamTask(),\n createWsListenerTask(ws),\n createRecvTask(),\n ]);\n } catch (e) {\n this.#logger.error('Error in SynthesizeStream', { error: e });\n } finally {\n resourceCleanup();\n }\n }\n}\n"],"mappings":"AAIA,SAAS,iBAAiB;AAC1B,SAAS,UAAU,sBAAsB;AACzC,SAAS,uBAAuB;AAChC,SAAS,WAAW;AACpB,SAAS,2BAA2B;AACpC,SAAS,SAAS,qBAAqB;AACvC;AAAA,EACE,oBAAoB;AAAA,EACpB,OAAO;AAAA,EACP;AAAA,OACK;AACP,SAAiC,mCAAmC;AACpE,SAAS,iBAAiB;AAC1B;AAAA,EAIE;AAAA,EACA;AAAA,OACK;AACP,SAAyB,WAAW,yBAAyB;AAoD7D,MAAM,mBAAgC;AACtC,MAAM,sBAAsB;AAC5B,MAAM,mBAAmB;AACzB,MAAM,eAAe;AACrB,MAAM,mBAAmB;AAclB,MAAM,YAAsC,QAAQ;AAAA,EACjD;AAAA,EACA,UAAyC,oBAAI,IAAI;AAAA,EAEzD,UAAU,IAAI;AAAA,EAEd,YAAY,MAUT;AACD,UAAM,cAAa,6BAAM,eAAc;AACvC,UAAM,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAExC,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,cAAc,CAAC;AAAA,IACjB,IAAI,QAAQ,CAAC;AAEb,UAAM,YAAY,WAAW,QAAQ,IAAI,yBAAyB;AAClE,UAAM,WAAW,UAAU,QAAQ,IAAI,6BAA6B,QAAQ,IAAI;AAChF,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAEA,UAAM,cACJ,aAAa,QAAQ,IAAI,gCAAgC,QAAQ,IAAI;AACvE,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,iEAAiE;AAAA,IACnF;AAGA,QAAI,YAAY;AAChB,QAAI,YAAY;AAChB,QAAI,OAAO,cAAc,UAAU;AACjC,YAAM,MAAM,UAAU,YAAY,GAAG;AACrC,UAAI,QAAQ,IAAI;AACd,cAAM,iBAAiB,UAAU,MAAM,MAAM,CAAC;AAC9C,YAAI,aAAa,cAAc,gBAAgB;AAC7C,eAAK,QAAQ;AAAA,YACX;AAAA,YACA,EAAE,OAAO,WAAW,OAAO,UAAU;AAAA,UACvC;AAAA,QACF,OAAO;AACL,sBAAY;AAAA,QACd;AACA,oBAAY,UAAU,MAAM,GAAG,GAAG;AAAA,MACpC;AAAA,IACF;AAEA,SAAK,OAAO;AAAA,MACV,OAAO;AAAA,MACP,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,WAAW;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,MAAkF;AAC9F,SAAK,OAAO,EAAE,GAAG,KAAK,MAAM,GAAG,KAAK;AACpC,eAAW,UAAU,KAAK,SAAS;AACjC,aAAO,cAAc,IAAI;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,WAAW,GAA0B;AACnC,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AAAA,EAEA,OAAO,SAAyE;AAC9E,UAAM,EAAE,cAAc,4BAA4B,IAAI,WAAW,CAAC;AAClE,UAAM,SAAS,IAAI,iBAAiB,MAAM,EAAE,GAAG,KAAK,KAAK,GAAG,WAAW;AACvE,SAAK,QAAQ,IAAI,MAAM;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,SAAqC;AACnD,QAAI,UAAU,KAAK,KAAK;AACxB,QAAI,QAAQ,WAAW,SAAS,KAAK,QAAQ,WAAW,UAAU,GAAG;AACnE,gBAAU,QAAQ,QAAQ,QAAQ,IAAI;AAAA,IACxC;AAEA,UAAM,QAAQ,MAAM,kBAAkB,KAAK,KAAK,QAAQ,KAAK,KAAK,SAAS;AAC3E,UAAM,MAAM,GAAG,OAAO;AACtB,UAAM,UAAU,EAAE,eAAe,UAAU,KAAK,GAAG;AAEnD,UAAM,SAAS;AAAA,MACb,MAAM;AAAA,MACN,aAAa,OAAO,KAAK,KAAK,UAAU;AAAA,MACxC,UAAU,KAAK,KAAK;AAAA,MACpB,OAAO,KAAK,KAAK;AAAA,IACnB;AAEA,QAAI,KAAK,KAAK,MAAO,QAAO,QAAQ,KAAK,KAAK;AAC9C,QAAI,KAAK,KAAK,MAAO,QAAO,QAAQ,KAAK,KAAK;AAC9C,QAAI,KAAK,KAAK,SAAU,QAAO,WAAW,KAAK,KAAK;AAEpD,UAAM,SAAS,MAAM,UAAU,KAAK,SAAS,OAAO;AACpD,WAAO,KAAK,KAAK,UAAU,MAAM,CAAC;AAClC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,IAAe;AAC3B,UAAM,GAAG,MAAM;AAAA,EACjB;AAAA,EAEA,MAAM,QAAQ;AACZ,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,OAAO,MAAM;AAAA,IACrB;AACA,SAAK,QAAQ,MAAM;AAAA,EACrB;AACF;AAEO,MAAM,yBAAmD,qBAAqB;AAAA,EAC3E;AAAA,EACA;AAAA,EACA;AAAA,EAER,UAAU,IAAI;AAAA,EAEd,YAAY,KAAkB,MAAmC,aAAgC;AAC/F,UAAM,KAAK,WAAW;AACtB,SAAK,OAAO;AACZ,SAAK,MAAM;AACX,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,MAAkF;AAC9F,SAAK,OAAO,EAAE,GAAG,KAAK,MAAM,GAAG,KAAK;AAAA,EACtC;AAAA,EAEA,MAAgB,MAAqB;AACnC,QAAI,KAAuB;AAC3B,QAAI,UAAU;AACd,QAAI,gBAAgB;AACpB,QAAI;AAEJ,UAAM,sBAAsB,IAAI,cAAc,kBAAkB,EAAE,OAAO;AACzE,UAAM,eAAe,oBAAoC;AACzD,UAAM,YAAY,UAAU,cAAc;AAE1C,UAAM,kBAAkB,MAAM;AAC5B,UAAI,QAAS;AACb,gBAAU;AACV,0BAAoB,MAAM;AAC1B,mBAAa,MAAM;AACnB,+BAAI;AACJ,+BAAI;AAAA,IACN;AAEA,UAAM,kBAAkB,OAAO,UAA0B;AACvD,YAAM,iBAAiB,MAAM,qBAAqB,WAAW,KAAK;AAClE,UAAI,CAAC,MAAM,GAAG,eAAe,UAAU,MAAM;AAC3C,aAAK,QAAQ,KAAK,uDAAuD;AACzE;AAAA,MACF;AACA,SAAG,KAAK,KAAK,UAAU,cAAc,CAAC;AAAA,IACxC;AAEA,UAAM,gBAAgB,CAAC,WAAmB,UAAmB;AAC3D,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,MAAM,CAAC;AAChE,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,UAAM,kBAAkB,YAAY;AAClC,uBAAiB,QAAQ,KAAK,OAAO;AACnC,YAAI,KAAK,gBAAgB,OAAO,QAAS;AACzC,YAAI,SAAS,iBAAiB,gBAAgB;AAC5C,8BAAoB,MAAM;AAC1B;AAAA,QACF;AACA,4BAAoB,SAAS,IAAI;AAAA,MACnC;AACA,0BAAoB,SAAS;AAAA,IAC/B;AAEA,UAAM,2BAA2B,YAAY;AAC3C,uBAAiB,MAAM,qBAAqB;AAC1C,YAAI,KAAK,gBAAgB,OAAO,QAAS;AAEzC,wBAAgB;AAAA,UACd,MAAM;AAAA,UACN,YAAY,GAAG,QAAQ;AAAA,QACzB,CAAC;AAAA,MACH;AAEA,sBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAAA,IAC3C;AAEA,UAAM,uBAAuB,OAAOA,QAAkB;AACpD,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,aAAK,gBAAgB,OAAO,iBAAiB,SAAS,MAAM;AAC1D,0BAAgB;AAChB,iBAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,QAClD,CAAC;AAED,QAAAA,IAAG,GAAG,WAAW,OAAO,SAAS;AAC/B,gBAAM,YAAY,KAAK,MAAM,KAAK,SAAS,CAAC;AAC5C,gBAAM,iBAAiB,qBAAqB,MAAM,SAAS;AAC3D,uBAAa,MAAM,cAAc;AAAA,QACnC,CAAC;AAED,QAAAA,IAAG,GAAG,SAAS,CAAC,MAAM;AACpB,eAAK,QAAQ,MAAM,EAAE,OAAO,EAAE,GAAG,iBAAiB;AAClD,0BAAgB;AAChB,iBAAO,CAAC;AAAA,QACV,CAAC;AAED,QAAAA,IAAG,GAAG,SAAS,MAAM;AACnB,0BAAgB;AAEhB,cAAI,CAAC,QAAS,QAAO,KAAK,QAAQ,MAAM,+BAA+B;AACvE,cAAI,cAAe,QAAO,QAAQ;AAElC;AAAA,YACE,IAAI,eAAe;AAAA,cACjB,SAAS;AAAA,cACT,SAAS,EAAE,UAAU;AAAA,YACvB,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,UAAM,iBAAiB,YAAY;AACjC,UAAI,mBAAkC;AAEtC,YAAM,UAAU,IAAI,gBAAgB,KAAK,KAAK,YAAY,YAAY;AACtE,YAAM,oBAAoB,aAAa,OAAO;AAC9C,YAAM,SAAS,kBAAkB,UAAU;AAE3C,UAAI;AACF,eAAO,CAAC,KAAK,UAAU,CAAC,KAAK,gBAAgB,OAAO,SAAS;AAC3D,gBAAM,SAAS,MAAM,OAAO,KAAK;AACjC,cAAI,KAAK,gBAAgB,OAAO,QAAS;AACzC,cAAI,OAAO,KAAM;AAEjB,gBAAM,cAAc,OAAO;AAC3B,kBAAQ,YAAY,MAAM;AAAA,YACxB,KAAK;AACH,iCAAmB,YAAY;AAC/B;AAAA,YACF,KAAK;AACH,oBAAM,aAAa,IAAI,UAAU,OAAO,KAAK,YAAY,OAAO,QAAQ,CAAC;AACzE,yBAAW,SAAS,QAAQ,MAAM,WAAW,MAAM,GAAG;AACpD,8BAAc,kBAAmB,KAAK;AACtC,4BAAY;AAAA,cACd;AACA;AAAA,YACF,KAAK;AACH,8BAAgB;AAChB,yBAAW,SAAS,QAAQ,MAAM,GAAG;AACnC,8BAAc,kBAAmB,KAAK;AACtC,4BAAY;AAAA,cACd;AACA,4BAAc,kBAAmB,IAAI;AACrC,mBAAK,MAAM,IAAI,iBAAiB,aAAa;AAC7C;AAAA,YACF,KAAK;AACH,8BAAgB;AAChB;AAAA,YACF,KAAK;AACH,mBAAK,QAAQ;AAAA,gBACX,EAAE,YAAY;AAAA,gBACd;AAAA,cACF;AACA,8BAAgB;AAChB,oBAAM,IAAI,SAAS,+BAA+B,YAAY,OAAO,EAAE;AAAA,YACzE;AACE,mBAAK,QAAQ,KAAK,yBAAyB,WAAW;AACtD;AAAA,UACJ;AAAA,QACF;AAAA,MACF,UAAE;AACA,eAAO,YAAY;AACnB,YAAI;AACF,gBAAM,kBAAkB,OAAO;AAAA,QACjC,SAAS,GAAG;AACV,eAAK,QAAQ,MAAM,kEAAkE,CAAC;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,WAAK,MAAM,KAAK,IAAI,UAAU,KAAK,YAAY,SAAS;AAExD,YAAM,QAAQ,IAAI;AAAA,QAChB,gBAAgB;AAAA,QAChB,yBAAyB;AAAA,QACzB,qBAAqB,EAAE;AAAA,QACvB,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,GAAG;AACV,WAAK,QAAQ,MAAM,6BAA6B,EAAE,OAAO,EAAE,CAAC;AAAA,IAC9D,UAAE;AACA,sBAAgB;AAAA,IAClB;AAAA,EACF;AACF;","names":["ws"]}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var utils_exports = {};
20
+ __export(utils_exports, {
21
+ connectWs: () => connectWs,
22
+ createAccessToken: () => createAccessToken
23
+ });
24
+ module.exports = __toCommonJS(utils_exports);
25
+ var import_livekit_server_sdk = require("livekit-server-sdk");
26
+ var import_ws = require("ws");
27
+ var import__ = require("../index.cjs");
28
+ async function createAccessToken(apiKey, apiSecret, ttl = 600) {
29
+ const token = new import_livekit_server_sdk.AccessToken(apiKey, apiSecret, { identity: "agent", ttl });
30
+ token.addInferenceGrant({ perform: true });
31
+ return await token.toJwt();
32
+ }
33
+ async function connectWs(url, headers, timeoutMs) {
34
+ return new Promise((resolve, reject) => {
35
+ const socket = new import_ws.WebSocket(url, { headers });
36
+ const timeout = setTimeout(() => {
37
+ reject(new import__.APIConnectionError({ message: "Timeout connecting to LiveKit WebSocket" }));
38
+ }, timeoutMs);
39
+ const onOpen = () => {
40
+ clearTimeout(timeout);
41
+ resolve(socket);
42
+ };
43
+ const onError = (err) => {
44
+ clearTimeout(timeout);
45
+ if (err && typeof err === "object" && "code" in err && err.code === 429) {
46
+ reject(
47
+ new import__.APIStatusError({
48
+ message: "LiveKit gateway quota exceeded",
49
+ options: { statusCode: 429 }
50
+ })
51
+ );
52
+ } else {
53
+ reject(new import__.APIConnectionError({ message: "Error connecting to LiveKit WebSocket" }));
54
+ }
55
+ };
56
+ const onClose = (code) => {
57
+ clearTimeout(timeout);
58
+ if (code !== 1e3) {
59
+ reject(
60
+ new import__.APIConnectionError({
61
+ message: "Connection closed unexpectedly"
62
+ })
63
+ );
64
+ }
65
+ };
66
+ socket.once("open", onOpen);
67
+ socket.once("error", onError);
68
+ socket.once("close", onClose);
69
+ });
70
+ }
71
+ // Annotate the CommonJS export names for ESM import in node:
72
+ 0 && (module.exports = {
73
+ connectWs,
74
+ createAccessToken
75
+ });
76
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inference/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AccessToken } from 'livekit-server-sdk';\nimport { WebSocket } from 'ws';\nimport { APIConnectionError, APIStatusError } from '../index.js';\n\nexport type AnyModels = string & NonNullable<unknown>;\n\nexport async function createAccessToken(\n apiKey: string,\n apiSecret: string,\n ttl: number = 600,\n): Promise<string> {\n const token = new AccessToken(apiKey, apiSecret, { identity: 'agent', ttl });\n token.addInferenceGrant({ perform: true });\n\n return await token.toJwt();\n}\n\nexport async function connectWs(\n url: string,\n headers: Record<string, string>,\n timeoutMs: number,\n): Promise<WebSocket> {\n return new Promise<WebSocket>((resolve, reject) => {\n const socket = new WebSocket(url, { headers: headers });\n\n const timeout = setTimeout(() => {\n reject(new APIConnectionError({ message: 'Timeout connecting to LiveKit WebSocket' }));\n }, timeoutMs);\n\n const onOpen = () => {\n clearTimeout(timeout);\n resolve(socket);\n };\n\n const onError = (err: unknown) => {\n clearTimeout(timeout);\n if (err && typeof err === 'object' && 'code' in err && (err as any).code === 429) {\n reject(\n new APIStatusError({\n message: 'LiveKit gateway quota exceeded',\n options: { statusCode: 429 },\n }),\n );\n } else {\n reject(new APIConnectionError({ message: 'Error connecting to LiveKit WebSocket' }));\n }\n };\n\n const onClose = (code: number) => {\n clearTimeout(timeout);\n if (code !== 1000) {\n reject(\n new APIConnectionError({\n message: 'Connection closed unexpectedly',\n }),\n );\n }\n };\n socket.once('open', onOpen);\n socket.once('error', onError);\n socket.once('close', onClose);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,gCAA4B;AAC5B,gBAA0B;AAC1B,eAAmD;AAInD,eAAsB,kBACpB,QACA,WACA,MAAc,KACG;AACjB,QAAM,QAAQ,IAAI,sCAAY,QAAQ,WAAW,EAAE,UAAU,SAAS,IAAI,CAAC;AAC3E,QAAM,kBAAkB,EAAE,SAAS,KAAK,CAAC;AAEzC,SAAO,MAAM,MAAM,MAAM;AAC3B;AAEA,eAAsB,UACpB,KACA,SACA,WACoB;AACpB,SAAO,IAAI,QAAmB,CAAC,SAAS,WAAW;AACjD,UAAM,SAAS,IAAI,oBAAU,KAAK,EAAE,QAAiB,CAAC;AAEtD,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,IAAI,4BAAmB,EAAE,SAAS,0CAA0C,CAAC,CAAC;AAAA,IACvF,GAAG,SAAS;AAEZ,UAAM,SAAS,MAAM;AACnB,mBAAa,OAAO;AACpB,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,UAAU,CAAC,QAAiB;AAChC,mBAAa,OAAO;AACpB,UAAI,OAAO,OAAO,QAAQ,YAAY,UAAU,OAAQ,IAAY,SAAS,KAAK;AAChF;AAAA,UACE,IAAI,wBAAe;AAAA,YACjB,SAAS;AAAA,YACT,SAAS,EAAE,YAAY,IAAI;AAAA,UAC7B,CAAC;AAAA,QACH;AAAA,MACF,OAAO;AACL,eAAO,IAAI,4BAAmB,EAAE,SAAS,wCAAwC,CAAC,CAAC;AAAA,MACrF;AAAA,IACF;AAEA,UAAM,UAAU,CAAC,SAAiB;AAChC,mBAAa,OAAO;AACpB,UAAI,SAAS,KAAM;AACjB;AAAA,UACE,IAAI,4BAAmB;AAAA,YACrB,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACA,WAAO,KAAK,QAAQ,MAAM;AAC1B,WAAO,KAAK,SAAS,OAAO;AAC5B,WAAO,KAAK,SAAS,OAAO;AAAA,EAC9B,CAAC;AACH;","names":[]}
@@ -0,0 +1,5 @@
1
+ import { WebSocket } from 'ws';
2
+ export type AnyModels = string & NonNullable<unknown>;
3
+ export declare function createAccessToken(apiKey: string, apiSecret: string, ttl?: number): Promise<string>;
4
+ export declare function connectWs(url: string, headers: Record<string, string>, timeoutMs: number): Promise<WebSocket>;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { WebSocket } from 'ws';
2
+ export type AnyModels = string & NonNullable<unknown>;
3
+ export declare function createAccessToken(apiKey: string, apiSecret: string, ttl?: number): Promise<string>;
4
+ export declare function connectWs(url: string, headers: Record<string, string>, timeoutMs: number): Promise<WebSocket>;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/inference/utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAG/B,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAEtD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,SAAS,CAAC,CAyCpB"}