@livekit/agents 1.2.0 → 1.2.1
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/_exceptions.cjs.map +1 -1
- package/dist/_exceptions.d.ts.map +1 -1
- package/dist/_exceptions.js.map +1 -1
- package/dist/beta/workflows/task_group.cjs +7 -4
- package/dist/beta/workflows/task_group.cjs.map +1 -1
- package/dist/beta/workflows/task_group.d.ts.map +1 -1
- package/dist/beta/workflows/task_group.js +7 -4
- package/dist/beta/workflows/task_group.js.map +1 -1
- package/dist/inference/interruption/http_transport.cjs.map +1 -1
- package/dist/inference/interruption/http_transport.d.cts +3 -1
- package/dist/inference/interruption/http_transport.d.ts +3 -1
- package/dist/inference/interruption/http_transport.d.ts.map +1 -1
- package/dist/inference/interruption/http_transport.js.map +1 -1
- package/dist/inference/interruption/ws_transport.cjs +37 -32
- package/dist/inference/interruption/ws_transport.cjs.map +1 -1
- package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.js +37 -32
- package/dist/inference/interruption/ws_transport.js.map +1 -1
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +42 -4
- package/dist/inference/tts.d.ts +42 -4
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js.map +1 -1
- package/dist/inference/tts.test.cjs +72 -0
- package/dist/inference/tts.test.cjs.map +1 -1
- package/dist/inference/tts.test.js +72 -0
- package/dist/inference/tts.test.js.map +1 -1
- package/dist/llm/chat_context.cjs +102 -31
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +102 -31
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/chat_context.test.cjs +123 -5
- package/dist/llm/chat_context.test.cjs.map +1 -1
- package/dist/llm/chat_context.test.js +123 -5
- package/dist/llm/chat_context.test.js.map +1 -1
- package/dist/llm/fallback_adapter.cjs +2 -0
- package/dist/llm/fallback_adapter.cjs.map +1 -1
- package/dist/llm/fallback_adapter.d.ts.map +1 -1
- package/dist/llm/fallback_adapter.js +2 -0
- package/dist/llm/fallback_adapter.js.map +1 -1
- package/dist/llm/index.cjs +2 -0
- package/dist/llm/index.cjs.map +1 -1
- package/dist/llm/index.d.cts +1 -1
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +2 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/utils.cjs +89 -0
- package/dist/llm/utils.cjs.map +1 -1
- package/dist/llm/utils.d.cts +8 -0
- package/dist/llm/utils.d.ts +8 -0
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +88 -0
- package/dist/llm/utils.js.map +1 -1
- package/dist/llm/utils.test.cjs +90 -0
- package/dist/llm/utils.test.cjs.map +1 -1
- package/dist/llm/utils.test.js +98 -2
- package/dist/llm/utils.test.js.map +1 -1
- package/dist/stt/stt.cjs +8 -0
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +8 -0
- package/dist/stt/stt.d.ts +8 -0
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +8 -0
- package/dist/stt/stt.js.map +1 -1
- package/dist/tts/fallback_adapter.cjs +6 -0
- package/dist/tts/fallback_adapter.cjs.map +1 -1
- package/dist/tts/fallback_adapter.d.ts.map +1 -1
- package/dist/tts/fallback_adapter.js +6 -0
- package/dist/tts/fallback_adapter.js.map +1 -1
- package/dist/typed_promise.cjs +48 -0
- package/dist/typed_promise.cjs.map +1 -0
- package/dist/typed_promise.d.cts +24 -0
- package/dist/typed_promise.d.ts +24 -0
- package/dist/typed_promise.d.ts.map +1 -0
- package/dist/typed_promise.js +28 -0
- package/dist/typed_promise.js.map +1 -0
- package/dist/utils.cjs +2 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
- package/src/_exceptions.ts +5 -0
- package/src/beta/workflows/task_group.ts +14 -5
- package/src/inference/interruption/http_transport.ts +2 -1
- package/src/inference/interruption/ws_transport.ts +44 -34
- package/src/inference/tts.test.ts +87 -0
- package/src/inference/tts.ts +46 -6
- package/src/llm/chat_context.test.ts +137 -5
- package/src/llm/chat_context.ts +119 -38
- package/src/llm/fallback_adapter.ts +5 -2
- package/src/llm/index.ts +2 -0
- package/src/llm/utils.test.ts +103 -2
- package/src/llm/utils.ts +128 -0
- package/src/stt/stt.ts +9 -1
- package/src/tts/fallback_adapter.ts +9 -2
- package/src/typed_promise.ts +67 -0
- package/src/utils.ts +2 -2
package/src/llm/utils.ts
CHANGED
|
@@ -7,6 +7,7 @@ import sharp from 'sharp';
|
|
|
7
7
|
import type { UnknownUserData } from '../voice/run_context.js';
|
|
8
8
|
import type { ChatContext } from './chat_context.js';
|
|
9
9
|
import {
|
|
10
|
+
type ChatContent,
|
|
10
11
|
type ChatItem,
|
|
11
12
|
FunctionCall,
|
|
12
13
|
FunctionCallOutput,
|
|
@@ -241,6 +242,133 @@ export async function executeToolCall(
|
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
|
|
245
|
+
export interface FormatChatHistoryOptions {
|
|
246
|
+
includeIds?: boolean;
|
|
247
|
+
includeTimestamps?: boolean;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Render a chat context into a readable multiline string for debugging and logging.
|
|
252
|
+
*/
|
|
253
|
+
export function formatChatHistory(
|
|
254
|
+
chatCtx: ChatContext,
|
|
255
|
+
options: FormatChatHistoryOptions = {},
|
|
256
|
+
): string {
|
|
257
|
+
const { includeIds = false, includeTimestamps = false } = options;
|
|
258
|
+
|
|
259
|
+
if (chatCtx.items.length === 0) {
|
|
260
|
+
return 'Chat history (0 items)';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const formattedItems = chatCtx.items.map((item, index) =>
|
|
264
|
+
formatChatHistoryItem(item, index, {
|
|
265
|
+
includeIds,
|
|
266
|
+
includeTimestamps,
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
return [
|
|
271
|
+
`Chat history (${chatCtx.items.length} items)`,
|
|
272
|
+
...formattedItems.flatMap((item) => ['', item]),
|
|
273
|
+
].join('\n');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function formatChatHistoryItem(
|
|
277
|
+
item: ChatItem,
|
|
278
|
+
index: number,
|
|
279
|
+
options: Required<FormatChatHistoryOptions>,
|
|
280
|
+
): string {
|
|
281
|
+
const headerParts = [`[${index}]`];
|
|
282
|
+
|
|
283
|
+
if (item.type === 'message') {
|
|
284
|
+
headerParts.push('message', item.role);
|
|
285
|
+
} else if (item.type === 'function_call') {
|
|
286
|
+
headerParts.push('function_call', item.name, `call_id=${item.callId}`);
|
|
287
|
+
} else if (item.type === 'function_call_output') {
|
|
288
|
+
headerParts.push('function_call_output', item.name || '(unnamed)', `call_id=${item.callId}`);
|
|
289
|
+
if (item.isError) {
|
|
290
|
+
headerParts.push('error=true');
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
headerParts.push('agent_handoff');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (options.includeIds) {
|
|
297
|
+
headerParts.push(`id=${item.id}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (options.includeTimestamps) {
|
|
301
|
+
headerParts.push(`created_at=${item.createdAt.toFixed(3)}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const body = formatChatHistoryItemBody(item);
|
|
305
|
+
if (!body) {
|
|
306
|
+
return headerParts.join(' ');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return `${headerParts.join(' ')}\n${indentBlock(body, ' ')}`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function formatChatHistoryItemBody(item: ChatItem): string {
|
|
313
|
+
if (item.type === 'message') {
|
|
314
|
+
const content = item.content.map((part) => formatMessageContentPart(part)).join('\n');
|
|
315
|
+
return content.trim() ? content : '(empty)';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (item.type === 'function_call') {
|
|
319
|
+
return prettyJsonText(item.args);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (item.type === 'function_call_output') {
|
|
323
|
+
return prettyJsonText(item.output);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return `${item.oldAgentId ?? '(none)'} -> ${item.newAgentId}`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function formatMessageContentPart(part: ChatContent): string {
|
|
330
|
+
if (typeof part === 'string') {
|
|
331
|
+
return part;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (part.type === 'image_content') {
|
|
335
|
+
if (typeof part.image === 'string') {
|
|
336
|
+
return `[image url=${truncateText(part.image, 120)}]`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return `[image frame=${part.image.width}x${part.image.height}]`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (part.transcript) {
|
|
343
|
+
return `[audio transcript=${JSON.stringify(truncateText(part.transcript, 120))}]`;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return `[audio frames=${part.frame.length}]`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function prettyJsonText(text: string): string {
|
|
350
|
+
try {
|
|
351
|
+
return JSON.stringify(JSON.parse(text), null, 2);
|
|
352
|
+
} catch {
|
|
353
|
+
return text;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function truncateText(text: string, maxLength: number): string {
|
|
358
|
+
if (text.length <= maxLength) {
|
|
359
|
+
return text;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function indentBlock(text: string, indent: string): string {
|
|
366
|
+
return text
|
|
367
|
+
.split('\n')
|
|
368
|
+
.map((line) => `${indent}${line}`)
|
|
369
|
+
.join('\n');
|
|
370
|
+
}
|
|
371
|
+
|
|
244
372
|
/**
|
|
245
373
|
* Standard dynamic-programming LCS to get the common subsequence
|
|
246
374
|
* of IDs (in order) that appear in both old_ids and new_ids.
|
package/src/stt/stt.ts
CHANGED
|
@@ -248,7 +248,15 @@ export abstract class SpeechStream implements AsyncIterableIterator<SpeechEvent>
|
|
|
248
248
|
startSoon(() => this.mainTask().finally(() => this.queue.close()));
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Runs the STT with retry logic. Errors are emitted via {@link STT} error events
|
|
253
|
+
* and then re-thrown to trigger `.finally()` cleanup.
|
|
254
|
+
*
|
|
255
|
+
* @throws {APIError} When the STT request fails with a non-retryable error
|
|
256
|
+
* @throws {APIConnectionError} When all retry attempts are exhausted
|
|
257
|
+
* @internal Not annotated with Throws<> because this is fire-and-forget via startSoon()
|
|
258
|
+
*/
|
|
259
|
+
private async mainTask(): Promise<void> {
|
|
252
260
|
for (let i = 0; i < this._connOptions.maxRetry + 1; i++) {
|
|
253
261
|
try {
|
|
254
262
|
return await this.run();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import { AudioResampler } from '@livekit/rtc-node';
|
|
5
|
+
import type { Throws } from '@livekit/throws-transformer/throws';
|
|
5
6
|
import { APIConnectionError, APIError } from '../_exceptions.js';
|
|
6
7
|
import { log } from '../log.js';
|
|
7
8
|
import { basic } from '../tokenize/index.js';
|
|
@@ -306,7 +307,10 @@ class FallbackChunkedStream extends ChunkedStream {
|
|
|
306
307
|
this.connOptions = connOptions;
|
|
307
308
|
}
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
/**
|
|
311
|
+
* @throws {APIConnectionError} When all TTS providers have been exhausted
|
|
312
|
+
*/
|
|
313
|
+
protected async run(): Promise<Throws<void, APIConnectionError>> {
|
|
310
314
|
const allTTSFailed = this.adapter.status.every((s) => !s.available);
|
|
311
315
|
let lastRequestId: string = '';
|
|
312
316
|
let lastSegmentId: string = '';
|
|
@@ -406,7 +410,10 @@ class FallbackSynthesizeStream extends SynthesizeStream {
|
|
|
406
410
|
this.adapter = adapter;
|
|
407
411
|
}
|
|
408
412
|
|
|
409
|
-
|
|
413
|
+
/**
|
|
414
|
+
* @throws {APIConnectionError} When all TTS providers have been exhausted
|
|
415
|
+
*/
|
|
416
|
+
protected async run(): Promise<Throws<void, APIConnectionError>> {
|
|
410
417
|
const allTTSFailed = this.adapter.status.every((s) => !s.available);
|
|
411
418
|
if (allTTSFailed) {
|
|
412
419
|
this._logger.warn('All fallback TTS instances failed, retrying from first...');
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
type InferErrors<T> = T extends TypedPromise<any, infer E> ? E : never;
|
|
5
|
+
|
|
6
|
+
interface PromiseRejectedResult<E> {
|
|
7
|
+
status: 'rejected';
|
|
8
|
+
reason: E;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type SettledResult<T> =
|
|
12
|
+
T extends TypedPromise<infer U, infer E>
|
|
13
|
+
? PromiseFulfilledResult<U> | PromiseRejectedResult<E>
|
|
14
|
+
: T extends PromiseLike<infer U>
|
|
15
|
+
? PromiseFulfilledResult<U> | PromiseRejectedResult<unknown>
|
|
16
|
+
: PromiseFulfilledResult<T> | PromiseRejectedResult<unknown>;
|
|
17
|
+
|
|
18
|
+
export default class TypedPromise<T, E extends Error> extends Promise<T> {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
20
|
+
constructor(
|
|
21
|
+
executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason: E) => void) => void,
|
|
22
|
+
) {
|
|
23
|
+
super(executor);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
catch<TResult = never>(
|
|
27
|
+
onrejected?: ((reason: E) => TResult | PromiseLike<TResult>) | null | undefined,
|
|
28
|
+
): TypedPromise<T | TResult, E> {
|
|
29
|
+
return super.catch(onrejected);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static resolve: {
|
|
33
|
+
(): TypedPromise<void, never>;
|
|
34
|
+
<V>(value: V): TypedPromise<Awaited<V>, never>;
|
|
35
|
+
} = <V>(value?: V): TypedPromise<Awaited<V>, never> => {
|
|
36
|
+
return super.resolve(value) as TypedPromise<Awaited<V>, never>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
static reject<E extends Error>(reason: E): TypedPromise<never, E> {
|
|
40
|
+
return super.reject(reason);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static all<T extends readonly unknown[] | []>(
|
|
44
|
+
values: T,
|
|
45
|
+
): TypedPromise<{ -readonly [P in keyof T]: Awaited<T[P]> }, InferErrors<T[number]>> {
|
|
46
|
+
return super.all(values) as any;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static allSettled<T extends readonly unknown[] | []>(
|
|
50
|
+
values: T,
|
|
51
|
+
): TypedPromise<{ -readonly [P in keyof T]: SettledResult<T[P]> }, never> {
|
|
52
|
+
return super.allSettled(values) as any;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static race<T extends readonly (TypedPromise<any, any> | any)[]>(
|
|
56
|
+
values: T,
|
|
57
|
+
): TypedPromise<
|
|
58
|
+
T[number] extends TypedPromise<infer U, any>
|
|
59
|
+
? U
|
|
60
|
+
: T[number] extends PromiseLike<infer U>
|
|
61
|
+
? U
|
|
62
|
+
: Awaited<T[number]>,
|
|
63
|
+
InferErrors<T[number]>
|
|
64
|
+
> {
|
|
65
|
+
return super.race(values);
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -789,11 +789,11 @@ export type DelayOptions = {
|
|
|
789
789
|
*/
|
|
790
790
|
export function delay(ms: number, options: DelayOptions = {}): Promise<void> {
|
|
791
791
|
const { signal } = options;
|
|
792
|
-
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
792
|
+
if (signal?.aborted) return Promise.reject(signal.reason ?? new Error('delay aborted'));
|
|
793
793
|
return new Promise((resolve, reject) => {
|
|
794
794
|
const abort = () => {
|
|
795
795
|
clearTimeout(i);
|
|
796
|
-
reject(signal?.reason);
|
|
796
|
+
reject(signal?.reason ?? new Error('delay aborted'));
|
|
797
797
|
};
|
|
798
798
|
const done = () => {
|
|
799
799
|
signal?.removeEventListener('abort', abort);
|