@livekit/agents 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +26 -0
- package/dist/audio.d.ts +1 -4
- package/dist/audio.d.ts.map +1 -1
- package/dist/audio.js +28 -11
- package/dist/audio.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +36 -13
- package/dist/cli.js.map +1 -1
- package/dist/generator.d.ts +5 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +11 -0
- package/dist/generator.js.map +1 -1
- package/dist/http_server.d.ts.map +1 -1
- package/dist/http_server.js +5 -0
- package/dist/http_server.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc/job_main.js +9 -1
- package/dist/ipc/job_main.js.map +1 -1
- package/dist/ipc/proc_pool.d.ts.map +1 -1
- package/dist/ipc/proc_pool.js +1 -0
- package/dist/ipc/proc_pool.js.map +1 -1
- package/dist/job.d.ts +1 -0
- package/dist/job.d.ts.map +1 -1
- package/dist/job.js +17 -1
- package/dist/job.js.map +1 -1
- package/dist/multimodal/agent_playout.d.ts +34 -0
- package/dist/multimodal/agent_playout.d.ts.map +1 -0
- package/dist/multimodal/agent_playout.js +221 -0
- package/dist/multimodal/agent_playout.js.map +1 -0
- package/dist/multimodal/index.d.ts +3 -0
- package/dist/multimodal/index.d.ts.map +1 -0
- package/dist/multimodal/index.js +6 -0
- package/dist/multimodal/index.js.map +1 -0
- package/dist/multimodal/multimodal_agent.d.ts +47 -0
- package/dist/multimodal/multimodal_agent.d.ts.map +1 -0
- package/dist/multimodal/multimodal_agent.js +331 -0
- package/dist/multimodal/multimodal_agent.js.map +1 -0
- package/dist/transcription.d.ts +22 -0
- package/dist/transcription.d.ts.map +1 -0
- package/dist/transcription.js +111 -0
- package/dist/transcription.js.map +1 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +107 -9
- package/dist/utils.js.map +1 -1
- package/dist/worker.d.ts +3 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +44 -8
- package/dist/worker.js.map +1 -1
- package/package.json +6 -4
- package/src/audio.ts +19 -19
- package/src/cli.ts +37 -13
- package/src/generator.ts +14 -0
- package/src/http_server.ts +5 -0
- package/src/index.ts +3 -1
- package/src/ipc/job_main.ts +9 -2
- package/src/ipc/proc_pool.ts +1 -0
- package/src/job.ts +21 -1
- package/src/multimodal/agent_playout.ts +254 -0
- package/src/multimodal/index.ts +5 -0
- package/src/multimodal/multimodal_agent.ts +428 -0
- package/src/transcription.ts +128 -0
- package/src/utils.ts +138 -6
- package/src/worker.ts +54 -10
- package/tsconfig.json +1 -1
package/src/utils.ts
CHANGED
|
@@ -116,7 +116,8 @@ export class Mutex {
|
|
|
116
116
|
|
|
117
117
|
/** @internal */
|
|
118
118
|
export class Queue<T> {
|
|
119
|
-
|
|
119
|
+
/** @internal */
|
|
120
|
+
items: T[] = [];
|
|
120
121
|
#limit?: number;
|
|
121
122
|
#events = new EventEmitter();
|
|
122
123
|
|
|
@@ -125,19 +126,19 @@ export class Queue<T> {
|
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
async get(): Promise<T> {
|
|
128
|
-
if (this
|
|
129
|
+
if (this.items.length === 0) {
|
|
129
130
|
await once(this.#events, 'put');
|
|
130
131
|
}
|
|
131
|
-
const item = this
|
|
132
|
+
const item = this.items.shift()!;
|
|
132
133
|
this.#events.emit('get');
|
|
133
134
|
return item;
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
async put(item: T) {
|
|
137
|
-
if (this.#limit && this
|
|
138
|
+
if (this.#limit && this.items.length >= this.#limit) {
|
|
138
139
|
await once(this.#events, 'get');
|
|
139
140
|
}
|
|
140
|
-
this
|
|
141
|
+
this.items.push(item);
|
|
141
142
|
this.#events.emit('put');
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -148,12 +149,143 @@ export class Future {
|
|
|
148
149
|
this.resolve = resolve;
|
|
149
150
|
this.reject = reject;
|
|
150
151
|
});
|
|
152
|
+
#done: boolean = false;
|
|
151
153
|
|
|
152
154
|
get await() {
|
|
153
155
|
return this.#await;
|
|
154
156
|
}
|
|
155
|
-
|
|
157
|
+
|
|
158
|
+
get done() {
|
|
159
|
+
return this.#done;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
resolve() {
|
|
163
|
+
this.#done = true;
|
|
164
|
+
}
|
|
165
|
+
|
|
156
166
|
reject(_: Error) {
|
|
167
|
+
this.#done = true;
|
|
157
168
|
_;
|
|
158
169
|
}
|
|
159
170
|
}
|
|
171
|
+
|
|
172
|
+
/** @internal */
|
|
173
|
+
export class CancellablePromise<T> {
|
|
174
|
+
#promise: Promise<T>;
|
|
175
|
+
#cancelFn: () => void;
|
|
176
|
+
#isCancelled: boolean = false;
|
|
177
|
+
#error: Error | null = null;
|
|
178
|
+
|
|
179
|
+
constructor(
|
|
180
|
+
executor: (
|
|
181
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
182
|
+
reject: (reason?: any) => void,
|
|
183
|
+
onCancel: (cancelFn: () => void) => void,
|
|
184
|
+
) => void,
|
|
185
|
+
) {
|
|
186
|
+
let cancel: () => void;
|
|
187
|
+
|
|
188
|
+
this.#promise = new Promise<T>((resolve, reject) => {
|
|
189
|
+
executor(
|
|
190
|
+
resolve,
|
|
191
|
+
(reason) => {
|
|
192
|
+
this.#error = reason instanceof Error ? reason : new Error(String(reason));
|
|
193
|
+
reject(reason);
|
|
194
|
+
},
|
|
195
|
+
(cancelFn) => {
|
|
196
|
+
cancel = () => {
|
|
197
|
+
this.#isCancelled = true;
|
|
198
|
+
cancelFn();
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
this.#cancelFn = cancel!;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
get isCancelled(): boolean {
|
|
208
|
+
return this.#isCancelled;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get error(): Error | null {
|
|
212
|
+
return this.#error;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
then<TResult1 = T, TResult2 = never>(
|
|
216
|
+
onfulfilled?: ((value: T) => TResult1 | Promise<TResult1>) | null,
|
|
217
|
+
onrejected?: ((reason: any) => TResult2 | Promise<TResult2>) | null,
|
|
218
|
+
): Promise<TResult1 | TResult2> {
|
|
219
|
+
return this.#promise.then(onfulfilled, onrejected);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
catch<TResult = never>(
|
|
223
|
+
onrejected?: ((reason: any) => TResult | Promise<TResult>) | null,
|
|
224
|
+
): Promise<T | TResult> {
|
|
225
|
+
return this.#promise.catch(onrejected);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
finally(onfinally?: (() => void) | null): Promise<T> {
|
|
229
|
+
return this.#promise.finally(onfinally);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
cancel(): void {
|
|
233
|
+
this.#cancelFn();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
static from<T>(promise: Promise<T>): CancellablePromise<T> {
|
|
237
|
+
return new CancellablePromise<T>((resolve, reject) => {
|
|
238
|
+
promise.then(resolve).catch(reject);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** @internal */
|
|
244
|
+
export async function gracefullyCancel<T>(promise: CancellablePromise<T>): Promise<void> {
|
|
245
|
+
if (!promise.isCancelled) {
|
|
246
|
+
promise.cancel();
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
await promise;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
// Ignore the error, as it's expected due to cancellation
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** @internal */
|
|
256
|
+
export class AsyncIterableQueue<T> implements AsyncIterable<T> {
|
|
257
|
+
private queue: Queue<T | typeof AsyncIterableQueue.QUEUE_END_MARKER>;
|
|
258
|
+
private closed = false;
|
|
259
|
+
private static readonly QUEUE_END_MARKER = Symbol('QUEUE_END_MARKER');
|
|
260
|
+
|
|
261
|
+
constructor() {
|
|
262
|
+
this.queue = new Queue<T | typeof AsyncIterableQueue.QUEUE_END_MARKER>();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
put(item: T): void {
|
|
266
|
+
if (this.closed) {
|
|
267
|
+
throw new Error('Queue is closed');
|
|
268
|
+
}
|
|
269
|
+
this.queue.put(item);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
close(): void {
|
|
273
|
+
this.closed = true;
|
|
274
|
+
this.queue.put(AsyncIterableQueue.QUEUE_END_MARKER);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
278
|
+
return {
|
|
279
|
+
next: async (): Promise<IteratorResult<T>> => {
|
|
280
|
+
if (this.closed && this.queue.items.length === 0) {
|
|
281
|
+
return { value: undefined, done: true };
|
|
282
|
+
}
|
|
283
|
+
const item = await this.queue.get();
|
|
284
|
+
if (item === AsyncIterableQueue.QUEUE_END_MARKER && this.closed) {
|
|
285
|
+
return { value: undefined, done: true };
|
|
286
|
+
}
|
|
287
|
+
return { value: item as T, done: false };
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
package/src/worker.ts
CHANGED
|
@@ -31,6 +31,32 @@ const MAX_RECONNECT_ATTEMPTS = 10;
|
|
|
31
31
|
const ASSIGNMENT_TIMEOUT = 7.5 * 1000;
|
|
32
32
|
const UPDATE_LOAD_INTERVAL = 2.5 * 1000;
|
|
33
33
|
|
|
34
|
+
class Default {
|
|
35
|
+
static loadThreshold(production: boolean): number {
|
|
36
|
+
if (production) {
|
|
37
|
+
return 0.65;
|
|
38
|
+
} else {
|
|
39
|
+
return Infinity;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static numIdleProcesses(production: boolean): number {
|
|
44
|
+
if (production) {
|
|
45
|
+
return 3;
|
|
46
|
+
} else {
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static port(production: boolean): number {
|
|
52
|
+
if (production) {
|
|
53
|
+
return 8081;
|
|
54
|
+
} else {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
34
60
|
/** Necessary credentials not provided and not found in an appropriate environmental variable. */
|
|
35
61
|
export class MissingCredentialsError extends Error {
|
|
36
62
|
constructor(msg?: string) {
|
|
@@ -132,14 +158,15 @@ export class WorkerOptions {
|
|
|
132
158
|
host: string;
|
|
133
159
|
port: number;
|
|
134
160
|
logLevel: string;
|
|
161
|
+
production: boolean;
|
|
135
162
|
|
|
136
163
|
/** @param options */
|
|
137
164
|
constructor({
|
|
138
165
|
agent,
|
|
139
166
|
requestFunc = defaultRequestFunc,
|
|
140
167
|
loadFunc = defaultCpuLoad,
|
|
141
|
-
loadThreshold =
|
|
142
|
-
numIdleProcesses =
|
|
168
|
+
loadThreshold = undefined,
|
|
169
|
+
numIdleProcesses = undefined,
|
|
143
170
|
shutdownProcessTimeout = 60 * 1000,
|
|
144
171
|
initializeProcessTimeout = 10 * 1000,
|
|
145
172
|
permissions = new WorkerPermissions(),
|
|
@@ -150,8 +177,9 @@ export class WorkerOptions {
|
|
|
150
177
|
apiKey = undefined,
|
|
151
178
|
apiSecret = undefined,
|
|
152
179
|
host = 'localhost',
|
|
153
|
-
port =
|
|
180
|
+
port = undefined,
|
|
154
181
|
logLevel = 'info',
|
|
182
|
+
production = false,
|
|
155
183
|
}: {
|
|
156
184
|
/**
|
|
157
185
|
* Path to a file that has {@link Agent} as a default export, dynamically imported later for
|
|
@@ -176,12 +204,16 @@ export class WorkerOptions {
|
|
|
176
204
|
host?: string;
|
|
177
205
|
port?: number;
|
|
178
206
|
logLevel?: string;
|
|
207
|
+
production?: boolean;
|
|
179
208
|
}) {
|
|
180
209
|
this.agent = agent;
|
|
210
|
+
if (!this.agent) {
|
|
211
|
+
throw new Error('No Agent file was passed to the worker');
|
|
212
|
+
}
|
|
181
213
|
this.requestFunc = requestFunc;
|
|
182
214
|
this.loadFunc = loadFunc;
|
|
183
|
-
this.loadThreshold = loadThreshold;
|
|
184
|
-
this.numIdleProcesses = numIdleProcesses;
|
|
215
|
+
this.loadThreshold = loadThreshold || Default.loadThreshold(production);
|
|
216
|
+
this.numIdleProcesses = numIdleProcesses || Default.numIdleProcesses(production);
|
|
185
217
|
this.shutdownProcessTimeout = shutdownProcessTimeout;
|
|
186
218
|
this.initializeProcessTimeout = initializeProcessTimeout;
|
|
187
219
|
this.permissions = permissions;
|
|
@@ -192,8 +224,9 @@ export class WorkerOptions {
|
|
|
192
224
|
this.apiKey = apiKey;
|
|
193
225
|
this.apiSecret = apiSecret;
|
|
194
226
|
this.host = host;
|
|
195
|
-
this.port = port;
|
|
227
|
+
this.port = port || Default.port(production);
|
|
196
228
|
this.logLevel = logLevel;
|
|
229
|
+
this.production = production;
|
|
197
230
|
}
|
|
198
231
|
}
|
|
199
232
|
|
|
@@ -239,12 +272,16 @@ export class Worker {
|
|
|
239
272
|
opts.apiSecret = opts.apiSecret || process.env.LIVEKIT_API_SECRET || '';
|
|
240
273
|
|
|
241
274
|
if (opts.wsURL === '')
|
|
242
|
-
throw new MissingCredentialsError(
|
|
275
|
+
throw new MissingCredentialsError(
|
|
276
|
+
'URL is required: Set LIVEKIT_URL, run with --url, or pass wsURL in WorkerOptions',
|
|
277
|
+
);
|
|
243
278
|
if (opts.apiKey === '')
|
|
244
|
-
throw new MissingCredentialsError(
|
|
279
|
+
throw new MissingCredentialsError(
|
|
280
|
+
'API Key is required: Set LIVEKIT_API_KEY, run with --api-key, or pass apiKey in WorkerOptions',
|
|
281
|
+
);
|
|
245
282
|
if (opts.apiSecret === '')
|
|
246
283
|
throw new MissingCredentialsError(
|
|
247
|
-
'
|
|
284
|
+
'API Secret is required: Set LIVEKIT_API_SECRET, run with --api-secret, or pass apiSecret in WorkerOptions',
|
|
248
285
|
);
|
|
249
286
|
|
|
250
287
|
this.#procPool = new ProcPool(
|
|
@@ -373,7 +410,14 @@ export class Worker {
|
|
|
373
410
|
const room = await client.createRoom({ name: roomName });
|
|
374
411
|
let participant: ParticipantInfo | undefined = undefined;
|
|
375
412
|
if (participantIdentity) {
|
|
376
|
-
|
|
413
|
+
try {
|
|
414
|
+
participant = await client.getParticipant(roomName, participantIdentity);
|
|
415
|
+
} catch (e) {
|
|
416
|
+
this.#logger.fatal(
|
|
417
|
+
`participant with identity ${participantIdentity} not found in room ${roomName}`,
|
|
418
|
+
);
|
|
419
|
+
throw e;
|
|
420
|
+
}
|
|
377
421
|
}
|
|
378
422
|
|
|
379
423
|
this.event!.emit(
|