@livekit/agents-plugin-baseten 1.0.31
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/LICENSE +201 -0
- package/README.md +92 -0
- package/dist/index.cjs +48 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.cjs +143 -0
- package/dist/llm.cjs.map +1 -0
- package/dist/llm.d.cts +44 -0
- package/dist/llm.d.ts +44 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +117 -0
- package/dist/llm.js.map +1 -0
- package/dist/llm.test.cjs +14 -0
- package/dist/llm.test.cjs.map +1 -0
- package/dist/llm.test.d.cts +2 -0
- package/dist/llm.test.d.ts +2 -0
- package/dist/llm.test.d.ts.map +1 -0
- package/dist/llm.test.js +13 -0
- package/dist/llm.test.js.map +1 -0
- package/dist/stt.cjs +271 -0
- package/dist/stt.cjs.map +1 -0
- package/dist/stt.d.cts +18 -0
- package/dist/stt.d.ts +18 -0
- package/dist/stt.d.ts.map +1 -0
- package/dist/stt.js +246 -0
- package/dist/stt.js.map +1 -0
- package/dist/stt.test.cjs +9 -0
- package/dist/stt.test.cjs.map +1 -0
- package/dist/stt.test.d.cts +2 -0
- package/dist/stt.test.d.ts +2 -0
- package/dist/stt.test.d.ts.map +1 -0
- package/dist/stt.test.js +8 -0
- package/dist/stt.test.js.map +1 -0
- package/dist/tts.cjs +161 -0
- package/dist/tts.cjs.map +1 -0
- package/dist/tts.d.cts +45 -0
- package/dist/tts.d.ts +45 -0
- package/dist/tts.d.ts.map +1 -0
- package/dist/tts.js +141 -0
- package/dist/tts.js.map +1 -0
- package/dist/tts.test.cjs +9 -0
- package/dist/tts.test.cjs.map +1 -0
- package/dist/tts.test.d.cts +2 -0
- package/dist/tts.test.d.ts +2 -0
- package/dist/tts.test.d.ts.map +1 -0
- package/dist/tts.test.js +8 -0
- package/dist/tts.test.js.map +1 -0
- package/dist/types.cjs +17 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +54 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -0
- package/src/index.ts +20 -0
- package/src/llm.test.ts +16 -0
- package/src/llm.ts +172 -0
- package/src/stt.test.ts +11 -0
- package/src/stt.ts +298 -0
- package/src/tts.test.ts +11 -0
- package/src/tts.ts +202 -0
- package/src/types.ts +55 -0
package/dist/tts.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AudioByteStream,
|
|
3
|
+
shortuuid,
|
|
4
|
+
tts,
|
|
5
|
+
waitForAbort
|
|
6
|
+
} from "@livekit/agents";
|
|
7
|
+
const defaultTTSOptions = {
|
|
8
|
+
voice: "tara",
|
|
9
|
+
language: "en",
|
|
10
|
+
temperature: 0.6
|
|
11
|
+
};
|
|
12
|
+
class TTS extends tts.TTS {
|
|
13
|
+
opts;
|
|
14
|
+
label = "baseten.TTS";
|
|
15
|
+
abortController = new AbortController();
|
|
16
|
+
constructor(opts = {}) {
|
|
17
|
+
super(24e3, 1, { streaming: false });
|
|
18
|
+
const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;
|
|
19
|
+
const modelEndpoint = opts.modelEndpoint ?? process.env.BASETEN_MODEL_ENDPOINT;
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
"Baseten API key is required, either pass it as `apiKey` or set $BASETEN_API_KEY"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (!modelEndpoint) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
"Baseten model endpoint is required, either pass it as `modelEndpoint` or set $BASETEN_MODEL_ENDPOINT"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
this.opts = {
|
|
31
|
+
...defaultTTSOptions,
|
|
32
|
+
...opts,
|
|
33
|
+
apiKey,
|
|
34
|
+
modelEndpoint
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
updateOptions(opts) {
|
|
38
|
+
this.opts = {
|
|
39
|
+
...this.opts,
|
|
40
|
+
...opts
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Synthesize speech for a given piece of text. Returns a `ChunkedStream`
|
|
45
|
+
* which will asynchronously fetch audio from Baseten and push frames into
|
|
46
|
+
* LiveKit's playback pipeline. If you need to cancel synthesis you can
|
|
47
|
+
* call {@link ChunkedStream.stop} on the returned object.
|
|
48
|
+
*/
|
|
49
|
+
synthesize(text, connOptions, abortSignal) {
|
|
50
|
+
return new ChunkedStream(this, text, this.opts, connOptions, abortSignal);
|
|
51
|
+
}
|
|
52
|
+
stream() {
|
|
53
|
+
throw new Error("Streaming is not supported on Baseten TTS");
|
|
54
|
+
}
|
|
55
|
+
async close() {
|
|
56
|
+
this.abortController.abort();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
class ChunkedStream extends tts.ChunkedStream {
|
|
60
|
+
label = "baseten.ChunkedStream";
|
|
61
|
+
opts;
|
|
62
|
+
constructor(tts2, text, opts, connOptions, abortSignal) {
|
|
63
|
+
super(text, tts2, connOptions, abortSignal);
|
|
64
|
+
this.opts = opts;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute the synthesis request. This method is automatically invoked
|
|
68
|
+
* by the base class when the stream starts. It performs a POST request
|
|
69
|
+
* to the configured `modelEndpoint` with the input text and optional
|
|
70
|
+
* parameters. Audio chunks are streamed as they arrive and transformed
|
|
71
|
+
* into a sequence of `AudioFrame` objects that are enqueued immediately
|
|
72
|
+
* for playback.
|
|
73
|
+
*/
|
|
74
|
+
async run() {
|
|
75
|
+
const { apiKey, modelEndpoint, voice, language, temperature, maxTokens } = this.opts;
|
|
76
|
+
const payload = {
|
|
77
|
+
prompt: this.inputText
|
|
78
|
+
};
|
|
79
|
+
if (voice) payload.voice = voice;
|
|
80
|
+
if (language) payload.language = language;
|
|
81
|
+
if (temperature !== void 0) payload.temperature = temperature;
|
|
82
|
+
if (maxTokens !== void 0) payload.max_tokens = maxTokens;
|
|
83
|
+
const headers = {
|
|
84
|
+
Authorization: `Api-Key ${apiKey}`,
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
};
|
|
87
|
+
const response = await fetch(modelEndpoint, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers,
|
|
90
|
+
body: JSON.stringify(payload),
|
|
91
|
+
signal: this.abortSignal
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
let errText;
|
|
95
|
+
try {
|
|
96
|
+
errText = await response.text();
|
|
97
|
+
} catch {
|
|
98
|
+
errText = response.statusText;
|
|
99
|
+
}
|
|
100
|
+
throw new Error(`Baseten TTS request failed: ${response.status} ${errText}`);
|
|
101
|
+
}
|
|
102
|
+
if (!response.body) {
|
|
103
|
+
throw new Error("Response body is not available for streaming");
|
|
104
|
+
}
|
|
105
|
+
const requestId = shortuuid();
|
|
106
|
+
const audioByteStream = new AudioByteStream(24e3, 1);
|
|
107
|
+
const reader = response.body.getReader();
|
|
108
|
+
try {
|
|
109
|
+
let lastFrame;
|
|
110
|
+
const sendLastFrame = (segmentId, final) => {
|
|
111
|
+
if (lastFrame) {
|
|
112
|
+
this.queue.put({ requestId, segmentId, frame: lastFrame, final });
|
|
113
|
+
lastFrame = void 0;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const abortPromise = waitForAbort(this.abortSignal);
|
|
117
|
+
while (!this.abortSignal.aborted) {
|
|
118
|
+
const result = await Promise.race([reader.read(), abortPromise]);
|
|
119
|
+
if (result === void 0) break;
|
|
120
|
+
const { done, value } = result;
|
|
121
|
+
if (done) {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
const frames = audioByteStream.write(value.buffer);
|
|
125
|
+
for (const frame of frames) {
|
|
126
|
+
sendLastFrame(requestId, false);
|
|
127
|
+
lastFrame = frame;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
sendLastFrame(requestId, true);
|
|
131
|
+
} finally {
|
|
132
|
+
reader.releaseLock();
|
|
133
|
+
this.queue.close();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
ChunkedStream,
|
|
139
|
+
TTS
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=tts.js.map
|
package/dist/tts.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type APIConnectOptions,\n AudioByteStream,\n shortuuid,\n tts,\n waitForAbort,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { BasetenTTSOptions } from './types.js';\n\nconst defaultTTSOptions: Partial<BasetenTTSOptions> = {\n voice: 'tara',\n language: 'en',\n temperature: 0.6,\n};\n\n/**\n * Baseten TTS implementation (streaming, 24kHz mono)\n */\nexport class TTS extends tts.TTS {\n private opts: BasetenTTSOptions;\n label = 'baseten.TTS';\n private abortController = new AbortController();\n constructor(opts: Partial<BasetenTTSOptions> = {}) {\n /**\n * Baseten audio is 24kHz mono.\n * The Orpheus model generates audio chunks that are processed as they arrive,\n * which reduces latency and improves agent responsiveness.\n */\n super(24000, 1, { streaming: false });\n\n // Apply defaults and environment fallbacks.\n const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;\n const modelEndpoint = opts.modelEndpoint ?? process.env.BASETEN_MODEL_ENDPOINT;\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) {\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 ...defaultTTSOptions,\n ...opts,\n apiKey,\n modelEndpoint,\n } as BasetenTTSOptions;\n }\n\n updateOptions(opts: Partial<Omit<BasetenTTSOptions, 'apiKey' | 'modelEndpoint'>>) {\n this.opts = {\n ...this.opts,\n ...opts,\n } as BasetenTTSOptions;\n }\n\n /**\n * Synthesize speech for a given piece of text. Returns a `ChunkedStream`\n * which will asynchronously fetch audio from Baseten and push frames into\n * LiveKit's playback pipeline. If you need to cancel synthesis you can\n * call {@link ChunkedStream.stop} on the returned object.\n */\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream {\n return new ChunkedStream(this, text, this.opts, connOptions, abortSignal);\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on Baseten TTS');\n }\n\n async close(): Promise<void> {\n this.abortController.abort();\n }\n}\n\n/**\n * Internal helper that performs the actual HTTP request and converts the\n * response into audio frames. It inherits from `tts.ChunkedStream` to\n * integrate with LiveKit's event and cancellation framework.\n *\n * This implementation streams audio chunks as they arrive from the Baseten\n * model endpoint, processing them incrementally instead of waiting for the\n * complete response.\n */\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'baseten.ChunkedStream';\n private readonly opts: BasetenTTSOptions;\n\n constructor(\n tts: TTS,\n text: string,\n opts: BasetenTTSOptions,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(text, tts, connOptions, abortSignal);\n this.opts = opts;\n }\n\n /**\n * Execute the synthesis request. This method is automatically invoked\n * by the base class when the stream starts. It performs a POST request\n * to the configured `modelEndpoint` with the input text and optional\n * parameters. Audio chunks are streamed as they arrive and transformed\n * into a sequence of `AudioFrame` objects that are enqueued immediately\n * for playback.\n */\n protected async run() {\n const { apiKey, modelEndpoint, voice, language, temperature, maxTokens } = this.opts;\n const payload: Record<string, unknown> = {\n prompt: this.inputText,\n };\n if (voice) payload.voice = voice;\n if (language) payload.language = language;\n if (temperature !== undefined) payload.temperature = temperature;\n if (maxTokens !== undefined) payload.max_tokens = maxTokens;\n\n const headers: Record<string, string> = {\n Authorization: `Api-Key ${apiKey}`,\n 'Content-Type': 'application/json',\n };\n\n const response = await fetch(modelEndpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n signal: this.abortSignal,\n });\n\n if (!response.ok) {\n let errText: string;\n try {\n errText = await response.text();\n } catch {\n errText = response.statusText;\n }\n throw new Error(`Baseten TTS request failed: ${response.status} ${errText}`);\n }\n\n // Stream the response body as chunks arrive\n if (!response.body) {\n throw new Error('Response body is not available for streaming');\n }\n\n const requestId = shortuuid();\n const audioByteStream = new AudioByteStream(24000, 1);\n const reader = response.body.getReader();\n\n try {\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n // waitForAbort internally sets up an abort listener on the abort signal\n // we need to put it outside loop to avoid constant re-registration of the listener\n const abortPromise = waitForAbort(this.abortSignal);\n\n while (!this.abortSignal.aborted) {\n const result = await Promise.race([reader.read(), abortPromise]);\n\n if (result === undefined) break; // aborted\n\n const { done, value } = result;\n\n if (done) {\n break;\n }\n\n // Process the chunk and convert to audio frames\n // Convert Uint8Array to ArrayBuffer for AudioByteStream\n const frames = audioByteStream.write(value.buffer);\n\n for (const frame of frames) {\n sendLastFrame(requestId, false);\n lastFrame = frame;\n }\n }\n\n // Send the final frame\n sendLastFrame(requestId, true);\n } finally {\n reader.releaseLock();\n this.queue.close();\n }\n }\n}\n"],"mappings":"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","names":["tts"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_agents_plugins_test = require("@livekit/agents-plugins-test");
|
|
3
|
+
var import_vitest = require("vitest");
|
|
4
|
+
var import_stt = require("./stt.cjs");
|
|
5
|
+
var import_tts = require("./tts.cjs");
|
|
6
|
+
(0, import_vitest.describe)("Baseten", async () => {
|
|
7
|
+
await (0, import_agents_plugins_test.tts)(new import_tts.TTS(), new import_stt.STT(), { streaming: false });
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=tts.test.cjs.map
|
|
@@ -0,0 +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\ndescribe('Baseten', async () => {\n await tts(new TTS(), new STT(), { streaming: false });\n});\n"],"mappings":";AAGA,iCAAoB;AACpB,oBAAyB;AACzB,iBAAoB;AACpB,iBAAoB;AAAA,IAEpB,wBAAS,WAAW,YAAY;AAC9B,YAAM,gCAAI,IAAI,eAAI,GAAG,IAAI,eAAI,GAAG,EAAE,WAAW,MAAM,CAAC;AACtD,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tts.test.d.ts","sourceRoot":"","sources":["../src/tts.test.ts"],"names":[],"mappings":""}
|
package/dist/tts.test.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { tts } from "@livekit/agents-plugins-test";
|
|
2
|
+
import { describe } from "vitest";
|
|
3
|
+
import { STT } from "./stt.js";
|
|
4
|
+
import { TTS } from "./tts.js";
|
|
5
|
+
describe("Baseten", async () => {
|
|
6
|
+
await tts(new TTS(), new STT(), { streaming: false });
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=tts.test.js.map
|
|
@@ -0,0 +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\ndescribe('Baseten', async () => {\n await tts(new TTS(), new STT(), { streaming: false });\n});\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,gBAAgB;AACzB,SAAS,WAAW;AACpB,SAAS,WAAW;AAEpB,SAAS,WAAW,YAAY;AAC9B,QAAM,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,WAAW,MAAM,CAAC;AACtD,CAAC;","names":[]}
|
package/dist/types.cjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var types_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(types_exports);
|
|
17
|
+
//# sourceMappingURL=types.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Baseten plugin types and interfaces\n */\n\n/**\n * Options for configuring the Baseten LLM\n * Since Baseten provides an OpenAI-compatible API, these options\n * map to standard OpenAI parameters.\n */\nexport interface BasetenLLMOptions {\n apiKey?: string;\n model: string;\n temperature?: number;\n maxTokens?: number;\n user?: string;\n toolChoice?: 'none' | 'auto' | 'required' | { type: 'function'; function: { name: string } };\n parallelToolCalls?: boolean;\n}\n\n/**\n * Options for configuring the Baseten STT service\n */\nexport interface BasetenSttOptions {\n apiKey: string;\n modelId: string;\n environment?: string;\n encoding?: string;\n sampleRate?: number;\n bufferSizeSeconds?: number;\n vadThreshold?: number;\n vadMinSilenceDurationMs?: number;\n vadSpeechPadMs?: number;\n enablePartialTranscripts?: boolean;\n partialTranscriptIntervalS?: number;\n finalTranscriptMaxDurationS?: number;\n audioLanguage?: string;\n prompt?: string;\n languageDetectionOnly?: boolean;\n}\n\n/**\n * Options for configuring the Baseten TTS service\n */\nexport interface BasetenTTSOptions {\n apiKey: string;\n modelEndpoint: string;\n voice?: string;\n language?: string;\n temperature?: number;\n maxTokens?: number;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
package/dist/types.d.cts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseten plugin types and interfaces
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for configuring the Baseten LLM
|
|
6
|
+
* Since Baseten provides an OpenAI-compatible API, these options
|
|
7
|
+
* map to standard OpenAI parameters.
|
|
8
|
+
*/
|
|
9
|
+
export interface BasetenLLMOptions {
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
model: string;
|
|
12
|
+
temperature?: number;
|
|
13
|
+
maxTokens?: number;
|
|
14
|
+
user?: string;
|
|
15
|
+
toolChoice?: 'none' | 'auto' | 'required' | {
|
|
16
|
+
type: 'function';
|
|
17
|
+
function: {
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
parallelToolCalls?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Options for configuring the Baseten STT service
|
|
25
|
+
*/
|
|
26
|
+
export interface BasetenSttOptions {
|
|
27
|
+
apiKey: string;
|
|
28
|
+
modelId: string;
|
|
29
|
+
environment?: string;
|
|
30
|
+
encoding?: string;
|
|
31
|
+
sampleRate?: number;
|
|
32
|
+
bufferSizeSeconds?: number;
|
|
33
|
+
vadThreshold?: number;
|
|
34
|
+
vadMinSilenceDurationMs?: number;
|
|
35
|
+
vadSpeechPadMs?: number;
|
|
36
|
+
enablePartialTranscripts?: boolean;
|
|
37
|
+
partialTranscriptIntervalS?: number;
|
|
38
|
+
finalTranscriptMaxDurationS?: number;
|
|
39
|
+
audioLanguage?: string;
|
|
40
|
+
prompt?: string;
|
|
41
|
+
languageDetectionOnly?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for configuring the Baseten TTS service
|
|
45
|
+
*/
|
|
46
|
+
export interface BasetenTTSOptions {
|
|
47
|
+
apiKey: string;
|
|
48
|
+
modelEndpoint: string;
|
|
49
|
+
voice?: string;
|
|
50
|
+
language?: string;
|
|
51
|
+
temperature?: number;
|
|
52
|
+
maxTokens?: number;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseten plugin types and interfaces
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for configuring the Baseten LLM
|
|
6
|
+
* Since Baseten provides an OpenAI-compatible API, these options
|
|
7
|
+
* map to standard OpenAI parameters.
|
|
8
|
+
*/
|
|
9
|
+
export interface BasetenLLMOptions {
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
model: string;
|
|
12
|
+
temperature?: number;
|
|
13
|
+
maxTokens?: number;
|
|
14
|
+
user?: string;
|
|
15
|
+
toolChoice?: 'none' | 'auto' | 'required' | {
|
|
16
|
+
type: 'function';
|
|
17
|
+
function: {
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
parallelToolCalls?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Options for configuring the Baseten STT service
|
|
25
|
+
*/
|
|
26
|
+
export interface BasetenSttOptions {
|
|
27
|
+
apiKey: string;
|
|
28
|
+
modelId: string;
|
|
29
|
+
environment?: string;
|
|
30
|
+
encoding?: string;
|
|
31
|
+
sampleRate?: number;
|
|
32
|
+
bufferSizeSeconds?: number;
|
|
33
|
+
vadThreshold?: number;
|
|
34
|
+
vadMinSilenceDurationMs?: number;
|
|
35
|
+
vadSpeechPadMs?: number;
|
|
36
|
+
enablePartialTranscripts?: boolean;
|
|
37
|
+
partialTranscriptIntervalS?: number;
|
|
38
|
+
finalTranscriptMaxDurationS?: number;
|
|
39
|
+
audioLanguage?: string;
|
|
40
|
+
prompt?: string;
|
|
41
|
+
languageDetectionOnly?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for configuring the Baseten TTS service
|
|
45
|
+
*/
|
|
46
|
+
export interface BasetenTTSOptions {
|
|
47
|
+
apiKey: string;
|
|
48
|
+
modelEndpoint: string;
|
|
49
|
+
voice?: string;
|
|
50
|
+
language?: string;
|
|
51
|
+
temperature?: number;
|
|
52
|
+
maxTokens?: number;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC7F,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@livekit/agents-plugin-baseten",
|
|
3
|
+
"version": "1.0.31",
|
|
4
|
+
"description": "Baseten plugin for LiveKit Node Agents",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"require": "dist/index.cjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"require": {
|
|
14
|
+
"types": "./dist/index.d.cts",
|
|
15
|
+
"default": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"author": "LiveKit",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"repository": "git@github.com:livekit/agents-js.git",
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"livekit",
|
|
29
|
+
"agents",
|
|
30
|
+
"baseten",
|
|
31
|
+
"llm",
|
|
32
|
+
"stt",
|
|
33
|
+
"tts",
|
|
34
|
+
"voice-ai"
|
|
35
|
+
],
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@livekit/rtc-node": "^0.13.22",
|
|
38
|
+
"@microsoft/api-extractor": "^7.35.0",
|
|
39
|
+
"@types/node": "^22.18.11",
|
|
40
|
+
"@types/ws": "^8.5.8",
|
|
41
|
+
"tsx": "^4.7.0",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"@livekit/agents-plugin-silero": "1.0.31",
|
|
44
|
+
"@livekit/agents-plugins-test": "1.0.31",
|
|
45
|
+
"@livekit/agents": "1.0.31"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"dotenv": "^17.2.3",
|
|
49
|
+
"openai": "^6.8.1",
|
|
50
|
+
"ws": "^8.14.2"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@livekit/rtc-node": "^0.13.22",
|
|
54
|
+
"@livekit/agents": "1.0.31"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup --onSuccess \"pnpm build:types\"",
|
|
58
|
+
"build:types": "tsc --declaration --emitDeclarationOnly && node ../../scripts/copyDeclarationOutput.js",
|
|
59
|
+
"clean": "rm -rf dist",
|
|
60
|
+
"clean:build": "pnpm clean && pnpm build",
|
|
61
|
+
"dev": "tsc --watch",
|
|
62
|
+
"typecheck": "tsc --noEmit",
|
|
63
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
64
|
+
"lint:fix": "eslint --fix \"src/**/*.ts\"",
|
|
65
|
+
"api:check": "api-extractor run --typescript-compiler-folder ../../node_modules/typescript",
|
|
66
|
+
"api:update": "api-extractor run --local --typescript-compiler-folder ../../node_modules/typescript --verbose"
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { Plugin } from '@livekit/agents';
|
|
5
|
+
|
|
6
|
+
export { LLM } from './llm.js';
|
|
7
|
+
export { STT } from './stt.js';
|
|
8
|
+
export { TTS, ChunkedStream } from './tts.js';
|
|
9
|
+
export type { BasetenLLMOptions, BasetenSttOptions, BasetenTTSOptions } from './types.js';
|
|
10
|
+
class BasetenPlugin extends Plugin {
|
|
11
|
+
constructor() {
|
|
12
|
+
super({
|
|
13
|
+
title: 'baseten',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
package: 'livekit-plugin-baseten',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Plugin.registerPlugin(new BasetenPlugin());
|
package/src/llm.test.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { llm } from '@livekit/agents-plugins-test';
|
|
5
|
+
import { describe } from 'vitest';
|
|
6
|
+
import { LLM } from './llm.js';
|
|
7
|
+
|
|
8
|
+
describe('Baseten', async () => {
|
|
9
|
+
await llm(
|
|
10
|
+
new LLM({
|
|
11
|
+
model: 'openai/gpt-4o-mini',
|
|
12
|
+
temperature: 0,
|
|
13
|
+
}),
|
|
14
|
+
false,
|
|
15
|
+
);
|
|
16
|
+
});
|
package/src/llm.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Baseten LLM plugin for LiveKit Agents
|
|
7
|
+
* Configures the OpenAI plugin to work with Baseten's OpenAI-compatible API
|
|
8
|
+
*/
|
|
9
|
+
import type { APIConnectOptions } from '@livekit/agents';
|
|
10
|
+
import { DEFAULT_API_CONNECT_OPTIONS, inference, llm } from '@livekit/agents';
|
|
11
|
+
import { OpenAI } from 'openai';
|
|
12
|
+
import type { BasetenLLMOptions } from './types.js';
|
|
13
|
+
|
|
14
|
+
export interface LLMOptions {
|
|
15
|
+
model: string;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
baseURL?: string;
|
|
18
|
+
user?: string;
|
|
19
|
+
temperature?: number;
|
|
20
|
+
client?: OpenAI;
|
|
21
|
+
toolChoice?: llm.ToolChoice;
|
|
22
|
+
parallelToolCalls?: boolean;
|
|
23
|
+
metadata?: Record<string, string>;
|
|
24
|
+
maxCompletionTokens?: number;
|
|
25
|
+
serviceTier?: string;
|
|
26
|
+
store?: boolean;
|
|
27
|
+
strictToolSchema?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const defaultLLMOptions: LLMOptions = {
|
|
31
|
+
model: 'openai/gpt-4o-mini',
|
|
32
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
33
|
+
parallelToolCalls: true,
|
|
34
|
+
strictToolSchema: false,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export class OpenAILLM extends llm.LLM {
|
|
38
|
+
#opts: LLMOptions;
|
|
39
|
+
#client: OpenAI;
|
|
40
|
+
#providerFmt: llm.ProviderFormat;
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
opts: Partial<LLMOptions> = defaultLLMOptions,
|
|
44
|
+
providerFmt: llm.ProviderFormat = 'openai',
|
|
45
|
+
) {
|
|
46
|
+
super();
|
|
47
|
+
|
|
48
|
+
this.#opts = { ...defaultLLMOptions, ...opts };
|
|
49
|
+
this.#providerFmt = providerFmt;
|
|
50
|
+
if (this.#opts.apiKey === undefined) {
|
|
51
|
+
throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.#client =
|
|
55
|
+
this.#opts.client ||
|
|
56
|
+
new OpenAI({
|
|
57
|
+
baseURL: opts.baseURL,
|
|
58
|
+
apiKey: opts.apiKey,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
label(): string {
|
|
63
|
+
return 'openai.LLM';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get model(): string {
|
|
67
|
+
return this.#opts.model;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
chat({
|
|
71
|
+
chatCtx,
|
|
72
|
+
toolCtx,
|
|
73
|
+
connOptions = DEFAULT_API_CONNECT_OPTIONS,
|
|
74
|
+
parallelToolCalls,
|
|
75
|
+
toolChoice,
|
|
76
|
+
extraKwargs,
|
|
77
|
+
}: {
|
|
78
|
+
chatCtx: llm.ChatContext;
|
|
79
|
+
toolCtx?: llm.ToolContext;
|
|
80
|
+
connOptions?: APIConnectOptions;
|
|
81
|
+
parallelToolCalls?: boolean;
|
|
82
|
+
toolChoice?: llm.ToolChoice;
|
|
83
|
+
extraKwargs?: Record<string, unknown>;
|
|
84
|
+
}): inference.LLMStream {
|
|
85
|
+
const extras: Record<string, unknown> = { ...extraKwargs };
|
|
86
|
+
|
|
87
|
+
if (this.#opts.metadata) {
|
|
88
|
+
extras.metadata = this.#opts.metadata;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (this.#opts.user) {
|
|
92
|
+
extras.user = this.#opts.user;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (this.#opts.maxCompletionTokens) {
|
|
96
|
+
extras.max_completion_tokens = this.#opts.maxCompletionTokens;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.#opts.temperature) {
|
|
100
|
+
extras.temperature = this.#opts.temperature;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (this.#opts.serviceTier) {
|
|
104
|
+
extras.service_tier = this.#opts.serviceTier;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (this.#opts.store !== undefined) {
|
|
108
|
+
extras.store = this.#opts.store;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
parallelToolCalls =
|
|
112
|
+
parallelToolCalls !== undefined ? parallelToolCalls : this.#opts.parallelToolCalls;
|
|
113
|
+
if (toolCtx && Object.keys(toolCtx).length > 0 && parallelToolCalls !== undefined) {
|
|
114
|
+
extras.parallel_tool_calls = parallelToolCalls;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
toolChoice = toolChoice !== undefined ? toolChoice : this.#opts.toolChoice;
|
|
118
|
+
if (toolChoice) {
|
|
119
|
+
extras.tool_choice = toolChoice;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return new LLMStream(this as unknown as inference.LLM, {
|
|
123
|
+
model: this.#opts.model,
|
|
124
|
+
providerFmt: this.#providerFmt,
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
client: this.#client as any,
|
|
127
|
+
chatCtx,
|
|
128
|
+
toolCtx,
|
|
129
|
+
connOptions,
|
|
130
|
+
modelOptions: extras,
|
|
131
|
+
strictToolSchema: this.#opts.strictToolSchema || false,
|
|
132
|
+
gatewayOptions: undefined, // OpenAI plugin doesn't use gateway authentication
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export class LLMStream extends inference.LLMStream {}
|
|
138
|
+
|
|
139
|
+
export class LLM extends OpenAILLM {
|
|
140
|
+
constructor(opts: BasetenLLMOptions) {
|
|
141
|
+
const apiKey = opts.apiKey ?? process.env.BASETEN_API_KEY;
|
|
142
|
+
if (!apiKey) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
'Baseten API key is required. Set BASETEN_API_KEY environment variable or pass apiKey in options.',
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!opts.model) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
'Model is required. Please specify a model name (e.g., "openai/gpt-4o-mini").',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const model = opts.model;
|
|
155
|
+
|
|
156
|
+
// Configure the OpenAI plugin with Baseten's endpoint
|
|
157
|
+
super({
|
|
158
|
+
model,
|
|
159
|
+
apiKey,
|
|
160
|
+
baseURL: 'https://inference.baseten.co/v1',
|
|
161
|
+
temperature: opts.temperature,
|
|
162
|
+
user: opts.user,
|
|
163
|
+
maxCompletionTokens: opts.maxTokens,
|
|
164
|
+
toolChoice: opts.toolChoice,
|
|
165
|
+
parallelToolCalls: opts.parallelToolCalls,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
label(): string {
|
|
170
|
+
return 'baseten.LLM';
|
|
171
|
+
}
|
|
172
|
+
}
|
package/src/stt.test.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { VAD } from '@livekit/agents-plugin-silero';
|
|
5
|
+
import { stt } from '@livekit/agents-plugins-test';
|
|
6
|
+
import { describe } from 'vitest';
|
|
7
|
+
import { STT } from './stt.js';
|
|
8
|
+
|
|
9
|
+
describe('Baseten', async () => {
|
|
10
|
+
await stt(new STT(), await VAD.load(), { streaming: true });
|
|
11
|
+
});
|