@livekit/agents 1.0.14 → 1.0.16
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/cli.cjs +12 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +3 -3
- package/dist/cli.d.ts +3 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -13
- package/dist/cli.js.map +1 -1
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +1 -1
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +2 -1
- package/dist/inference/tts.d.ts +2 -1
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +1 -5
- package/dist/inference/tts.js.map +1 -1
- package/dist/llm/chat_context.cjs +78 -0
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.cts +16 -0
- package/dist/llm/chat_context.d.ts +16 -0
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +78 -0
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/chat_context.test.cjs +531 -0
- package/dist/llm/chat_context.test.cjs.map +1 -1
- package/dist/llm/chat_context.test.js +531 -0
- package/dist/llm/chat_context.test.js.map +1 -1
- package/dist/llm/tool_context.cjs +43 -2
- package/dist/llm/tool_context.cjs.map +1 -1
- package/dist/llm/tool_context.d.cts +39 -11
- package/dist/llm/tool_context.d.ts +39 -11
- package/dist/llm/tool_context.d.ts.map +1 -1
- package/dist/llm/tool_context.js +42 -3
- package/dist/llm/tool_context.js.map +1 -1
- package/dist/llm/tool_context.test.cjs +197 -0
- package/dist/llm/tool_context.test.cjs.map +1 -1
- package/dist/llm/tool_context.test.js +175 -0
- package/dist/llm/tool_context.test.js.map +1 -1
- package/dist/llm/utils.cjs +17 -11
- package/dist/llm/utils.cjs.map +1 -1
- package/dist/llm/utils.d.cts +1 -2
- package/dist/llm/utils.d.ts +1 -2
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +17 -11
- package/dist/llm/utils.js.map +1 -1
- package/dist/llm/zod-utils.cjs +99 -0
- package/dist/llm/zod-utils.cjs.map +1 -0
- package/dist/llm/zod-utils.d.cts +65 -0
- package/dist/llm/zod-utils.d.ts +65 -0
- package/dist/llm/zod-utils.d.ts.map +1 -0
- package/dist/llm/zod-utils.js +61 -0
- package/dist/llm/zod-utils.js.map +1 -0
- package/dist/llm/zod-utils.test.cjs +389 -0
- package/dist/llm/zod-utils.test.cjs.map +1 -0
- package/dist/llm/zod-utils.test.js +372 -0
- package/dist/llm/zod-utils.test.js.map +1 -0
- package/dist/metrics/base.cjs.map +1 -1
- package/dist/metrics/base.d.cts +7 -0
- package/dist/metrics/base.d.ts +7 -0
- package/dist/metrics/base.d.ts.map +1 -1
- package/dist/stt/stt.cjs +1 -0
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +7 -1
- package/dist/stt/stt.d.ts +7 -1
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +1 -0
- package/dist/stt/stt.js.map +1 -1
- package/dist/vad.cjs +16 -0
- package/dist/vad.cjs.map +1 -1
- package/dist/vad.d.cts +6 -0
- package/dist/vad.d.ts +6 -0
- package/dist/vad.d.ts.map +1 -1
- package/dist/vad.js +16 -0
- package/dist/vad.js.map +1 -1
- package/dist/voice/agent_activity.cjs +83 -8
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +6 -2
- package/dist/voice/agent_activity.d.ts +6 -2
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +83 -8
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +3 -2
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +2 -1
- package/dist/voice/agent_session.d.ts +2 -1
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +3 -2
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +138 -16
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +11 -0
- package/dist/voice/audio_recognition.d.ts +11 -0
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +138 -16
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/generation.cjs +8 -3
- package/dist/voice/generation.cjs.map +1 -1
- package/dist/voice/generation.d.ts.map +1 -1
- package/dist/voice/generation.js +8 -3
- package/dist/voice/generation.js.map +1 -1
- package/dist/voice/room_io/_input.cjs.map +1 -1
- package/dist/voice/room_io/_input.d.ts.map +1 -1
- package/dist/voice/room_io/_input.js +0 -1
- package/dist/voice/room_io/_input.js.map +1 -1
- package/dist/worker.cjs +17 -11
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +16 -9
- package/dist/worker.d.ts +16 -9
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +16 -12
- package/dist/worker.js.map +1 -1
- package/package.json +5 -4
- package/src/cli.ts +17 -17
- package/src/inference/stt.ts +2 -1
- package/src/inference/tts.ts +2 -5
- package/src/llm/__snapshots__/zod-utils.test.ts.snap +341 -0
- package/src/llm/chat_context.test.ts +607 -0
- package/src/llm/chat_context.ts +106 -0
- package/src/llm/tool_context.test.ts +210 -1
- package/src/llm/tool_context.ts +101 -17
- package/src/llm/utils.ts +18 -15
- package/src/llm/zod-utils.test.ts +476 -0
- package/src/llm/zod-utils.ts +144 -0
- package/src/metrics/base.ts +7 -0
- package/src/stt/stt.ts +6 -0
- package/src/vad.ts +18 -0
- package/src/voice/agent_activity.ts +119 -9
- package/src/voice/agent_session.ts +3 -1
- package/src/voice/audio_recognition.ts +235 -57
- package/src/voice/generation.ts +8 -3
- package/src/voice/room_io/_input.ts +1 -1
- package/src/worker.ts +29 -18
|
@@ -1 +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 AnyString, connectWs, createAccessToken } from './utils.js';\n\nexport type CartesiaModels =\n | 'cartesia'\n | 'cartesia/sonic'\n | 'cartesia/sonic-2'\n | 'cartesia/sonic-turbo';\n\nexport type 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 RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';\n\nexport type InworldModels = 'inworld' | 'inworld/inworld-tts-1';\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\ntype _TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels;\n\nexport type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyString;\n\nexport type ModelWithVoice = `${_TTSModels}:${string}` | TTSModels;\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 modelOptions: TTSOptions<TModel>;\n}\n\n/**\n * Livekit Cloud Inference TTS\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 modelOptions?: 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 modelOptions = {} 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 modelOptions,\n };\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n static fromModelString(modelString: string): TTS<AnyString> {\n if (modelString.includes(':')) {\n const [model, voice] = modelString.split(':') as [TTSModels, string];\n return new TTS({ model, voice });\n }\n return new TTS({ model: modelString });\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.modelOptions,\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":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,gBAA0B;AAC1B,wBAAyC;AACzC,mBAAgC;AAChC,iBAAoB;AACpB,4BAAoC;AACpC,sBAAuC;AACvC,iBAIO;AACP,mBAAoE;AACpE,mBAA0B;AAC1B,wBAMO;AACP,IAAAA,gBAA6D;AAoD7D,MAAM,mBAAgC;AACtC,MAAM,sBAAsB;AAC5B,MAAM,mBAAmB;AACzB,MAAM,eAAe;AACrB,MAAM,mBAAmB;AAiBlB,MAAM,YAAsC,WAAAC,IAAQ;AAAA,EACjD;AAAA,EACA,UAAyC,oBAAI,IAAI;AAAA,EAEzD,cAAU,gBAAI;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,eAAe,CAAC;AAAA,IAClB,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,OAAO,gBAAgB,aAAqC;AAC1D,QAAI,YAAY,SAAS,GAAG,GAAG;AAC7B,YAAM,CAAC,OAAO,KAAK,IAAI,YAAY,MAAM,GAAG;AAC5C,aAAO,IAAI,IAAI,EAAE,OAAO,MAAM,CAAC;AAAA,IACjC;AACA,WAAO,IAAI,IAAI,EAAE,OAAO,YAAY,CAAC;AAAA,EACvC;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,yCAA4B,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,UAAM,iCAAkB,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,UAAM,yBAAU,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,WAAAC,iBAAqB;AAAA,EAC3E;AAAA,EACA;AAAA,EACA;AAAA,EAER,cAAU,gBAAI;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,gBAAAC,MAAc,kBAAkB,EAAE,OAAO;AACzE,UAAM,mBAAe,2CAAoC;AACzD,UAAM,gBAAY,wBAAU,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,uCAAqB,WAAW,KAAK;AAClE,UAAI,CAAC,MAAM,GAAG,eAAe,oBAAU,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,OAAOC,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,uCAAqB,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,iCAAe;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,6BAAgB,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,2BAAS,+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":["import_utils","BaseTTS","BaseSynthesizeStream","tokenizeBasic","ws"]}
|
|
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 type { ChunkedStream } from '../tts/index.js';\nimport { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } 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 AnyString, connectWs, createAccessToken } from './utils.js';\n\nexport type CartesiaModels =\n | 'cartesia'\n | 'cartesia/sonic'\n | 'cartesia/sonic-2'\n | 'cartesia/sonic-turbo';\n\nexport type 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 RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';\n\nexport type InworldModels = 'inworld' | 'inworld/inworld-tts-1';\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\ntype _TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels;\n\nexport type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyString;\n\nexport type ModelWithVoice = `${_TTSModels}:${string}` | TTSModels;\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 modelOptions: TTSOptions<TModel>;\n}\n\n/**\n * Livekit Cloud Inference TTS\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 modelOptions?: 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 modelOptions = {} 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 modelOptions,\n };\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n static fromModelString(modelString: string): TTS<AnyString> {\n if (modelString.includes(':')) {\n const [model, voice] = modelString.split(':') as [TTSModels, string];\n return new TTS({ model, voice });\n }\n return new TTS({ model: modelString });\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.modelOptions,\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"]}
|
package/dist/inference/tts.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
|
-
import {
|
|
2
|
+
import type { ChunkedStream } from '../tts/index.js';
|
|
3
|
+
import { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } from '../tts/index.js';
|
|
3
4
|
import { type APIConnectOptions } from '../types.js';
|
|
4
5
|
import { type AnyString } from './utils.js';
|
|
5
6
|
export type CartesiaModels = 'cartesia' | 'cartesia/sonic' | 'cartesia/sonic-2' | 'cartesia/sonic-turbo';
|
package/dist/inference/tts.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
|
-
import {
|
|
2
|
+
import type { ChunkedStream } from '../tts/index.js';
|
|
3
|
+
import { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } from '../tts/index.js';
|
|
3
4
|
import { type APIConnectOptions } from '../types.js';
|
|
4
5
|
import { type AnyString } from './utils.js';
|
|
5
6
|
export type CartesiaModels = 'cartesia' | 'cartesia/sonic' | 'cartesia/sonic-2' | 'cartesia/sonic-turbo';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/inference/tts.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAM/B,OAAO,
|
|
1
|
+
{"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/inference/tts.ts"],"names":[],"mappings":"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"}
|
package/dist/inference/tts.js
CHANGED
|
@@ -4,11 +4,7 @@ import { AudioByteStream } from "../audio.js";
|
|
|
4
4
|
import { log } from "../log.js";
|
|
5
5
|
import { createStreamChannel } from "../stream/stream_channel.js";
|
|
6
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";
|
|
7
|
+
import { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } from "../tts/index.js";
|
|
12
8
|
import { DEFAULT_API_CONNECT_OPTIONS } from "../types.js";
|
|
13
9
|
import { shortuuid } from "../utils.js";
|
|
14
10
|
import {
|
|
@@ -1 +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 AnyString, connectWs, createAccessToken } from './utils.js';\n\nexport type CartesiaModels =\n | 'cartesia'\n | 'cartesia/sonic'\n | 'cartesia/sonic-2'\n | 'cartesia/sonic-turbo';\n\nexport type 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 RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';\n\nexport type InworldModels = 'inworld' | 'inworld/inworld-tts-1';\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\ntype _TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels;\n\nexport type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyString;\n\nexport type ModelWithVoice = `${_TTSModels}:${string}` | TTSModels;\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 modelOptions: TTSOptions<TModel>;\n}\n\n/**\n * Livekit Cloud Inference TTS\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 modelOptions?: 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 modelOptions = {} 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 modelOptions,\n };\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n static fromModelString(modelString: string): TTS<AnyString> {\n if (modelString.includes(':')) {\n const [model, voice] = modelString.split(':') as [TTSModels, string];\n return new TTS({ model, voice });\n }\n return new TTS({ model: modelString });\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.modelOptions,\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":["ws"]}
|
|
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 type { ChunkedStream } from '../tts/index.js';\nimport { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } 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 AnyString, connectWs, createAccessToken } from './utils.js';\n\nexport type CartesiaModels =\n | 'cartesia'\n | 'cartesia/sonic'\n | 'cartesia/sonic-2'\n | 'cartesia/sonic-turbo';\n\nexport type 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 RimeModels = 'rime' | 'rime/mist' | 'rime/mistv2' | 'rime/arcana';\n\nexport type InworldModels = 'inworld' | 'inworld/inworld-tts-1';\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\ntype _TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels;\n\nexport type TTSModels = CartesiaModels | ElevenlabsModels | RimeModels | InworldModels | AnyString;\n\nexport type ModelWithVoice = `${_TTSModels}:${string}` | TTSModels;\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 modelOptions: TTSOptions<TModel>;\n}\n\n/**\n * Livekit Cloud Inference TTS\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 modelOptions?: 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 modelOptions = {} 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 modelOptions,\n };\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n static fromModelString(modelString: string): TTS<AnyString> {\n if (modelString.includes(':')) {\n const [model, voice] = modelString.split(':') as [TTSModels, string];\n return new TTS({ model, voice });\n }\n return new TTS({ model: modelString });\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.modelOptions,\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":["ws"]}
|
|
@@ -344,6 +344,84 @@ class ChatContext {
|
|
|
344
344
|
}
|
|
345
345
|
return 0;
|
|
346
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Return true if `other` has the same sequence of items with matching
|
|
349
|
+
* essential fields (IDs, types, and payload) as this context.
|
|
350
|
+
*
|
|
351
|
+
* Comparison rules:
|
|
352
|
+
* - Messages: compares the full `content` list, `role` and `interrupted`.
|
|
353
|
+
* - Function calls: compares `name`, `callId`, and `args`.
|
|
354
|
+
* - Function call outputs: compares `name`, `callId`, `output`, and `isError`.
|
|
355
|
+
*
|
|
356
|
+
* Does not consider timestamps or other metadata.
|
|
357
|
+
*/
|
|
358
|
+
isEquivalent(other) {
|
|
359
|
+
if (this === other) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
if (this.items.length !== other.items.length) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
366
|
+
const a = this.items[i];
|
|
367
|
+
const b = other.items[i];
|
|
368
|
+
if (a.id !== b.id || a.type !== b.type) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
if (a.type === "message" && b.type === "message") {
|
|
372
|
+
if (a.role !== b.role || a.interrupted !== b.interrupted || !this.compareContent(a.content, b.content)) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
} else if (a.type === "function_call" && b.type === "function_call") {
|
|
376
|
+
if (a.name !== b.name || a.callId !== b.callId || a.args !== b.args) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
} else if (a.type === "function_call_output" && b.type === "function_call_output") {
|
|
380
|
+
if (a.name !== b.name || a.callId !== b.callId || a.output !== b.output || a.isError !== b.isError) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Compare two content arrays for equality.
|
|
389
|
+
*/
|
|
390
|
+
compareContent(a, b) {
|
|
391
|
+
if (a.length !== b.length) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
for (let i = 0; i < a.length; i++) {
|
|
395
|
+
const contentA = a[i];
|
|
396
|
+
const contentB = b[i];
|
|
397
|
+
if (typeof contentA === "string" && typeof contentB === "string") {
|
|
398
|
+
if (contentA !== contentB) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (typeof contentA !== typeof contentB) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
if (typeof contentA === "object" && typeof contentB === "object") {
|
|
407
|
+
if (contentA.type === "image_content" && contentB.type === "image_content") {
|
|
408
|
+
if (contentA.id !== contentB.id || contentA.image !== contentB.image || contentA.inferenceDetail !== contentB.inferenceDetail || contentA.inferenceWidth !== contentB.inferenceWidth || contentA.inferenceHeight !== contentB.inferenceHeight || contentA.mimeType !== contentB.mimeType) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
} else if (contentA.type === "audio_content" && contentB.type === "audio_content") {
|
|
412
|
+
if (contentA.frame.length !== contentB.frame.length) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
if (contentA.transcript !== contentB.transcript) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
347
425
|
/**
|
|
348
426
|
* Indicates whether the context is read-only
|
|
349
427
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/llm/chat_context.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, VideoFrame } from '@livekit/rtc-node';\nimport { createImmutableArray, shortuuid } from '../utils.js';\nimport { type ProviderFormat, toChatCtx } from './provider_format/index.js';\nimport type { JSONObject, JSONValue, ToolContext } from './tool_context.js';\n\nexport type ChatRole = 'developer' | 'system' | 'user' | 'assistant';\nexport interface ImageContent {\n id: string;\n\n type: 'image_content';\n\n /**\n * Either a string URL or a VideoFrame object.\n */\n image: string | VideoFrame;\n\n inferenceDetail: 'auto' | 'high' | 'low';\n\n inferenceWidth?: number;\n\n inferenceHeight?: number;\n\n mimeType?: string;\n\n _cache: Record<any, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n}\n\nexport interface AudioContent {\n type: 'audio_content';\n\n frame: AudioFrame[];\n\n transcript?: string;\n}\n\nexport type ChatContent = ImageContent | AudioContent | string;\n\nexport function createImageContent(params: {\n image: string | VideoFrame;\n id?: string;\n inferenceDetail?: 'auto' | 'high' | 'low';\n inferenceWidth?: number;\n inferenceHeight?: number;\n mimeType?: string;\n}): ImageContent {\n const {\n image,\n id = shortuuid('img_'),\n inferenceDetail = 'auto',\n inferenceWidth,\n inferenceHeight,\n mimeType,\n } = params;\n\n return {\n id,\n type: 'image_content',\n image,\n inferenceDetail,\n inferenceWidth,\n inferenceHeight,\n mimeType,\n _cache: {},\n };\n}\n\nexport function createAudioContent(params: {\n frame: AudioFrame[];\n transcript?: string;\n}): AudioContent {\n const { frame, transcript } = params;\n\n return {\n type: 'audio_content',\n frame,\n transcript,\n };\n}\n\nexport class ChatMessage {\n readonly id: string;\n\n readonly type = 'message' as const;\n\n readonly role: ChatRole;\n\n content: ChatContent[];\n\n interrupted: boolean;\n\n hash?: Uint8Array;\n\n createdAt: number;\n\n constructor(params: {\n role: ChatRole;\n content: ChatContent[] | string;\n id?: string;\n interrupted?: boolean;\n createdAt?: number;\n }) {\n const {\n role,\n content,\n id = shortuuid('item_'),\n interrupted = false,\n createdAt = Date.now(),\n } = params;\n this.id = id;\n this.role = role;\n this.content = Array.isArray(content) ? content : [content];\n this.interrupted = interrupted;\n this.createdAt = createdAt;\n }\n\n static create(params: {\n role: ChatRole;\n content: ChatContent[] | string;\n id?: string;\n interrupted?: boolean;\n createdAt?: number;\n }) {\n return new ChatMessage(params);\n }\n\n /**\n * Returns a single string with all text parts of the message joined by new\n * lines. If no string content is present, returns `null`.\n */\n get textContent(): string | undefined {\n const parts = this.content.filter((c): c is string => typeof c === 'string');\n return parts.length > 0 ? parts.join('\\n') : undefined;\n }\n\n toJSONContent(): JSONValue[] {\n return this.content.map((c) => {\n if (typeof c === 'string') {\n return c as JSONValue;\n } else if (c.type === 'image_content') {\n return {\n id: c.id,\n type: c.type,\n image: c.image,\n inferenceDetail: c.inferenceDetail,\n inferenceWidth: c.inferenceWidth,\n inferenceHeight: c.inferenceHeight,\n mimeType: c.mimeType,\n } as JSONObject;\n } else {\n return {\n type: c.type,\n transcript: c.transcript,\n } as JSONObject;\n }\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n toJSON(excludeTimestamp: boolean = false): JSONValue {\n const result: JSONValue = {\n id: this.id,\n type: this.type,\n role: this.role,\n content: this.toJSONContent(),\n interrupted: this.interrupted,\n };\n\n if (!excludeTimestamp) {\n result.createdAt = this.createdAt;\n }\n\n return result;\n }\n}\n\nexport class FunctionCall {\n readonly id: string;\n\n readonly type = 'function_call' as const;\n\n callId: string;\n\n args: string;\n\n name: string;\n\n createdAt: number;\n\n constructor(params: {\n callId: string;\n name: string;\n args: string;\n id?: string;\n createdAt?: number;\n }) {\n const { callId, name, args, id = shortuuid('item_'), createdAt = Date.now() } = params;\n this.id = id;\n this.callId = callId;\n this.args = args;\n this.name = name;\n this.createdAt = createdAt;\n }\n\n static create(params: {\n callId: string;\n name: string;\n args: string;\n id?: string;\n createdAt?: number;\n }) {\n return new FunctionCall(params);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n toJSON(excludeTimestamp: boolean = false): JSONValue {\n const result: JSONValue = {\n id: this.id,\n type: this.type,\n callId: this.callId,\n name: this.name,\n args: this.args,\n };\n\n if (!excludeTimestamp) {\n result.createdAt = this.createdAt;\n }\n\n return result;\n }\n}\n\nexport class FunctionCallOutput {\n readonly id: string;\n\n readonly type = 'function_call_output' as const;\n\n name = '';\n\n callId: string;\n\n output: string;\n\n isError: boolean;\n\n createdAt: number;\n\n constructor(params: {\n callId: string;\n output: string;\n isError: boolean;\n id?: string;\n createdAt?: number;\n name?: string;\n }) {\n const {\n callId,\n output,\n isError,\n id = shortuuid('item_'),\n createdAt = Date.now(),\n name = '',\n } = params;\n this.id = id;\n this.callId = callId;\n this.output = output;\n this.isError = isError;\n this.name = name;\n this.createdAt = createdAt;\n }\n\n static create(params: {\n callId: string;\n output: string;\n isError: boolean;\n id?: string;\n createdAt?: number;\n name?: string;\n }) {\n return new FunctionCallOutput(params);\n }\n\n toJSON(excludeTimestamp: boolean = false): JSONValue {\n const result: JSONValue = {\n id: this.id,\n type: this.type,\n name: this.name,\n callId: this.callId,\n output: this.output,\n isError: this.isError,\n };\n\n if (!excludeTimestamp) {\n result.createdAt = this.createdAt;\n }\n\n return result;\n }\n}\n\nexport type ChatItem = ChatMessage | FunctionCall | FunctionCallOutput;\n\nexport class ChatContext {\n protected _items: ChatItem[];\n\n constructor(items?: ChatItem[]) {\n this._items = items ? items : [];\n }\n\n static empty(): ChatContext {\n return new ChatContext([]);\n }\n\n get items(): ChatItem[] {\n return this._items;\n }\n\n set items(items: ChatItem[]) {\n this._items = items;\n }\n\n /**\n * Add a new message to the context and return it.\n */\n addMessage(params: {\n role: ChatRole;\n content: ChatContent[] | string;\n id?: string;\n interrupted?: boolean;\n createdAt?: number;\n }): ChatMessage {\n const msg = new ChatMessage(params);\n if (params.createdAt !== undefined) {\n const idx = this.findInsertionIndex(params.createdAt);\n this._items.splice(idx, 0, msg);\n } else {\n this._items.push(msg);\n }\n return msg;\n }\n\n /**\n * Insert a single item or multiple items based on their `createdAt` field so\n * that the array keeps its chronological order.\n */\n insert(item: ChatItem | ChatItem[]): void {\n const arr = Array.isArray(item) ? item : [item];\n for (const it of arr) {\n const idx = this.findInsertionIndex(it.createdAt);\n this._items.splice(idx, 0, it);\n }\n }\n\n getById(itemId: string): ChatItem | undefined {\n return this._items.find((i) => i.id === itemId);\n }\n\n indexById(itemId: string): number | undefined {\n const idx = this._items.findIndex((i) => i.id === itemId);\n return idx !== -1 ? idx : undefined;\n }\n\n copy(\n options: {\n excludeFunctionCall?: boolean;\n excludeInstructions?: boolean;\n excludeEmptyMessage?: boolean;\n toolCtx?: ToolContext<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n } = {},\n ): ChatContext {\n const {\n excludeFunctionCall = false,\n excludeInstructions = false,\n excludeEmptyMessage = false,\n toolCtx,\n } = options;\n const items: ChatItem[] = [];\n\n const isToolCallOrOutput = (item: ChatItem): item is FunctionCall | FunctionCallOutput =>\n ['function_call', 'function_call_output'].includes(item.type);\n const isChatMessage = (item: ChatItem): item is ChatMessage => item.type === 'message';\n\n for (const item of this._items) {\n if (excludeFunctionCall && isToolCallOrOutput(item)) {\n continue;\n }\n\n if (\n excludeInstructions &&\n isChatMessage(item) &&\n ['system', 'developer'].includes(item.role)\n ) {\n continue;\n }\n\n if (excludeEmptyMessage && isChatMessage(item) && item.content.length === 0) {\n continue;\n }\n\n if (toolCtx !== undefined && isToolCallOrOutput(item) && toolCtx[item.name] === undefined) {\n continue;\n }\n\n items.push(item);\n }\n\n return new ChatContext(items);\n }\n\n truncate(maxItems: number): ChatContext {\n if (maxItems <= 0) return this;\n\n const instructions = this._items.find((i) => i.type === 'message' && i.role === 'system') as\n | ChatMessage\n | undefined;\n\n let newItems = this._items.slice(-maxItems);\n\n // Ensure the first item is not a function-call artefact.\n while (\n newItems.length > 0 &&\n ['function_call', 'function_call_output'].includes(newItems[0]!.type)\n ) {\n newItems.shift();\n }\n\n if (instructions) {\n // At this point `instructions` is defined, so it is safe to pass to `includes`.\n if (!newItems.includes(instructions)) {\n newItems = [instructions, ...newItems];\n }\n }\n\n // replace the items in place to keep the reference\n this._items.splice(0, this._items.length, ...newItems);\n return this;\n }\n\n toJSON(\n options: {\n excludeImage?: boolean;\n excludeAudio?: boolean;\n excludeTimestamp?: boolean;\n excludeFunctionCall?: boolean;\n } = {},\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): JSONObject {\n const {\n excludeImage = true,\n excludeAudio = true,\n excludeTimestamp = true,\n excludeFunctionCall = false,\n } = options;\n\n const items: ChatItem[] = [];\n\n for (const item of this._items) {\n let processedItem = item;\n\n if (excludeFunctionCall && ['function_call', 'function_call_output'].includes(item.type)) {\n continue;\n }\n\n if (item.type === 'message') {\n processedItem = ChatMessage.create({\n role: item.role,\n content: item.content,\n id: item.id,\n interrupted: item.interrupted,\n createdAt: item.createdAt,\n });\n\n // Filter content based on options\n if (excludeImage) {\n processedItem.content = processedItem.content.filter((c) => {\n return !(typeof c === 'object' && c.type === 'image_content');\n });\n }\n\n if (excludeAudio) {\n processedItem.content = processedItem.content.filter((c) => {\n return !(typeof c === 'object' && c.type === 'audio_content');\n });\n }\n }\n\n items.push(processedItem);\n }\n\n return {\n items: items.map((item) => item.toJSON(excludeTimestamp)),\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async toProviderFormat(format: ProviderFormat, injectDummyUserMessage: boolean = true) {\n return await toChatCtx(format, this, injectDummyUserMessage);\n }\n\n /**\n * Internal helper used by `truncate` & `addMessage` to find the correct\n * insertion index for a timestamp so the list remains sorted.\n */\n private findInsertionIndex(createdAt: number): number {\n for (let i = this._items.length - 1; i >= 0; i -= 1) {\n const item = this._items[i];\n if (item!.createdAt <= createdAt) {\n return i + 1;\n }\n }\n return 0;\n }\n\n /**\n * Indicates whether the context is read-only\n */\n get readonly(): boolean {\n return false;\n }\n}\n\nexport class ReadonlyChatContext extends ChatContext {\n static readonly errorMsg =\n 'Please use .copy() and agent.update_chat_ctx() to modify the chat context.';\n\n constructor(items: ChatItem[]) {\n super(createImmutableArray(items, ReadonlyChatContext.errorMsg));\n }\n\n get items(): ChatItem[] {\n return this._items;\n }\n\n set items(items: ChatItem[]) {\n throw new Error(\n `Cannot set items on a read-only chat context. ${ReadonlyChatContext.errorMsg}`,\n );\n }\n\n get readonly(): boolean {\n return true;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/llm/chat_context.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, VideoFrame } from '@livekit/rtc-node';\nimport { createImmutableArray, shortuuid } from '../utils.js';\nimport { type ProviderFormat, toChatCtx } from './provider_format/index.js';\nimport type { JSONObject, JSONValue, ToolContext } from './tool_context.js';\n\nexport type ChatRole = 'developer' | 'system' | 'user' | 'assistant';\nexport interface ImageContent {\n id: string;\n\n type: 'image_content';\n\n /**\n * Either a string URL or a VideoFrame object.\n */\n image: string | VideoFrame;\n\n inferenceDetail: 'auto' | 'high' | 'low';\n\n inferenceWidth?: number;\n\n inferenceHeight?: number;\n\n mimeType?: string;\n\n _cache: Record<any, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n}\n\nexport interface AudioContent {\n type: 'audio_content';\n\n frame: AudioFrame[];\n\n transcript?: string;\n}\n\nexport type ChatContent = ImageContent | AudioContent | string;\n\nexport function createImageContent(params: {\n image: string | VideoFrame;\n id?: string;\n inferenceDetail?: 'auto' | 'high' | 'low';\n inferenceWidth?: number;\n inferenceHeight?: number;\n mimeType?: string;\n}): ImageContent {\n const {\n image,\n id = shortuuid('img_'),\n inferenceDetail = 'auto',\n inferenceWidth,\n inferenceHeight,\n mimeType,\n } = params;\n\n return {\n id,\n type: 'image_content',\n image,\n inferenceDetail,\n inferenceWidth,\n inferenceHeight,\n mimeType,\n _cache: {},\n };\n}\n\nexport function createAudioContent(params: {\n frame: AudioFrame[];\n transcript?: string;\n}): AudioContent {\n const { frame, transcript } = params;\n\n return {\n type: 'audio_content',\n frame,\n transcript,\n };\n}\n\nexport class ChatMessage {\n readonly id: string;\n\n readonly type = 'message' as const;\n\n readonly role: ChatRole;\n\n content: ChatContent[];\n\n interrupted: boolean;\n\n hash?: Uint8Array;\n\n createdAt: number;\n\n constructor(params: {\n role: ChatRole;\n content: ChatContent[] | string;\n id?: string;\n interrupted?: boolean;\n createdAt?: number;\n }) {\n const {\n role,\n content,\n id = shortuuid('item_'),\n interrupted = false,\n createdAt = Date.now(),\n } = params;\n this.id = id;\n this.role = role;\n this.content = Array.isArray(content) ? content : [content];\n this.interrupted = interrupted;\n this.createdAt = createdAt;\n }\n\n static create(params: {\n role: ChatRole;\n content: ChatContent[] | string;\n id?: string;\n interrupted?: boolean;\n createdAt?: number;\n }) {\n return new ChatMessage(params);\n }\n\n /**\n * Returns a single string with all text parts of the message joined by new\n * lines. If no string content is present, returns `null`.\n */\n get textContent(): string | undefined {\n const parts = this.content.filter((c): c is string => typeof c === 'string');\n return parts.length > 0 ? parts.join('\\n') : undefined;\n }\n\n toJSONContent(): JSONValue[] {\n return this.content.map((c) => {\n if (typeof c === 'string') {\n return c as JSONValue;\n } else if (c.type === 'image_content') {\n return {\n id: c.id,\n type: c.type,\n image: c.image,\n inferenceDetail: c.inferenceDetail,\n inferenceWidth: c.inferenceWidth,\n inferenceHeight: c.inferenceHeight,\n mimeType: c.mimeType,\n } as JSONObject;\n } else {\n return {\n type: c.type,\n transcript: c.transcript,\n } as JSONObject;\n }\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n toJSON(excludeTimestamp: boolean = false): JSONValue {\n const result: JSONValue = {\n id: this.id,\n type: this.type,\n role: this.role,\n content: this.toJSONContent(),\n interrupted: this.interrupted,\n };\n\n if (!excludeTimestamp) {\n result.createdAt = this.createdAt;\n }\n\n return result;\n }\n}\n\nexport class FunctionCall {\n readonly id: string;\n\n readonly type = 'function_call' as const;\n\n callId: string;\n\n args: string;\n\n name: string;\n\n createdAt: number;\n\n constructor(params: {\n callId: string;\n name: string;\n args: string;\n id?: string;\n createdAt?: number;\n }) {\n const { callId, name, args, id = shortuuid('item_'), createdAt = Date.now() } = params;\n this.id = id;\n this.callId = callId;\n this.args = args;\n this.name = name;\n this.createdAt = createdAt;\n }\n\n static create(params: {\n callId: string;\n name: string;\n args: string;\n id?: string;\n createdAt?: number;\n }) {\n return new FunctionCall(params);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n toJSON(excludeTimestamp: boolean = false): JSONValue {\n const result: JSONValue = {\n id: this.id,\n type: this.type,\n callId: this.callId,\n name: this.name,\n args: this.args,\n };\n\n if (!excludeTimestamp) {\n result.createdAt = this.createdAt;\n }\n\n return result;\n }\n}\n\nexport class FunctionCallOutput {\n readonly id: string;\n\n readonly type = 'function_call_output' as const;\n\n name = '';\n\n callId: string;\n\n output: string;\n\n isError: boolean;\n\n createdAt: number;\n\n constructor(params: {\n callId: string;\n output: string;\n isError: boolean;\n id?: string;\n createdAt?: number;\n name?: string;\n }) {\n const {\n callId,\n output,\n isError,\n id = shortuuid('item_'),\n createdAt = Date.now(),\n name = '',\n } = params;\n this.id = id;\n this.callId = callId;\n this.output = output;\n this.isError = isError;\n this.name = name;\n this.createdAt = createdAt;\n }\n\n static create(params: {\n callId: string;\n output: string;\n isError: boolean;\n id?: string;\n createdAt?: number;\n name?: string;\n }) {\n return new FunctionCallOutput(params);\n }\n\n toJSON(excludeTimestamp: boolean = false): JSONValue {\n const result: JSONValue = {\n id: this.id,\n type: this.type,\n name: this.name,\n callId: this.callId,\n output: this.output,\n isError: this.isError,\n };\n\n if (!excludeTimestamp) {\n result.createdAt = this.createdAt;\n }\n\n return result;\n }\n}\n\nexport type ChatItem = ChatMessage | FunctionCall | FunctionCallOutput;\n\nexport class ChatContext {\n protected _items: ChatItem[];\n\n constructor(items?: ChatItem[]) {\n this._items = items ? items : [];\n }\n\n static empty(): ChatContext {\n return new ChatContext([]);\n }\n\n get items(): ChatItem[] {\n return this._items;\n }\n\n set items(items: ChatItem[]) {\n this._items = items;\n }\n\n /**\n * Add a new message to the context and return it.\n */\n addMessage(params: {\n role: ChatRole;\n content: ChatContent[] | string;\n id?: string;\n interrupted?: boolean;\n createdAt?: number;\n }): ChatMessage {\n const msg = new ChatMessage(params);\n if (params.createdAt !== undefined) {\n const idx = this.findInsertionIndex(params.createdAt);\n this._items.splice(idx, 0, msg);\n } else {\n this._items.push(msg);\n }\n return msg;\n }\n\n /**\n * Insert a single item or multiple items based on their `createdAt` field so\n * that the array keeps its chronological order.\n */\n insert(item: ChatItem | ChatItem[]): void {\n const arr = Array.isArray(item) ? item : [item];\n for (const it of arr) {\n const idx = this.findInsertionIndex(it.createdAt);\n this._items.splice(idx, 0, it);\n }\n }\n\n getById(itemId: string): ChatItem | undefined {\n return this._items.find((i) => i.id === itemId);\n }\n\n indexById(itemId: string): number | undefined {\n const idx = this._items.findIndex((i) => i.id === itemId);\n return idx !== -1 ? idx : undefined;\n }\n\n copy(\n options: {\n excludeFunctionCall?: boolean;\n excludeInstructions?: boolean;\n excludeEmptyMessage?: boolean;\n toolCtx?: ToolContext<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n } = {},\n ): ChatContext {\n const {\n excludeFunctionCall = false,\n excludeInstructions = false,\n excludeEmptyMessage = false,\n toolCtx,\n } = options;\n const items: ChatItem[] = [];\n\n const isToolCallOrOutput = (item: ChatItem): item is FunctionCall | FunctionCallOutput =>\n ['function_call', 'function_call_output'].includes(item.type);\n const isChatMessage = (item: ChatItem): item is ChatMessage => item.type === 'message';\n\n for (const item of this._items) {\n if (excludeFunctionCall && isToolCallOrOutput(item)) {\n continue;\n }\n\n if (\n excludeInstructions &&\n isChatMessage(item) &&\n ['system', 'developer'].includes(item.role)\n ) {\n continue;\n }\n\n if (excludeEmptyMessage && isChatMessage(item) && item.content.length === 0) {\n continue;\n }\n\n if (toolCtx !== undefined && isToolCallOrOutput(item) && toolCtx[item.name] === undefined) {\n continue;\n }\n\n items.push(item);\n }\n\n return new ChatContext(items);\n }\n\n truncate(maxItems: number): ChatContext {\n if (maxItems <= 0) return this;\n\n const instructions = this._items.find((i) => i.type === 'message' && i.role === 'system') as\n | ChatMessage\n | undefined;\n\n let newItems = this._items.slice(-maxItems);\n\n // Ensure the first item is not a function-call artefact.\n while (\n newItems.length > 0 &&\n ['function_call', 'function_call_output'].includes(newItems[0]!.type)\n ) {\n newItems.shift();\n }\n\n if (instructions) {\n // At this point `instructions` is defined, so it is safe to pass to `includes`.\n if (!newItems.includes(instructions)) {\n newItems = [instructions, ...newItems];\n }\n }\n\n // replace the items in place to keep the reference\n this._items.splice(0, this._items.length, ...newItems);\n return this;\n }\n\n toJSON(\n options: {\n excludeImage?: boolean;\n excludeAudio?: boolean;\n excludeTimestamp?: boolean;\n excludeFunctionCall?: boolean;\n } = {},\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): JSONObject {\n const {\n excludeImage = true,\n excludeAudio = true,\n excludeTimestamp = true,\n excludeFunctionCall = false,\n } = options;\n\n const items: ChatItem[] = [];\n\n for (const item of this._items) {\n let processedItem = item;\n\n if (excludeFunctionCall && ['function_call', 'function_call_output'].includes(item.type)) {\n continue;\n }\n\n if (item.type === 'message') {\n processedItem = ChatMessage.create({\n role: item.role,\n content: item.content,\n id: item.id,\n interrupted: item.interrupted,\n createdAt: item.createdAt,\n });\n\n // Filter content based on options\n if (excludeImage) {\n processedItem.content = processedItem.content.filter((c) => {\n return !(typeof c === 'object' && c.type === 'image_content');\n });\n }\n\n if (excludeAudio) {\n processedItem.content = processedItem.content.filter((c) => {\n return !(typeof c === 'object' && c.type === 'audio_content');\n });\n }\n }\n\n items.push(processedItem);\n }\n\n return {\n items: items.map((item) => item.toJSON(excludeTimestamp)),\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async toProviderFormat(format: ProviderFormat, injectDummyUserMessage: boolean = true) {\n return await toChatCtx(format, this, injectDummyUserMessage);\n }\n\n /**\n * Internal helper used by `truncate` & `addMessage` to find the correct\n * insertion index for a timestamp so the list remains sorted.\n */\n private findInsertionIndex(createdAt: number): number {\n for (let i = this._items.length - 1; i >= 0; i -= 1) {\n const item = this._items[i];\n if (item!.createdAt <= createdAt) {\n return i + 1;\n }\n }\n return 0;\n }\n\n /**\n * Return true if `other` has the same sequence of items with matching\n * essential fields (IDs, types, and payload) as this context.\n *\n * Comparison rules:\n * - Messages: compares the full `content` list, `role` and `interrupted`.\n * - Function calls: compares `name`, `callId`, and `args`.\n * - Function call outputs: compares `name`, `callId`, `output`, and `isError`.\n *\n * Does not consider timestamps or other metadata.\n */\n isEquivalent(other: ChatContext): boolean {\n if (this === other) {\n return true;\n }\n\n if (this.items.length !== other.items.length) {\n return false;\n }\n\n for (let i = 0; i < this.items.length; i++) {\n const a = this.items[i]!;\n const b = other.items[i]!;\n\n if (a.id !== b.id || a.type !== b.type) {\n return false;\n }\n\n if (a.type === 'message' && b.type === 'message') {\n if (\n a.role !== b.role ||\n a.interrupted !== b.interrupted ||\n !this.compareContent(a.content, b.content)\n ) {\n return false;\n }\n } else if (a.type === 'function_call' && b.type === 'function_call') {\n if (a.name !== b.name || a.callId !== b.callId || a.args !== b.args) {\n return false;\n }\n } else if (a.type === 'function_call_output' && b.type === 'function_call_output') {\n if (\n a.name !== b.name ||\n a.callId !== b.callId ||\n a.output !== b.output ||\n a.isError !== b.isError\n ) {\n return false;\n }\n }\n }\n\n return true;\n }\n\n /**\n * Compare two content arrays for equality.\n */\n private compareContent(a: ChatContent[], b: ChatContent[]): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n for (let i = 0; i < a.length; i++) {\n const contentA = a[i]!;\n const contentB = b[i]!;\n\n if (typeof contentA === 'string' && typeof contentB === 'string') {\n if (contentA !== contentB) {\n return false;\n }\n continue;\n }\n\n if (typeof contentA !== typeof contentB) {\n return false;\n }\n\n if (typeof contentA === 'object' && typeof contentB === 'object') {\n if (contentA.type === 'image_content' && contentB.type === 'image_content') {\n if (\n contentA.id !== contentB.id ||\n contentA.image !== contentB.image ||\n contentA.inferenceDetail !== contentB.inferenceDetail ||\n contentA.inferenceWidth !== contentB.inferenceWidth ||\n contentA.inferenceHeight !== contentB.inferenceHeight ||\n contentA.mimeType !== contentB.mimeType\n ) {\n return false;\n }\n } else if (contentA.type === 'audio_content' && contentB.type === 'audio_content') {\n if (contentA.frame.length !== contentB.frame.length) {\n return false;\n }\n if (contentA.transcript !== contentB.transcript) {\n return false;\n }\n } else {\n return false;\n }\n }\n }\n\n return true;\n }\n\n /**\n * Indicates whether the context is read-only\n */\n get readonly(): boolean {\n return false;\n }\n}\n\nexport class ReadonlyChatContext extends ChatContext {\n static readonly errorMsg =\n 'Please use .copy() and agent.update_chat_ctx() to modify the chat context.';\n\n constructor(items: ChatItem[]) {\n super(createImmutableArray(items, ReadonlyChatContext.errorMsg));\n }\n\n get items(): ChatItem[] {\n return this._items;\n }\n\n set items(items: ChatItem[]) {\n throw new Error(\n `Cannot set items on a read-only chat context. ${ReadonlyChatContext.errorMsg}`,\n );\n }\n\n get readonly(): boolean {\n return true;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -155,6 +155,22 @@ export declare class ChatContext {
|
|
|
155
155
|
* insertion index for a timestamp so the list remains sorted.
|
|
156
156
|
*/
|
|
157
157
|
private findInsertionIndex;
|
|
158
|
+
/**
|
|
159
|
+
* Return true if `other` has the same sequence of items with matching
|
|
160
|
+
* essential fields (IDs, types, and payload) as this context.
|
|
161
|
+
*
|
|
162
|
+
* Comparison rules:
|
|
163
|
+
* - Messages: compares the full `content` list, `role` and `interrupted`.
|
|
164
|
+
* - Function calls: compares `name`, `callId`, and `args`.
|
|
165
|
+
* - Function call outputs: compares `name`, `callId`, `output`, and `isError`.
|
|
166
|
+
*
|
|
167
|
+
* Does not consider timestamps or other metadata.
|
|
168
|
+
*/
|
|
169
|
+
isEquivalent(other: ChatContext): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Compare two content arrays for equality.
|
|
172
|
+
*/
|
|
173
|
+
private compareContent;
|
|
158
174
|
/**
|
|
159
175
|
* Indicates whether the context is read-only
|
|
160
176
|
*/
|
|
@@ -155,6 +155,22 @@ export declare class ChatContext {
|
|
|
155
155
|
* insertion index for a timestamp so the list remains sorted.
|
|
156
156
|
*/
|
|
157
157
|
private findInsertionIndex;
|
|
158
|
+
/**
|
|
159
|
+
* Return true if `other` has the same sequence of items with matching
|
|
160
|
+
* essential fields (IDs, types, and payload) as this context.
|
|
161
|
+
*
|
|
162
|
+
* Comparison rules:
|
|
163
|
+
* - Messages: compares the full `content` list, `role` and `interrupted`.
|
|
164
|
+
* - Function calls: compares `name`, `callId`, and `args`.
|
|
165
|
+
* - Function call outputs: compares `name`, `callId`, `output`, and `isError`.
|
|
166
|
+
*
|
|
167
|
+
* Does not consider timestamps or other metadata.
|
|
168
|
+
*/
|
|
169
|
+
isEquivalent(other: ChatContext): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Compare two content arrays for equality.
|
|
172
|
+
*/
|
|
173
|
+
private compareContent;
|
|
158
174
|
/**
|
|
159
175
|
* Indicates whether the context is read-only
|
|
160
176
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat_context.d.ts","sourceRoot":"","sources":["../../src/llm/chat_context.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat_context.d.ts","sourceRoot":"","sources":["../../src/llm/chat_context.ts"],"names":[],"mappings":"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"}
|
package/dist/llm/chat_context.js
CHANGED
|
@@ -315,6 +315,84 @@ class ChatContext {
|
|
|
315
315
|
}
|
|
316
316
|
return 0;
|
|
317
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* Return true if `other` has the same sequence of items with matching
|
|
320
|
+
* essential fields (IDs, types, and payload) as this context.
|
|
321
|
+
*
|
|
322
|
+
* Comparison rules:
|
|
323
|
+
* - Messages: compares the full `content` list, `role` and `interrupted`.
|
|
324
|
+
* - Function calls: compares `name`, `callId`, and `args`.
|
|
325
|
+
* - Function call outputs: compares `name`, `callId`, `output`, and `isError`.
|
|
326
|
+
*
|
|
327
|
+
* Does not consider timestamps or other metadata.
|
|
328
|
+
*/
|
|
329
|
+
isEquivalent(other) {
|
|
330
|
+
if (this === other) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
if (this.items.length !== other.items.length) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
337
|
+
const a = this.items[i];
|
|
338
|
+
const b = other.items[i];
|
|
339
|
+
if (a.id !== b.id || a.type !== b.type) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
if (a.type === "message" && b.type === "message") {
|
|
343
|
+
if (a.role !== b.role || a.interrupted !== b.interrupted || !this.compareContent(a.content, b.content)) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
} else if (a.type === "function_call" && b.type === "function_call") {
|
|
347
|
+
if (a.name !== b.name || a.callId !== b.callId || a.args !== b.args) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
} else if (a.type === "function_call_output" && b.type === "function_call_output") {
|
|
351
|
+
if (a.name !== b.name || a.callId !== b.callId || a.output !== b.output || a.isError !== b.isError) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Compare two content arrays for equality.
|
|
360
|
+
*/
|
|
361
|
+
compareContent(a, b) {
|
|
362
|
+
if (a.length !== b.length) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
for (let i = 0; i < a.length; i++) {
|
|
366
|
+
const contentA = a[i];
|
|
367
|
+
const contentB = b[i];
|
|
368
|
+
if (typeof contentA === "string" && typeof contentB === "string") {
|
|
369
|
+
if (contentA !== contentB) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (typeof contentA !== typeof contentB) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
if (typeof contentA === "object" && typeof contentB === "object") {
|
|
378
|
+
if (contentA.type === "image_content" && contentB.type === "image_content") {
|
|
379
|
+
if (contentA.id !== contentB.id || contentA.image !== contentB.image || contentA.inferenceDetail !== contentB.inferenceDetail || contentA.inferenceWidth !== contentB.inferenceWidth || contentA.inferenceHeight !== contentB.inferenceHeight || contentA.mimeType !== contentB.mimeType) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
} else if (contentA.type === "audio_content" && contentB.type === "audio_content") {
|
|
383
|
+
if (contentA.frame.length !== contentB.frame.length) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (contentA.transcript !== contentB.transcript) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
318
396
|
/**
|
|
319
397
|
* Indicates whether the context is read-only
|
|
320
398
|
*/
|