@livekit/agents-plugin-baseten 1.0.49 → 1.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/llm.test.cjs +17 -9
- package/dist/llm.test.cjs.map +1 -1
- package/dist/llm.test.js +18 -10
- package/dist/llm.test.js.map +1 -1
- package/dist/stt.cjs +8 -3
- package/dist/stt.cjs.map +1 -1
- package/dist/stt.d.ts.map +1 -1
- package/dist/stt.js +16 -4
- package/dist/stt.js.map +1 -1
- package/dist/stt.test.cjs +13 -3
- package/dist/stt.test.cjs.map +1 -1
- package/dist/stt.test.js +14 -4
- package/dist/stt.test.js.map +1 -1
- package/dist/tts.test.cjs +13 -3
- package/dist/tts.test.cjs.map +1 -1
- package/dist/tts.test.js +14 -4
- package/dist/tts.test.js.map +1 -1
- package/package.json +5 -5
- package/src/llm.test.ts +18 -10
- package/src/stt.test.ts +15 -4
- package/src/stt.ts +20 -4
- package/src/tts.test.ts +14 -4
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/dist/llm.test.cjs
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
var import_agents_plugins_test = require("@livekit/agents-plugins-test");
|
|
3
3
|
var import_vitest = require("vitest");
|
|
4
4
|
var import_llm = require("./llm.cjs");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const hasBasetenApiKey = Boolean(process.env.BASETEN_API_KEY);
|
|
6
|
+
if (hasBasetenApiKey) {
|
|
7
|
+
(0, import_vitest.describe)("Baseten", async () => {
|
|
8
|
+
await (0, import_agents_plugins_test.llm)(
|
|
9
|
+
new import_llm.LLM({
|
|
10
|
+
model: "openai/gpt-4o-mini",
|
|
11
|
+
temperature: 0
|
|
12
|
+
}),
|
|
13
|
+
false
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
(0, import_vitest.describe)("Baseten", () => {
|
|
18
|
+
import_vitest.it.skip("requires BASETEN_API_KEY", () => {
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
14
22
|
//# sourceMappingURL=llm.test.cjs.map
|
package/dist/llm.test.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { LLM } from './llm.js';\n\
|
|
1
|
+
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { LLM } from './llm.js';\n\nconst hasBasetenApiKey = Boolean(process.env.BASETEN_API_KEY);\n\nif (hasBasetenApiKey) {\n describe('Baseten', async () => {\n await llm(\n new LLM({\n model: 'openai/gpt-4o-mini',\n temperature: 0,\n }),\n false,\n );\n });\n} else {\n describe('Baseten', () => {\n it.skip('requires BASETEN_API_KEY', () => {});\n });\n}\n"],"mappings":";AAGA,iCAAoB;AACpB,oBAA6B;AAC7B,iBAAoB;AAEpB,MAAM,mBAAmB,QAAQ,QAAQ,IAAI,eAAe;AAE5D,IAAI,kBAAkB;AACpB,8BAAS,WAAW,YAAY;AAC9B,cAAM;AAAA,MACJ,IAAI,eAAI;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,MACf,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF,CAAC;AACH,OAAO;AACL,8BAAS,WAAW,MAAM;AACxB,qBAAG,KAAK,4BAA4B,MAAM;AAAA,IAAC,CAAC;AAAA,EAC9C,CAAC;AACH;","names":[]}
|
package/dist/llm.test.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { llm } from "@livekit/agents-plugins-test";
|
|
2
|
-
import { describe } from "vitest";
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
3
|
import { LLM } from "./llm.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
const hasBasetenApiKey = Boolean(process.env.BASETEN_API_KEY);
|
|
5
|
+
if (hasBasetenApiKey) {
|
|
6
|
+
describe("Baseten", async () => {
|
|
7
|
+
await llm(
|
|
8
|
+
new LLM({
|
|
9
|
+
model: "openai/gpt-4o-mini",
|
|
10
|
+
temperature: 0
|
|
11
|
+
}),
|
|
12
|
+
false
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
describe("Baseten", () => {
|
|
17
|
+
it.skip("requires BASETEN_API_KEY", () => {
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
13
21
|
//# sourceMappingURL=llm.test.js.map
|
package/dist/llm.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { LLM } from './llm.js';\n\
|
|
1
|
+
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { LLM } from './llm.js';\n\nconst hasBasetenApiKey = Boolean(process.env.BASETEN_API_KEY);\n\nif (hasBasetenApiKey) {\n describe('Baseten', async () => {\n await llm(\n new LLM({\n model: 'openai/gpt-4o-mini',\n temperature: 0,\n }),\n false,\n );\n });\n} else {\n describe('Baseten', () => {\n it.skip('requires BASETEN_API_KEY', () => {});\n });\n}\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,UAAU,UAAU;AAC7B,SAAS,WAAW;AAEpB,MAAM,mBAAmB,QAAQ,QAAQ,IAAI,eAAe;AAE5D,IAAI,kBAAkB;AACpB,WAAS,WAAW,YAAY;AAC9B,UAAM;AAAA,MACJ,IAAI,IAAI;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,MACf,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF,CAAC;AACH,OAAO;AACL,WAAS,WAAW,MAAM;AACxB,OAAG,KAAK,4BAA4B,MAAM;AAAA,IAAC,CAAC;AAAA,EAC9C,CAAC;AACH;","names":[]}
|
package/dist/stt.cjs
CHANGED
|
@@ -66,7 +66,8 @@ class STT extends import_agents.stt.STT {
|
|
|
66
66
|
...opts,
|
|
67
67
|
apiKey,
|
|
68
68
|
modelEndpoint,
|
|
69
|
-
modelId
|
|
69
|
+
modelId,
|
|
70
|
+
audioLanguage: (0, import_agents.normalizeLanguage)(opts.audioLanguage ?? defaultSTTOptions.audioLanguage)
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
73
|
// eslint-disable-next-line
|
|
@@ -74,7 +75,11 @@ class STT extends import_agents.stt.STT {
|
|
|
74
75
|
throw new Error("Recognize is not supported on Baseten STT");
|
|
75
76
|
}
|
|
76
77
|
updateOptions(opts) {
|
|
77
|
-
this.#opts = {
|
|
78
|
+
this.#opts = {
|
|
79
|
+
...this.#opts,
|
|
80
|
+
...opts,
|
|
81
|
+
audioLanguage: opts.audioLanguage !== void 0 ? (0, import_agents.normalizeLanguage)(opts.audioLanguage) : this.#opts.audioLanguage
|
|
82
|
+
};
|
|
78
83
|
}
|
|
79
84
|
stream() {
|
|
80
85
|
return new SpeechStream(this, this.#opts);
|
|
@@ -205,7 +210,7 @@ class SpeechStream extends import_agents.stt.SpeechStream {
|
|
|
205
210
|
const segments = msg.segments ?? [];
|
|
206
211
|
const transcript = msg.transcript ?? "";
|
|
207
212
|
const confidence = msg.confidence ?? 0;
|
|
208
|
-
const languageCode = msg.language_code ?? this.#opts.audioLanguage;
|
|
213
|
+
const languageCode = (0, import_agents.normalizeLanguage)(msg.language_code ?? this.#opts.audioLanguage);
|
|
209
214
|
if (!transcript) {
|
|
210
215
|
this.#logger.debug("Received non-transcript message:", msg);
|
|
211
216
|
return;
|
package/dist/stt.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { type AudioBuffer, AudioByteStream, Task, log, stt, waitForAbort } from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { BasetenSttOptions } from './types.js';\n\nconst defaultSTTOptions: Partial<BasetenSttOptions> = {\n environment: 'production',\n encoding: 'pcm_s16le',\n sampleRate: 16000,\n bufferSizeSeconds: 0.032,\n enablePartialTranscripts: true,\n partialTranscriptIntervalS: 0.5,\n finalTranscriptMaxDurationS: 5,\n audioLanguage: 'en',\n languageDetectionOnly: false,\n vadThreshold: 0.5,\n vadMinSilenceDurationMs: 300,\n vadSpeechPadMs: 30,\n};\n\nexport class STT extends stt.STT {\n #opts: BasetenSttOptions;\n #logger = log();\n label = 'baseten.STT';\n\n constructor(opts: Partial<BasetenSttOptions> = {}) {\n super({\n streaming: true,\n interimResults: opts.enablePartialTranscripts ?? defaultSTTOptions.enablePartialTranscripts!,\n alignedTranscript: 'word',\n });\n\n const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;\n const modelEndpoint = opts.modelEndpoint ?? process.env.BASETEN_MODEL_ENDPOINT;\n const modelId = opts.modelId ?? process.env.BASETEN_STT_MODEL_ID;\n\n if (!apiKey) {\n throw new Error(\n 'Baseten API key is required, either pass it as `apiKey` or set $BASETEN_API_KEY',\n );\n }\n if (!modelEndpoint && !modelId) {\n throw new Error(\n 'Baseten model endpoint is required, either pass it as `modelEndpoint` or set $BASETEN_MODEL_ENDPOINT',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n apiKey,\n modelEndpoint,\n modelId,\n } as BasetenSttOptions;\n }\n\n // eslint-disable-next-line\n async _recognize(_: AudioBuffer): Promise<stt.SpeechEvent> {\n throw new Error('Recognize is not supported on Baseten STT');\n }\n\n updateOptions(opts: Partial<BasetenSttOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n stream(): SpeechStream {\n return new SpeechStream(this, this.#opts);\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: BasetenSttOptions;\n #logger = log();\n #speaking = false;\n #requestId = '';\n label = 'baseten.SpeechStream';\n\n constructor(stt: STT, opts: BasetenSttOptions) {\n super(stt, opts.sampleRate);\n this.#opts = opts;\n this.closed = false;\n }\n\n private getWsUrl(): string {\n if (this.#opts.modelEndpoint) {\n return this.#opts.modelEndpoint;\n }\n // Fallback to constructing URL from modelId (deprecated)\n return `wss://model-${this.#opts.modelId}.api.baseten.co/environments/${this.#opts.environment}/websocket`;\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n\n while (!this.input.closed && !this.closed) {\n const url = this.getWsUrl();\n const headers = {\n Authorization: `Api-Key ${this.#opts.apiKey}`,\n };\n\n const ws = new WebSocket(url, { headers, rejectUnauthorized: false });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Baseten after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Baseten, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Baseten disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n async #runWS(ws: WebSocket) {\n let closing = false;\n\n // Send initial metadata\n // Note: Baseten server expects 'vad_params' and 'streaming_whisper_params' field names\n // (not 'streaming_vad_config', 'streaming_params', 'whisper_params' as in older versions)\n const metadata = {\n vad_params: {\n threshold: this.#opts.vadThreshold,\n min_silence_duration_ms: this.#opts.vadMinSilenceDurationMs,\n speech_pad_ms: this.#opts.vadSpeechPadMs,\n },\n streaming_whisper_params: {\n encoding: this.#opts.encoding ?? 'pcm_s16le',\n sample_rate: this.#opts.sampleRate ?? 16000,\n enable_partial_transcripts: false,\n audio_language: this.#opts.audioLanguage ?? 'en',\n show_word_timestamps: true,\n },\n };\n\n ws.send(JSON.stringify(metadata));\n\n const sendTask = async () => {\n const sampleRate = this.#opts.sampleRate ?? 16000;\n const samplesPerChunk = sampleRate === 16000 ? 512 : 256;\n const audioByteStream = new AudioByteStream(sampleRate, 1, samplesPerChunk);\n\n try {\n while (!this.closed) {\n const result = await this.input.next();\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n if (data === SpeechStream.FLUSH_SENTINEL) {\n // Flush any remaining buffered audio\n frames = audioByteStream.flush();\n } else {\n if (data.sampleRate !== sampleRate || data.channels !== 1) {\n throw new Error(\n `sample rate or channel count mismatch: expected ${sampleRate}Hz/1ch, got ${data.sampleRate}Hz/${data.channels}ch`,\n );\n }\n frames = audioByteStream.write(data.data.buffer as ArrayBuffer);\n }\n\n for (const frame of frames) {\n const buffer = Buffer.from(\n frame.data.buffer,\n frame.data.byteOffset,\n frame.data.byteLength,\n );\n ws.send(buffer);\n }\n }\n } finally {\n closing = true;\n ws.close();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (data) => {\n try {\n let jsonString: string;\n\n if (typeof data === 'string') {\n jsonString = data;\n } else if (data instanceof Buffer) {\n jsonString = data.toString('utf-8');\n } else if (Array.isArray(data)) {\n jsonString = Buffer.concat(data).toString('utf-8');\n } else {\n return;\n }\n\n const msg = JSON.parse(jsonString);\n const isFinal = msg.is_final ?? true;\n const segments = msg.segments ?? [];\n const transcript = msg.transcript ?? '';\n const confidence = msg.confidence ?? 0.0;\n const languageCode = msg.language_code ?? this.#opts.audioLanguage;\n\n // Skip if no transcript text\n if (!transcript) {\n this.#logger.debug('Received non-transcript message:', msg);\n return;\n }\n\n // Emit START_OF_SPEECH if not already speaking (only for interim or first final)\n if (!this.#speaking && !isFinal) {\n this.#speaking = true;\n this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH });\n }\n\n // Note: Baseten uses 'start_time' and 'end_time' field names (with underscores)\n const startTime =\n segments.length > 0\n ? (segments[0].start_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n const endTime =\n segments.length > 0\n ? (segments[segments.length - 1].end_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n\n // Note: Baseten returns segments (chunks) which we treat as words for aligned transcripts\n const words = segments.map(\n (segment: { text?: string; start_time?: number; end_time?: number }) => ({\n text: segment.text ?? '',\n startTime: (segment.start_time ?? 0.0) + this.startTimeOffset,\n endTime: (segment.end_time ?? 0.0) + this.startTimeOffset,\n startTimeOffset: this.startTimeOffset,\n confidence: confidence,\n }),\n );\n\n const speechData: stt.SpeechData = {\n language: languageCode!,\n text: transcript,\n startTime,\n endTime,\n confidence,\n words: words.length > 0 ? words : undefined,\n };\n\n // Handle interim vs final transcripts (matching Python implementation)\n if (!isFinal) {\n // Interim transcript\n this.queue.put({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [speechData],\n });\n } else {\n // Final transcript\n this.queue.put({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [speechData],\n });\n\n // Emit END_OF_SPEECH after final transcript\n if (this.#speaking) {\n this.#speaking = false;\n this.queue.put({ type: stt.SpeechEventType.END_OF_SPEECH });\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(`STT: Error processing message: ${data}`);\n reject(err);\n }\n });\n\n ws.on('error', (err) => {\n if (!closing) {\n reject(err);\n }\n });\n\n ws.on('close', () => {\n if (!closing) {\n resolve();\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.all([sendTask(), listenTask.result]);\n closing = true;\n ws.close();\n }\n}\n"],"mappings":"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","names":["stt"]}
|
|
1
|
+
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type AudioBuffer,\n AudioByteStream,\n Task,\n log,\n normalizeLanguage,\n stt,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { BasetenSttOptions } from './types.js';\n\nconst defaultSTTOptions: Partial<BasetenSttOptions> = {\n environment: 'production',\n encoding: 'pcm_s16le',\n sampleRate: 16000,\n bufferSizeSeconds: 0.032,\n enablePartialTranscripts: true,\n partialTranscriptIntervalS: 0.5,\n finalTranscriptMaxDurationS: 5,\n audioLanguage: 'en',\n languageDetectionOnly: false,\n vadThreshold: 0.5,\n vadMinSilenceDurationMs: 300,\n vadSpeechPadMs: 30,\n};\n\nexport class STT extends stt.STT {\n #opts: BasetenSttOptions;\n #logger = log();\n label = 'baseten.STT';\n\n constructor(opts: Partial<BasetenSttOptions> = {}) {\n super({\n streaming: true,\n interimResults: opts.enablePartialTranscripts ?? defaultSTTOptions.enablePartialTranscripts!,\n alignedTranscript: 'word',\n });\n\n const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;\n const modelEndpoint = opts.modelEndpoint ?? process.env.BASETEN_MODEL_ENDPOINT;\n const modelId = opts.modelId ?? process.env.BASETEN_STT_MODEL_ID;\n\n if (!apiKey) {\n throw new Error(\n 'Baseten API key is required, either pass it as `apiKey` or set $BASETEN_API_KEY',\n );\n }\n if (!modelEndpoint && !modelId) {\n throw new Error(\n 'Baseten model endpoint is required, either pass it as `modelEndpoint` or set $BASETEN_MODEL_ENDPOINT',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n apiKey,\n modelEndpoint,\n modelId,\n audioLanguage: normalizeLanguage((opts.audioLanguage ?? defaultSTTOptions.audioLanguage)!),\n } as BasetenSttOptions;\n }\n\n // eslint-disable-next-line\n async _recognize(_: AudioBuffer): Promise<stt.SpeechEvent> {\n throw new Error('Recognize is not supported on Baseten STT');\n }\n\n updateOptions(opts: Partial<BasetenSttOptions>) {\n this.#opts = {\n ...this.#opts,\n ...opts,\n audioLanguage:\n opts.audioLanguage !== undefined\n ? normalizeLanguage(opts.audioLanguage)\n : this.#opts.audioLanguage,\n };\n }\n\n stream(): SpeechStream {\n return new SpeechStream(this, this.#opts);\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: BasetenSttOptions;\n #logger = log();\n #speaking = false;\n #requestId = '';\n label = 'baseten.SpeechStream';\n\n constructor(stt: STT, opts: BasetenSttOptions) {\n super(stt, opts.sampleRate);\n this.#opts = opts;\n this.closed = false;\n }\n\n private getWsUrl(): string {\n if (this.#opts.modelEndpoint) {\n return this.#opts.modelEndpoint;\n }\n // Fallback to constructing URL from modelId (deprecated)\n return `wss://model-${this.#opts.modelId}.api.baseten.co/environments/${this.#opts.environment}/websocket`;\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n\n while (!this.input.closed && !this.closed) {\n const url = this.getWsUrl();\n const headers = {\n Authorization: `Api-Key ${this.#opts.apiKey}`,\n };\n\n const ws = new WebSocket(url, { headers, rejectUnauthorized: false });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Baseten after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Baseten, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Baseten disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n async #runWS(ws: WebSocket) {\n let closing = false;\n\n // Send initial metadata\n // Note: Baseten server expects 'vad_params' and 'streaming_whisper_params' field names\n // (not 'streaming_vad_config', 'streaming_params', 'whisper_params' as in older versions)\n const metadata = {\n vad_params: {\n threshold: this.#opts.vadThreshold,\n min_silence_duration_ms: this.#opts.vadMinSilenceDurationMs,\n speech_pad_ms: this.#opts.vadSpeechPadMs,\n },\n streaming_whisper_params: {\n encoding: this.#opts.encoding ?? 'pcm_s16le',\n sample_rate: this.#opts.sampleRate ?? 16000,\n enable_partial_transcripts: false,\n audio_language: this.#opts.audioLanguage ?? 'en',\n show_word_timestamps: true,\n },\n };\n\n ws.send(JSON.stringify(metadata));\n\n const sendTask = async () => {\n const sampleRate = this.#opts.sampleRate ?? 16000;\n const samplesPerChunk = sampleRate === 16000 ? 512 : 256;\n const audioByteStream = new AudioByteStream(sampleRate, 1, samplesPerChunk);\n\n try {\n while (!this.closed) {\n const result = await this.input.next();\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n if (data === SpeechStream.FLUSH_SENTINEL) {\n // Flush any remaining buffered audio\n frames = audioByteStream.flush();\n } else {\n if (data.sampleRate !== sampleRate || data.channels !== 1) {\n throw new Error(\n `sample rate or channel count mismatch: expected ${sampleRate}Hz/1ch, got ${data.sampleRate}Hz/${data.channels}ch`,\n );\n }\n frames = audioByteStream.write(data.data.buffer as ArrayBuffer);\n }\n\n for (const frame of frames) {\n const buffer = Buffer.from(\n frame.data.buffer,\n frame.data.byteOffset,\n frame.data.byteLength,\n );\n ws.send(buffer);\n }\n }\n } finally {\n closing = true;\n ws.close();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (data) => {\n try {\n let jsonString: string;\n\n if (typeof data === 'string') {\n jsonString = data;\n } else if (data instanceof Buffer) {\n jsonString = data.toString('utf-8');\n } else if (Array.isArray(data)) {\n jsonString = Buffer.concat(data).toString('utf-8');\n } else {\n return;\n }\n\n const msg = JSON.parse(jsonString);\n const isFinal = msg.is_final ?? true;\n const segments = msg.segments ?? [];\n const transcript = msg.transcript ?? '';\n const confidence = msg.confidence ?? 0.0;\n const languageCode = normalizeLanguage(msg.language_code ?? this.#opts.audioLanguage);\n\n // Skip if no transcript text\n if (!transcript) {\n this.#logger.debug('Received non-transcript message:', msg);\n return;\n }\n\n // Emit START_OF_SPEECH if not already speaking (only for interim or first final)\n if (!this.#speaking && !isFinal) {\n this.#speaking = true;\n this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH });\n }\n\n // Note: Baseten uses 'start_time' and 'end_time' field names (with underscores)\n const startTime =\n segments.length > 0\n ? (segments[0].start_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n const endTime =\n segments.length > 0\n ? (segments[segments.length - 1].end_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n\n // Note: Baseten returns segments (chunks) which we treat as words for aligned transcripts\n const words = segments.map(\n (segment: { text?: string; start_time?: number; end_time?: number }) => ({\n text: segment.text ?? '',\n startTime: (segment.start_time ?? 0.0) + this.startTimeOffset,\n endTime: (segment.end_time ?? 0.0) + this.startTimeOffset,\n startTimeOffset: this.startTimeOffset,\n confidence: confidence,\n }),\n );\n\n const speechData: stt.SpeechData = {\n language: languageCode,\n text: transcript,\n startTime,\n endTime,\n confidence,\n words: words.length > 0 ? words : undefined,\n };\n\n // Handle interim vs final transcripts (matching Python implementation)\n if (!isFinal) {\n // Interim transcript\n this.queue.put({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [speechData],\n });\n } else {\n // Final transcript\n this.queue.put({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [speechData],\n });\n\n // Emit END_OF_SPEECH after final transcript\n if (this.#speaking) {\n this.#speaking = false;\n this.queue.put({ type: stt.SpeechEventType.END_OF_SPEECH });\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(`STT: Error processing message: ${data}`);\n reject(err);\n }\n });\n\n ws.on('error', (err) => {\n if (!closing) {\n reject(err);\n }\n });\n\n ws.on('close', () => {\n if (!closing) {\n resolve();\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.all([sendTask(), listenTask.result]);\n closing = true;\n ws.close();\n }\n}\n"],"mappings":"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","names":["stt"]}
|
package/dist/stt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stt.d.ts","sourceRoot":"","sources":["../src/stt.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"stt.d.ts","sourceRoot":"","sources":["../src/stt.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAKhB,GAAG,EAEJ,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiBpD,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAG9B,KAAK,SAAiB;gBAEV,IAAI,GAAE,OAAO,CAAC,iBAAiB,CAAM;IAiC3C,UAAU,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAI1D,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAW9C,MAAM,IAAI,YAAY;CAGvB;AAED,qBAAa,YAAa,SAAQ,GAAG,CAAC,YAAY;;IAKhD,KAAK,SAA0B;gBAEnB,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,iBAAiB;IAM7C,OAAO,CAAC,QAAQ;cAQA,GAAG;CAgOpB"}
|
package/dist/stt.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AudioByteStream,
|
|
3
|
+
Task,
|
|
4
|
+
log,
|
|
5
|
+
normalizeLanguage,
|
|
6
|
+
stt,
|
|
7
|
+
waitForAbort
|
|
8
|
+
} from "@livekit/agents";
|
|
2
9
|
import { WebSocket } from "ws";
|
|
3
10
|
const defaultSTTOptions = {
|
|
4
11
|
environment: "production",
|
|
@@ -42,7 +49,8 @@ class STT extends stt.STT {
|
|
|
42
49
|
...opts,
|
|
43
50
|
apiKey,
|
|
44
51
|
modelEndpoint,
|
|
45
|
-
modelId
|
|
52
|
+
modelId,
|
|
53
|
+
audioLanguage: normalizeLanguage(opts.audioLanguage ?? defaultSTTOptions.audioLanguage)
|
|
46
54
|
};
|
|
47
55
|
}
|
|
48
56
|
// eslint-disable-next-line
|
|
@@ -50,7 +58,11 @@ class STT extends stt.STT {
|
|
|
50
58
|
throw new Error("Recognize is not supported on Baseten STT");
|
|
51
59
|
}
|
|
52
60
|
updateOptions(opts) {
|
|
53
|
-
this.#opts = {
|
|
61
|
+
this.#opts = {
|
|
62
|
+
...this.#opts,
|
|
63
|
+
...opts,
|
|
64
|
+
audioLanguage: opts.audioLanguage !== void 0 ? normalizeLanguage(opts.audioLanguage) : this.#opts.audioLanguage
|
|
65
|
+
};
|
|
54
66
|
}
|
|
55
67
|
stream() {
|
|
56
68
|
return new SpeechStream(this, this.#opts);
|
|
@@ -181,7 +193,7 @@ class SpeechStream extends stt.SpeechStream {
|
|
|
181
193
|
const segments = msg.segments ?? [];
|
|
182
194
|
const transcript = msg.transcript ?? "";
|
|
183
195
|
const confidence = msg.confidence ?? 0;
|
|
184
|
-
const languageCode = msg.language_code ?? this.#opts.audioLanguage;
|
|
196
|
+
const languageCode = normalizeLanguage(msg.language_code ?? this.#opts.audioLanguage);
|
|
185
197
|
if (!transcript) {
|
|
186
198
|
this.#logger.debug("Received non-transcript message:", msg);
|
|
187
199
|
return;
|
package/dist/stt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { type AudioBuffer, AudioByteStream, Task, log, stt, waitForAbort } from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { BasetenSttOptions } from './types.js';\n\nconst defaultSTTOptions: Partial<BasetenSttOptions> = {\n environment: 'production',\n encoding: 'pcm_s16le',\n sampleRate: 16000,\n bufferSizeSeconds: 0.032,\n enablePartialTranscripts: true,\n partialTranscriptIntervalS: 0.5,\n finalTranscriptMaxDurationS: 5,\n audioLanguage: 'en',\n languageDetectionOnly: false,\n vadThreshold: 0.5,\n vadMinSilenceDurationMs: 300,\n vadSpeechPadMs: 30,\n};\n\nexport class STT extends stt.STT {\n #opts: BasetenSttOptions;\n #logger = log();\n label = 'baseten.STT';\n\n constructor(opts: Partial<BasetenSttOptions> = {}) {\n super({\n streaming: true,\n interimResults: opts.enablePartialTranscripts ?? defaultSTTOptions.enablePartialTranscripts!,\n alignedTranscript: 'word',\n });\n\n const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;\n const modelEndpoint = opts.modelEndpoint ?? process.env.BASETEN_MODEL_ENDPOINT;\n const modelId = opts.modelId ?? process.env.BASETEN_STT_MODEL_ID;\n\n if (!apiKey) {\n throw new Error(\n 'Baseten API key is required, either pass it as `apiKey` or set $BASETEN_API_KEY',\n );\n }\n if (!modelEndpoint && !modelId) {\n throw new Error(\n 'Baseten model endpoint is required, either pass it as `modelEndpoint` or set $BASETEN_MODEL_ENDPOINT',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n apiKey,\n modelEndpoint,\n modelId,\n } as BasetenSttOptions;\n }\n\n // eslint-disable-next-line\n async _recognize(_: AudioBuffer): Promise<stt.SpeechEvent> {\n throw new Error('Recognize is not supported on Baseten STT');\n }\n\n updateOptions(opts: Partial<BasetenSttOptions>) {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n stream(): SpeechStream {\n return new SpeechStream(this, this.#opts);\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: BasetenSttOptions;\n #logger = log();\n #speaking = false;\n #requestId = '';\n label = 'baseten.SpeechStream';\n\n constructor(stt: STT, opts: BasetenSttOptions) {\n super(stt, opts.sampleRate);\n this.#opts = opts;\n this.closed = false;\n }\n\n private getWsUrl(): string {\n if (this.#opts.modelEndpoint) {\n return this.#opts.modelEndpoint;\n }\n // Fallback to constructing URL from modelId (deprecated)\n return `wss://model-${this.#opts.modelId}.api.baseten.co/environments/${this.#opts.environment}/websocket`;\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n\n while (!this.input.closed && !this.closed) {\n const url = this.getWsUrl();\n const headers = {\n Authorization: `Api-Key ${this.#opts.apiKey}`,\n };\n\n const ws = new WebSocket(url, { headers, rejectUnauthorized: false });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Baseten after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Baseten, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Baseten disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n async #runWS(ws: WebSocket) {\n let closing = false;\n\n // Send initial metadata\n // Note: Baseten server expects 'vad_params' and 'streaming_whisper_params' field names\n // (not 'streaming_vad_config', 'streaming_params', 'whisper_params' as in older versions)\n const metadata = {\n vad_params: {\n threshold: this.#opts.vadThreshold,\n min_silence_duration_ms: this.#opts.vadMinSilenceDurationMs,\n speech_pad_ms: this.#opts.vadSpeechPadMs,\n },\n streaming_whisper_params: {\n encoding: this.#opts.encoding ?? 'pcm_s16le',\n sample_rate: this.#opts.sampleRate ?? 16000,\n enable_partial_transcripts: false,\n audio_language: this.#opts.audioLanguage ?? 'en',\n show_word_timestamps: true,\n },\n };\n\n ws.send(JSON.stringify(metadata));\n\n const sendTask = async () => {\n const sampleRate = this.#opts.sampleRate ?? 16000;\n const samplesPerChunk = sampleRate === 16000 ? 512 : 256;\n const audioByteStream = new AudioByteStream(sampleRate, 1, samplesPerChunk);\n\n try {\n while (!this.closed) {\n const result = await this.input.next();\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n if (data === SpeechStream.FLUSH_SENTINEL) {\n // Flush any remaining buffered audio\n frames = audioByteStream.flush();\n } else {\n if (data.sampleRate !== sampleRate || data.channels !== 1) {\n throw new Error(\n `sample rate or channel count mismatch: expected ${sampleRate}Hz/1ch, got ${data.sampleRate}Hz/${data.channels}ch`,\n );\n }\n frames = audioByteStream.write(data.data.buffer as ArrayBuffer);\n }\n\n for (const frame of frames) {\n const buffer = Buffer.from(\n frame.data.buffer,\n frame.data.byteOffset,\n frame.data.byteLength,\n );\n ws.send(buffer);\n }\n }\n } finally {\n closing = true;\n ws.close();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (data) => {\n try {\n let jsonString: string;\n\n if (typeof data === 'string') {\n jsonString = data;\n } else if (data instanceof Buffer) {\n jsonString = data.toString('utf-8');\n } else if (Array.isArray(data)) {\n jsonString = Buffer.concat(data).toString('utf-8');\n } else {\n return;\n }\n\n const msg = JSON.parse(jsonString);\n const isFinal = msg.is_final ?? true;\n const segments = msg.segments ?? [];\n const transcript = msg.transcript ?? '';\n const confidence = msg.confidence ?? 0.0;\n const languageCode = msg.language_code ?? this.#opts.audioLanguage;\n\n // Skip if no transcript text\n if (!transcript) {\n this.#logger.debug('Received non-transcript message:', msg);\n return;\n }\n\n // Emit START_OF_SPEECH if not already speaking (only for interim or first final)\n if (!this.#speaking && !isFinal) {\n this.#speaking = true;\n this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH });\n }\n\n // Note: Baseten uses 'start_time' and 'end_time' field names (with underscores)\n const startTime =\n segments.length > 0\n ? (segments[0].start_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n const endTime =\n segments.length > 0\n ? (segments[segments.length - 1].end_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n\n // Note: Baseten returns segments (chunks) which we treat as words for aligned transcripts\n const words = segments.map(\n (segment: { text?: string; start_time?: number; end_time?: number }) => ({\n text: segment.text ?? '',\n startTime: (segment.start_time ?? 0.0) + this.startTimeOffset,\n endTime: (segment.end_time ?? 0.0) + this.startTimeOffset,\n startTimeOffset: this.startTimeOffset,\n confidence: confidence,\n }),\n );\n\n const speechData: stt.SpeechData = {\n language: languageCode!,\n text: transcript,\n startTime,\n endTime,\n confidence,\n words: words.length > 0 ? words : undefined,\n };\n\n // Handle interim vs final transcripts (matching Python implementation)\n if (!isFinal) {\n // Interim transcript\n this.queue.put({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [speechData],\n });\n } else {\n // Final transcript\n this.queue.put({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [speechData],\n });\n\n // Emit END_OF_SPEECH after final transcript\n if (this.#speaking) {\n this.#speaking = false;\n this.queue.put({ type: stt.SpeechEventType.END_OF_SPEECH });\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(`STT: Error processing message: ${data}`);\n reject(err);\n }\n });\n\n ws.on('error', (err) => {\n if (!closing) {\n reject(err);\n }\n });\n\n ws.on('close', () => {\n if (!closing) {\n resolve();\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.all([sendTask(), listenTask.result]);\n closing = true;\n ws.close();\n }\n}\n"],"mappings":"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","names":["stt"]}
|
|
1
|
+
{"version":3,"sources":["../src/stt.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type AudioBuffer,\n AudioByteStream,\n Task,\n log,\n normalizeLanguage,\n stt,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport type { BasetenSttOptions } from './types.js';\n\nconst defaultSTTOptions: Partial<BasetenSttOptions> = {\n environment: 'production',\n encoding: 'pcm_s16le',\n sampleRate: 16000,\n bufferSizeSeconds: 0.032,\n enablePartialTranscripts: true,\n partialTranscriptIntervalS: 0.5,\n finalTranscriptMaxDurationS: 5,\n audioLanguage: 'en',\n languageDetectionOnly: false,\n vadThreshold: 0.5,\n vadMinSilenceDurationMs: 300,\n vadSpeechPadMs: 30,\n};\n\nexport class STT extends stt.STT {\n #opts: BasetenSttOptions;\n #logger = log();\n label = 'baseten.STT';\n\n constructor(opts: Partial<BasetenSttOptions> = {}) {\n super({\n streaming: true,\n interimResults: opts.enablePartialTranscripts ?? defaultSTTOptions.enablePartialTranscripts!,\n alignedTranscript: 'word',\n });\n\n const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;\n const modelEndpoint = opts.modelEndpoint ?? process.env.BASETEN_MODEL_ENDPOINT;\n const modelId = opts.modelId ?? process.env.BASETEN_STT_MODEL_ID;\n\n if (!apiKey) {\n throw new Error(\n 'Baseten API key is required, either pass it as `apiKey` or set $BASETEN_API_KEY',\n );\n }\n if (!modelEndpoint && !modelId) {\n throw new Error(\n 'Baseten model endpoint is required, either pass it as `modelEndpoint` or set $BASETEN_MODEL_ENDPOINT',\n );\n }\n\n this.#opts = {\n ...defaultSTTOptions,\n ...opts,\n apiKey,\n modelEndpoint,\n modelId,\n audioLanguage: normalizeLanguage((opts.audioLanguage ?? defaultSTTOptions.audioLanguage)!),\n } as BasetenSttOptions;\n }\n\n // eslint-disable-next-line\n async _recognize(_: AudioBuffer): Promise<stt.SpeechEvent> {\n throw new Error('Recognize is not supported on Baseten STT');\n }\n\n updateOptions(opts: Partial<BasetenSttOptions>) {\n this.#opts = {\n ...this.#opts,\n ...opts,\n audioLanguage:\n opts.audioLanguage !== undefined\n ? normalizeLanguage(opts.audioLanguage)\n : this.#opts.audioLanguage,\n };\n }\n\n stream(): SpeechStream {\n return new SpeechStream(this, this.#opts);\n }\n}\n\nexport class SpeechStream extends stt.SpeechStream {\n #opts: BasetenSttOptions;\n #logger = log();\n #speaking = false;\n #requestId = '';\n label = 'baseten.SpeechStream';\n\n constructor(stt: STT, opts: BasetenSttOptions) {\n super(stt, opts.sampleRate);\n this.#opts = opts;\n this.closed = false;\n }\n\n private getWsUrl(): string {\n if (this.#opts.modelEndpoint) {\n return this.#opts.modelEndpoint;\n }\n // Fallback to constructing URL from modelId (deprecated)\n return `wss://model-${this.#opts.modelId}.api.baseten.co/environments/${this.#opts.environment}/websocket`;\n }\n\n protected async run() {\n const maxRetry = 32;\n let retries = 0;\n\n while (!this.input.closed && !this.closed) {\n const url = this.getWsUrl();\n const headers = {\n Authorization: `Api-Key ${this.#opts.apiKey}`,\n };\n\n const ws = new WebSocket(url, { headers, rejectUnauthorized: false });\n\n try {\n await new Promise((resolve, reject) => {\n ws.on('open', resolve);\n ws.on('error', (error) => reject(error));\n ws.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n await this.#runWS(ws);\n } catch (e) {\n if (!this.closed && !this.input.closed) {\n if (retries >= maxRetry) {\n throw new Error(`failed to connect to Baseten after ${retries} attempts: ${e}`);\n }\n\n const delay = Math.min(retries * 5, 10);\n retries++;\n\n this.#logger.warn(\n `failed to connect to Baseten, retrying in ${delay} seconds: ${e} (${retries}/${maxRetry})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n } else {\n this.#logger.warn(\n `Baseten disconnected, connection is closed: ${e} (inputClosed: ${this.input.closed}, isClosed: ${this.closed})`,\n );\n }\n }\n }\n\n this.closed = true;\n }\n\n async #runWS(ws: WebSocket) {\n let closing = false;\n\n // Send initial metadata\n // Note: Baseten server expects 'vad_params' and 'streaming_whisper_params' field names\n // (not 'streaming_vad_config', 'streaming_params', 'whisper_params' as in older versions)\n const metadata = {\n vad_params: {\n threshold: this.#opts.vadThreshold,\n min_silence_duration_ms: this.#opts.vadMinSilenceDurationMs,\n speech_pad_ms: this.#opts.vadSpeechPadMs,\n },\n streaming_whisper_params: {\n encoding: this.#opts.encoding ?? 'pcm_s16le',\n sample_rate: this.#opts.sampleRate ?? 16000,\n enable_partial_transcripts: false,\n audio_language: this.#opts.audioLanguage ?? 'en',\n show_word_timestamps: true,\n },\n };\n\n ws.send(JSON.stringify(metadata));\n\n const sendTask = async () => {\n const sampleRate = this.#opts.sampleRate ?? 16000;\n const samplesPerChunk = sampleRate === 16000 ? 512 : 256;\n const audioByteStream = new AudioByteStream(sampleRate, 1, samplesPerChunk);\n\n try {\n while (!this.closed) {\n const result = await this.input.next();\n if (result.done) {\n break;\n }\n\n const data = result.value;\n\n let frames: AudioFrame[];\n if (data === SpeechStream.FLUSH_SENTINEL) {\n // Flush any remaining buffered audio\n frames = audioByteStream.flush();\n } else {\n if (data.sampleRate !== sampleRate || data.channels !== 1) {\n throw new Error(\n `sample rate or channel count mismatch: expected ${sampleRate}Hz/1ch, got ${data.sampleRate}Hz/${data.channels}ch`,\n );\n }\n frames = audioByteStream.write(data.data.buffer as ArrayBuffer);\n }\n\n for (const frame of frames) {\n const buffer = Buffer.from(\n frame.data.buffer,\n frame.data.byteOffset,\n frame.data.byteLength,\n );\n ws.send(buffer);\n }\n }\n } finally {\n closing = true;\n ws.close();\n }\n };\n\n const listenTask = Task.from(async (controller) => {\n const listenMessage = new Promise<void>((resolve, reject) => {\n ws.on('message', (data) => {\n try {\n let jsonString: string;\n\n if (typeof data === 'string') {\n jsonString = data;\n } else if (data instanceof Buffer) {\n jsonString = data.toString('utf-8');\n } else if (Array.isArray(data)) {\n jsonString = Buffer.concat(data).toString('utf-8');\n } else {\n return;\n }\n\n const msg = JSON.parse(jsonString);\n const isFinal = msg.is_final ?? true;\n const segments = msg.segments ?? [];\n const transcript = msg.transcript ?? '';\n const confidence = msg.confidence ?? 0.0;\n const languageCode = normalizeLanguage(msg.language_code ?? this.#opts.audioLanguage);\n\n // Skip if no transcript text\n if (!transcript) {\n this.#logger.debug('Received non-transcript message:', msg);\n return;\n }\n\n // Emit START_OF_SPEECH if not already speaking (only for interim or first final)\n if (!this.#speaking && !isFinal) {\n this.#speaking = true;\n this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH });\n }\n\n // Note: Baseten uses 'start_time' and 'end_time' field names (with underscores)\n const startTime =\n segments.length > 0\n ? (segments[0].start_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n const endTime =\n segments.length > 0\n ? (segments[segments.length - 1].end_time ?? 0.0) + this.startTimeOffset\n : this.startTimeOffset;\n\n // Note: Baseten returns segments (chunks) which we treat as words for aligned transcripts\n const words = segments.map(\n (segment: { text?: string; start_time?: number; end_time?: number }) => ({\n text: segment.text ?? '',\n startTime: (segment.start_time ?? 0.0) + this.startTimeOffset,\n endTime: (segment.end_time ?? 0.0) + this.startTimeOffset,\n startTimeOffset: this.startTimeOffset,\n confidence: confidence,\n }),\n );\n\n const speechData: stt.SpeechData = {\n language: languageCode,\n text: transcript,\n startTime,\n endTime,\n confidence,\n words: words.length > 0 ? words : undefined,\n };\n\n // Handle interim vs final transcripts (matching Python implementation)\n if (!isFinal) {\n // Interim transcript\n this.queue.put({\n type: stt.SpeechEventType.INTERIM_TRANSCRIPT,\n alternatives: [speechData],\n });\n } else {\n // Final transcript\n this.queue.put({\n type: stt.SpeechEventType.FINAL_TRANSCRIPT,\n alternatives: [speechData],\n });\n\n // Emit END_OF_SPEECH after final transcript\n if (this.#speaking) {\n this.#speaking = false;\n this.queue.put({ type: stt.SpeechEventType.END_OF_SPEECH });\n }\n }\n\n if (this.closed || closing) {\n resolve();\n }\n } catch (err) {\n this.#logger.error(`STT: Error processing message: ${data}`);\n reject(err);\n }\n });\n\n ws.on('error', (err) => {\n if (!closing) {\n reject(err);\n }\n });\n\n ws.on('close', () => {\n if (!closing) {\n resolve();\n }\n });\n });\n\n await Promise.race([listenMessage, waitForAbort(controller.signal)]);\n }, this.abortController);\n\n await Promise.all([sendTask(), listenTask.result]);\n closing = true;\n ws.close();\n }\n}\n"],"mappings":"AAGA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,iBAAiB;AAG1B,MAAM,oBAAgD;AAAA,EACpD,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,0BAA0B;AAAA,EAC1B,4BAA4B;AAAA,EAC5B,6BAA6B;AAAA,EAC7B,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,yBAAyB;AAAA,EACzB,gBAAgB;AAClB;AAEO,MAAM,YAAY,IAAI,IAAI;AAAA,EAC/B;AAAA,EACA,UAAU,IAAI;AAAA,EACd,QAAQ;AAAA,EAER,YAAY,OAAmC,CAAC,GAAG;AACjD,UAAM;AAAA,MACJ,WAAW;AAAA,MACX,gBAAgB,KAAK,4BAA4B,kBAAkB;AAAA,MACnE,mBAAmB;AAAA,IACrB,CAAC;AAED,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,UAAM,gBAAgB,KAAK,iBAAiB,QAAQ,IAAI;AACxD,UAAM,UAAU,KAAK,WAAW,QAAQ,IAAI;AAE5C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,iBAAiB,CAAC,SAAS;AAC9B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,SAAK,QAAQ;AAAA,MACX,GAAG;AAAA,MACH,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,kBAAmB,KAAK,iBAAiB,kBAAkB,aAAe;AAAA,IAC3F;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,WAAW,GAA0C;AACzD,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AAAA,EAEA,cAAc,MAAkC;AAC9C,SAAK,QAAQ;AAAA,MACX,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,MACH,eACE,KAAK,kBAAkB,SACnB,kBAAkB,KAAK,aAAa,IACpC,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,SAAuB;AACrB,WAAO,IAAI,aAAa,MAAM,KAAK,KAAK;AAAA,EAC1C;AACF;AAEO,MAAM,qBAAqB,IAAI,aAAa;AAAA,EACjD;AAAA,EACA,UAAU,IAAI;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,QAAQ;AAAA,EAER,YAAYA,MAAU,MAAyB;AAC7C,UAAMA,MAAK,KAAK,UAAU;AAC1B,SAAK,QAAQ;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEQ,WAAmB;AACzB,QAAI,KAAK,MAAM,eAAe;AAC5B,aAAO,KAAK,MAAM;AAAA,IACpB;AAEA,WAAO,eAAe,KAAK,MAAM,OAAO,gCAAgC,KAAK,MAAM,WAAW;AAAA,EAChG;AAAA,EAEA,MAAgB,MAAM;AACpB,UAAM,WAAW;AACjB,QAAI,UAAU;AAEd,WAAO,CAAC,KAAK,MAAM,UAAU,CAAC,KAAK,QAAQ;AACzC,YAAM,MAAM,KAAK,SAAS;AAC1B,YAAM,UAAU;AAAA,QACd,eAAe,WAAW,KAAK,MAAM,MAAM;AAAA,MAC7C;AAEA,YAAM,KAAK,IAAI,UAAU,KAAK,EAAE,SAAS,oBAAoB,MAAM,CAAC;AAEpE,UAAI;AACF,cAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AACrC,aAAG,GAAG,QAAQ,OAAO;AACrB,aAAG,GAAG,SAAS,CAAC,UAAU,OAAO,KAAK,CAAC;AACvC,aAAG,GAAG,SAAS,CAAC,SAAS,OAAO,sBAAsB,IAAI,EAAE,CAAC;AAAA,QAC/D,CAAC;AAED,cAAM,KAAK,OAAO,EAAE;AAAA,MACtB,SAAS,GAAG;AACV,YAAI,CAAC,KAAK,UAAU,CAAC,KAAK,MAAM,QAAQ;AACtC,cAAI,WAAW,UAAU;AACvB,kBAAM,IAAI,MAAM,sCAAsC,OAAO,cAAc,CAAC,EAAE;AAAA,UAChF;AAEA,gBAAM,QAAQ,KAAK,IAAI,UAAU,GAAG,EAAE;AACtC;AAEA,eAAK,QAAQ;AAAA,YACX,6CAA6C,KAAK,aAAa,CAAC,KAAK,OAAO,IAAI,QAAQ;AAAA,UAC1F;AACA,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,QAAQ,GAAI,CAAC;AAAA,QAClE,OAAO;AACL,eAAK,QAAQ;AAAA,YACX,+CAA+C,CAAC,kBAAkB,KAAK,MAAM,MAAM,eAAe,KAAK,MAAM;AAAA,UAC/G;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,OAAO,IAAe;AAC1B,QAAI,UAAU;AAKd,UAAM,WAAW;AAAA,MACf,YAAY;AAAA,QACV,WAAW,KAAK,MAAM;AAAA,QACtB,yBAAyB,KAAK,MAAM;AAAA,QACpC,eAAe,KAAK,MAAM;AAAA,MAC5B;AAAA,MACA,0BAA0B;AAAA,QACxB,UAAU,KAAK,MAAM,YAAY;AAAA,QACjC,aAAa,KAAK,MAAM,cAAc;AAAA,QACtC,4BAA4B;AAAA,QAC5B,gBAAgB,KAAK,MAAM,iBAAiB;AAAA,QAC5C,sBAAsB;AAAA,MACxB;AAAA,IACF;AAEA,OAAG,KAAK,KAAK,UAAU,QAAQ,CAAC;AAEhC,UAAM,WAAW,YAAY;AAC3B,YAAM,aAAa,KAAK,MAAM,cAAc;AAC5C,YAAM,kBAAkB,eAAe,OAAQ,MAAM;AACrD,YAAM,kBAAkB,IAAI,gBAAgB,YAAY,GAAG,eAAe;AAE1E,UAAI;AACF,eAAO,CAAC,KAAK,QAAQ;AACnB,gBAAM,SAAS,MAAM,KAAK,MAAM,KAAK;AACrC,cAAI,OAAO,MAAM;AACf;AAAA,UACF;AAEA,gBAAM,OAAO,OAAO;AAEpB,cAAI;AACJ,cAAI,SAAS,aAAa,gBAAgB;AAExC,qBAAS,gBAAgB,MAAM;AAAA,UACjC,OAAO;AACL,gBAAI,KAAK,eAAe,cAAc,KAAK,aAAa,GAAG;AACzD,oBAAM,IAAI;AAAA,gBACR,mDAAmD,UAAU,eAAe,KAAK,UAAU,MAAM,KAAK,QAAQ;AAAA,cAChH;AAAA,YACF;AACA,qBAAS,gBAAgB,MAAM,KAAK,KAAK,MAAqB;AAAA,UAChE;AAEA,qBAAW,SAAS,QAAQ;AAC1B,kBAAM,SAAS,OAAO;AAAA,cACpB,MAAM,KAAK;AAAA,cACX,MAAM,KAAK;AAAA,cACX,MAAM,KAAK;AAAA,YACb;AACA,eAAG,KAAK,MAAM;AAAA,UAChB;AAAA,QACF;AAAA,MACF,UAAE;AACA,kBAAU;AACV,WAAG,MAAM;AAAA,MACX;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,KAAK,OAAO,eAAe;AACjD,YAAM,gBAAgB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,WAAG,GAAG,WAAW,CAAC,SAAS;AACzB,cAAI;AACF,gBAAI;AAEJ,gBAAI,OAAO,SAAS,UAAU;AAC5B,2BAAa;AAAA,YACf,WAAW,gBAAgB,QAAQ;AACjC,2BAAa,KAAK,SAAS,OAAO;AAAA,YACpC,WAAW,MAAM,QAAQ,IAAI,GAAG;AAC9B,2BAAa,OAAO,OAAO,IAAI,EAAE,SAAS,OAAO;AAAA,YACnD,OAAO;AACL;AAAA,YACF;AAEA,kBAAM,MAAM,KAAK,MAAM,UAAU;AACjC,kBAAM,UAAU,IAAI,YAAY;AAChC,kBAAM,WAAW,IAAI,YAAY,CAAC;AAClC,kBAAM,aAAa,IAAI,cAAc;AACrC,kBAAM,aAAa,IAAI,cAAc;AACrC,kBAAM,eAAe,kBAAkB,IAAI,iBAAiB,KAAK,MAAM,aAAa;AAGpF,gBAAI,CAAC,YAAY;AACf,mBAAK,QAAQ,MAAM,oCAAoC,GAAG;AAC1D;AAAA,YACF;AAGA,gBAAI,CAAC,KAAK,aAAa,CAAC,SAAS;AAC/B,mBAAK,YAAY;AACjB,mBAAK,MAAM,IAAI,EAAE,MAAM,IAAI,gBAAgB,gBAAgB,CAAC;AAAA,YAC9D;AAGA,kBAAM,YACJ,SAAS,SAAS,KACb,SAAS,CAAC,EAAE,cAAc,KAAO,KAAK,kBACvC,KAAK;AACX,kBAAM,UACJ,SAAS,SAAS,KACb,SAAS,SAAS,SAAS,CAAC,EAAE,YAAY,KAAO,KAAK,kBACvD,KAAK;AAGX,kBAAM,QAAQ,SAAS;AAAA,cACrB,CAAC,aAAwE;AAAA,gBACvE,MAAM,QAAQ,QAAQ;AAAA,gBACtB,YAAY,QAAQ,cAAc,KAAO,KAAK;AAAA,gBAC9C,UAAU,QAAQ,YAAY,KAAO,KAAK;AAAA,gBAC1C,iBAAiB,KAAK;AAAA,gBACtB;AAAA,cACF;AAAA,YACF;AAEA,kBAAM,aAA6B;AAAA,cACjC,UAAU;AAAA,cACV,MAAM;AAAA,cACN;AAAA,cACA;AAAA,cACA;AAAA,cACA,OAAO,MAAM,SAAS,IAAI,QAAQ;AAAA,YACpC;AAGA,gBAAI,CAAC,SAAS;AAEZ,mBAAK,MAAM,IAAI;AAAA,gBACb,MAAM,IAAI,gBAAgB;AAAA,gBAC1B,cAAc,CAAC,UAAU;AAAA,cAC3B,CAAC;AAAA,YACH,OAAO;AAEL,mBAAK,MAAM,IAAI;AAAA,gBACb,MAAM,IAAI,gBAAgB;AAAA,gBAC1B,cAAc,CAAC,UAAU;AAAA,cAC3B,CAAC;AAGD,kBAAI,KAAK,WAAW;AAClB,qBAAK,YAAY;AACjB,qBAAK,MAAM,IAAI,EAAE,MAAM,IAAI,gBAAgB,cAAc,CAAC;AAAA,cAC5D;AAAA,YACF;AAEA,gBAAI,KAAK,UAAU,SAAS;AAC1B,sBAAQ;AAAA,YACV;AAAA,UACF,SAAS,KAAK;AACZ,iBAAK,QAAQ,MAAM,kCAAkC,IAAI,EAAE;AAC3D,mBAAO,GAAG;AAAA,UACZ;AAAA,QACF,CAAC;AAED,WAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,cAAI,CAAC,SAAS;AACZ,mBAAO,GAAG;AAAA,UACZ;AAAA,QACF,CAAC;AAED,WAAG,GAAG,SAAS,MAAM;AACnB,cAAI,CAAC,SAAS;AACZ,oBAAQ;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,YAAM,QAAQ,KAAK,CAAC,eAAe,aAAa,WAAW,MAAM,CAAC,CAAC;AAAA,IACrE,GAAG,KAAK,eAAe;AAEvB,UAAM,QAAQ,IAAI,CAAC,SAAS,GAAG,WAAW,MAAM,CAAC;AACjD,cAAU;AACV,OAAG,MAAM;AAAA,EACX;AACF;","names":["stt"]}
|
package/dist/stt.test.cjs
CHANGED
|
@@ -3,7 +3,17 @@ var import_agents_plugin_silero = require("@livekit/agents-plugin-silero");
|
|
|
3
3
|
var import_agents_plugins_test = require("@livekit/agents-plugins-test");
|
|
4
4
|
var import_vitest = require("vitest");
|
|
5
5
|
var import_stt = require("./stt.cjs");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const hasBasetenStreamingConfig = Boolean(
|
|
7
|
+
process.env.BASETEN_API_KEY && (process.env.BASETEN_MODEL_ENDPOINT || process.env.BASETEN_STT_MODEL_ID)
|
|
8
|
+
);
|
|
9
|
+
if (hasBasetenStreamingConfig) {
|
|
10
|
+
(0, import_vitest.describe)("Baseten", async () => {
|
|
11
|
+
await (0, import_agents_plugins_test.stt)(new import_stt.STT(), await import_agents_plugin_silero.VAD.load(), { streaming: true });
|
|
12
|
+
});
|
|
13
|
+
} else {
|
|
14
|
+
(0, import_vitest.describe)("Baseten", () => {
|
|
15
|
+
import_vitest.it.skip("requires Baseten streaming credentials/config", () => {
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
9
19
|
//# sourceMappingURL=stt.test.cjs.map
|
package/dist/stt.test.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stt.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VAD } from '@livekit/agents-plugin-silero';\nimport { stt } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { STT } from './stt.js';\n\
|
|
1
|
+
{"version":3,"sources":["../src/stt.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VAD } from '@livekit/agents-plugin-silero';\nimport { stt } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { STT } from './stt.js';\n\nconst hasBasetenStreamingConfig = Boolean(\n process.env.BASETEN_API_KEY &&\n (process.env.BASETEN_MODEL_ENDPOINT || process.env.BASETEN_STT_MODEL_ID),\n);\n\nif (hasBasetenStreamingConfig) {\n describe('Baseten', async () => {\n await stt(new STT(), await VAD.load(), { streaming: true });\n });\n} else {\n describe('Baseten', () => {\n it.skip('requires Baseten streaming credentials/config', () => {});\n });\n}\n"],"mappings":";AAGA,kCAAoB;AACpB,iCAAoB;AACpB,oBAA6B;AAC7B,iBAAoB;AAEpB,MAAM,4BAA4B;AAAA,EAChC,QAAQ,IAAI,oBACT,QAAQ,IAAI,0BAA0B,QAAQ,IAAI;AACvD;AAEA,IAAI,2BAA2B;AAC7B,8BAAS,WAAW,YAAY;AAC9B,cAAM,gCAAI,IAAI,eAAI,GAAG,MAAM,gCAAI,KAAK,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EAC5D,CAAC;AACH,OAAO;AACL,8BAAS,WAAW,MAAM;AACxB,qBAAG,KAAK,iDAAiD,MAAM;AAAA,IAAC,CAAC;AAAA,EACnE,CAAC;AACH;","names":[]}
|
package/dist/stt.test.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { VAD } from "@livekit/agents-plugin-silero";
|
|
2
2
|
import { stt } from "@livekit/agents-plugins-test";
|
|
3
|
-
import { describe } from "vitest";
|
|
3
|
+
import { describe, it } from "vitest";
|
|
4
4
|
import { STT } from "./stt.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const hasBasetenStreamingConfig = Boolean(
|
|
6
|
+
process.env.BASETEN_API_KEY && (process.env.BASETEN_MODEL_ENDPOINT || process.env.BASETEN_STT_MODEL_ID)
|
|
7
|
+
);
|
|
8
|
+
if (hasBasetenStreamingConfig) {
|
|
9
|
+
describe("Baseten", async () => {
|
|
10
|
+
await stt(new STT(), await VAD.load(), { streaming: true });
|
|
11
|
+
});
|
|
12
|
+
} else {
|
|
13
|
+
describe("Baseten", () => {
|
|
14
|
+
it.skip("requires Baseten streaming credentials/config", () => {
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
8
18
|
//# sourceMappingURL=stt.test.js.map
|
package/dist/stt.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stt.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VAD } from '@livekit/agents-plugin-silero';\nimport { stt } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { STT } from './stt.js';\n\
|
|
1
|
+
{"version":3,"sources":["../src/stt.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VAD } from '@livekit/agents-plugin-silero';\nimport { stt } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { STT } from './stt.js';\n\nconst hasBasetenStreamingConfig = Boolean(\n process.env.BASETEN_API_KEY &&\n (process.env.BASETEN_MODEL_ENDPOINT || process.env.BASETEN_STT_MODEL_ID),\n);\n\nif (hasBasetenStreamingConfig) {\n describe('Baseten', async () => {\n await stt(new STT(), await VAD.load(), { streaming: true });\n });\n} else {\n describe('Baseten', () => {\n it.skip('requires Baseten streaming credentials/config', () => {});\n });\n}\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,WAAW;AACpB,SAAS,UAAU,UAAU;AAC7B,SAAS,WAAW;AAEpB,MAAM,4BAA4B;AAAA,EAChC,QAAQ,IAAI,oBACT,QAAQ,IAAI,0BAA0B,QAAQ,IAAI;AACvD;AAEA,IAAI,2BAA2B;AAC7B,WAAS,WAAW,YAAY;AAC9B,UAAM,IAAI,IAAI,IAAI,GAAG,MAAM,IAAI,KAAK,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EAC5D,CAAC;AACH,OAAO;AACL,WAAS,WAAW,MAAM;AACxB,OAAG,KAAK,iDAAiD,MAAM;AAAA,IAAC,CAAC;AAAA,EACnE,CAAC;AACH;","names":[]}
|
package/dist/tts.test.cjs
CHANGED
|
@@ -3,7 +3,17 @@ var import_agents_plugins_test = require("@livekit/agents-plugins-test");
|
|
|
3
3
|
var import_vitest = require("vitest");
|
|
4
4
|
var import_stt = require("./stt.cjs");
|
|
5
5
|
var import_tts = require("./tts.cjs");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const hasBasetenStreamingConfig = Boolean(
|
|
7
|
+
process.env.BASETEN_API_KEY && process.env.BASETEN_MODEL_ENDPOINT
|
|
8
|
+
);
|
|
9
|
+
if (hasBasetenStreamingConfig) {
|
|
10
|
+
(0, import_vitest.describe)("Baseten", async () => {
|
|
11
|
+
await (0, import_agents_plugins_test.tts)(new import_tts.TTS(), new import_stt.STT(), { streaming: false });
|
|
12
|
+
});
|
|
13
|
+
} else {
|
|
14
|
+
(0, import_vitest.describe)("Baseten", () => {
|
|
15
|
+
import_vitest.it.skip("requires Baseten streaming credentials/config", () => {
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
9
19
|
//# sourceMappingURL=tts.test.cjs.map
|
package/dist/tts.test.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { STT } from './stt.js';\nimport { TTS } from './tts.js';\n\
|
|
1
|
+
{"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { STT } from './stt.js';\nimport { TTS } from './tts.js';\n\nconst hasBasetenStreamingConfig = Boolean(\n process.env.BASETEN_API_KEY && process.env.BASETEN_MODEL_ENDPOINT,\n);\n\nif (hasBasetenStreamingConfig) {\n describe('Baseten', async () => {\n await tts(new TTS(), new STT(), { streaming: false });\n });\n} else {\n describe('Baseten', () => {\n it.skip('requires Baseten streaming credentials/config', () => {});\n });\n}\n"],"mappings":";AAGA,iCAAoB;AACpB,oBAA6B;AAC7B,iBAAoB;AACpB,iBAAoB;AAEpB,MAAM,4BAA4B;AAAA,EAChC,QAAQ,IAAI,mBAAmB,QAAQ,IAAI;AAC7C;AAEA,IAAI,2BAA2B;AAC7B,8BAAS,WAAW,YAAY;AAC9B,cAAM,gCAAI,IAAI,eAAI,GAAG,IAAI,eAAI,GAAG,EAAE,WAAW,MAAM,CAAC;AAAA,EACtD,CAAC;AACH,OAAO;AACL,8BAAS,WAAW,MAAM;AACxB,qBAAG,KAAK,iDAAiD,MAAM;AAAA,IAAC,CAAC;AAAA,EACnE,CAAC;AACH;","names":[]}
|
package/dist/tts.test.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { tts } from "@livekit/agents-plugins-test";
|
|
2
|
-
import { describe } from "vitest";
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
3
|
import { STT } from "./stt.js";
|
|
4
4
|
import { TTS } from "./tts.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const hasBasetenStreamingConfig = Boolean(
|
|
6
|
+
process.env.BASETEN_API_KEY && process.env.BASETEN_MODEL_ENDPOINT
|
|
7
|
+
);
|
|
8
|
+
if (hasBasetenStreamingConfig) {
|
|
9
|
+
describe("Baseten", async () => {
|
|
10
|
+
await tts(new TTS(), new STT(), { streaming: false });
|
|
11
|
+
});
|
|
12
|
+
} else {
|
|
13
|
+
describe("Baseten", () => {
|
|
14
|
+
it.skip("requires Baseten streaming credentials/config", () => {
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
8
18
|
//# sourceMappingURL=tts.test.js.map
|
package/dist/tts.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { STT } from './stt.js';\nimport { TTS } from './tts.js';\n\
|
|
1
|
+
{"version":3,"sources":["../src/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { tts } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { STT } from './stt.js';\nimport { TTS } from './tts.js';\n\nconst hasBasetenStreamingConfig = Boolean(\n process.env.BASETEN_API_KEY && process.env.BASETEN_MODEL_ENDPOINT,\n);\n\nif (hasBasetenStreamingConfig) {\n describe('Baseten', async () => {\n await tts(new TTS(), new STT(), { streaming: false });\n });\n} else {\n describe('Baseten', () => {\n it.skip('requires Baseten streaming credentials/config', () => {});\n });\n}\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,UAAU,UAAU;AAC7B,SAAS,WAAW;AACpB,SAAS,WAAW;AAEpB,MAAM,4BAA4B;AAAA,EAChC,QAAQ,IAAI,mBAAmB,QAAQ,IAAI;AAC7C;AAEA,IAAI,2BAA2B;AAC7B,WAAS,WAAW,YAAY;AAC9B,UAAM,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,WAAW,MAAM,CAAC;AAAA,EACtD,CAAC;AACH,OAAO;AACL,WAAS,WAAW,MAAM;AACxB,OAAG,KAAK,iDAAiD,MAAM;AAAA,IAAC,CAAC;AAAA,EACnE,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livekit/agents-plugin-baseten",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.51",
|
|
4
4
|
"description": "Baseten plugin for LiveKit Node Agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"require": "dist/index.cjs",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"@types/ws": "^8.5.8",
|
|
41
41
|
"tsx": "^4.7.0",
|
|
42
42
|
"typescript": "^5.9.3",
|
|
43
|
-
"@livekit/agents": "1.0.
|
|
44
|
-
"@livekit/agents-plugin-silero": "1.0.
|
|
45
|
-
"@livekit/agents-plugins-test": "1.0.
|
|
43
|
+
"@livekit/agents": "1.0.51",
|
|
44
|
+
"@livekit/agents-plugin-silero": "1.0.51",
|
|
45
|
+
"@livekit/agents-plugins-test": "1.0.51"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"dotenv": "^17.2.3",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@livekit/rtc-node": "^0.13.24",
|
|
54
|
-
"@livekit/agents": "1.0.
|
|
54
|
+
"@livekit/agents": "1.0.51"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup --onSuccess \"pnpm build:types\"",
|
package/src/llm.test.ts
CHANGED
|
@@ -2,15 +2,23 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import { llm } from '@livekit/agents-plugins-test';
|
|
5
|
-
import { describe } from 'vitest';
|
|
5
|
+
import { describe, it } from 'vitest';
|
|
6
6
|
import { LLM } from './llm.js';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})
|
|
8
|
+
const hasBasetenApiKey = Boolean(process.env.BASETEN_API_KEY);
|
|
9
|
+
|
|
10
|
+
if (hasBasetenApiKey) {
|
|
11
|
+
describe('Baseten', async () => {
|
|
12
|
+
await llm(
|
|
13
|
+
new LLM({
|
|
14
|
+
model: 'openai/gpt-4o-mini',
|
|
15
|
+
temperature: 0,
|
|
16
|
+
}),
|
|
17
|
+
false,
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
describe('Baseten', () => {
|
|
22
|
+
it.skip('requires BASETEN_API_KEY', () => {});
|
|
23
|
+
});
|
|
24
|
+
}
|
package/src/stt.test.ts
CHANGED
|
@@ -3,9 +3,20 @@
|
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import { VAD } from '@livekit/agents-plugin-silero';
|
|
5
5
|
import { stt } from '@livekit/agents-plugins-test';
|
|
6
|
-
import { describe } from 'vitest';
|
|
6
|
+
import { describe, it } from 'vitest';
|
|
7
7
|
import { STT } from './stt.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const hasBasetenStreamingConfig = Boolean(
|
|
10
|
+
process.env.BASETEN_API_KEY &&
|
|
11
|
+
(process.env.BASETEN_MODEL_ENDPOINT || process.env.BASETEN_STT_MODEL_ID),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
if (hasBasetenStreamingConfig) {
|
|
15
|
+
describe('Baseten', async () => {
|
|
16
|
+
await stt(new STT(), await VAD.load(), { streaming: true });
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
describe('Baseten', () => {
|
|
20
|
+
it.skip('requires Baseten streaming credentials/config', () => {});
|
|
21
|
+
});
|
|
22
|
+
}
|
package/src/stt.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type AudioBuffer,
|
|
6
|
+
AudioByteStream,
|
|
7
|
+
Task,
|
|
8
|
+
log,
|
|
9
|
+
normalizeLanguage,
|
|
10
|
+
stt,
|
|
11
|
+
waitForAbort,
|
|
12
|
+
} from '@livekit/agents';
|
|
5
13
|
import type { AudioFrame } from '@livekit/rtc-node';
|
|
6
14
|
import { WebSocket } from 'ws';
|
|
7
15
|
import type { BasetenSttOptions } from './types.js';
|
|
@@ -54,6 +62,7 @@ export class STT extends stt.STT {
|
|
|
54
62
|
apiKey,
|
|
55
63
|
modelEndpoint,
|
|
56
64
|
modelId,
|
|
65
|
+
audioLanguage: normalizeLanguage((opts.audioLanguage ?? defaultSTTOptions.audioLanguage)!),
|
|
57
66
|
} as BasetenSttOptions;
|
|
58
67
|
}
|
|
59
68
|
|
|
@@ -63,7 +72,14 @@ export class STT extends stt.STT {
|
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
updateOptions(opts: Partial<BasetenSttOptions>) {
|
|
66
|
-
this.#opts = {
|
|
75
|
+
this.#opts = {
|
|
76
|
+
...this.#opts,
|
|
77
|
+
...opts,
|
|
78
|
+
audioLanguage:
|
|
79
|
+
opts.audioLanguage !== undefined
|
|
80
|
+
? normalizeLanguage(opts.audioLanguage)
|
|
81
|
+
: this.#opts.audioLanguage,
|
|
82
|
+
};
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
stream(): SpeechStream {
|
|
@@ -222,7 +238,7 @@ export class SpeechStream extends stt.SpeechStream {
|
|
|
222
238
|
const segments = msg.segments ?? [];
|
|
223
239
|
const transcript = msg.transcript ?? '';
|
|
224
240
|
const confidence = msg.confidence ?? 0.0;
|
|
225
|
-
const languageCode = msg.language_code ?? this.#opts.audioLanguage;
|
|
241
|
+
const languageCode = normalizeLanguage(msg.language_code ?? this.#opts.audioLanguage);
|
|
226
242
|
|
|
227
243
|
// Skip if no transcript text
|
|
228
244
|
if (!transcript) {
|
|
@@ -258,7 +274,7 @@ export class SpeechStream extends stt.SpeechStream {
|
|
|
258
274
|
);
|
|
259
275
|
|
|
260
276
|
const speechData: stt.SpeechData = {
|
|
261
|
-
language: languageCode
|
|
277
|
+
language: languageCode,
|
|
262
278
|
text: transcript,
|
|
263
279
|
startTime,
|
|
264
280
|
endTime,
|
package/src/tts.test.ts
CHANGED
|
@@ -2,10 +2,20 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import { tts } from '@livekit/agents-plugins-test';
|
|
5
|
-
import { describe } from 'vitest';
|
|
5
|
+
import { describe, it } from 'vitest';
|
|
6
6
|
import { STT } from './stt.js';
|
|
7
7
|
import { TTS } from './tts.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const hasBasetenStreamingConfig = Boolean(
|
|
10
|
+
process.env.BASETEN_API_KEY && process.env.BASETEN_MODEL_ENDPOINT,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
if (hasBasetenStreamingConfig) {
|
|
14
|
+
describe('Baseten', async () => {
|
|
15
|
+
await tts(new TTS(), new STT(), { streaming: false });
|
|
16
|
+
});
|
|
17
|
+
} else {
|
|
18
|
+
describe('Baseten', () => {
|
|
19
|
+
it.skip('requires Baseten streaming credentials/config', () => {});
|
|
20
|
+
});
|
|
21
|
+
}
|