From e08d21dc2236c469a13533716a701bf4a979458c Mon Sep 17 00:00:00 2001 From: Toubat Date: Fri, 5 Sep 2025 12:09:14 -0700 Subject: [PATCH 01/27] add --- agents/src/voice/avatar/datastream_io.ts | 90 ++++++++++++++++++++++++ agents/src/voice/avatar/index.ts | 0 2 files changed, 90 insertions(+) create mode 100644 agents/src/voice/avatar/datastream_io.ts create mode 100644 agents/src/voice/avatar/index.ts diff --git a/agents/src/voice/avatar/datastream_io.ts b/agents/src/voice/avatar/datastream_io.ts new file mode 100644 index 000000000..84eb1d518 --- /dev/null +++ b/agents/src/voice/avatar/datastream_io.ts @@ -0,0 +1,90 @@ +import { Mutex } from '@livekit/mutex'; +import { + ByteStreamWriter, + Room, + RoomEvent, + type RpcInvocationData, + TrackKind, +} from '@livekit/rtc-node'; +import { Future, Task } from 'agents/src/utils.js'; +import { AudioOutput } from '../io.js'; + +export interface DataStreamAudioOutputOptions { + room: Room; + destinationIdentity: string; + sampleRate?: number; + waitRemoteTrack?: TrackKind; +} + +export class DataStreamAudioOutput extends AudioOutput { + private room: Room; + private destinationIdentity: string; + private started: boolean; + private lock: Lock; + private startTask: Task; + private roomConnectedFuture: Future; + + private sampleRate?: number; + private waitRemoteTrack?: TrackKind; + private streamWriter?: ByteStreamWriter; + private pushedDuration: number = 0; + private tasks: Set> = new Set(); + private started: boolean = false; + private lock = new Mutex(); + private startTask?: Task; + + constructor(opts: DataStreamAudioOutputOptions) { + super(opts.sampleRate, undefined); + + const { room, destinationIdentity, sampleRate, waitRemoteTrack } = opts; + this.room = room; + this.destinationIdentity = destinationIdentity; + this.sampleRate = sampleRate; + this.waitRemoteTrack = waitRemoteTrack; + + const onRoomConnected = async () => { + if (this.startTask) return; + + await this.roomConnectedFuture.await; + + // register the rpc method right after the room is connected + DataStreamAudioOutput.registerPlaybackFinishedRpc({ + room, + callerIdentity: this.destinationIdentity, + handler: (data) => this.handlePlaybackFinished(data), + }); + + this.startTask = Task.from(({ signal }) => this._start(signal)); + }; + + this.roomConnectedFuture = new Future(); + + this.room.on(RoomEvent.ConnectionStateChanged, (_) => { + if (room.isConnected && !this.roomConnectedFuture.done) { + this.roomConnectedFuture.resolve(undefined); + } + }); + + if (this.room.isConnected) { + this.roomConnectedFuture.resolve(undefined); + } + + onRoomConnected(); + } + + private async _start(abortSignal: AbortSignal) {} + + private handlePlaybackFinished(data: RpcInvocationData): string { + return ''; + } + + static registerPlaybackFinishedRpc({ + room, + callerIdentity, + handler, + }: { + room: Room; + callerIdentity: string; + handler: (data: RpcInvocationData) => string; + }) {} +} diff --git a/agents/src/voice/avatar/index.ts b/agents/src/voice/avatar/index.ts new file mode 100644 index 000000000..e69de29bb From 85b05ffbac44f223c41f3c3971a32d301d197768 Mon Sep 17 00:00:00 2001 From: Toubat Date: Fri, 5 Sep 2025 16:34:38 -0700 Subject: [PATCH 02/27] Update datastream_io.ts --- agents/src/voice/avatar/datastream_io.ts | 87 ++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/agents/src/voice/avatar/datastream_io.ts b/agents/src/voice/avatar/datastream_io.ts index 84eb1d518..d5b96229e 100644 --- a/agents/src/voice/avatar/datastream_io.ts +++ b/agents/src/voice/avatar/datastream_io.ts @@ -1,14 +1,23 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 import { Mutex } from '@livekit/mutex'; import { + AudioFrame, ByteStreamWriter, Room, RoomEvent, type RpcInvocationData, TrackKind, } from '@livekit/rtc-node'; -import { Future, Task } from 'agents/src/utils.js'; +import { log } from 'agents/src/log.js'; +import { Future, Task, waitForParticipant, waitForTrackPublication } from 'agents/src/utils.js'; import { AudioOutput } from '../io.js'; +const RPC_CLEAR_BUFFER = 'lk.clear_buffer'; +const RPC_PLAYBACK_FINISHED = 'lk.playback_finished'; +const AUDIO_STREAM_TOPIC = 'lk.audio_stream'; + export interface DataStreamAudioOutputOptions { room: Room; destinationIdentity: string; @@ -16,15 +25,18 @@ export interface DataStreamAudioOutputOptions { waitRemoteTrack?: TrackKind; } +/** + * AudioOutput implementation that streams audio to a remote avatar worker using LiveKit DataStream. + */ export class DataStreamAudioOutput extends AudioOutput { + static _playbackFinishedRpcRegistered: boolean = false; + static _playbackFinishedHandlers: Record string> = {}; + + readonly sampleRate?: number; + private room: Room; private destinationIdentity: string; - private started: boolean; - private lock: Lock; - private startTask: Task; private roomConnectedFuture: Future; - - private sampleRate?: number; private waitRemoteTrack?: TrackKind; private streamWriter?: ByteStreamWriter; private pushedDuration: number = 0; @@ -33,6 +45,8 @@ export class DataStreamAudioOutput extends AudioOutput { private lock = new Mutex(); private startTask?: Task; + #logger = log(); + constructor(opts: DataStreamAudioOutputOptions) { super(opts.sampleRate, undefined); @@ -72,7 +86,66 @@ export class DataStreamAudioOutput extends AudioOutput { onRoomConnected(); } - private async _start(abortSignal: AbortSignal) {} + private async _start(abortSignal: AbortSignal) { + const unlock = await this.lock.lock(); + + try { + if (this.started) return; + + await this.roomConnectedFuture.await; + + this.#logger.debug( + { + identity: this.destinationIdentity, + }, + 'waiting for the remote participant', + ); + + await waitForParticipant({ + room: this.room, + identity: this.destinationIdentity, + }); + + if (this.waitRemoteTrack) { + this.#logger.debug( + { + identity: this.destinationIdentity, + kind: this.waitRemoteTrack, + }, + 'waiting for the remote track', + ); + + await waitForTrackPublication({ + room: this.room, + identity: this.destinationIdentity, + kind: this.waitRemoteTrack, + }); + } + + this.#logger.debug( + { + identity: this.destinationIdentity, + }, + 'remote participant ready', + ); + + this.started = true; + } finally { + unlock(); + } + } + + captureFrame(_frame: AudioFrame): Promise { + return Promise.resolve(); + } + + flush(): void { + return; + } + + clearBuffer(): void { + return; + } private handlePlaybackFinished(data: RpcInvocationData): string { return ''; From 1f98d41ebe8da3608fd92d0eded0cc54d1ddd774 Mon Sep 17 00:00:00 2001 From: Brian Yin Date: Fri, 5 Sep 2025 16:48:13 -0700 Subject: [PATCH 03/27] Add DataStreamAudioOutput Utils (#686) --- agents/src/utils.ts | 120 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/agents/src/utils.ts b/agents/src/utils.ts index 4197742ba..884727cce 100644 --- a/agents/src/utils.ts +++ b/agents/src/utils.ts @@ -1,7 +1,16 @@ // SPDX-FileCopyrightText: 2024 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { AudioFrame, AudioResampler } from '@livekit/rtc-node'; +import { + AudioFrame, + AudioResampler, + ParticipantKind, + RemoteParticipant, + RemoteTrackPublication, + Room, + RoomEvent, + TrackKind, +} from '@livekit/rtc-node'; import { EventEmitter, once } from 'node:events'; import type { ReadableStream } from 'node:stream/web'; import { TransformStream, type TransformStreamDefaultController } from 'node:stream/web'; @@ -697,3 +706,112 @@ export function delay(ms: number, options: DelayOptions = {}): Promise { signal?.addEventListener('abort', abort, { once: true }); }); } + +/** + * Returns a participant that matches the given identity. If identity is None, the first + * participant that joins the room will be returned. + * If the participant has already joined, the function will return immediately. + * @param room - The room to wait for a participant in. + * @param identity - The identity of the participant to wait for. + * @param kind - The kind of the participant to wait for. + * @returns A promise that resolves to the participant. + */ +export async function waitForParticipant({ + room, + identity, + kind, +}: { + room: Room; + identity?: string; + kind?: ParticipantKind | ParticipantKind[]; +}): Promise { + if (!room.isConnected) { + throw new Error('Room is not connected'); + } + + const fut = new Future(); + + const kindMatch = (participant: RemoteParticipant) => { + if (kind === undefined) return true; + + if (Array.isArray(kind)) { + return kind.includes(participant.kind); + } + + return participant.kind === kind; + }; + + const onParticipantConnected = (p: RemoteParticipant) => { + if ((identity === undefined || p.identity === identity) && kindMatch(p)) { + if (!fut.done) { + fut.resolve(p); + } + } + }; + + room.on(RoomEvent.ParticipantConnected, onParticipantConnected); + + try { + for (const p of room.remoteParticipants.values()) { + onParticipantConnected(p); + if (fut.done) { + break; + } + } + + return await fut.await; + } finally { + room.off(RoomEvent.ParticipantConnected, onParticipantConnected); + } +} + +export async function waitForTrackPublication({ + room, + identity, + kind, +}: { + room: Room; + identity: string; + kind: TrackKind; +}): Promise { + if (!room.isConnected) { + throw new Error('Room is not connected'); + } + + const fut = new Future(); + + const kindMatch = (k: TrackKind | undefined) => { + if (kind === undefined || kind === null) { + return true; + } + return k === kind; + }; + + const onTrackPublished = ( + publication: RemoteTrackPublication, + participant: RemoteParticipant, + ) => { + if (fut.done) return; + if ( + (identity === undefined || participant.identity === identity) && + kindMatch(publication.kind) + ) { + fut.resolve(publication); + } + }; + + room.on(RoomEvent.TrackPublished, onTrackPublished); + + try { + for (const p of room.remoteParticipants.values()) { + for (const publication of p.trackPublications.values()) { + onTrackPublished(publication, p); + if (fut.done) break; + } + } + + return await fut.await; + } finally { + room.off(RoomEvent.TrackPublished, onTrackPublished); + } +} From 65ba88da88f5ca4dc56b45779ea9bb917cdefe10 Mon Sep 17 00:00:00 2001 From: Toubat Date: Fri, 5 Sep 2025 17:41:20 -0700 Subject: [PATCH 04/27] Update datastream_io.ts --- agents/src/voice/avatar/datastream_io.ts | 106 ++++++++++++++++++++--- 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/agents/src/voice/avatar/datastream_io.ts b/agents/src/voice/avatar/datastream_io.ts index d5b96229e..c83e25235 100644 --- a/agents/src/voice/avatar/datastream_io.ts +++ b/agents/src/voice/avatar/datastream_io.ts @@ -11,8 +11,14 @@ import { TrackKind, } from '@livekit/rtc-node'; import { log } from 'agents/src/log.js'; -import { Future, Task, waitForParticipant, waitForTrackPublication } from 'agents/src/utils.js'; -import { AudioOutput } from '../io.js'; +import { + Future, + Task, + shortuuid, + waitForParticipant, + waitForTrackPublication, +} from 'agents/src/utils.js'; +import { AudioOutput, type PlaybackFinishedEvent } from '../io.js'; const RPC_CLEAR_BUFFER = 'lk.clear_buffer'; const RPC_PLAYBACK_FINISHED = 'lk.playback_finished'; @@ -62,7 +68,7 @@ export class DataStreamAudioOutput extends AudioOutput { await this.roomConnectedFuture.await; // register the rpc method right after the room is connected - DataStreamAudioOutput.registerPlaybackFinishedRpc({ + this.registerPlaybackFinishedRpc({ room, callerIdentity: this.destinationIdentity, handler: (data) => this.handlePlaybackFinished(data), @@ -135,23 +141,79 @@ export class DataStreamAudioOutput extends AudioOutput { } } - captureFrame(_frame: AudioFrame): Promise { - return Promise.resolve(); + async captureFrame(frame: AudioFrame): Promise { + if (!this.startTask) { + this.startTask = Task.from(({ signal }) => this._start(signal)); + } + + await this.startTask.result; + await super.captureFrame(frame); + + if (!this.streamWriter) { + this.streamWriter = await this.room.localParticipant!.streamBytes({ + name: shortuuid('AUDIO_'), + topic: AUDIO_STREAM_TOPIC, + destinationIdentities: [this.destinationIdentity], + attributes: { + sample_rate: this.sampleRate?.toString() ?? '', + num_channels: frame.channels.toString(), + }, + }); + this.pushedDuration = 0; + } + + // frame.data is a Int16Array, write accepts a Uint8Array + await this.streamWriter.write(new Uint8Array(frame.data.buffer)); + this.pushedDuration += frame.samplesPerChannel / frame.sampleRate; } flush(): void { - return; + super.flush(); + + if (this.streamWriter === undefined || !this.started) { + return; + } + + this.streamWriter.close().finally(() => { + this.streamWriter = undefined; + }); } clearBuffer(): void { - return; + if (!this.started) return; + + this.room.localParticipant!.performRpc({ + destinationIdentity: this.destinationIdentity, + method: RPC_CLEAR_BUFFER, + payload: '', + }); } private handlePlaybackFinished(data: RpcInvocationData): string { - return ''; + if (data.callerIdentity !== this.destinationIdentity) { + this.#logger.warn( + { + callerIdentity: data.callerIdentity, + destinationIdentity: this.destinationIdentity, + }, + 'playback finished event received from unexpected participant', + ); + return 'reject'; + } + + this.#logger.info( + { + callerIdentity: data.callerIdentity, + }, + 'playback finished event received', + ); + + const playbackFinishedEvent = JSON.parse(data.payload) as PlaybackFinishedEvent; + this.onPlaybackFinished(playbackFinishedEvent); + return 'ok'; } - static registerPlaybackFinishedRpc({ + registerPlaybackFinishedRpc({ room, callerIdentity, handler, @@ -159,5 +221,29 @@ export class DataStreamAudioOutput extends AudioOutput { room: Room; callerIdentity: string; handler: (data: RpcInvocationData) => string; - }) {} + }) { + DataStreamAudioOutput._playbackFinishedHandlers[callerIdentity] = handler; + + if (DataStreamAudioOutput._playbackFinishedRpcRegistered) { + return; + } + + const rpcHandler = async (data: RpcInvocationData): Promise => { + const handler = DataStreamAudioOutput._playbackFinishedHandlers[data.callerIdentity]; + if (!handler) { + this.#logger.warn( + { + callerIdentity: data.callerIdentity, + expectedIdentities: Object.keys(DataStreamAudioOutput._playbackFinishedHandlers), + }, + 'playback finished event received from unexpected participant', + ); + return 'reject'; + } + return handler(data); + }; + + room.localParticipant?.registerRpcMethod(RPC_PLAYBACK_FINISHED, rpcHandler); + DataStreamAudioOutput._playbackFinishedRpcRegistered = true; + } } From a134c8cf4880a02db22cd223e1aa5ac0406a4e9c Mon Sep 17 00:00:00 2001 From: Toubat Date: Fri, 5 Sep 2025 17:42:58 -0700 Subject: [PATCH 05/27] Update datastream_io.ts --- agents/src/voice/avatar/datastream_io.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agents/src/voice/avatar/datastream_io.ts b/agents/src/voice/avatar/datastream_io.ts index c83e25235..911476ccf 100644 --- a/agents/src/voice/avatar/datastream_io.ts +++ b/agents/src/voice/avatar/datastream_io.ts @@ -68,7 +68,7 @@ export class DataStreamAudioOutput extends AudioOutput { await this.roomConnectedFuture.await; // register the rpc method right after the room is connected - this.registerPlaybackFinishedRpc({ + DataStreamAudioOutput.registerPlaybackFinishedRpc({ room, callerIdentity: this.destinationIdentity, handler: (data) => this.handlePlaybackFinished(data), @@ -213,7 +213,7 @@ export class DataStreamAudioOutput extends AudioOutput { return 'ok'; } - registerPlaybackFinishedRpc({ + static registerPlaybackFinishedRpc({ room, callerIdentity, handler, @@ -231,13 +231,14 @@ export class DataStreamAudioOutput extends AudioOutput { const rpcHandler = async (data: RpcInvocationData): Promise => { const handler = DataStreamAudioOutput._playbackFinishedHandlers[data.callerIdentity]; if (!handler) { - this.#logger.warn( + log().warn( { callerIdentity: data.callerIdentity, expectedIdentities: Object.keys(DataStreamAudioOutput._playbackFinishedHandlers), }, 'playback finished event received from unexpected participant', ); + return 'reject'; } return handler(data); From 95cbf1a6af366cd8543e044f303b08ee9e1407b8 Mon Sep 17 00:00:00 2001 From: Toubat Date: Fri, 5 Sep 2025 17:46:44 -0700 Subject: [PATCH 06/27] fix lint --- agents/src/utils.ts | 6 ++---- agents/src/voice/avatar/datastream_io.ts | 15 ++++++--------- agents/src/voice/io.ts | 2 +- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/agents/src/utils.ts b/agents/src/utils.ts index 884727cce..cae454fe4 100644 --- a/agents/src/utils.ts +++ b/agents/src/utils.ts @@ -1,16 +1,14 @@ // SPDX-FileCopyrightText: 2024 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { - AudioFrame, - AudioResampler, +import type { ParticipantKind, RemoteParticipant, RemoteTrackPublication, Room, - RoomEvent, TrackKind, } from '@livekit/rtc-node'; +import { AudioFrame, AudioResampler, RoomEvent } from '@livekit/rtc-node'; import { EventEmitter, once } from 'node:events'; import type { ReadableStream } from 'node:stream/web'; import { TransformStream, type TransformStreamDefaultController } from 'node:stream/web'; diff --git a/agents/src/voice/avatar/datastream_io.ts b/agents/src/voice/avatar/datastream_io.ts index 911476ccf..012abf3df 100644 --- a/agents/src/voice/avatar/datastream_io.ts +++ b/agents/src/voice/avatar/datastream_io.ts @@ -3,12 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 import { Mutex } from '@livekit/mutex'; import { - AudioFrame, - ByteStreamWriter, - Room, + type AudioFrame, + type ByteStreamWriter, + type Room, RoomEvent, type RpcInvocationData, - TrackKind, + type TrackKind, } from '@livekit/rtc-node'; import { log } from 'agents/src/log.js'; import { @@ -38,15 +38,12 @@ export class DataStreamAudioOutput extends AudioOutput { static _playbackFinishedRpcRegistered: boolean = false; static _playbackFinishedHandlers: Record string> = {}; - readonly sampleRate?: number; - private room: Room; private destinationIdentity: string; private roomConnectedFuture: Future; private waitRemoteTrack?: TrackKind; private streamWriter?: ByteStreamWriter; private pushedDuration: number = 0; - private tasks: Set> = new Set(); private started: boolean = false; private lock = new Mutex(); private startTask?: Task; @@ -92,7 +89,7 @@ export class DataStreamAudioOutput extends AudioOutput { onRoomConnected(); } - private async _start(abortSignal: AbortSignal) { + private async _start(_abortSignal: AbortSignal) { const unlock = await this.lock.lock(); try { @@ -155,7 +152,7 @@ export class DataStreamAudioOutput extends AudioOutput { topic: AUDIO_STREAM_TOPIC, destinationIdentities: [this.destinationIdentity], attributes: { - sample_rate: this.sampleRate?.toString() ?? '', + sample_rate: frame.sampleRate.toString(), num_channels: frame.channels.toString(), }, }); diff --git a/agents/src/voice/io.ts b/agents/src/voice/io.ts index d21044c43..1808520b0 100644 --- a/agents/src/voice/io.ts +++ b/agents/src/voice/io.ts @@ -55,7 +55,7 @@ export abstract class AudioOutput extends EventEmitter { protected logger = log(); constructor( - readonly sampleRate?: number, + public sampleRate?: number, protected readonly nextInChain?: AudioOutput, ) { super(); From a03f8872dfdf22cd4e77c2c602897f983b211578 Mon Sep 17 00:00:00 2001 From: Toubat Date: Fri, 5 Sep 2025 17:48:20 -0700 Subject: [PATCH 07/27] import --- agents/src/voice/avatar/index.ts | 1 + agents/src/voice/index.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/agents/src/voice/avatar/index.ts b/agents/src/voice/avatar/index.ts index e69de29bb..25c4a762a 100644 --- a/agents/src/voice/avatar/index.ts +++ b/agents/src/voice/avatar/index.ts @@ -0,0 +1 @@ +export * from './datastream_io.js'; diff --git a/agents/src/voice/index.ts b/agents/src/voice/index.ts index b71a3e518..4d11b2d59 100644 --- a/agents/src/voice/index.ts +++ b/agents/src/voice/index.ts @@ -3,5 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 export { Agent, StopResponse, type AgentOptions, type ModelSettings } from './agent.js'; export { AgentSession, type AgentSessionOptions } from './agent_session.js'; + +export * from './avatar/index.js'; export * from './events.js'; export { RunContext } from './run_context.js'; From 41d237c98d610ca62426a3a5475733df6c0af628 Mon Sep 17 00:00:00 2001 From: Toubat Date: Thu, 11 Sep 2025 07:30:01 +0900 Subject: [PATCH 08/27] add license --- agents/src/voice/avatar/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agents/src/voice/avatar/index.ts b/agents/src/voice/avatar/index.ts index 25c4a762a..f9d316a17 100644 --- a/agents/src/voice/avatar/index.ts +++ b/agents/src/voice/avatar/index.ts @@ -1 +1,4 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 export * from './datastream_io.js'; From 2a2101b2ec35ea7970c5373831cde0e5fd82925c Mon Sep 17 00:00:00 2001 From: Toubat Date: Thu, 11 Sep 2025 07:33:24 +0900 Subject: [PATCH 09/27] Create cold-cooks-smash.md --- .changeset/cold-cooks-smash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cold-cooks-smash.md diff --git a/.changeset/cold-cooks-smash.md b/.changeset/cold-cooks-smash.md new file mode 100644 index 000000000..27e78706a --- /dev/null +++ b/.changeset/cold-cooks-smash.md @@ -0,0 +1,5 @@ +--- +'@livekit/agents': patch +--- + +Add avatar datastream io component From 2240e3f33cda4eb9a6dde2eb01ab594a1a8bd891 Mon Sep 17 00:00:00 2001 From: Toubat Date: Thu, 11 Sep 2025 13:49:26 +0800 Subject: [PATCH 10/27] resolve import path --- agents/src/voice/avatar/datastream_io.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agents/src/voice/avatar/datastream_io.ts b/agents/src/voice/avatar/datastream_io.ts index 012abf3df..ad3f6eb64 100644 --- a/agents/src/voice/avatar/datastream_io.ts +++ b/agents/src/voice/avatar/datastream_io.ts @@ -10,14 +10,14 @@ import { type RpcInvocationData, type TrackKind, } from '@livekit/rtc-node'; -import { log } from 'agents/src/log.js'; +import { log } from '../../log.js'; import { Future, Task, shortuuid, waitForParticipant, waitForTrackPublication, -} from 'agents/src/utils.js'; +} from '../../utils.js'; import { AudioOutput, type PlaybackFinishedEvent } from '../io.js'; const RPC_CLEAR_BUFFER = 'lk.clear_buffer'; From efba23311fad38ee944802075752c78781eb522d Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 13:51:55 +0100 Subject: [PATCH 11/27] add anam plugni --- agents/src/cli.ts | 6 +- examples/package.json | 8 +- examples/src/anam_realtime_agent.ts | 66 ++++++++++++++ examples/tsconfig.json | 3 +- plugins/anam/CHANGELOG.md | 1 + plugins/anam/README.md | 17 ++++ plugins/anam/api-extractor.json | 20 ++++ plugins/anam/package.json | 54 +++++++++++ plugins/anam/src/api.ts | 137 ++++++++++++++++++++++++++++ plugins/anam/src/avatar.ts | 123 +++++++++++++++++++++++++ plugins/anam/src/index.ts | 15 +++ plugins/anam/src/types.ts | 16 ++++ plugins/anam/tsconfig.json | 16 ++++ plugins/anam/tsup.config.ts | 7 ++ pnpm-lock.yaml | 46 ++++++++++ scripts/copyDeclarationOutput.js | 30 ------ 16 files changed, 528 insertions(+), 37 deletions(-) create mode 100644 examples/src/anam_realtime_agent.ts create mode 100644 plugins/anam/CHANGELOG.md create mode 100644 plugins/anam/README.md create mode 100644 plugins/anam/api-extractor.json create mode 100644 plugins/anam/package.json create mode 100644 plugins/anam/src/api.ts create mode 100644 plugins/anam/src/avatar.ts create mode 100644 plugins/anam/src/index.ts create mode 100644 plugins/anam/src/types.ts create mode 100644 plugins/anam/tsconfig.json create mode 100644 plugins/anam/tsup.config.ts delete mode 100644 scripts/copyDeclarationOutput.js diff --git a/agents/src/cli.ts b/agents/src/cli.ts index c4a17ec1d..8142d4245 100644 --- a/agents/src/cli.ts +++ b/agents/src/cli.ts @@ -59,8 +59,8 @@ const runWorker = async (args: CliArgs) => { try { await worker.run(); - } catch { - logger.fatal('closing worker due to error.'); + } catch (err) { + logger.fatal({ err }, 'closing worker due to error'); process.exit(1); } }; @@ -145,6 +145,7 @@ export const runApp = (opts: WorkerOptions) => { ) .action(() => { const options = program.optsWithGlobals(); + // Prefer explicit CLI flag, then env var, then existing/default opts.wsURL = options.url || opts.wsURL; opts.apiKey = options.apiKey || opts.apiKey; opts.apiSecret = options.apiSecret || opts.apiSecret; @@ -170,6 +171,7 @@ export const runApp = (opts: WorkerOptions) => { ) .action((...[, command]) => { const options = command.optsWithGlobals(); + // Prefer explicit CLI flag, then env var, then existing/default opts.wsURL = options.url || opts.wsURL; opts.apiKey = options.apiKey || opts.apiKey; opts.apiSecret = options.apiSecret || opts.apiSecret; diff --git a/examples/package.json b/examples/package.json index 3daeb2e5e..8944d56ed 100644 --- a/examples/package.json +++ b/examples/package.json @@ -22,17 +22,19 @@ }, "dependencies": { "@livekit/agents": "workspace:*", - "@livekit/agents-plugin-resemble": "workspace:*", + "@livekit/agents-plugin-anam": "workspace:*", + "@livekit/agents-plugin-cartesia": "workspace:*", "@livekit/agents-plugin-deepgram": "workspace:*", "@livekit/agents-plugin-elevenlabs": "workspace:*", "@livekit/agents-plugin-google": "workspace:*", "@livekit/agents-plugin-livekit": "workspace:*", + "@livekit/agents-plugin-neuphonic": "workspace:*", "@livekit/agents-plugin-openai": "workspace:*", + "@livekit/agents-plugin-resemble": "workspace:*", "@livekit/agents-plugin-silero": "workspace:*", - "@livekit/agents-plugin-cartesia": "workspace:*", - "@livekit/agents-plugin-neuphonic": "workspace:*", "@livekit/noise-cancellation-node": "^0.1.9", "@livekit/rtc-node": "^0.13.11", + "dotenv": "^16.6.1", "livekit-server-sdk": "^2.9.2", "zod": "^3.23.8" } diff --git a/examples/src/anam_realtime_agent.ts b/examples/src/anam_realtime_agent.ts new file mode 100644 index 000000000..5e26bdff6 --- /dev/null +++ b/examples/src/anam_realtime_agent.ts @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import dotenv from 'dotenv'; +dotenv.config({ path: new URL('../../.env', import.meta.url).pathname }); + +import { + type JobContext, + WorkerOptions, + cli, + defineAgent, + voice, +} from '@livekit/agents'; +import * as anam from '@livekit/agents-plugin-anam'; +import * as openai from '@livekit/agents-plugin-openai'; +import { fileURLToPath } from 'node:url'; + +// Uses OpenAI Advanced Voice (Realtime), so no separate STT/TTS/VAD. + +export default defineAgent({ + entry: async (ctx: JobContext) => { + const agent = new voice.Agent({ + instructions: + "You are a helpful assistant. Speak clearly and concisely.", + }); + + const session = new voice.AgentSession({ + llm: new openai.realtime.RealtimeModel(), + voiceOptions: { + // allow the model to call multiple tools in a single turn if needed + maxToolSteps: 3, + }, + }); + + await session.start({ + agent, + room: ctx.room, + }); + + // Join the LiveKit room first (ensures room name and identity available) + await ctx.connect(); + + // Configure the Anam avatar persona (requires avatarId) + const personaName = process.env.ANAM_PERSONA_NAME ?? 'Agent'; + const avatarId = process.env.ANAM_AVATAR_ID; + if (!avatarId) { + throw new Error('ANAM_AVATAR_ID is required'); + } + + // Start the Anam avatar session and route Agent audio to the avatar + const avatar = new anam.AvatarSession({ + personaConfig: { name: personaName, avatarId }, + // Allow overriding base URL via env + apiUrl: process.env.ANAM_API_URL, + // connOptions: { maxRetry: 5, retryInterval: 2, timeout: 15 }, + }); + await avatar.start(session, ctx.room); + + // With Realtime LLM, generateReply will synthesize audio via the model + session.generateReply({ + instructions: 'Greet the user briefly and confirm you are ready.', + }); + }, +}); + +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); diff --git a/examples/tsconfig.json b/examples/tsconfig.json index eb44cef99..80eeb2e0e 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -4,7 +4,6 @@ "compilerOptions": { // match output dir to input dir. e.g. dist/index instead of dist/src/index "rootDir": "./src", - "declarationDir": "./dist", - "outDir": "./dist" + "declarationDir": "./dist" } } diff --git a/plugins/anam/CHANGELOG.md b/plugins/anam/CHANGELOG.md new file mode 100644 index 000000000..47f5c9424 --- /dev/null +++ b/plugins/anam/CHANGELOG.md @@ -0,0 +1 @@ +# @livekit/agents-plugin-anam diff --git a/plugins/anam/README.md b/plugins/anam/README.md new file mode 100644 index 000000000..3b7cc417f --- /dev/null +++ b/plugins/anam/README.md @@ -0,0 +1,17 @@ + +# Anam plugin for LiveKit Agents + +The Agents Framework is designed for building realtime, programmable +participants that run on servers. Use it to create conversational, multi-modal +voice agents that can see, hear, and understand. + +This package contains the Anam plugin, which allows for voice synthesis. +Refer to the [documentation](https://docs.livekit.io/agents/overview/) for +information on how to use it, or browse the [API +reference](https://docs.livekit.io/agents-js/modules/plugins_agents_plugin_Anam.html). +See the [repository](https://github.com/livekit/agents-js) for more information +about the framework as a whole. diff --git a/plugins/anam/api-extractor.json b/plugins/anam/api-extractor.json new file mode 100644 index 000000000..1f75e0708 --- /dev/null +++ b/plugins/anam/api-extractor.json @@ -0,0 +1,20 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + "extends": "../../api-extractor-shared.json", + "mainEntryPointFilePath": "./dist/index.d.ts" +} diff --git a/plugins/anam/package.json b/plugins/anam/package.json new file mode 100644 index 000000000..cef910597 --- /dev/null +++ b/plugins/anam/package.json @@ -0,0 +1,54 @@ +{ + "name": "@livekit/agents-plugin-anam", + "version": "0.0.1", + "description": "Anam plugin for LiveKit Node Agents", + "main": "dist/index.js", + "require": "dist/index.cjs", + "types": "dist/index.d.ts", + "exports": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "author": "LiveKit", + "type": "module", + "repository": "git@github.com:livekit/agents-js.git", + "license": "Apache-2.0", + "files": [ + "dist", + "src", + "README.md" + ], + "scripts": { + "build": "tsup --onSuccess \"pnpm build:types\"", + "build:types": "tsc --declaration --emitDeclarationOnly && node ../../scripts/copyDeclarationOutput.js", + "clean": "rm -rf dist", + "clean:build": "pnpm clean && pnpm build", + "lint": "eslint -f unix \"src/**/*.{ts,js}\"", + "api:check": "api-extractor run --typescript-compiler-folder ../../node_modules/typescript", + "api:update": "api-extractor run --local --typescript-compiler-folder ../../node_modules/typescript --verbose" + }, + "devDependencies": { + "@livekit/agents": "workspace:*", + "@livekit/agents-plugin-openai": "workspace:*", + "@livekit/agents-plugins-test": "workspace:*", + "@livekit/rtc-node": "^0.13.12", + "@microsoft/api-extractor": "^7.35.0", + "@types/ws": "^8.5.10", + "tsup": "^8.3.5", + "typescript": "^5.0.0" + }, + "dependencies": { + "livekit-server-sdk": "^2.9.2", + "ws": "^8.16.0" + }, + "peerDependencies": { + "@livekit/agents": "workspace:*", + "@livekit/rtc-node": "^0.13.12" + } +} diff --git a/plugins/anam/src/api.ts b/plugins/anam/src/api.ts new file mode 100644 index 000000000..7e55bccaf --- /dev/null +++ b/plugins/anam/src/api.ts @@ -0,0 +1,137 @@ +// src/api.ts +import { AnamException, type APIConnectOptions } from './types.js'; +import { log } from '@livekit/agents'; + +const DEFAULT_API_URL = 'https://api.anam.ai'; + +type APIPaths = { tokenPath?: string; startPath?: string }; + +export class AnamAPI { + constructor( + private apiKey: string, + private apiUrl: string = DEFAULT_API_URL, + private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 }, + private paths: APIPaths = {}, + ) {} + + private get tokenPath(): string { + return ( + this.paths.tokenPath || + process.env.ANAM_SESSION_TOKEN_PATH || + // Default to the v1 auth endpoint; older '/sessions/token' is deprecated + '/v1/auth/session-token' + ); + } + + private get startPath(): string { + return ( + this.paths.startPath || + process.env.ANAM_SESSION_START_PATH || + '/v1/engine/session' + ); + } + + private async postWithHeaders( + path: string, + body: unknown, + headersIn: Record, + ): Promise { + const url = `${this.apiUrl}${path}`; + const { maxRetry = 3, retryInterval = 2, timeout = 10 } = this.conn; + let lastErr: unknown; + const logger = log().child({ module: 'AnamAPI' }); + + for (let attempt = 0; attempt < maxRetry; attempt++) { + try { + const headers: Record = { + 'Content-Type': 'application/json', + ...headersIn, + }; + + // redact token for logs + const redactedHeaders: Record = { ...headers }; + if (redactedHeaders.Authorization) { + redactedHeaders.Authorization = 'Bearer ****'; + } + const redactedBody = (() => { + if (body && typeof body === 'object') { + try { + const clone = { ...(body as Record) } as Record; + if ('livekitToken' in clone) clone.livekitToken = '****'; + if ('sessionToken' in clone) clone.sessionToken = '****' as unknown as never; + return clone; + } catch { + return { note: 'unserializable body' }; + } + } + return body; + })(); + + logger.debug({ url, method: 'POST', headers: redactedHeaders, body: redactedBody, attempt: attempt + 1 }, 'calling Anam API'); + + const res = await fetch(url, { + method: 'POST', + headers, + body: JSON.stringify(body), + // simple timeout: rely on AbortController in real impl + }); + if (!res.ok) { + const text = await res.text(); + logger.error({ url, method: 'POST', headers: redactedHeaders, body: redactedBody, status: res.status, response: text }, 'Anam API request failed'); + throw new AnamException(`Anam ${path} failed: ${res.status} ${text}`); + } + const json = (await res.json()) as T; + logger.debug({ url }, 'Anam API request succeeded'); + return json; + } catch (e) { + lastErr = e; + if (attempt === maxRetry - 1) break; + logger.warn({ url, method: 'POST', body: (body && typeof body === 'object') ? { ...(body as Record), livekitToken: '****' } : body, error: (e as Error)?.message, nextRetrySec: retryInterval }, 'Anam API error, retrying'); + await new Promise(r => setTimeout(r, retryInterval * 1000)); + } + } + throw lastErr instanceof Error ? lastErr : new AnamException('Anam API error'); + } + + private async post(path: string, body: unknown): Promise { + // Default auth with API key + return this.postWithHeaders(path, body, { Authorization: `Bearer ${this.apiKey}` }); + } + + createSessionToken(params: { + personaConfig: import('./types.js').PersonaConfig; + livekitUrl?: string; + livekitToken?: string; + }) { + // Build payload per API spec + const pc = params.personaConfig; + const personaPayload = { + type: 'ephemeral', + name: pc.name, + avatarId: pc.avatarId, + llmId: 'CUSTOMER_CLIENT_V1', + }; + + const payload: Record = { + personaConfig: personaPayload, + }; + if (params.livekitUrl && params.livekitToken) { + payload.environment = { + livekitUrl: params.livekitUrl, + livekitToken: params.livekitToken, + }; + } + + return this.post<{ sessionToken: string }>(this.tokenPath, payload); + } + + startEngineSession(params: { sessionToken: string }) { + // Per API, startEngineSession must authorize with the Session Token, + // not the API key. + return this.postWithHeaders<{ sessionId: string }>( + this.startPath, + {}, + { Authorization: `Bearer ${params.sessionToken}` }, + ); + } +} diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts new file mode 100644 index 000000000..977dc3ff9 --- /dev/null +++ b/plugins/anam/src/avatar.ts @@ -0,0 +1,123 @@ +import { Room, TrackKind } from '@livekit/rtc-node'; +import { voice, log } from '@livekit/agents'; +import { AnamAPI, } from './api.js'; +import { AnamException, type PersonaConfig, type APIConnectOptions } from './types.js'; +import { AccessToken, type VideoGrant } from 'livekit-server-sdk'; + +export async function mintAvatarJoinToken({ + roomName, + avatarIdentity, // e.g. `anam:${roomName}` + publishOnBehalf, // your agent's identity + apiKey = process.env.LIVEKIT_API_KEY!, + apiSecret = process.env.LIVEKIT_API_SECRET!, + ttl = '60s', + }: { + roomName: string; + avatarIdentity: string; + publishOnBehalf: string; + apiKey?: string; + apiSecret?: string; + ttl?: string | number; + }): Promise { + const at = new AccessToken(apiKey, apiSecret); + at.identity = avatarIdentity; + at.name = 'Anam Avatar'; + at.kind = 'agent'; // avatar joins as Agent + at.ttl = ttl; + at.attributes = { 'lk.publish_on_behalf': publishOnBehalf }; + + at.addGrant({ roomJoin: true, room: roomName } as VideoGrant); + return at.toJwt(); + } + + +const AVATAR_IDENTITY = 'anam-avatar-agent'; +const AVATAR_NAME = 'anam-avatar-agent'; + +export class AvatarSession { + private sessionId?: string; + + constructor( + private opts: { + personaConfig: PersonaConfig; + apiUrl?: string; + apiKey?: string; + avatarParticipantIdentity?: string; + avatarParticipantName?: string; + connOptions?: APIConnectOptions; + } + ) {} + + async start( + agentSession: voice.AgentSession, + room: Room, + params?: { + livekitUrl?: string; + livekitApiKey?: string; + livekitApiSecret?: string; + } + ) { + const logger = log().child({ module: 'AnamAvatar' }); + const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY; + if (!apiKey) throw new AnamException('ANAM_API_KEY is required'); + + const apiUrl = this.opts.apiUrl ?? process.env.ANAM_API_URL; + const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL; + const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY; + const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET; + const devMode = Boolean(apiUrl && apiUrl.includes('anam.dev')) || process.env.ANAM_DEV_MODE === '1'; + + if (!devMode) { + if (!livekitUrl || !lkKey || !lkSecret) { + throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set'); + } + } + + // who are we publishing on behalf of? + const localIdentity = + (room.localParticipant && room.localParticipant.identity) || 'agent'; + + logger.debug({ + personaName: this.opts.personaConfig?.name, + avatarId: this.opts.personaConfig?.avatarId, + apiUrl: apiUrl ?? '(default https://api.anam.ai)', + livekitUrl, + avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent', + publishOnBehalf: localIdentity, + }, 'starting Anam avatar session'); + + // build a LiveKit token for the avatar worker (mirrors Python) + let jwt: string | undefined = undefined; + if (!devMode) { + jwt = await mintAvatarJoinToken({ + roomName: room.name!, + avatarIdentity: this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY, + publishOnBehalf: localIdentity, + apiKey: lkKey, + apiSecret: lkSecret, + }); + } + + const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions); + logger.debug({ livekitUrl, devMode }, 'requesting Anam session token'); + + const { sessionToken } = await anam.createSessionToken({ + personaConfig: { + name: this.opts.personaConfig?.name, + avatarId: this.opts.personaConfig?.avatarId, + }, + livekitUrl, + livekitToken: jwt, + }); + logger.debug('starting Anam engine session'); + const started = await anam.startEngineSession({ sessionToken }); + this.sessionId = started.sessionId; + + // route the agent audio to the avatar via data channel & wait for video + agentSession.output.audio = new voice.DataStreamAudioOutput({ + room, + destinationIdentity: this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY, + waitRemoteTrack: TrackKind.KIND_VIDEO, + }); + } +} diff --git a/plugins/anam/src/index.ts b/plugins/anam/src/index.ts new file mode 100644 index 000000000..cd679e323 --- /dev/null +++ b/plugins/anam/src/index.ts @@ -0,0 +1,15 @@ +import { Plugin } from '@livekit/agents'; +export * from './types.js'; +export * from './api.js'; +export * from './avatar.js'; + +class AnamPlugin extends Plugin { + constructor() { + super({ + title: 'anam', + version: '0.0.1', + package: '@livekit/agents-plugin-anam', + }); + } +} +Plugin.registerPlugin(new AnamPlugin()); \ No newline at end of file diff --git a/plugins/anam/src/types.ts b/plugins/anam/src/types.ts new file mode 100644 index 000000000..6dd24beb4 --- /dev/null +++ b/plugins/anam/src/types.ts @@ -0,0 +1,16 @@ +export type PersonaConfig = { + /** Optional display name (prod flow) */ + name?: string; + /** Optional avatar asset id (prod flow) */ + avatarId?: string; + /** Optional persona id (dev flow) */ + personaId?: string; +}; + +export type APIConnectOptions = { + maxRetry?: number; + retryInterval?: number; // seconds + timeout?: number; // seconds +}; + +export class AnamException extends Error {} diff --git a/plugins/anam/tsconfig.json b/plugins/anam/tsconfig.json new file mode 100644 index 000000000..dacffdfec --- /dev/null +++ b/plugins/anam/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "include": ["./src"], + "compilerOptions": { + // match output dir to input dir. e.g. dist/index instead of dist/src/index + "rootDir": "./src", + "declarationDir": "./dist", + "outDir": "./dist" + }, + "typedocOptions": { + "name": "plugins/agents-plugin-anam", + "entryPointStrategy": "resolve", + "readme": "none", + "entryPoints": ["src/index.ts"] + } +} diff --git a/plugins/anam/tsup.config.ts b/plugins/anam/tsup.config.ts new file mode 100644 index 000000000..8ca20961f --- /dev/null +++ b/plugins/anam/tsup.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'tsup'; + +import defaults from '../../tsup.config'; + +export default defineConfig({ + ...defaults, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b55805158..3693e09d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,9 @@ importers: '@livekit/agents': specifier: workspace:* version: link:../agents + '@livekit/agents-plugin-anam': + specifier: workspace:* + version: link:../plugins/anam '@livekit/agents-plugin-cartesia': specifier: workspace:* version: link:../plugins/cartesia @@ -195,6 +198,9 @@ importers: '@livekit/rtc-node': specifier: ^0.13.11 version: 0.13.13 + dotenv: + specifier: ^16.6.1 + version: 16.6.1 livekit-server-sdk: specifier: ^2.9.2 version: 2.9.2 @@ -215,6 +221,40 @@ importers: specifier: ^3.2.2 version: 3.2.2(@types/node@22.15.30) + plugins/anam: + dependencies: + livekit-server-sdk: + specifier: ^2.9.2 + version: 2.9.2 + ws: + specifier: ^8.16.0 + version: 8.18.3 + devDependencies: + '@livekit/agents': + specifier: workspace:* + version: link:../../agents + '@livekit/agents-plugin-openai': + specifier: workspace:* + version: link:../openai + '@livekit/agents-plugins-test': + specifier: workspace:* + version: link:../test + '@livekit/rtc-node': + specifier: ^0.13.12 + version: 0.13.13 + '@microsoft/api-extractor': + specifier: ^7.35.0 + version: 7.43.7(@types/node@22.15.30) + '@types/ws': + specifier: ^8.5.10 + version: 8.5.10 + tsup: + specifier: ^8.3.5 + version: 8.4.0(@microsoft/api-extractor@7.43.7(@types/node@22.15.30))(postcss@8.4.38)(tsx@4.20.4)(typescript@5.4.5) + typescript: + specifier: ^5.0.0 + version: 5.4.5 + plugins/cartesia: dependencies: ws: @@ -2304,6 +2344,10 @@ packages: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + dotenv@8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} @@ -6091,6 +6135,8 @@ snapshots: dotenv@16.0.3: {} + dotenv@16.6.1: {} + dotenv@8.6.0: {} eastasianwidth@0.2.0: {} diff --git a/scripts/copyDeclarationOutput.js b/scripts/copyDeclarationOutput.js deleted file mode 100644 index 0f44f3b8e..000000000 --- a/scripts/copyDeclarationOutput.js +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// @ts-check -import fs from 'fs'; -import path from 'path'; - -/** - * @param {string} dir - * @param {(filePath: string) => void} callback - */ -const walkDir = (dir, callback) => { - fs.readdirSync(dir).forEach((f) => { - const dirPath = path.join(dir, f); - const isDirectory = fs.statSync(dirPath).isDirectory(); - isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f)); - }); -}; - -/** - * @param {string} dir - * @param {(filePath: string) => void} callback - */ -walkDir('dist', (filePath) => { - if (filePath.endsWith('.d.ts')) { - const newPath = filePath.replace(/\.d\.ts$/, '.d.cts'); - fs.copyFileSync(filePath, newPath); - } -}); -console.log('copied declaration .d.ts files to .d.cts files'); From d857367021462eab0e0958f1f319e46a8fe99ca4 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 13:57:44 +0100 Subject: [PATCH 12/27] fix --- scripts/copyDeclarationOutput.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 scripts/copyDeclarationOutput.js diff --git a/scripts/copyDeclarationOutput.js b/scripts/copyDeclarationOutput.js new file mode 100644 index 000000000..212b1355e --- /dev/null +++ b/scripts/copyDeclarationOutput.js @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// @ts-check +import fs from 'fs'; +import path from 'path'; + +/** + * @param {string} dir + * @param {(filePath: string) => void} callback + */ +const walkDir = (dir, callback) => { + fs.readdirSync(dir).forEach((f) => { + const dirPath = path.join(dir, f); + const isDirectory = fs.statSync(dirPath).isDirectory(); + isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f)); + }); +}; + +/** + * @param {string} dir + * @param {(filePath: string) => void} callback + */ +walkDir('dist', (filePath) => { + if (filePath.endsWith('.d.ts')) { + const newPath = filePath.replace(/\.d\.ts$/, '.d.cts'); + fs.copyFileSync(filePath, newPath); + } +}); +console.log('copied declaration .d.ts files to .d.cts files'); \ No newline at end of file From 02e2e8270d206ffeae6d260911240525816522c5 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 13:58:38 +0100 Subject: [PATCH 13/27] fix --- agents/src/cli.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/agents/src/cli.ts b/agents/src/cli.ts index 8142d4245..7644bb42e 100644 --- a/agents/src/cli.ts +++ b/agents/src/cli.ts @@ -59,8 +59,8 @@ const runWorker = async (args: CliArgs) => { try { await worker.run(); - } catch (err) { - logger.fatal({ err }, 'closing worker due to error'); + } catch { + logger.fatal('closing worker due to error.'); process.exit(1); } }; @@ -145,7 +145,6 @@ export const runApp = (opts: WorkerOptions) => { ) .action(() => { const options = program.optsWithGlobals(); - // Prefer explicit CLI flag, then env var, then existing/default opts.wsURL = options.url || opts.wsURL; opts.apiKey = options.apiKey || opts.apiKey; opts.apiSecret = options.apiSecret || opts.apiSecret; @@ -171,7 +170,6 @@ export const runApp = (opts: WorkerOptions) => { ) .action((...[, command]) => { const options = command.optsWithGlobals(); - // Prefer explicit CLI flag, then env var, then existing/default opts.wsURL = options.url || opts.wsURL; opts.apiKey = options.apiKey || opts.apiKey; opts.apiSecret = options.apiSecret || opts.apiSecret; @@ -223,4 +221,4 @@ export const runApp = (opts: WorkerOptions) => { }); program.parse(); -}; +}; \ No newline at end of file From 84b1f77ddc4b8a95f5c209a367ea465dd921f180 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 13:58:58 +0100 Subject: [PATCH 14/27] fix --- agents/src/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents/src/cli.ts b/agents/src/cli.ts index 7644bb42e..c4a17ec1d 100644 --- a/agents/src/cli.ts +++ b/agents/src/cli.ts @@ -221,4 +221,4 @@ export const runApp = (opts: WorkerOptions) => { }); program.parse(); -}; \ No newline at end of file +}; From 6cce4223d970d0a3e6dbc7939005e135651e80b6 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:00:34 +0100 Subject: [PATCH 15/27] fix --- examples/package.json | 1 - examples/src/anam_realtime_agent.ts | 3 --- pnpm-lock.yaml | 9 --------- 3 files changed, 13 deletions(-) diff --git a/examples/package.json b/examples/package.json index 8944d56ed..3161992fa 100644 --- a/examples/package.json +++ b/examples/package.json @@ -34,7 +34,6 @@ "@livekit/agents-plugin-silero": "workspace:*", "@livekit/noise-cancellation-node": "^0.1.9", "@livekit/rtc-node": "^0.13.11", - "dotenv": "^16.6.1", "livekit-server-sdk": "^2.9.2", "zod": "^3.23.8" } diff --git a/examples/src/anam_realtime_agent.ts b/examples/src/anam_realtime_agent.ts index 5e26bdff6..eac51631c 100644 --- a/examples/src/anam_realtime_agent.ts +++ b/examples/src/anam_realtime_agent.ts @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: 2025 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import dotenv from 'dotenv'; -dotenv.config({ path: new URL('../../.env', import.meta.url).pathname }); - import { type JobContext, WorkerOptions, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3693e09d3..0619851c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -198,9 +198,6 @@ importers: '@livekit/rtc-node': specifier: ^0.13.11 version: 0.13.13 - dotenv: - specifier: ^16.6.1 - version: 16.6.1 livekit-server-sdk: specifier: ^2.9.2 version: 2.9.2 @@ -2344,10 +2341,6 @@ packages: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dotenv@8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} @@ -6135,8 +6128,6 @@ snapshots: dotenv@16.0.3: {} - dotenv@16.6.1: {} - dotenv@8.6.0: {} eastasianwidth@0.2.0: {} From 2e14c2ad0f57505ebf0f7a694d0df63057f37c62 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:03:23 +0100 Subject: [PATCH 16/27] fix --- .changeset/add-anam-plugin.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .changeset/add-anam-plugin.md diff --git a/.changeset/add-anam-plugin.md b/.changeset/add-anam-plugin.md new file mode 100644 index 000000000..26fc1a0b0 --- /dev/null +++ b/.changeset/add-anam-plugin.md @@ -0,0 +1,13 @@ +--- +"@livekit/agents-plugin-anam": minor +--- + +Add new Anam plugin for avatar integration + +This adds a new plugin `@livekit/agents-plugin-anam` that provides: +- AvatarSession class for managing Anam avatar connections +- API integration for Anam avatar services +- Real-time avatar video streaming capabilities +- Integration with LiveKit agents for voice-to-avatar workflows + +The plugin enables developers to create conversational AI agents with realistic avatar representations using Anam's avatar technology. From 806afbcaa750fcb793f5dcc6e6f5f0b8fdac5b8d Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:04:36 +0100 Subject: [PATCH 17/27] fix --- plugins/anam/src/api.ts | 14 +++++++------- plugins/anam/src/avatar.ts | 3 +++ plugins/anam/src/index.ts | 3 +++ plugins/anam/src/types.ts | 3 +++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/plugins/anam/src/api.ts b/plugins/anam/src/api.ts index 7e55bccaf..b76da2613 100644 --- a/plugins/anam/src/api.ts +++ b/plugins/anam/src/api.ts @@ -1,4 +1,6 @@ -// src/api.ts +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 import { AnamException, type APIConnectOptions } from './types.js'; import { log } from '@livekit/agents'; @@ -115,12 +117,10 @@ export class AnamAPI { const payload: Record = { personaConfig: personaPayload, }; - if (params.livekitUrl && params.livekitToken) { - payload.environment = { - livekitUrl: params.livekitUrl, - livekitToken: params.livekitToken, - }; - } + payload.environment = { + livekitUrl: params.livekitUrl, + livekitToken: params.livekitToken, + }; return this.post<{ sessionToken: string }>(this.tokenPath, payload); } diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index 977dc3ff9..af67b08b4 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 import { Room, TrackKind } from '@livekit/rtc-node'; import { voice, log } from '@livekit/agents'; import { AnamAPI, } from './api.js'; diff --git a/plugins/anam/src/index.ts b/plugins/anam/src/index.ts index cd679e323..11e0f59ef 100644 --- a/plugins/anam/src/index.ts +++ b/plugins/anam/src/index.ts @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 import { Plugin } from '@livekit/agents'; export * from './types.js'; export * from './api.js'; diff --git a/plugins/anam/src/types.ts b/plugins/anam/src/types.ts index 6dd24beb4..ecc21ecf6 100644 --- a/plugins/anam/src/types.ts +++ b/plugins/anam/src/types.ts @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 export type PersonaConfig = { /** Optional display name (prod flow) */ name?: string; From 4234c233d164327bde875173849a6479526b9df0 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:08:51 +0100 Subject: [PATCH 18/27] fix --- examples/src/anam_realtime_agent.js | 48 ++ examples/src/anam_realtime_agent.js.map | 1 + examples/src/basic_agent.js | 46 ++ examples/src/basic_agent.js.map | 1 + examples/src/basic_eou.js | 38 + examples/src/basic_eou.js.map | 1 + examples/src/basic_tool_call_agent.js | 127 ++++ examples/src/basic_tool_call_agent.js.map | 1 + examples/src/cartersia_tts.js | 46 ++ examples/src/cartersia_tts.js.map | 1 + examples/src/comprehensive_test.js | 210 ++++++ examples/src/comprehensive_test.js.map | 1 + examples/src/drive-thru/database.js | 659 ++++++++++++++++++ examples/src/drive-thru/database.js.map | 1 + examples/src/drive-thru/drivethru_agent.js | 294 ++++++++ .../src/drive-thru/drivethru_agent.js.map | 1 + examples/src/drive-thru/order.js | 47 ++ examples/src/drive-thru/order.js.map | 1 + examples/src/frontdesk/calendar_api.js | 213 ++++++ examples/src/frontdesk/calendar_api.js.map | 1 + examples/src/frontdesk/calendar_api.test.js | 367 ++++++++++ .../src/frontdesk/calendar_api.test.js.map | 1 + .../frontdesk/calendar_integration.test.js | 35 + .../calendar_integration.test.js.map | 1 + examples/src/frontdesk/frontdesk_agent.js | 206 ++++++ examples/src/frontdesk/frontdesk_agent.js.map | 1 + examples/src/gemini_realtime_agent.js | 90 +++ examples/src/gemini_realtime_agent.js.map | 1 + examples/src/multi_agent.js | 76 ++ examples/src/multi_agent.js.map | 1 + examples/src/push_to_talk.js | 53 ++ examples/src/push_to_talk.js.map | 1 + examples/src/raw_function_description.js | 67 ++ examples/src/raw_function_description.js.map | 1 + examples/src/realtime_agent.js | 59 ++ examples/src/realtime_agent.js.map | 1 + examples/src/realtime_turn_detector.js | 40 ++ examples/src/realtime_turn_detector.js.map | 1 + examples/src/restaurant_agent.js | 338 +++++++++ examples/src/restaurant_agent.js.map | 1 + scripts/copyDeclarationOutput.js | 2 +- 41 files changed, 3080 insertions(+), 1 deletion(-) create mode 100644 examples/src/anam_realtime_agent.js create mode 100644 examples/src/anam_realtime_agent.js.map create mode 100644 examples/src/basic_agent.js create mode 100644 examples/src/basic_agent.js.map create mode 100644 examples/src/basic_eou.js create mode 100644 examples/src/basic_eou.js.map create mode 100644 examples/src/basic_tool_call_agent.js create mode 100644 examples/src/basic_tool_call_agent.js.map create mode 100644 examples/src/cartersia_tts.js create mode 100644 examples/src/cartersia_tts.js.map create mode 100644 examples/src/comprehensive_test.js create mode 100644 examples/src/comprehensive_test.js.map create mode 100644 examples/src/drive-thru/database.js create mode 100644 examples/src/drive-thru/database.js.map create mode 100644 examples/src/drive-thru/drivethru_agent.js create mode 100644 examples/src/drive-thru/drivethru_agent.js.map create mode 100644 examples/src/drive-thru/order.js create mode 100644 examples/src/drive-thru/order.js.map create mode 100644 examples/src/frontdesk/calendar_api.js create mode 100644 examples/src/frontdesk/calendar_api.js.map create mode 100644 examples/src/frontdesk/calendar_api.test.js create mode 100644 examples/src/frontdesk/calendar_api.test.js.map create mode 100644 examples/src/frontdesk/calendar_integration.test.js create mode 100644 examples/src/frontdesk/calendar_integration.test.js.map create mode 100644 examples/src/frontdesk/frontdesk_agent.js create mode 100644 examples/src/frontdesk/frontdesk_agent.js.map create mode 100644 examples/src/gemini_realtime_agent.js create mode 100644 examples/src/gemini_realtime_agent.js.map create mode 100644 examples/src/multi_agent.js create mode 100644 examples/src/multi_agent.js.map create mode 100644 examples/src/push_to_talk.js create mode 100644 examples/src/push_to_talk.js.map create mode 100644 examples/src/raw_function_description.js create mode 100644 examples/src/raw_function_description.js.map create mode 100644 examples/src/realtime_agent.js create mode 100644 examples/src/realtime_agent.js.map create mode 100644 examples/src/realtime_turn_detector.js create mode 100644 examples/src/realtime_turn_detector.js.map create mode 100644 examples/src/restaurant_agent.js create mode 100644 examples/src/restaurant_agent.js.map diff --git a/examples/src/anam_realtime_agent.js b/examples/src/anam_realtime_agent.js new file mode 100644 index 000000000..d9ab3e7eb --- /dev/null +++ b/examples/src/anam_realtime_agent.js @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, voice, } from '@livekit/agents'; +import * as anam from '@livekit/agents-plugin-anam'; +import * as openai from '@livekit/agents-plugin-openai'; +import { fileURLToPath } from 'node:url'; +// Uses OpenAI Advanced Voice (Realtime), so no separate STT/TTS/VAD. +export default defineAgent({ + entry: async (ctx) => { + const agent = new voice.Agent({ + instructions: "You are a helpful assistant. Speak clearly and concisely.", + }); + const session = new voice.AgentSession({ + llm: new openai.realtime.RealtimeModel(), + voiceOptions: { + // allow the model to call multiple tools in a single turn if needed + maxToolSteps: 3, + }, + }); + await session.start({ + agent, + room: ctx.room, + }); + // Join the LiveKit room first (ensures room name and identity available) + await ctx.connect(); + // Configure the Anam avatar persona (requires avatarId) + const personaName = process.env.ANAM_PERSONA_NAME ?? 'Agent'; + const avatarId = process.env.ANAM_AVATAR_ID; + if (!avatarId) { + throw new Error('ANAM_AVATAR_ID is required'); + } + // Start the Anam avatar session and route Agent audio to the avatar + const avatar = new anam.AvatarSession({ + personaConfig: { name: personaName, avatarId }, + // Allow overriding base URL via env + apiUrl: process.env.ANAM_API_URL, + // connOptions: { maxRetry: 5, retryInterval: 2, timeout: 15 }, + }); + await avatar.start(session, ctx.room); + // With Realtime LLM, generateReply will synthesize audio via the model + session.generateReply({ + instructions: 'Greet the user briefly and confirm you are ready.', + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=anam_realtime_agent.js.map \ No newline at end of file diff --git a/examples/src/anam_realtime_agent.js.map b/examples/src/anam_realtime_agent.js.map new file mode 100644 index 000000000..99cf8724a --- /dev/null +++ b/examples/src/anam_realtime_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"anam_realtime_agent.js","sourceRoot":"","sources":["anam_realtime_agent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAEL,aAAa,EACb,GAAG,EACH,WAAW,EACX,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,6BAA6B,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,qEAAqE;AAErE,eAAe,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,2DAA2D;SAC9D,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE;YACxC,YAAY,EAAE;gBACZ,oEAAoE;gBACpE,YAAY,EAAE,CAAC;aAChB;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,yEAAyE;QACzE,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QAEpB,wDAAwD;QACxD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC;QAC7D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,oEAAoE;QACpE,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC;YACpC,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE;YAC9C,oCAAoC;YACpC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;YAChC,+DAA+D;SAChE,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAEtC,uEAAuE;QACvE,OAAO,CAAC,aAAa,CAAC;YACpB,YAAY,EAAE,mDAAmD;SAClE,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/basic_agent.js b/examples/src/basic_agent.js new file mode 100644 index 000000000..cec51a276 --- /dev/null +++ b/examples/src/basic_agent.js @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, metrics, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; +import { fileURLToPath } from 'node:url'; +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const agent = new voice.Agent({ + instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", + }); + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + stt: new deepgram.STT(), + tts: new elevenlabs.TTS(), + llm: new openai.LLM(), + // to use realtime model, replace the stt, llm, tts and vad with the following + // llm: new openai.realtime.RealtimeModel(), + turnDetection: new livekit.turnDetector.MultilingualModel(), + }); + const usageCollector = new metrics.UsageCollector(); + session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { + metrics.logMetrics(ev.metrics); + usageCollector.collect(ev.metrics); + }); + await session.start({ + agent, + room: ctx.room, + inputOptions: { + noiseCancellation: BackgroundVoiceCancellation(), + }, + }); + session.say('Hello, how can I help you today?'); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=basic_agent.js.map \ No newline at end of file diff --git a/examples/src/basic_agent.js.map b/examples/src/basic_agent.js.map new file mode 100644 index 000000000..cde90d353 --- /dev/null +++ b/examples/src/basic_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"basic_agent.js","sourceRoot":"","sources":["basic_agent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,OAAO,EACP,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,iFAAiF;SACpF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB,CAAC;QAEjD,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG;YACH,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,8EAA8E;YAC9E,4CAA4C;YAC5C,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE;SAC5D,CAAC,CAAC;QAEH,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAEpD,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YAC/D,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAC/B,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,YAAY,EAAE;gBACZ,iBAAiB,EAAE,2BAA2B,EAAE;aACjD;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/basic_eou.js b/examples/src/basic_eou.js new file mode 100644 index 000000000..fe2e85b83 --- /dev/null +++ b/examples/src/basic_eou.js @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, log } from '@livekit/agents'; +import { turnDetector } from '@livekit/agents-plugin-livekit'; +import { fileURLToPath } from 'node:url'; +export default defineAgent({ + entry: async (ctx) => { + const logger = log(); + // Manual connection required since this example doesn't use AgentSession + await ctx.connect(); + // const eouModel = new turnDetector.EnglishModel(); + const eouModel = new turnDetector.MultilingualModel(); + const unlikelyThreshold = await eouModel.unlikelyThreshold('en'); + logger.info({ unlikelyThreshold }, 'unlikelyThreshold'); + const chatCtx = llm.ChatContext.empty(); + chatCtx.addMessage({ + role: 'assistant', + content: 'Hi, how can I help you today?', + }); + const nonEndingTurn = chatCtx.copy(); + nonEndingTurn.addMessage({ + role: 'user', + content: 'What is the weather in', + }); + const nonEndingTurnResult = await eouModel.predictEndOfTurn(nonEndingTurn); + logger.info({ nonEndingTurnResult }, 'nonEndingTurnResult'); + const endingTurn = chatCtx.copy(); + endingTurn.addMessage({ + role: 'user', + content: 'What is the weather in San Francisco?', + }); + const endingTurnResult = await eouModel.predictEndOfTurn(endingTurn); + logger.info({ endingTurnResult }, 'endingTurnResult'); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=basic_eou.js.map \ No newline at end of file diff --git a/examples/src/basic_eou.js.map b/examples/src/basic_eou.js.map new file mode 100644 index 000000000..fb5475a99 --- /dev/null +++ b/examples/src/basic_eou.js.map @@ -0,0 +1 @@ +{"version":3,"file":"basic_eou.js","sourceRoot":"","sources":["basic_eou.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAAmB,aAAa,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC;QAErB,yEAAyE;QACzE,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QAEpB,oDAAoD;QACpD,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,iBAAiB,EAAE,CAAC;QAEtD,MAAM,iBAAiB,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,EAAE,iBAAiB,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAExD,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACxC,OAAO,CAAC,UAAU,CAAC;YACjB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,+BAA+B;SACzC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QACrC,aAAa,CAAC,UAAU,CAAC;YACvB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,wBAAwB;SAClC,CAAC,CAAC;QAEH,MAAM,mBAAmB,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,CAAC,EAAE,mBAAmB,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAClC,UAAU,CAAC,UAAU,CAAC;YACpB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,uCAAuC;SACjD,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACxD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/basic_tool_call_agent.js b/examples/src/basic_tool_call_agent.js new file mode 100644 index 000000000..bba80a55b --- /dev/null +++ b/examples/src/basic_tool_call_agent.js @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as google from '@livekit/agents-plugin-google'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +const roomNameSchema = z.enum(['bedroom', 'living room', 'kitchen', 'bathroom', 'office']); +class RouterAgent extends voice.Agent { + async onEnter() { + this.session.say("Hello, I'm a router agent. I can help you with your tasks."); + } +} +class GameAgent extends voice.Agent { + async onEnter() { + this.session.generateReply({ + userInput: 'Ask the user for a number, then check the stored number', + toolChoice: 'none', + }); + } +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const getWeather = llm.tool({ + description: ' Called when the user asks about the weather.', + parameters: z.object({ + location: z.string().describe('The location to get the weather for'), + }), + execute: async ({ location }, { ctx }) => { + ctx.session.say('Checking the weather, please wait a moment haha...'); + return `The weather in ${location} is sunny today.`; + }, + }); + const toggleLight = llm.tool({ + description: 'Called when the user asks to turn on or off the light.', + parameters: z.object({ + room: roomNameSchema.describe('The room to turn the light in'), + switchTo: z.enum(['on', 'off']).describe('The state to turn the light to'), + }), + execute: async ({ room, switchTo }, { ctx }) => { + ctx.session.generateReply({ + userInput: 'Tell user wait a moment for about 10 seconds', + }); + return `The light in the ${room} is now ${switchTo}.`; + }, + }); + const getNumber = llm.tool({ + description: 'Called when the user wants to get a number value, None if user want a random value', + parameters: z.object({ + value: z.number().nullable().describe('The number value'), + }), + execute: async ({ value }) => { + if (value === null) { + value = Math.floor(Math.random() * 100); + } + return `The number value is ${value}.`; + }, + }); + const checkStoredNumber = llm.tool({ + description: 'Called when the user wants to check the stored number.', + execute: async (_, { ctx }) => { + return `The stored number is ${ctx.userData.number}.`; + }, + }); + const updateStoredNumber = llm.tool({ + description: 'Called when the user wants to update the stored number.', + parameters: z.object({ + number: z.number().describe('The number to update the stored number to'), + }), + execute: async ({ number }, { ctx }) => { + ctx.userData.number = number; + return `The stored number is now ${number}.`; + }, + }); + const routerAgent = new RouterAgent({ + instructions: 'You are a helpful assistant.', + tools: { + getWeather, + toggleLight, + playGame: llm.tool({ + description: 'Called when the user wants to play a game (transfer user to a game agent).', + execute: async () => { + return llm.handoff({ agent: gameAgent, returns: 'The game is now playing.' }); + }, + }), + }, + }); + const gameAgent = new GameAgent({ + instructions: 'You are a game agent. You are playing a game with the user.', + tools: { + getNumber, + checkStoredNumber, + updateStoredNumber, + finishGame: llm.tool({ + description: 'Called when the user wants to finish the game.', + execute: async () => { + return llm.handoff({ agent: routerAgent, returns: 'The game is now finished.' }); + }, + }), + }, + }); + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + stt: new deepgram.STT(), + tts: new elevenlabs.TTS(), + llm: new google.LLM(), + // to use realtime model, replace the stt, llm, tts and vad with the following + // llm: new openai.realtime.RealtimeModel(), + userData: { number: 0 }, + turnDetection: new livekit.turnDetector.EnglishModel(), + }); + await session.start({ + agent: routerAgent, + room: ctx.room, + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=basic_tool_call_agent.js.map \ No newline at end of file diff --git a/examples/src/basic_tool_call_agent.js.map b/examples/src/basic_tool_call_agent.js.map new file mode 100644 index 000000000..ae039aa9e --- /dev/null +++ b/examples/src/basic_tool_call_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"basic_tool_call_agent.js","sourceRoot":"","sources":["basic_tool_call_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/cartersia_tts.js b/examples/src/cartersia_tts.js new file mode 100644 index 000000000..9812e04aa --- /dev/null +++ b/examples/src/cartersia_tts.js @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, metrics, voice, } from '@livekit/agents'; +import * as cartesia from '@livekit/agents-plugin-cartesia'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; +import { fileURLToPath } from 'node:url'; +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const agent = new voice.Agent({ + instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", + }); + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + stt: new deepgram.STT(), + tts: new cartesia.TTS(), + llm: new openai.LLM(), + // to use realtime model, replace the stt, llm, tts and vad with the following + // llm: new openai.realtime.RealtimeModel(), + turnDetection: new livekit.turnDetector.MultilingualModel(), + }); + const usageCollector = new metrics.UsageCollector(); + session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { + metrics.logMetrics(ev.metrics); + usageCollector.collect(ev.metrics); + }); + await session.start({ + agent, + room: ctx.room, + inputOptions: { + noiseCancellation: BackgroundVoiceCancellation(), + }, + }); + session.say('Hello, how can I help you today?'); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=cartersia_tts.js.map \ No newline at end of file diff --git a/examples/src/cartersia_tts.js.map b/examples/src/cartersia_tts.js.map new file mode 100644 index 000000000..780907662 --- /dev/null +++ b/examples/src/cartersia_tts.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cartersia_tts.js","sourceRoot":"","sources":["cartersia_tts.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,OAAO,EACP,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,iFAAiF;SACpF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB,CAAC;QAEjD,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG;YACH,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,8EAA8E;YAC9E,4CAA4C;YAC5C,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE;SAC5D,CAAC,CAAC;QAEH,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAEpD,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YAC/D,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAC/B,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,YAAY,EAAE;gBACZ,iBAAiB,EAAE,2BAA2B,EAAE;aACjD;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/comprehensive_test.js b/examples/src/comprehensive_test.js new file mode 100644 index 000000000..1eaa47862 --- /dev/null +++ b/examples/src/comprehensive_test.js @@ -0,0 +1,210 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, metrics, voice, } from '@livekit/agents'; +import * as cartesia from '@livekit/agents-plugin-cartesia'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as google from '@livekit/agents-plugin-google'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as neuphonic from '@livekit/agents-plugin-neuphonic'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as resemble from '@livekit/agents-plugin-resemble'; +import * as silero from '@livekit/agents-plugin-silero'; +import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +const sttOptions = { + deepgram: () => new deepgram.STT(), +}; +const ttsOptions = { + cartesia: () => new cartesia.TTS(), + elevenlabs: () => new elevenlabs.TTS(), + openai: () => new openai.TTS(), + gemini: () => new google.beta.TTS(), + neuphonic: () => new neuphonic.TTS(), + resemble: () => new resemble.TTS(), +}; +const eouOptions = { + english: () => new livekit.turnDetector.EnglishModel(), + multilingual: () => new livekit.turnDetector.MultilingualModel(), +}; +const llmOptions = { + openai: () => new openai.LLM(), + gemini: () => new google.LLM(), +}; +const realtimeLlmOptions = { + openai: () => new openai.realtime.RealtimeModel(), + gemini: () => new google.beta.realtime.RealtimeModel(), +}; +const sttChoices = Object.keys(sttOptions); +const ttsChoices = Object.keys(ttsOptions); +const eouChoices = Object.keys(eouOptions); +const llmChoices = Object.keys(llmOptions); +const realtimeLlmChoices = Object.keys(realtimeLlmOptions); +class MainAgent extends voice.Agent { + constructor() { + super({ + instructions: `You are a main route agent, you can test the agent's ability to switch between different agents`, + stt: sttOptions['deepgram'](), + tts: ttsOptions['elevenlabs'](), + llm: llmOptions['openai'](), + turnDetection: eouOptions['multilingual'](), + tools: { + testAgent: llm.tool({ + description: 'Called when user want to test an agent with STT, TTS, EOU, LLM, and optionally realtime LLM configuration', + parameters: z.object({ + sttChoice: z.enum(sttChoices), + ttsChoice: z.enum(ttsChoices), + eouChoice: z.enum(eouChoices), + llmChoice: z.enum(llmChoices), + realtimeLlmChoice: z.enum(realtimeLlmChoices).nullable(), + }), + execute: async (params) => { + const { sttChoice, ttsChoice, eouChoice, llmChoice, realtimeLlmChoice } = params; + return llm.handoff({ + agent: new TestAgent({ + sttChoice: sttChoice, + ttsChoice: ttsChoice, + eouChoice: eouChoice, + llmChoice: llmChoice, + realtimeLlmChoice: realtimeLlmChoice, + }), + returns: 'Transfer to next agent', + }); + }, + }), + }, + }); + } + async onEnter() { + if (this.llm instanceof llm.RealtimeModel) { + this.session.generateReply({ + userInput: `Tell user that you are main route agent, you can test the agent's ability to switch between different agents`, + }); + } + else { + this.session.say(`Hi, I'm a main route agent, you can test the agent's ability to switch between different agents`); + } + } +} +class TestAgent extends voice.Agent { + sttChoice; + ttsChoice; + eouChoice; + llmChoice; + realtimeLlmChoice; + constructor({ sttChoice, ttsChoice, eouChoice, llmChoice, realtimeLlmChoice, }) { + const stt = sttOptions[sttChoice](); + const tts = ttsOptions[ttsChoice](); + const eou = eouOptions[eouChoice](); + const model = llmOptions[llmChoice](); + const realtimeModel = realtimeLlmChoice ? realtimeLlmOptions[realtimeLlmChoice]() : undefined; + const modelName = realtimeModel ? `${realtimeLlmChoice} realtime` : llmChoice; + super({ + instructions: `You are a test voice-based agent, you can hear the user's message and respond to it. User is testing your hearing & speaking abilities. + You are using ${sttChoice} STT, ${ttsChoice} TTS, ${eouChoice} EOU, ${modelName} LLM. + You can use the following tools to test your abilities: + - testTool: Testing agent's tool calling ability + - nextAgent: Called when user confirm current agent is working and want to proceed to next agent`, + stt: stt, + tts: tts, + llm: realtimeModel ?? model, + turnDetection: eou, + tools: { + testTool: llm.tool({ + description: "Testing agent's tool calling ability", + parameters: z + .object({ + randomString: z.string().describe('A random string'), + }) + .describe('Test parameter'), + execute: async (input) => { + return { + result: 'Tool been called with input: ' + JSON.stringify(input), + }; + }, + }), + nextAgent: llm.tool({ + description: 'Called when user confirm current agent is working and want to proceed to next agent', + parameters: z.object({ + sttChoice: z.enum(sttChoices).optional(), + ttsChoice: z.enum(ttsChoices).optional(), + eouChoice: z.enum(eouChoices).optional(), + llmChoice: z.enum(llmChoices).optional(), + realtimeLlmChoice: z.enum(realtimeLlmChoices).optional(), + }), + execute: async (params) => { + const { sttChoice = this.sttChoice, ttsChoice = this.ttsChoice, eouChoice = this.eouChoice, llmChoice = this.llmChoice, realtimeLlmChoice = this.realtimeLlmChoice, } = params; + return llm.handoff({ + agent: new TestAgent({ + sttChoice: sttChoice, + ttsChoice: ttsChoice, + eouChoice: eouChoice, + llmChoice: llmChoice, + realtimeLlmChoice: realtimeLlmChoice, + }), + returns: 'Transfer to next agent', + }); + }, + }), + }, + }); + this.sttChoice = sttChoice; + this.ttsChoice = ttsChoice; + this.eouChoice = eouChoice; + this.llmChoice = llmChoice; + this.realtimeLlmChoice = realtimeLlmChoice; + } + async onEnter() { + if (this.llm instanceof llm.RealtimeModel) { + this.session.generateReply({ + userInput: `Tell user that you are voice agent with ${this.sttChoice} STT, ${this.ttsChoice} TTS, ${this.eouChoice} EOU, ${this.llmChoice} LLM`, + }); + } + else { + this.session.say(`Hi, I'm a voice agent with ${this.sttChoice} STT, ${this.ttsChoice} TTS, ${this.eouChoice} EOU, ${this.llmChoice} LLM. I'm ready to test your hearing & speaking abilities.`); + } + setTimeout(() => { + if (this.session.currentAgent !== this) + return; + this.session.interrupt(); + this.session.generateReply({ + userInput: 'Tell user that this test is over, going back to main agent', + }); + this.session.updateAgent(new MainAgent()); + }, 45 * 1000); + } +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + userData: { + testedSttChoices: new Set(), + testedTtsChoices: new Set(), + testedEouChoices: new Set(), + testedLlmChoices: new Set(), + testedRealtimeLlmChoices: new Set(), + }, + }); + const usageCollector = new metrics.UsageCollector(); + session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { + metrics.logMetrics(ev.metrics); + usageCollector.collect(ev.metrics); + }); + await session.start({ + agent: new MainAgent(), + room: ctx.room, + inputOptions: { + noiseCancellation: BackgroundVoiceCancellation(), + }, + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=comprehensive_test.js.map \ No newline at end of file diff --git a/examples/src/comprehensive_test.js.map b/examples/src/comprehensive_test.js.map new file mode 100644 index 000000000..006bc5f0d --- /dev/null +++ b/examples/src/comprehensive_test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"comprehensive_test.js","sourceRoot":"","sources":["comprehensive_test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/drive-thru/database.js b/examples/src/drive-thru/database.js new file mode 100644 index 000000000..413d423ca --- /dev/null +++ b/examples/src/drive-thru/database.js @@ -0,0 +1,659 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +export const COMMON_INSTRUCTIONS = `You are Kelly, a quick and friendly McDonald's drive-thru attendant. +Your job is to guide the customer smoothly through their order, speaking in short, natural voice responses. +This is a voice interaction-assume the customer just pulled up and is speaking to you through a drive-thru speaker. +Respond like you're hearing them, not reading text. +Assume they want food, even if they don't start with a clear request, and help them get what they're looking for. + +If an item comes in different sizes, always ask for the size unless the customer already gave one. +If a customer orders a 'large meal', automatically assume both the fries and the drink should be large. +Do not ask again to confirm the size of the drink or fries. This inference is meant to streamline the interaction. +If the customer clearly indicates a different size for the fries or drink, respect their preference. + +Be fast-keep responses short and snappy. +Sound human-sprinkle in light vocal pauses like 'Mmh…', 'Let me see…', or 'Alright…' at natural moments-but not too often. +Keep everything upbeat and easy to follow. Never overwhelm the customer, don't ask multiple questions at the same time. + +When a customer is confused or asks for something that doesn't exist, let them know politely and suggest something close. +Always confirm what they picked in a warm, clear way, like: 'Alright, one Big Mac Combo!' +If something's unavailable, say so with empathy: 'Ah, we're out of Sweet Tea right now-can I get you a Coke instead?' + +Whenever a customer asks for, changes, or removes something from their order, you MUST use a tool to make it happen. +Don't fake it. Don't pretend something was added - actually **call** the tool and make it real on the ordering system. + +Transcripts often contain speech-to-text errors-don't mention the transcript, don't repeat its mistakes. +Instead treat each user input as a rough draft of what was said. +If you can guess the user's intent and it's safe to do so, infer their meaning and respond naturally. +If the transcript is ambiguous/nonsense and you can't guess their intent, ask the customer to repeat again. +Stay on-topic; if input is nonsensical in a drive-thru context, ask for concise clarification. + +Do not add any item on the user's behalf unless they specifically request it. If the user hasn't asked for an item, NEVER add it. + +When a customer changes an item or meal, make sure to remove the previous version before adding the new one. +Otherwise, the order may contain duplicates. + +Strictly stick to the defined menu, Do not invent or suggest any new sizes or items. +If the item specified by the user is unclear or not **exactly** on the menu, ask for clarification or say you don't have this specific item +E.g: an hamburger isn't a cheeseburger +Do not ask for size unless the item has more than one size option specified. +If an item does not require a size according to the menu, **NEVER** ask the customer to choose one or mention size at all.`; +export class FakeDB { + async listDrinks() { + const drinkData = [ + { + id: 'coca_cola', + name: 'Coca-Cola®', + sizes: { + S: { calories: 200, price: 1.49 }, + M: { calories: 270, price: 1.69 }, + L: { calories: 380, price: 1.89 }, + }, + }, + { + id: 'sprite', + name: 'Sprite®', + sizes: { + S: { calories: 190, price: 1.49 }, + M: { calories: 250, price: 1.69 }, + L: { calories: 350, price: 1.89 }, + }, + }, + { + id: 'diet_coke', + name: 'Diet Coke®', + sizes: { + S: { calories: 0, price: 1.49 }, + M: { calories: 0, price: 1.69 }, + L: { calories: 0, price: 1.89 }, + }, + }, + { + id: 'dr_pepper', + name: 'Dr Pepper®', + sizes: { + S: { calories: 200, price: 1.49 }, + M: { calories: 270, price: 1.69 }, + L: { calories: 380, price: 1.89 }, + }, + }, + { + id: 'fanta_orange', + name: 'Fanta® Orange', + sizes: { + S: { calories: 210, price: 1.49 }, + M: { calories: 280, price: 1.69 }, + L: { calories: 390, price: 1.89 }, + }, + }, + { + id: 'hi_c_orange_lavaburst', + name: 'Hi-C® Orange Lavaburst®', + sizes: { + S: { calories: 210, price: 1.49 }, + M: { calories: 280, price: 1.69 }, + L: { calories: 390, price: 1.89 }, + }, + }, + { + id: 'sweet_tea', + name: 'Sweet Tea', + sizes: { + S: { calories: 140, price: 1.39 }, + M: { calories: 180, price: 1.59 }, + L: { calories: 220, price: 1.79 }, + }, + available: false, + }, + { + id: 'unsweetened_iced_tea', + name: 'Unsweetened Iced Tea', + sizes: { + S: { calories: 0, price: 1.39 }, + M: { calories: 0, price: 1.59 }, + L: { calories: 0, price: 1.79 }, + }, + }, + { + id: 'minute_maid_orange_juice', + name: 'Minute Maid® Premium Orange Juice', + sizes: { + S: { calories: 190, price: 2.59 }, + M: { calories: 240, price: 2.79 }, + L: { calories: 300, price: 2.99 }, + }, + }, + { + id: 'milk', + name: 'Milk', + calories: 100, + price: 1.29, + }, + { + id: 'chocolate_milk', + name: 'Chocolate Milk', + calories: 150, + price: 1.39, + }, + { + id: 'dasani_water', + name: 'DASANI® Water', + calories: 0, + price: 1.59, + }, + ]; + const items = []; + for (const item of drinkData) { + if ('sizes' in item && item.sizes) { + for (const [size, sizeDetails] of Object.entries(item.sizes)) { + items.push({ + id: item.id, + name: item.name, + calories: sizeDetails.calories, + price: sizeDetails.price, + size: size, + available: item.available ?? true, + category: 'drink', + }); + } + } + else { + items.push({ + id: item.id, + name: item.name, + calories: item.calories, + price: item.price, + available: true, + category: 'drink', + }); + } + } + return items; + } + async listComboMeals() { + const rawMeals = [ + { + id: 'combo_big_mac', + name: 'Big Mac® Combo', + alias: '1', + calories: 970, + price: 9.49, + }, + { + id: 'combo_quarter_pounder_2a', + name: 'Quarter Pounder® with Cheese Combo', + alias: '2a', + calories: 840, + price: 9.89, + }, + { + id: 'combo_quarter_pounder_2b', + name: 'Quarter Pounder® with Cheese & Bacon Combo', + alias: '2b', + calories: 950, + price: 10.39, + }, + { + id: 'combo_quarter_pounder_2c', + name: 'Quarter Pounder® Deluxe Combo', + alias: '2c', + calories: 950, + price: 10.39, + }, + { + id: 'combo_double_quarter', + name: 'Double Quarter Pounder® with Cheese Combo', + alias: '3', + calories: 1060, + price: 10.29, + }, + { + id: 'combo_mccrispy_4a', + name: 'McCrispy™ Original Combo', + alias: '4a', + calories: 790, + price: 8.99, + }, + { + id: 'combo_mccrispy_4b', + name: 'McCrispy™ Spicy Combo', + alias: '4b', + calories: 850, + price: 8.99, + }, + { + id: 'combo_mccrispy_4c', + name: 'McCrispy™ Deluxe Combo', + alias: '4c', + calories: 880, + price: 9.89, + }, + { + id: 'combo_mccrispy_4d', + name: 'McCrispy™ Spicy Deluxe Combo', + alias: '4d', + calories: 860, + price: 9.99, + }, + { + id: 'combo_chicken_mcnuggets_10pc', + name: '10 pc. Chicken McNuggets® Combo', + alias: '5', + calories: 740, + price: 9.49, + }, + { + id: 'combo_filet_o_fish', + name: 'Filet-O-Fish® Combo', + alias: '6', + calories: 700, + price: 7.89, + }, + { + id: 'combo_cheeseburgers_2pc', + name: '2 Cheeseburgers Combo', + alias: '7', + calories: 920, + price: 7.89, + }, + ]; + return rawMeals.map((item) => ({ + id: item.id, + name: item.name, + calories: item.calories, + price: item.price, + voiceAlias: item.alias, + category: 'combo_meal', + available: true, + })); + } + async listHappyMeals() { + const rawHappyMeals = [ + { + id: 'happy_meal_4pc_mcnuggets', + name: '4 pc. Chicken McNuggets® Happy Meal', + calories: 430, + price: 5.99, + }, + { + id: 'happy_meal_6pc_mcnuggets', + name: '6 pc. Chicken McNuggets® Happy Meal', + calories: 530, + price: 6.99, + }, + { + id: 'happy_meal_hamburger', + name: 'Hamburger Happy Meal', + calories: 510, + price: 5.59, + }, + ]; + return rawHappyMeals.map((item) => ({ + id: item.id, + name: item.name, + calories: item.calories, + price: item.price, + available: true, + category: 'happy_meal', + })); + } + async listRegulars() { + const rawItems = [ + { + id: 'big_mac', + name: 'Big Mac®', + calories: 590, + price: 5.89, + }, + { + id: 'quarter_pounder_cheese', + name: 'Quarter Pounder® with Cheese', + calories: 520, + price: 6.29, + }, + { + id: 'quarter_pounder_bacon', + name: 'Quarter Pounder® with Cheese & Bacon', + calories: 590, + price: 6.79, + }, + { + id: 'quarter_pounder_deluxe', + name: 'Quarter Pounder® Deluxe', + calories: 530, + price: 6.39, + }, + { + id: 'double_quarter_pounder', + name: 'Double Quarter Pounder® with Cheese', + calories: 740, + price: 7.49, + }, + { + id: 'mccrispy_original', + name: 'McCrispy™ Original', + calories: 470, + price: 5.69, + }, + { + id: 'mccrispy_spicy', + name: 'McCrispy™ Spicy', + calories: 500, + price: 5.69, + }, + { + id: 'mccrispy_deluxe', + name: 'McCrispy™ Deluxe', + calories: 530, + price: 6.39, + }, + { + id: 'mccrispy_spicy_deluxe', + name: 'McCrispy™ Spicy Deluxe', + calories: 530, + price: 6.59, + }, + { + id: 'mcnuggets_10pc', + name: '10 pc. Chicken McNuggets®', + calories: 410, + price: 6.79, + }, + { + id: 'filet_o_fish', + name: 'Filet-O-Fish®', + calories: 390, + price: 5.89, + }, + { + id: 'hamburger', + name: 'Hamburger', + calories: 300, + price: 2.0, + }, + { + id: 'cheeseburger', + name: 'Cheeseburger', + calories: 600, + price: 2.58, + }, + { + id: 'fries', + name: 'Fries', + sizes: { + S: { calories: 230, price: 1.89 }, + M: { calories: 350, price: 3.99 }, + L: { calories: 521, price: 4.75 }, + }, + }, + { + id: 'sweet_sundae', + name: 'Sundae', + calories: 330, + price: 3.69, + }, + { + id: 'sweet_mcflurry_oreo', + name: 'McFlurry® (Oreo)', + calories: 480, + price: 4.89, + }, + { + id: 'shake_vanilla', + name: 'Vanilla Shake', + sizes: { + S: { calories: 510, price: 2.79 }, + M: { calories: 610, price: 3.59 }, + L: { calories: 820, price: 3.89 }, + }, + }, + { + id: 'shake_chocolate', + name: 'Chocolate Shake', + sizes: { + S: { calories: 520, price: 2.79 }, + M: { calories: 620, price: 3.59 }, + L: { calories: 830, price: 3.89 }, + }, + }, + { + id: 'shake_strawberry', + name: 'Strawberry Shake', + sizes: { + S: { calories: 530, price: 2.79 }, + M: { calories: 620, price: 3.59 }, + L: { calories: 840, price: 3.89 }, + }, + }, + { + id: 'sweet_cone', + name: 'Cone', + calories: 200, + price: 3.19, + }, + ]; + const items = []; + for (const item of rawItems) { + if ('sizes' in item && item.sizes) { + for (const [size, sizeDetails] of Object.entries(item.sizes)) { + items.push({ + id: item.id, + name: item.name, + calories: sizeDetails.calories, + price: sizeDetails.price, + size: size, + available: true, + category: 'regular', + }); + } + } + else { + items.push({ + id: item.id, + name: item.name, + calories: item.calories, + price: item.price, + available: true, + category: 'regular', + }); + } + } + return items; + } + async listSauces() { + const rawItems = [ + { + id: 'jalapeno_ranch', + name: 'Jalapeño Ranch', + calories: 70, + price: 0.25, + }, + { + id: 'garlic_sauce', + name: 'Garlic Sauce', + calories: 45, + price: 0.25, + }, + { + id: 'mayonnaise', + name: 'Mayonnaise', + calories: 90, + price: 0.2, + }, + { + id: 'frietsaus', + name: 'Frietsaus', + calories: 100, + price: 0.2, + }, + { + id: 'curry_sauce', + name: 'Curry sauce', + calories: 60, + price: 0.2, + }, + { + id: 'ketchup', + name: 'Ketchup', + calories: 20, + price: 0.1, + }, + { + id: 'barbecue_sauce', + name: 'Barbecue Sauce', + calories: 45, + price: 0.2, + }, + { + id: 'sweet_and_sour_sauce', + name: 'Sweet-and-sour sauce', + calories: 50, + price: 0.4, + }, + { + id: 'honey_mustard_dressing', + name: 'Honey mustard dressing', + calories: 60, + price: 0.2, + }, + ]; + return rawItems.map((item) => ({ + id: item.id, + name: item.name, + calories: item.calories, + price: item.price, + available: true, + category: 'sauce', + })); + } +} +// Helper functions +export function findItemsById(items, itemId, size) { + return items.filter((item) => item.id === itemId && (size === undefined || item.size === size)); +} +export function menuInstructions(category, items) { + switch (category) { + case 'drink': + return drinkMenuInstructions(items); + case 'combo_meal': + return comboMenuInstructions(items); + case 'happy_meal': + return happyMenuInstructions(items); + case 'sauce': + return sauceMenuInstructions(items); + case 'regular': + return regularMenuInstructions(items); + default: + return ''; + } +} +function mapBySizes(items) { + const sized = {}; + const leftovers = []; + for (const item of items) { + if (item.size) { + if (!sized[item.id]) { + sized[item.id] = {}; + } + sized[item.id][item.size] = item; + } + else { + leftovers.push(item); + } + } + return { sized, leftovers }; +} +function drinkMenuInstructions(items) { + const { sized, leftovers } = mapBySizes(items); + const menuLines = []; + for (const [_itemId, sizeMap] of Object.entries(sized)) { + const firstItem = Object.values(sizeMap)[0]; + if (!firstItem) + continue; + menuLines.push(` - ${firstItem.name} (id:${firstItem.id}):`); + for (const [size, item] of Object.entries(sizeMap)) { + let line = ` - Size ${size}: ${item.calories} Cal, $${item.price.toFixed(2)}`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + } + for (const item of leftovers) { + let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id}) - Not size-selectable`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + return '# Drinks:\n' + menuLines.join('\n'); +} +function comboMenuInstructions(items) { + const menuLines = []; + for (const item of items) { + let line = ` **${item.voiceAlias}**. ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id})`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + const instructions = `# Combo Meals: +The user can select a combo meal by saying its voice alias (e.g., '1', '2a', '4c'). Use the alias to identify which combo they chose. +But don't mention the voice alias to the user if not needed.`; + return instructions + '\n' + menuLines.join('\n'); +} +function happyMenuInstructions(items) { + const menuLines = []; + for (const item of items) { + let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id})`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + return `# Happy Meals: +${menuLines.join('\n')} + +Recommended drinks with the Happy Meal: + - Milk chocolate/white + - DASANI Water + - Or any other small drink.`; +} +function sauceMenuInstructions(items) { + const menuLines = []; + for (const item of items) { + let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id})`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + return '# Sauces:\n' + menuLines.join('\n'); +} +function regularMenuInstructions(items) { + const { sized, leftovers } = mapBySizes(items); + const menuLines = []; + for (const [_itemId, sizeMap] of Object.entries(sized)) { + const firstItem = Object.values(sizeMap)[0]; + if (!firstItem) + continue; + menuLines.push(` - ${firstItem.name} (id:${firstItem.id}):`); + for (const [size, item] of Object.entries(sizeMap)) { + let line = ` - Size ${size}: ${item.calories} Cal, $${item.price.toFixed(2)}`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + } + for (const item of leftovers) { + let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id}) - Not size-selectable`; + if (!item.available) { + line += ' UNAVAILABLE'; + } + menuLines.push(line); + } + return '# Regular items/À la carte:\n' + menuLines.join('\n'); +} +//# sourceMappingURL=database.js.map \ No newline at end of file diff --git a/examples/src/drive-thru/database.js.map b/examples/src/drive-thru/database.js.map new file mode 100644 index 000000000..1b73c87c9 --- /dev/null +++ b/examples/src/drive-thru/database.js.map @@ -0,0 +1 @@ +{"version":3,"file":"database.js","sourceRoot":"","sources":["database.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/drive-thru/drivethru_agent.js b/examples/src/drive-thru/drivethru_agent.js new file mode 100644 index 000000000..7cfa31ddd --- /dev/null +++ b/examples/src/drive-thru/drivethru_agent.js @@ -0,0 +1,294 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +import { COMMON_INSTRUCTIONS, FakeDB, findItemsById, menuInstructions, } from './database.js'; +import { OrderState, createOrderedCombo, createOrderedHappy, createOrderedRegular, } from './order.js'; +class DriveThruAgent extends voice.Agent { + constructor(userdata) { + const instructions = COMMON_INSTRUCTIONS + + '\n\n' + + menuInstructions('drink', userdata.drinkItems) + + '\n\n' + + menuInstructions('combo_meal', userdata.comboItems) + + '\n\n' + + menuInstructions('happy_meal', userdata.happyItems) + + '\n\n' + + menuInstructions('regular', userdata.regularItems) + + '\n\n' + + menuInstructions('sauce', userdata.sauceItems); + super({ + instructions, + tools: { + orderComboMeal: DriveThruAgent.buildComboOrderTool(userdata.comboItems, userdata.drinkItems, userdata.sauceItems), + orderHappyMeal: DriveThruAgent.buildHappyOrderTool(userdata.happyItems, userdata.drinkItems, userdata.sauceItems), + orderRegularItem: DriveThruAgent.buildRegularOrderTool(userdata.regularItems, userdata.drinkItems, userdata.sauceItems), + removeOrderItem: llm.tool({ + description: `Removes one or more items from the user's order using their \`orderId\`s. + +Useful when the user asks to cancel or delete existing items (e.g., "Remove the cheeseburger"). + +If the \`orderId\`s are unknown, call \`listOrderItems\` first to retrieve them.`, + parameters: z.object({ + orderIds: z + .array(z.string()) + .describe('A list of internal `orderId`s of the items to remove. Use `listOrderItems` to look it up if needed.'), + }), + execute: async ({ orderIds }, { ctx }) => { + const notFound = orderIds.filter((oid) => !ctx.userData.order.items[oid]); + if (notFound.length > 0) { + throw new llm.ToolError(`error: no item(s) found with order_id(s): ${notFound.join(', ')}`); + } + const removedItems = await Promise.all(orderIds.map((oid) => ctx.userData.order.remove(oid))); + return 'Removed items:\n' + removedItems.map((item) => JSON.stringify(item)).join('\n'); + }, + }), + listOrderItems: llm.tool({ + description: `Retrieves the current list of items in the user's order, including each item's internal \`orderId\`. + +Helpful when: +- An \`orderId\` is required before modifying or removing an existing item. +- Confirming details or contents of the current order. + +Examples: +- User requests modifying an item, but the item's \`orderId\` is unknown (e.g., "Change the fries from small to large"). +- User requests removing an item, but the item's \`orderId\` is unknown (e.g., "Remove the cheeseburger"). +- User asks about current order details (e.g., "What's in my order so far?").`, + execute: async (_, { ctx }) => { + const items = Object.values(ctx.userData.order.items); + if (items.length === 0) { + return 'The order is empty'; + } + return items.map((item) => JSON.stringify(item)).join('\n'); + }, + }), + }, + }); + } + static buildComboOrderTool(comboItems, drinkItems, sauceItems) { + const availableComboIds = [...new Set(comboItems.map((item) => item.id))]; + const availableDrinkIds = [...new Set(drinkItems.map((item) => item.id))]; + const availableSauceIds = [...new Set(sauceItems.map((item) => item.id))]; + return llm.tool({ + description: `Call this when the user orders a **Combo Meal**, like: "Number 4b with a large Sprite" or "I'll do a medium meal." + +Do not call this tool unless the user clearly refers to a known combo meal by name or number. +Regular items like a single cheeseburger cannot be made into a meal unless such a combo explicitly exists. + +Only call this function once the user has clearly specified both a drink and a sauce — always ask for them if they're missing. + +A meal can only be Medium or Large; Small is not an available option. +Drink and fries sizes can differ (e.g., "large fries but a medium Coke"). + +If the user says just "a large meal," assume both drink and fries are that size.`, + parameters: z.object({ + mealId: z + .enum(availableComboIds) + .describe('The ID of the combo meal the user requested.'), + drinkId: z + .enum(availableDrinkIds) + .describe('The ID of the drink the user requested.'), + drinkSize: z.enum(['M', 'L']).nullable().describe('The size of the drink'), + friesSize: z.enum(['M', 'L']).describe('The size of the fries'), + sauceId: z + .enum(availableSauceIds) + .nullable() + .describe('The ID of the sauce the user requested.'), + }), + execute: async ({ mealId, drinkId, drinkSize, friesSize, sauceId }, { ctx }) => { + if (!findItemsById(comboItems, mealId).length) { + throw new llm.ToolError(`error: the meal ${mealId} was not found`); + } + const drinkSizes = findItemsById(drinkItems, drinkId); + if (!drinkSizes.length) { + throw new llm.ToolError(`error: the drink ${drinkId} was not found`); + } + let actualDrinkSize = drinkSize || undefined; + const actualSauceId = sauceId || undefined; + const availableSizes = [ + ...new Set(drinkSizes.map((item) => item.size).filter((size) => size !== undefined)), + ]; + if (actualDrinkSize === undefined && availableSizes.length > 1) { + throw new llm.ToolError(`error: ${drinkId} comes with multiple sizes: ${availableSizes.join(', ')}. Please clarify which size should be selected.`); + } + if (actualDrinkSize !== undefined && !availableSizes.length) { + throw new llm.ToolError(`error: size should not be specified for item ${drinkId} as it does not support sizing options.`); + } + if (actualDrinkSize && !availableSizes.includes(actualDrinkSize)) { + actualDrinkSize = undefined; + } + if (actualSauceId && !findItemsById(sauceItems, actualSauceId).length) { + throw new llm.ToolError(`error: the sauce ${actualSauceId} was not found`); + } + const item = createOrderedCombo({ + mealId, + drinkId, + drinkSize: actualDrinkSize, + sauceId: actualSauceId, + friesSize, + }); + await ctx.userData.order.add(item); + return `The item was added: ${JSON.stringify(item)}`; + }, + }); + } + static buildHappyOrderTool(happyItems, drinkItems, sauceItems) { + const availableHappyIds = [...new Set(happyItems.map((item) => item.id))]; + const availableDrinkIds = [...new Set(drinkItems.map((item) => item.id))]; + const availableSauceIds = [...new Set(sauceItems.map((item) => item.id))]; + return llm.tool({ + description: `Call this when the user orders a **Happy Meal**, typically for children. These meals come with a main item, a drink, and a sauce. + +The user must clearly specify a valid Happy Meal option (e.g., "Can I get a Happy Meal?"). + +Before calling this tool: +- Ensure the user has provided all required components: a valid meal, drink, drink size, and sauce. +- If any of these are missing, prompt the user for the missing part before proceeding. + +Assume Small as default only if the user says "Happy Meal" and gives no size preference, but always ask for clarification if unsure.`, + parameters: z.object({ + mealId: z + .enum(availableHappyIds) + .describe('The ID of the happy meal the user requested.'), + drinkId: z + .enum(availableDrinkIds) + .describe('The ID of the drink the user requested.'), + drinkSize: z.enum(['S', 'M', 'L']).nullable().describe('The size of the drink'), + sauceId: z + .enum(availableSauceIds) + .nullable() + .describe('The ID of the sauce the user requested.'), + }), + execute: async ({ mealId, drinkId, drinkSize, sauceId }, { ctx }) => { + if (!findItemsById(happyItems, mealId).length) { + throw new llm.ToolError(`error: the meal ${mealId} was not found`); + } + const drinkSizes = findItemsById(drinkItems, drinkId); + if (!drinkSizes.length) { + throw new llm.ToolError(`error: the drink ${drinkId} was not found`); + } + let actualDrinkSize = drinkSize || undefined; + const actualSauceId = sauceId || undefined; + const availableSizes = [ + ...new Set(drinkSizes.map((item) => item.size).filter((size) => size !== undefined)), + ]; + if (actualDrinkSize === undefined && availableSizes.length > 1) { + throw new llm.ToolError(`error: ${drinkId} comes with multiple sizes: ${availableSizes.join(', ')}. Please clarify which size should be selected.`); + } + if (actualDrinkSize !== undefined && !availableSizes.length) { + actualDrinkSize = undefined; + } + if (actualSauceId && !findItemsById(sauceItems, actualSauceId).length) { + throw new llm.ToolError(`error: the sauce ${actualSauceId} was not found`); + } + const item = createOrderedHappy({ + mealId, + drinkId, + drinkSize: actualDrinkSize, + sauceId: actualSauceId, + }); + await ctx.userData.order.add(item); + return `The item was added: ${JSON.stringify(item)}`; + }, + }); + } + static buildRegularOrderTool(regularItems, drinkItems, sauceItems) { + const allItems = [...regularItems, ...drinkItems, ...sauceItems]; + const availableIds = [...new Set(allItems.map((item) => item.id))]; + return llm.tool({ + description: `Call this when the user orders **a single item on its own**, not as part of a Combo Meal or Happy Meal. + +The customer must provide clear and specific input. For example, item variants such as flavor must **always** be explicitly stated. + +The user might say—for example: +- "Just the cheeseburger, no meal" +- "A medium Coke" +- "Can I get some ketchup?" +- "Can I get a McFlurry Oreo?"`, + parameters: z.object({ + itemId: z + .enum(availableIds) + .describe('The ID of the item the user requested.'), + size: z + .enum(['S', 'M', 'L']) + .nullable() + .describe('Size of the item, if applicable (e.g., "S", "M", "L").'), + }), + execute: async ({ itemId, size }, { ctx }) => { + const itemSizes = findItemsById(allItems, itemId); + if (!itemSizes.length) { + throw new llm.ToolError(`error: ${itemId} was not found.`); + } + let actualSize = size || undefined; + const availableSizes = [ + ...new Set(itemSizes.map((item) => item.size).filter((size) => size !== undefined)), + ]; + if (actualSize === undefined && availableSizes.length > 1) { + throw new llm.ToolError(`${itemId} comes with multiple sizes: ${availableSizes.join(', ')}. Please clarify which size should be selected.`); + } + if (actualSize !== undefined && !availableSizes.length) { + actualSize = undefined; + } + if (actualSize && availableSizes.length && !availableSizes.includes(actualSize)) { + throw new llm.ToolError(`error: unknown size ${actualSize} for ${itemId}. Available sizes: ${availableSizes.join(', ')}.`); + } + const item = createOrderedRegular({ + itemId, + size: actualSize, + }); + await ctx.userData.order.add(item); + return `The item was added: ${JSON.stringify(item)}`; + }, + }); + } +} +async function newUserData() { + const fakeDb = new FakeDB(); + const drinkItems = await fakeDb.listDrinks(); + const comboItems = await fakeDb.listComboMeals(); + const happyItems = await fakeDb.listHappyMeals(); + const regularItems = await fakeDb.listRegulars(); + const sauceItems = await fakeDb.listSauces(); + const orderState = new OrderState(); + return { + order: orderState, + drinkItems, + comboItems, + happyItems, + regularItems, + sauceItems, + }; +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const userdata = await newUserData(); + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + stt: new deepgram.STT(), + llm: new openai.LLM({ model: 'gpt-4.1', temperature: 0.45 }), + tts: new elevenlabs.TTS(), + turnDetection: new livekit.turnDetector.MultilingualModel(), + userData: userdata, + voiceOptions: { + maxToolSteps: 10, + }, + }); + await session.start({ + agent: new DriveThruAgent(userdata), + room: ctx.room, + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=drivethru_agent.js.map \ No newline at end of file diff --git a/examples/src/drive-thru/drivethru_agent.js.map b/examples/src/drive-thru/drivethru_agent.js.map new file mode 100644 index 000000000..1dc76b0ad --- /dev/null +++ b/examples/src/drive-thru/drivethru_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"drivethru_agent.js","sourceRoot":"","sources":["drivethru_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/drive-thru/order.js b/examples/src/drive-thru/order.js new file mode 100644 index 000000000..4ab07edee --- /dev/null +++ b/examples/src/drive-thru/order.js @@ -0,0 +1,47 @@ +export function orderUid() { + const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + let result = 'O_'; + for (let i = 0; i < 6; i++) { + result += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); + } + return result; +} +export class OrderState { + items = {}; + async add(item) { + this.items[item.orderId] = item; + } + async remove(orderId) { + const item = this.items[orderId]; + if (!item) { + throw new Error(`Order item with ID ${orderId} not found`); + } + delete this.items[orderId]; + return item; + } + get(orderId) { + return this.items[orderId]; + } +} +export function createOrderedCombo(params) { + return { + type: 'combo_meal', + orderId: orderUid(), + ...params, + }; +} +export function createOrderedHappy(params) { + return { + type: 'happy_meal', + orderId: orderUid(), + ...params, + }; +} +export function createOrderedRegular(params) { + return { + type: 'regular', + orderId: orderUid(), + ...params, + }; +} +//# sourceMappingURL=order.js.map \ No newline at end of file diff --git a/examples/src/drive-thru/order.js.map b/examples/src/drive-thru/order.js.map new file mode 100644 index 000000000..9761b3f46 --- /dev/null +++ b/examples/src/drive-thru/order.js.map @@ -0,0 +1 @@ +{"version":3,"file":"order.js","sourceRoot":"","sources":["order.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,QAAQ;IACtB,MAAM,QAAQ,GAAG,sCAAsC,CAAC;IACxD,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AA8BD,MAAM,OAAO,UAAU;IACrB,KAAK,GAAgC,EAAE,CAAC;IAExC,KAAK,CAAC,GAAG,CAAC,IAAiB;QACzB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,YAAY,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,MAMlC;IACC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,QAAQ,EAAE;QACnB,GAAG,MAAM;KACV,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAKlC;IACC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,QAAQ,EAAE;QACnB,GAAG,MAAM;KACV,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAA2C;IAC9E,OAAO;QACL,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE;QACnB,GAAG,MAAM;KACV,CAAC;AACJ,CAAC"} \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.js b/examples/src/frontdesk/calendar_api.js new file mode 100644 index 000000000..1a172ef79 --- /dev/null +++ b/examples/src/frontdesk/calendar_api.js @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { createHash } from 'crypto'; +export class SlotUnavailableError extends Error { + constructor(message) { + super(message); + this.name = 'SlotUnavailableError'; + } +} +export function createAvailableSlot(startTime, durationMin) { + return { startTime, durationMin }; +} +// Base32 alphabet (RFC 4648) +const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; +function toBase32(buffer) { + let result = ''; + let bits = 0; + let value = 0; + for (let i = 0; i < buffer.length; i++) { + value = (value << 8) | buffer[i]; + bits += 8; + while (bits >= 5) { + result += BASE32_ALPHABET[(value >>> (bits - 5)) & 31]; + bits -= 5; + } + } + if (bits > 0) { + result += BASE32_ALPHABET[(value << (5 - bits)) & 31]; + } + return result; +} +export function getUniqueHash(slot) { + // unique id based on the start_time & duration_min + const raw = `${slot.startTime.toISOString()}|${slot.durationMin}`; + const hash = createHash('blake2s256').update(raw).digest(); + const truncated = hash.subarray(0, 5); + // Use base32 encoding like Python version and remove padding, then lowercase + return `ST_${toBase32(truncated).replace(/=/g, '').toLowerCase()}`; +} +export function randomSample(array, size) { + const shuffled = [...array].sort(() => 0.5 - Math.random()); + return shuffled.slice(0, size); +} +export class FakeCalendar { + tz; + _slots; + constructor(options) { + this.tz = options.timezone; + this._slots = []; + if (options.slots) { + this._slots.push(...options.slots); + return; + } + const today = new Date(); + for (let dayOffset = 1; dayOffset <= 90; dayOffset++) { + const currentDay = new Date(today); + currentDay.setDate(today.getDate() + dayOffset); + // Skip weekends (Saturday = 6, Sunday = 0) + if (currentDay.getDay() === 0 || currentDay.getDay() === 6) { + continue; + } + // Build all possible 30-min slots between 09:00 and 17:00 + const dayStart = new Date(currentDay); + dayStart.setHours(9, 0, 0, 0); + const slotsInDay = []; + for (let i = 0; i < 16; i++) { + // (17-9)=8 hours => 16 slots + const slotTime = new Date(dayStart); + slotTime.setMinutes(dayStart.getMinutes() + 30 * i); + slotsInDay.push(slotTime); + } + const numSlots = Math.floor(Math.random() * 4) + 3; // random between 3-6 + const chosen = randomSample(slotsInDay, numSlots); + for (const slotStart of chosen.sort((a, b) => a.getTime() - b.getTime())) { + this._slots.push(createAvailableSlot(slotStart, 30)); + } + } + } + async initialize() { } + async scheduleAppointment(options) { + // Fake it by just removing it from our slots list + this._slots = this._slots.filter((slot) => slot.startTime.getTime() !== options.startTime.getTime()); + } + async listAvailableSlots(options) { + return this._slots.filter((slot) => slot.startTime >= options.startTime && slot.startTime < options.endTime); + } +} +// --- cal.com impl --- +const _CAL_COM_EVENT_TYPE = 'livekit-front-desk'; +const _EVENT_DURATION_MIN = 30; +const _BASE_URL = 'https://api.cal.com/v2/'; +export class CalComCalendar { + tz; + _apiKey; + _lkEventId; + _logger; + constructor(options) { + this.tz = options.timezone; + this._apiKey = options.apiKey; + this._logger = { + info: (message) => console.info(`[cal.com] ${message}`), + }; + } + async initialize() { + const meResponse = await fetch(`${_BASE_URL}me/`, { + headers: this._buildHeaders({ apiVersion: '2024-06-14' }), + }); + if (!meResponse.ok) { + throw new Error(`Failed to get user info: ${meResponse.status} ${meResponse.statusText}`); + } + const meData = await meResponse.json(); + const username = meData.data.username; + this._logger.info(`using cal.com username: ${username}`); + const params = new URLSearchParams({ username }); + const eventTypesResponse = await fetch(`${_BASE_URL}event-types/?${params}`, { + headers: this._buildHeaders({ apiVersion: '2024-06-14' }), + }); + if (!eventTypesResponse.ok) { + throw new Error(`Failed to get event types: ${eventTypesResponse.status} ${eventTypesResponse.statusText}`); + } + const eventTypesData = await eventTypesResponse.json(); + const data = eventTypesData.data; + const lkEventType = data.find((event) => event.slug === _CAL_COM_EVENT_TYPE) || null; + if (lkEventType) { + this._lkEventId = lkEventType.id; + } + else { + const createResponse = await fetch(`${_BASE_URL}event-types`, { + method: 'POST', + headers: { + ...this._buildHeaders({ apiVersion: '2024-06-14' }), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + lengthInMinutes: _EVENT_DURATION_MIN, + title: 'LiveKit Front-Desk', + slug: _CAL_COM_EVENT_TYPE, + }), + }); + if (!createResponse.ok) { + throw new Error(`Failed to create event type: ${createResponse.status} ${createResponse.statusText}`); + } + this._logger.info(`successfully added ${_CAL_COM_EVENT_TYPE} event type`); + const createData = await createResponse.json(); + this._lkEventId = createData.data.id; + } + this._logger.info(`event type id: ${this._lkEventId}`); + } + async scheduleAppointment(options) { + const startTimeUtc = new Date(options.startTime.getTime()); + const response = await fetch(`${_BASE_URL}bookings`, { + method: 'POST', + headers: { + ...this._buildHeaders({ apiVersion: '2024-08-13' }), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + start: startTimeUtc.toISOString(), + attendee: { + name: options.attendeeEmail, + email: options.attendeeEmail, + timeZone: this.tz, + }, + eventTypeId: this._lkEventId, + }), + }); + const data = await response.json(); + if (data.error) { + const message = data.error.message; + if (message.includes('User either already has booking at this time or is not available')) { + throw new SlotUnavailableError(data.error.message); + } + } + if (!response.ok) { + throw new Error(`Failed to schedule appointment: ${response.status} ${response.statusText}`); + } + } + async listAvailableSlots(options) { + const startTimeUtc = new Date(options.startTime.getTime()); + const endTimeUtc = new Date(options.endTime.getTime()); + const params = new URLSearchParams({ + eventTypeId: this._lkEventId.toString(), + start: startTimeUtc.toISOString(), + end: endTimeUtc.toISOString(), + }); + const response = await fetch(`${_BASE_URL}slots/?${params}`, { + headers: this._buildHeaders({ apiVersion: '2024-09-04' }), + }); + if (!response.ok) { + throw new Error(`Failed to get available slots: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + const availableSlots = []; + for (const [, slots] of Object.entries(rawData.data)) { + for (const slot of slots) { + const startDt = new Date(slot.start.replace('Z', '+00:00')); + availableSlots.push(createAvailableSlot(startDt, _EVENT_DURATION_MIN)); + } + } + return availableSlots; + } + _buildHeaders(options) { + const headers = { + Authorization: `Bearer ${this._apiKey}`, + }; + if (options.apiVersion) { + headers['cal-api-version'] = options.apiVersion; + } + return headers; + } +} +//# sourceMappingURL=calendar_api.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.js.map b/examples/src/frontdesk/calendar_api.js.map new file mode 100644 index 000000000..d5a805e4d --- /dev/null +++ b/examples/src/frontdesk/calendar_api.js.map @@ -0,0 +1 @@ +{"version":3,"file":"calendar_api.js","sourceRoot":"","sources":["calendar_api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.test.js b/examples/src/frontdesk/calendar_api.test.js new file mode 100644 index 000000000..ec22b1acc --- /dev/null +++ b/examples/src/frontdesk/calendar_api.test.js @@ -0,0 +1,367 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { CalComCalendar, FakeCalendar, SlotUnavailableError, createAvailableSlot, getUniqueHash, } from './calendar_api.js'; +describe('Calendar API', () => { + describe('createAvailableSlot', () => { + it('should create a valid AvailableSlot', () => { + const startTime = new Date('2025-01-20T10:00:00Z'); + const durationMin = 30; + const slot = createAvailableSlot(startTime, durationMin); + expect(slot.startTime).toEqual(startTime); + expect(slot.durationMin).toBe(durationMin); + }); + }); + describe('getUniqueHash', () => { + it('should generate consistent hash for same slot', () => { + const slot = createAvailableSlot(new Date('2025-01-20T10:00:00Z'), 30); + const hash1 = getUniqueHash(slot); + const hash2 = getUniqueHash(slot); + expect(hash1).toBe(hash2); + expect(hash1).toMatch(/^ST_[a-z0-9]+$/); + }); + it('should generate different hashes for different slots', () => { + const slot1 = createAvailableSlot(new Date('2025-01-20T10:00:00Z'), 30); + const slot2 = createAvailableSlot(new Date('2025-01-20T10:30:00Z'), 30); + const hash1 = getUniqueHash(slot1); + const hash2 = getUniqueHash(slot2); + expect(hash1).not.toBe(hash2); + }); + it('should generate different hashes for different durations', () => { + const startTime = new Date('2025-01-20T10:00:00Z'); + const slot1 = createAvailableSlot(startTime, 30); + const slot2 = createAvailableSlot(startTime, 60); + const hash1 = getUniqueHash(slot1); + const hash2 = getUniqueHash(slot2); + expect(hash1).not.toBe(hash2); + }); + }); + describe('FakeCalendar', () => { + let calendar; + beforeEach(() => { + calendar = new FakeCalendar({ timezone: 'America/New_York' }); + }); + it('should initialize without error', async () => { + await expect(calendar.initialize()).resolves.not.toThrow(); + }); + it('should generate weekday slots only', async () => { + await calendar.initialize(); + const startTime = new Date('2025-01-20T00:00:00Z'); + const endTime = new Date('2025-01-27T00:00:00Z'); + const slots = await calendar.listAvailableSlots({ startTime, endTime }); + // Check that all slots are on weekdays + for (const slot of slots) { + const dayOfWeek = slot.startTime.getDay(); + expect(dayOfWeek).toBeGreaterThan(0); + expect(dayOfWeek).toBeLessThan(6); + } + }); + it('should generate slots within business hours', async () => { + await calendar.initialize(); + const startTime = new Date('2025-01-20T00:00:00Z'); + const endTime = new Date('2025-01-21T00:00:00Z'); + const slots = await calendar.listAvailableSlots({ startTime, endTime }); + // Check that all slots are within business hours (9-17) + for (const slot of slots) { + const hour = slot.startTime.getHours(); + expect(hour).toBeGreaterThanOrEqual(9); + expect(hour).toBeLessThan(17); + } + }); + it('should filter slots by date range', async () => { + await calendar.initialize(); + const startTime = new Date('2025-01-20T00:00:00Z'); + const endTime = new Date('2025-01-21T00:00:00Z'); + const slots = await calendar.listAvailableSlots({ startTime, endTime }); + for (const slot of slots) { + expect(slot.startTime.getTime()).toBeGreaterThanOrEqual(startTime.getTime()); + expect(slot.startTime.getTime()).toBeLessThan(endTime.getTime()); + } + }); + it('should schedule appointment and remove slot', async () => { + const predefinedSlots = [ + createAvailableSlot(new Date('2025-01-20T10:00:00Z'), 30), + createAvailableSlot(new Date('2025-01-20T11:00:00Z'), 30), + ]; + calendar = new FakeCalendar({ + timezone: 'America/New_York', + slots: predefinedSlots, + }); + await calendar.initialize(); + await calendar.scheduleAppointment({ + startTime: predefinedSlots[0].startTime, + attendeeEmail: 'test@example.com', + }); + const remainingSlots = await calendar.listAvailableSlots({ + startTime: new Date('2025-01-20T00:00:00Z'), + endTime: new Date('2025-01-21T00:00:00Z'), + }); + expect(remainingSlots).toHaveLength(1); + expect(remainingSlots[0].startTime).toEqual(predefinedSlots[1].startTime); + }); + it('should work with custom slots', async () => { + const customSlots = [ + createAvailableSlot(new Date('2025-01-20T14:00:00Z'), 30), + createAvailableSlot(new Date('2025-01-20T15:30:00Z'), 60), + ]; + calendar = new FakeCalendar({ + timezone: 'UTC', + slots: customSlots, + }); + await calendar.initialize(); + const slots = await calendar.listAvailableSlots({ + startTime: new Date('2025-01-20T00:00:00Z'), + endTime: new Date('2025-01-21T00:00:00Z'), + }); + expect(slots).toHaveLength(2); + expect(slots).toEqual(expect.arrayContaining(customSlots)); + }); + }); + describe('CalComCalendar (mocked)', () => { + let calendar; + beforeEach(() => { + calendar = new CalComCalendar({ + apiKey: 'test-api-key', + timezone: 'America/New_York', + }); + global.fetch = vi.fn(); + }); + afterEach(() => { + vi.restoreAllMocks(); + }); + describe('initialize', () => { + it('should initialize successfully with existing event type', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: { username: 'testuser' }, + }), + }); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: [{ id: 123, slug: 'livekit-front-desk' }], + }), + }); + const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => { }); + await calendar.initialize(); + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(consoleSpy).toHaveBeenCalledWith('[cal.com] using cal.com username: testuser'); + expect(consoleSpy).toHaveBeenCalledWith('[cal.com] event type id: 123'); + consoleSpy.mockRestore(); + }); + it('should create new event type when not exists', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: { username: 'testuser' }, + }), + }); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: [], + }), + }); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: { id: 456 }, + }), + }); + const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => { }); + await calendar.initialize(); + expect(mockFetch).toHaveBeenCalledTimes(3); + expect(consoleSpy).toHaveBeenCalledWith('[cal.com] using cal.com username: testuser'); + expect(consoleSpy).toHaveBeenCalledWith('[cal.com] successfully added livekit-front-desk event type'); + expect(consoleSpy).toHaveBeenCalledWith('[cal.com] event type id: 456'); + consoleSpy.mockRestore(); + }); + it('should handle API errors', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 401, + statusText: 'Unauthorized', + }); + await expect(calendar.initialize()).rejects.toThrow('Failed to get user info: 401 Unauthorized'); + }); + }); + describe('scheduleAppointment', () => { + beforeEach(async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ data: { username: 'testuser' } }), + }); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ data: [{ id: 123, slug: 'livekit-front-desk' }] }), + }); + vi.spyOn(console, 'info').mockImplementation(() => { }); + await calendar.initialize(); + vi.mocked(console.info).mockRestore(); + mockFetch.mockClear(); + }); + it('should schedule appointment successfully', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ success: true }), + }); + await calendar.scheduleAppointment({ + startTime: new Date('2025-01-20T10:00:00Z'), + attendeeEmail: 'test@example.com', + }); + expect(mockFetch).toHaveBeenCalledWith('https://api.cal.com/v2/bookings', expect.objectContaining({ + method: 'POST', + headers: expect.objectContaining({ + Authorization: 'Bearer test-api-key', + 'cal-api-version': '2024-08-13', + }), + body: JSON.stringify({ + start: '2025-01-20T10:00:00.000Z', + attendee: { + name: 'test@example.com', + email: 'test@example.com', + timeZone: 'America/New_York', + }, + eventTypeId: 123, + }), + })); + }); + it('should throw SlotUnavailableError for booking conflicts', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: false, + json: async () => ({ + error: { + message: 'User either already has booking at this time or is not available', + }, + }), + }); + await expect(calendar.scheduleAppointment({ + startTime: new Date('2025-01-20T10:00:00Z'), + attendeeEmail: 'test@example.com', + })).rejects.toThrow(SlotUnavailableError); + }); + it('should handle other API errors', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 500, + statusText: 'Internal Server Error', + json: async () => ({}), + }); + await expect(calendar.scheduleAppointment({ + startTime: new Date('2025-01-20T10:00:00Z'), + attendeeEmail: 'test@example.com', + })).rejects.toThrow('Failed to schedule appointment: 500 Internal Server Error'); + }); + }); + describe('listAvailableSlots', () => { + beforeEach(async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ data: { username: 'testuser' } }), + }); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ data: [{ id: 123, slug: 'livekit-front-desk' }] }), + }); + vi.spyOn(console, 'info').mockImplementation(() => { }); + await calendar.initialize(); + vi.mocked(console.info).mockRestore(); + mockFetch.mockClear(); + }); + it('should list available slots successfully', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: { + '2025-01-20': [{ start: '2025-01-20T10:00:00Z' }, { start: '2025-01-20T11:00:00Z' }], + }, + }), + }); + const startTime = new Date('2025-01-20T00:00:00Z'); + const endTime = new Date('2025-01-21T00:00:00Z'); + const slots = await calendar.listAvailableSlots({ startTime, endTime }); + expect(slots).toHaveLength(2); + expect(slots[0].startTime).toEqual(new Date('2025-01-20T10:00:00Z')); + expect(slots[0].durationMin).toBe(30); + expect(slots[1].startTime).toEqual(new Date('2025-01-20T11:00:00Z')); + expect(slots[1].durationMin).toBe(30); + expect(mockFetch).toHaveBeenCalledWith('https://api.cal.com/v2/slots/?eventTypeId=123&start=2025-01-20T00%3A00%3A00.000Z&end=2025-01-21T00%3A00%3A00.000Z', expect.objectContaining({ + headers: expect.objectContaining({ + Authorization: 'Bearer test-api-key', + 'cal-api-version': '2024-09-04', + }), + })); + }); + it('should handle API errors', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 404, + statusText: 'Not Found', + }); + const startTime = new Date('2025-01-20T00:00:00Z'); + const endTime = new Date('2025-01-21T00:00:00Z'); + await expect(calendar.listAvailableSlots({ startTime, endTime })).rejects.toThrow('Failed to get available slots: 404 Not Found'); + }); + it('should return empty array when no slots available', async () => { + const mockFetch = vi.mocked(fetch); + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => ({ data: {} }), + }); + const startTime = new Date('2025-01-20T00:00:00Z'); + const endTime = new Date('2025-01-21T00:00:00Z'); + const slots = await calendar.listAvailableSlots({ startTime, endTime }); + expect(slots).toHaveLength(0); + }); + }); + }); + describe('SlotUnavailableError', () => { + it('should create error with correct name and message', () => { + const message = 'Slot is not available'; + const error = new SlotUnavailableError(message); + expect(error.name).toBe('SlotUnavailableError'); + expect(error.message).toBe(message); + expect(error).toBeInstanceOf(Error); + }); + }); + describe('FakeCalendar end-to-end', () => { + it('should work as a complete calendar system', async () => { + const calendar = new FakeCalendar({ timezone: 'UTC' }); + await calendar.initialize(); + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + tomorrow.setHours(0, 0, 0, 0); + const dayAfter = new Date(tomorrow); + dayAfter.setDate(dayAfter.getDate() + 1); + const slots = await calendar.listAvailableSlots({ + startTime: tomorrow, + endTime: dayAfter, + }); + expect(slots.length).toBeGreaterThan(0); + const firstSlot = slots[0]; + await calendar.scheduleAppointment({ + startTime: firstSlot.startTime, + attendeeEmail: 'test@example.com', + }); + const remainingSlots = await calendar.listAvailableSlots({ + startTime: tomorrow, + endTime: dayAfter, + }); + expect(remainingSlots.length).toBe(slots.length - 1); + expect(remainingSlots.find((slot) => slot.startTime.getTime() === firstSlot.startTime.getTime())).toBeUndefined(); + }); + }); +}); +//# sourceMappingURL=calendar_api.test.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.test.js.map b/examples/src/frontdesk/calendar_api.test.js.map new file mode 100644 index 000000000..20157d4a3 --- /dev/null +++ b/examples/src/frontdesk/calendar_api.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"calendar_api.test.js","sourceRoot":"","sources":["calendar_api.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_integration.test.js b/examples/src/frontdesk/calendar_integration.test.js new file mode 100644 index 000000000..36766fb47 --- /dev/null +++ b/examples/src/frontdesk/calendar_integration.test.js @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { describe, expect, it } from 'vitest'; +import { CalComCalendar } from './calendar_api.js'; +describe('Calendar Integration Tests', () => { + describe('CalComCalendar with real API', () => { + it.skipIf(!process.env.CAL_API_KEY)('should initialize with real API key', async () => { + const calendar = new CalComCalendar({ + apiKey: process.env.CAL_API_KEY, + timezone: 'America/New_York', + }); + await expect(calendar.initialize()).resolves.not.toThrow(); + }, 10000); + it.skipIf(!process.env.CAL_API_KEY)('should list available slots with real API key', async () => { + const calendar = new CalComCalendar({ + apiKey: process.env.CAL_API_KEY, + timezone: 'America/New_York', + }); + await calendar.initialize(); + const startTime = new Date(); + startTime.setDate(startTime.getDate() + 1); + const endTime = new Date(startTime); + endTime.setDate(endTime.getDate() + 7); + const slots = await calendar.listAvailableSlots({ startTime, endTime }); + expect(Array.isArray(slots)).toBe(true); + slots.forEach((slot) => { + expect(slot.startTime).toBeInstanceOf(Date); + expect(typeof slot.durationMin).toBe('number'); + expect(slot.durationMin).toBeGreaterThan(0); + }); + }, 10000); + }); +}); +//# sourceMappingURL=calendar_integration.test.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_integration.test.js.map b/examples/src/frontdesk/calendar_integration.test.js.map new file mode 100644 index 000000000..83c08f40d --- /dev/null +++ b/examples/src/frontdesk/calendar_integration.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"calendar_integration.test.js","sourceRoot":"","sources":["calendar_integration.test.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CACjC,qCAAqC,EACrC,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC;gBAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAY;gBAChC,QAAQ,EAAE,kBAAkB;aAC7B,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7D,CAAC,EACD,KAAK,CACN,CAAC;QAEF,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CACjC,+CAA+C,EAC/C,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC;gBAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAY;gBAChC,QAAQ,EAAE,kBAAkB;aAC7B,CAAC,CAAC;YAEH,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;YAE5B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;YACpC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAEvC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAExE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBACrB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;gBAC5C,MAAM,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC,EACD,KAAK,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/frontdesk/frontdesk_agent.js b/examples/src/frontdesk/frontdesk_agent.js new file mode 100644 index 000000000..0bc0d4ea4 --- /dev/null +++ b/examples/src/frontdesk/frontdesk_agent.js @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +import { CalComCalendar, FakeCalendar, SlotUnavailableError, getUniqueHash, } from './calendar_api.js'; +export class FrontDeskAgent extends voice.Agent { + tz; + _slotsMap = new Map(); + constructor(options) { + const today = new Date().toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + timeZone: options.timezone, + }); + const instructions = `You are Front-Desk, a helpful and efficient voice assistant. ` + + `Today is ${today}. Your main goal is to schedule an appointment for the user. ` + + `This is a voice conversation — speak naturally, clearly, and concisely. ` + + `When the user says hello or greets you, don't just respond with a greeting — use it as an opportunity to move things forward. ` + + `For example, follow up with a helpful question like: 'Would you like to book a time?' ` + + `When asked for availability, call list_available_slots and offer a few clear, simple options. ` + + `Say things like 'Monday at 2 PM' — avoid timezones, timestamps, and avoid saying 'AM' or 'PM'. ` + + `Use natural phrases like 'in the morning' or 'in the evening', and don't mention the year unless it's different from the current one. ` + + `Offer a few options at a time, pause for a response, then guide the user to confirm. ` + + `If the time is no longer available, let them know gently and offer the next options. ` + + `Always keep the conversation flowing — be proactive, human, and focused on helping the user schedule with ease.`; + super({ + instructions, + tools: { + scheduleAppointment: llm.tool({ + description: 'Schedule an appointment at the given slot.', + parameters: z.object({ + slotId: z + .string() + .describe('The identifier for the selected time slot (as shown in the list of available slots).'), + }), + execute: async ({ slotId }, { ctx }) => { + const slot = this._slotsMap.get(slotId); + if (!slot) { + throw new llm.ToolError(`error: slot ${slotId} was not found`); + } + // Note: The Python version uses beta.workflows.GetEmailTask which is not available in TypeScript yet + // For now, we'll use a placeholder email + const placeholderEmail = 'user@example.com'; + console.warn('Note: Email collection workflow not implemented in TypeScript version yet. Using placeholder email.'); + try { + await ctx.userData.cal.scheduleAppointment({ + startTime: slot.startTime, + attendeeEmail: placeholderEmail, + }); + } + catch (error) { + if (error instanceof SlotUnavailableError) { + throw new llm.ToolError("This slot isn't available anymore"); + } + throw error; + } + const local = new Date(slot.startTime.toLocaleString('en-US', { timeZone: this.tz })); + const formatted = local.toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + timeZoneName: 'short', + timeZone: this.tz, + }); + return `The appointment was successfully scheduled for ${formatted}.`; + }, + }), + listAvailableSlots: llm.tool({ + description: `Return a plain-text list of available slots, one per line. + + - , , at () + +You must infer the appropriate range implicitly from the conversational context and must not prompt the user to pick a value explicitly.`, + parameters: z.object({ + range: z + .enum(['+2week', '+1month', '+3month', 'default']) + .describe('Determines how far ahead to search for free time slots.'), + }), + execute: async ({ range }, { ctx }) => { + const now = new Date(); + const lines = []; + let rangeDays; + if (range === '+2week' || range === 'default') { + rangeDays = 14; + } + else if (range === '+1month') { + rangeDays = 30; + } + else if (range === '+3month') { + rangeDays = 90; + } + else { + rangeDays = 14; + } + const endTime = new Date(now.getTime() + rangeDays * 24 * 60 * 60 * 1000); + const slots = await ctx.userData.cal.listAvailableSlots({ + startTime: now, + endTime: endTime, + }); + for (const slot of slots) { + const local = new Date(slot.startTime.toLocaleString('en-US', { timeZone: this.tz })); + const delta = local.getTime() - now.getTime(); + const days = Math.floor(delta / (24 * 60 * 60 * 1000)); + const seconds = Math.floor((delta % (24 * 60 * 60 * 1000)) / 1000); + let rel; + if (local.toDateString() === now.toDateString()) { + if (seconds < 3600) { + rel = 'in less than an hour'; + } + else { + rel = 'later today'; + } + } + else if (local.toDateString() === + new Date(now.getTime() + 24 * 60 * 60 * 1000).toDateString()) { + rel = 'tomorrow'; + } + else if (days < 7) { + rel = `in ${days} days`; + } + else if (days < 14) { + rel = 'in 1 week'; + } + else { + rel = `in ${Math.floor(days / 7)} weeks`; + } + const uniqueHash = getUniqueHash(slot); + const formatted = local.toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + timeZoneName: 'short', + timeZone: this.tz, + }); + lines.push(`${uniqueHash} - ${formatted} (${rel})`); + this._slotsMap.set(uniqueHash, slot); + } + return lines.join('\n') || 'No slots available at the moment.'; + }, + }), + }, + }); + this.tz = options.timezone; + } +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const timezone = 'UTC'; + let cal; + const calApiKey = process.env.CAL_API_KEY; + if (calApiKey) { + console.log('CAL_API_KEY detected, using cal.com calendar'); + cal = new CalComCalendar({ apiKey: calApiKey, timezone }); + } + else { + console.warn('CAL_API_KEY is not set. Falling back to FakeCalendar; set CAL_API_KEY to enable Cal.com integration.'); + cal = new FakeCalendar({ timezone }); + } + await cal.initialize(); + const userdata = { cal }; + const session = new voice.AgentSession({ + vad: ctx.proc.userData.vad, + stt: new deepgram.STT(), + llm: new openai.LLM({ + model: 'gpt-4.1', + }), + tts: new elevenlabs.TTS(), + turnDetection: new livekit.turnDetector.MultilingualModel(), + userData: userdata, + voiceOptions: { + maxToolSteps: 1, + }, + }); + await session.start({ + agent: new FrontDeskAgent({ timezone }), + room: ctx.room, + inputOptions: { + noiseCancellation: BackgroundVoiceCancellation(), + }, + }); + session.generateReply({ + userInput: 'Greet to the user', + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=frontdesk_agent.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/frontdesk_agent.js.map b/examples/src/frontdesk/frontdesk_agent.js.map new file mode 100644 index 000000000..10fe2f383 --- /dev/null +++ b/examples/src/frontdesk/frontdesk_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"frontdesk_agent.js","sourceRoot":"","sources":["frontdesk_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/gemini_realtime_agent.js b/examples/src/gemini_realtime_agent.js new file mode 100644 index 000000000..789c60df2 --- /dev/null +++ b/examples/src/gemini_realtime_agent.js @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as google from '@livekit/agents-plugin-google'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +const roomNameSchema = z.enum(['bedroom', 'living room', 'kitchen', 'bathroom', 'office']); +const getWeather = llm.tool({ + description: ' Called when the user asks about the weather.', + parameters: z.object({ + location: z.string().describe('The location to get the weather for'), + }), + execute: async ({ location }) => { + return `The weather in ${location} is sunny today.`; + }, +}); +const toggleLight = llm.tool({ + description: 'Called when the user asks to turn on or off the light.', + parameters: z.object({ + room: roomNameSchema.describe('The room to turn the light in'), + switchTo: z.enum(['on', 'off']).describe('The state to turn the light to'), + }), + execute: async ({ room, switchTo }) => { + return `The light in the ${room} is now ${switchTo}.`; + }, +}); +// Use inheritance to create agent with custom hooks +class IntroAgent extends voice.Agent { + async onEnter() { + this.session.generateReply({ + instructions: '"greet the user and gather information"', + }); + } + static create() { + return new IntroAgent({ + instructions: `You are a story teller. Your goal is to gather a few pieces of information from the user to make the story personalized and engaging. Ask the user for their name and where they are from.`, + tools: { + informationGathered: llm.tool({ + description: 'Called when the user has provided the information needed to make the story personalized and engaging.', + parameters: z.object({ + name: z.string().describe('The name of the user'), + location: z.string().describe('The location of the user'), + }), + execute: async ({ name, location }, { ctx }) => { + ctx.userData.name = name; + ctx.userData.location = location; + const storyAgent = StoryAgent.create(name, location); + return llm.handoff({ agent: storyAgent, returns: "Let's start the story!" }); + }, + }), + getWeather, + toggleLight, + }, + }); + } +} +class StoryAgent extends voice.Agent { + async onEnter() { + this.session.generateReply(); + } + static create(name, location) { + return new StoryAgent({ + instructions: `You are a storyteller. Use the user's information in order to make the story personalized. + The user's name is ${name}, from ${location}`, + }); + } +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const userdata = {}; + const session = new voice.AgentSession({ + vad: ctx.proc.userData.vad, + llm: new google.beta.realtime.RealtimeModel(), + userData: userdata, + }); + await session.start({ + agent: IntroAgent.create(), + room: ctx.room, + }); + const participant = await ctx.waitForParticipant(); + console.log('participant joined: ', participant.identity); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=gemini_realtime_agent.js.map \ No newline at end of file diff --git a/examples/src/gemini_realtime_agent.js.map b/examples/src/gemini_realtime_agent.js.map new file mode 100644 index 000000000..f9ab6507f --- /dev/null +++ b/examples/src/gemini_realtime_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"gemini_realtime_agent.js","sourceRoot":"","sources":["gemini_realtime_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/multi_agent.js b/examples/src/multi_agent.js new file mode 100644 index 000000000..7e61bb426 --- /dev/null +++ b/examples/src/multi_agent.js @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +// Use inheritance to create agent with custom hooks +class IntroAgent extends voice.Agent { + async onEnter() { + this.session.generateReply({ + instructions: '"greet the user and gather information"', + }); + } + static create() { + return new IntroAgent({ + instructions: `You are a story teller. Your goal is to gather a few pieces of information from the user to make the story personalized and engaging. Ask the user for their name and where they are from.`, + tools: { + informationGathered: llm.tool({ + description: 'Called when the user has provided the information needed to make the story personalized and engaging.', + parameters: z.object({ + name: z.string().describe('The name of the user'), + location: z.string().describe('The location of the user'), + }), + execute: async ({ name, location }, { ctx }) => { + ctx.userData.name = name; + ctx.userData.location = location; + const storyAgent = StoryAgent.create(name, location); + return llm.handoff({ agent: storyAgent, returns: "Let's start the story!" }); + }, + }), + }, + }); + } +} +class StoryAgent extends voice.Agent { + async onEnter() { + this.session.generateReply(); + } + static create(name, location) { + return new StoryAgent({ + instructions: `You are a storyteller. Use the user's information in order to make the story personalized. + The user's name is ${name}, from ${location}`, + }); + } +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const userdata = {}; + const session = new voice.AgentSession({ + vad: ctx.proc.userData.vad, + stt: new deepgram.STT(), + tts: new elevenlabs.TTS(), + llm: new openai.LLM(), + // to use realtime model, replace the stt, llm, tts and vad with the following + // llm: new openai.realtime.RealtimeModel(), + userData: userdata, + turnDetection: new livekit.turnDetector.EnglishModel(), + }); + await session.start({ + agent: IntroAgent.create(), + room: ctx.room, + }); + const participant = await ctx.waitForParticipant(); + console.log('participant joined: ', participant.identity); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=multi_agent.js.map \ No newline at end of file diff --git a/examples/src/multi_agent.js.map b/examples/src/multi_agent.js.map new file mode 100644 index 000000000..83f64b600 --- /dev/null +++ b/examples/src/multi_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"multi_agent.js","sourceRoot":"","sources":["multi_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/push_to_talk.js b/examples/src/push_to_talk.js new file mode 100644 index 000000000..e569e6396 --- /dev/null +++ b/examples/src/push_to_talk.js @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +class MyAgent extends voice.Agent { + async onUserTurnCompleted(chatCtx, newMessage) { + if (!newMessage.textContent || newMessage.textContent.length === 0) { + console.log('ignore empty user turn'); + throw new voice.StopResponse(); + } + } +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const session = new voice.AgentSession({ + vad: ctx.proc.userData.vad, + stt: new deepgram.STT(), + llm: new openai.LLM(), + tts: new elevenlabs.TTS(), + turnDetection: 'manual', + }); + const agent = new MyAgent({ + instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", + }); + await session.start({ + agent, + room: ctx.room, + }); + ctx.room.localParticipant?.registerRpcMethod('start_turn', async () => { + session.interrupt(); + session.clearUserTurn(); + return 'ok'; + }); + ctx.room.localParticipant?.registerRpcMethod('end_turn', async () => { + session.commitUserTurn(); + return 'ok'; + }); + ctx.room.localParticipant?.registerRpcMethod('cancel_turn', async () => { + session.clearUserTurn(); + return 'ok'; + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=push_to_talk.js.map \ No newline at end of file diff --git a/examples/src/push_to_talk.js.map b/examples/src/push_to_talk.js.map new file mode 100644 index 000000000..bb378f024 --- /dev/null +++ b/examples/src/push_to_talk.js.map @@ -0,0 +1 @@ +{"version":3,"file":"push_to_talk.js","sourceRoot":"","sources":["push_to_talk.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAQ,SAAQ,KAAK,CAAC,KAAK;IAC/B,KAAK,CAAC,mBAAmB,CAAC,OAAoB,EAAE,UAAuB;QACrE,IAAI,CAAC,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;CACF;AAED,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB;YACzC,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,aAAa,EAAE,QAAQ;SACxB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC;YACxB,YAAY,EACV,iFAAiF;SACpF,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACpE,OAAO,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YAClE,OAAO,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;YACrE,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/raw_function_description.js b/examples/src/raw_function_description.js new file mode 100644 index 000000000..7e70a7140 --- /dev/null +++ b/examples/src/raw_function_description.js @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +function createRawFunctionAgent() { + return new voice.Agent({ + instructions: 'You are a helpful assistant.', + tools: { + openGate: llm.tool({ + description: 'Opens a specified gate from a predefined set of access points.', + parameters: { + type: 'object', + properties: { + gateId: { + type: 'string', + description: 'The ID of the gate to open', + enum: [ + 'main_entrance', + 'north_parking', + 'loading_dock', + 'side_gate', + 'service_entry', + ], + }, + }, + required: ['gateId'], + additionalProperties: false, + }, + execute: async ({ gateId }) => { + return `The gate ${gateId} is now open.`; + }, + }), + }, + }); +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + stt: new deepgram.STT({ + sampleRate: 24000, + }), + tts: new elevenlabs.TTS(), + llm: new openai.LLM(), + // to use realtime model, replace the stt, llm, tts and vad with the following + // llm: new openai.realtime.RealtimeModel(), + userData: { number: 0 }, + turnDetection: new livekit.turnDetector.EnglishModel(), + }); + await session.start({ + agent: createRawFunctionAgent(), + room: ctx.room, + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=raw_function_description.js.map \ No newline at end of file diff --git a/examples/src/raw_function_description.js.map b/examples/src/raw_function_description.js.map new file mode 100644 index 000000000..521b01cd0 --- /dev/null +++ b/examples/src/raw_function_description.js.map @@ -0,0 +1 @@ +{"version":3,"file":"raw_function_description.js","sourceRoot":"","sources":["raw_function_description.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,GAAG,EACH,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,SAAS,sBAAsB;IAC7B,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC;QACrB,YAAY,EAAE,8BAA8B;QAC5C,KAAK,EAAE;YACL,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC;gBACjB,WAAW,EAAE,gEAAgE;gBAC7E,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,4BAA4B;4BACzC,IAAI,EAAE;gCACJ,eAAe;gCACf,eAAe;gCACf,cAAc;gCACd,WAAW;gCACX,eAAe;6BAChB;yBACF;qBACF;oBACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;oBACpB,oBAAoB,EAAE,KAAK;iBAC5B;gBACD,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;oBAC5B,OAAO,YAAY,MAAM,eAAe,CAAC;gBAC3C,CAAC;aACF,CAAC;SACH;KACF,CAAC,CAAC;AACL,CAAC;AAED,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB,CAAC;QAEjD,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG;YACH,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC;gBACpB,UAAU,EAAE,KAAK;aAClB,CAAC;YACF,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,8EAA8E;YAC9E,4CAA4C;YAC5C,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;YACvB,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE;SACvD,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK,EAAE,sBAAsB,EAAE;YAC/B,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/realtime_agent.js b/examples/src/realtime_agent.js new file mode 100644 index 000000000..34f4f6bfa --- /dev/null +++ b/examples/src/realtime_agent.js @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +const roomNameSchema = z.enum(['bedroom', 'living room', 'kitchen', 'bathroom', 'office']); +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const getWeather = llm.tool({ + description: ' Called when the user asks about the weather.', + parameters: z.object({ + location: z.string().describe('The location to get the weather for'), + }), + execute: async ({ location }) => { + return `The weather in ${location} is sunny today.`; + }, + }); + const toggleLight = llm.tool({ + description: 'Called when the user asks to turn on or off the light.', + parameters: z.object({ + room: roomNameSchema.describe('The room to turn the light in'), + switchTo: z.enum(['on', 'off']).describe('The state to turn the light to'), + }), + execute: async ({ room, switchTo }) => { + return `The light in the ${room} is now ${switchTo}.`; + }, + }); + const agent = new voice.Agent({ + instructions: "You are a helpful assistant created by LiveKit, always speaking English, you can hear the user's message and respond to it.", + tools: { + getWeather, + toggleLight, + }, + }); + const session = new voice.AgentSession({ + // llm: new openai.realtime.RealtimeModel(), + llm: new openai.realtime.RealtimeModel(), + // enable to allow chaining of tool calls + voiceOptions: { + maxToolSteps: 5, + }, + }); + await session.start({ + agent, + room: ctx.room, + }); + session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { + console.log('metrics_collected', ev); + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=realtime_agent.js.map \ No newline at end of file diff --git a/examples/src/realtime_agent.js.map b/examples/src/realtime_agent.js.map new file mode 100644 index 000000000..06d902137 --- /dev/null +++ b/examples/src/realtime_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"realtime_agent.js","sourceRoot":"","sources":["realtime_agent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,GAAG,EACH,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE3F,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;YAC1B,WAAW,EAAE,+CAA+C;YAC5D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;gBACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;aACrE,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;gBAC9B,OAAO,kBAAkB,QAAQ,kBAAkB,CAAC;YACtD,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;YAC3B,WAAW,EAAE,wDAAwD;YACrE,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;gBACnB,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAA+B,CAAC;gBAC9D,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,gCAAgC,CAAC;aAC3E,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;gBACpC,OAAO,oBAAoB,IAAI,WAAW,QAAQ,GAAG,CAAC;YACxD,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,6HAA6H;YAC/H,KAAK,EAAE;gBACL,UAAU;gBACV,WAAW;aACZ;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,4CAA4C;YAC5C,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE;YACxC,yCAAyC;YACzC,YAAY,EAAE;gBACZ,YAAY,EAAE,CAAC;aAChB;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YAC/D,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/realtime_turn_detector.js b/examples/src/realtime_turn_detector.js new file mode 100644 index 000000000..151eb1801 --- /dev/null +++ b/examples/src/realtime_turn_detector.js @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const session = new voice.AgentSession({ + vad: ctx.proc.userData.vad, + stt: new deepgram.STT(), + tts: new elevenlabs.TTS(), + // To use OpenAI Realtime API + llm: new openai.realtime.RealtimeModel({ + voice: 'alloy', + // it's necessary to turn off turn detection in the OpenAI Realtime API in order to use + // LiveKit's turn detection model + turnDetection: null, + inputAudioTranscription: null, + }), + turnDetection: new livekit.turnDetector.EnglishModel(), + }); + await session.start({ + agent: new voice.Agent({ + instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", + }), + room: ctx.room, + }); + session.say('Hello, how can I help you today?'); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=realtime_turn_detector.js.map \ No newline at end of file diff --git a/examples/src/realtime_turn_detector.js.map b/examples/src/realtime_turn_detector.js.map new file mode 100644 index 000000000..a374a46ae --- /dev/null +++ b/examples/src/realtime_turn_detector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"realtime_turn_detector.js","sourceRoot":"","sources":["realtime_turn_detector.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB;YACzC,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,6BAA6B;YAC7B,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;gBACrC,KAAK,EAAE,OAAO;gBACd,uFAAuF;gBACvF,iCAAiC;gBACjC,aAAa,EAAE,IAAI;gBACnB,uBAAuB,EAAE,IAAI;aAC9B,CAAC;YACF,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE;SACvD,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC;gBACrB,YAAY,EACV,iFAAiF;aACpF,CAAC;YACF,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/restaurant_agent.js b/examples/src/restaurant_agent.js new file mode 100644 index 000000000..77ad23c9f --- /dev/null +++ b/examples/src/restaurant_agent.js @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; +import * as deepgram from '@livekit/agents-plugin-deepgram'; +import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; +import { z } from 'zod'; +const voices = { + greeter: { + id: '9BWtsMINqrJLrRacOk9x', // Aria - calm, professional female voice + name: 'Aria', + category: 'premade', + }, + reservation: { + id: 'EXAVITQu4vr4xnSDxMaL', // Sarah - warm, reassuring professional tone + name: 'Sarah', + category: 'premade', + }, + takeaway: { + id: 'CwhRBWXzGAHq8TQ4Fs17', // Roger - confident middle-aged male + name: 'Roger', + category: 'premade', + }, + checkout: { + id: '5Q0t7uMcjvnagumLfvZi', // Paul - authoritative middle-aged male + name: 'Paul', + category: 'premade', + }, +}; +function createUserData(agents) { + return { + customer: {}, + creditCard: {}, + agents, + }; +} +function summarize({ customer, reservationTime, order, creditCard, expense, checkedOut, }) { + return JSON.stringify({ + customer: customer.name ?? 'unknown', + customerPhone: customer.phone ?? 'unknown', + reservationTime: reservationTime ?? 'unknown', + order: order ?? 'unknown', + creditCard: creditCard + ? { + number: creditCard.number ?? 'unknown', + expiry: creditCard.expiry ?? 'unknown', + cvv: creditCard.cvv ?? 'unknown', + } + : undefined, + expense: expense ?? 'unknown', + checkedOut: checkedOut ?? false, + }, null, 2); +} +const updateName = llm.tool({ + description: 'Called when the user provides their name. Confirm the spelling with the user before calling the function.', + parameters: z.object({ + name: z.string().describe('The customer name'), + }), + execute: async ({ name }, { ctx }) => { + ctx.userData.customer.name = name; + return `The name is updated to ${name}`; + }, +}); +const updatePhone = llm.tool({ + description: 'Called when the user provides their phone number. Confirm the spelling with the user before calling the function.', + parameters: z.object({ + phone: z.string().describe('The customer phone number'), + }), + execute: async ({ phone }, { ctx }) => { + ctx.userData.customer.phone = phone; + return `The phone number is updated to ${phone}`; + }, +}); +const toGreeter = llm.tool({ + description: 'Called when user asks any unrelated questions or requests any other services not in your job description.', + execute: async (_, { ctx }) => { + const currAgent = ctx.session.currentAgent; + return await currAgent.transferToAgent({ + name: 'greeter', + ctx, + }); + }, +}); +class BaseAgent extends voice.Agent { + name; + constructor(options) { + const { name, ...opts } = options; + super(opts); + this.name = name; + } + async onEnter() { + const userdata = this.session.userData; + const chatCtx = this.chatCtx.copy(); + // add the previous agent's chat history to the current agent + if (userdata.prevAgent) { + const truncatedChatCtx = userdata.prevAgent.chatCtx + .copy({ + excludeInstructions: true, + excludeFunctionCall: false, + }) + .truncate(6); + const existingIds = new Set(chatCtx.items.map((item) => item.id)); + const newItems = truncatedChatCtx.items.filter((item) => !existingIds.has(item.id)); + chatCtx.items.push(...newItems); + } + // add an instructions including the user data as system message + chatCtx.addMessage({ + role: 'system', + content: `You are ${this.name} agent. Current user data is ${summarize(userdata)}`, + }); + await this.updateChatCtx(chatCtx); + this.session.generateReply({ toolChoice: 'none' }); + } + async transferToAgent(options) { + const { name, ctx } = options; + const userdata = ctx.userData; + const currentAgent = ctx.session.currentAgent; + const nextAgent = userdata.agents[name]; + if (!nextAgent) { + throw new Error(`Agent ${name} not found`); + } + userdata.prevAgent = currentAgent; + return llm.handoff({ + agent: nextAgent, + returns: `Transferring to ${name}`, + }); + } +} +function createGreeterAgent(menu) { + const greeter = new BaseAgent({ + name: 'greeter', + instructions: `You are a friendly restaurant receptionist. The menu is: ${menu}\nYour jobs are to greet the caller and understand if they want to make a reservation or order takeaway. Guide them to the right agent using tools.`, + // TODO(brian): support parallel tool calls + tts: new elevenlabs.TTS({ voice: voices.greeter }), + tools: { + toReservation: llm.tool({ + description: `Called when user wants to make or update a reservation. + This function handles transitioning to the reservation agent + who will collect the necessary details like reservation time, + customer name and phone number.`, + execute: async (_, { ctx }) => { + return await greeter.transferToAgent({ + name: 'reservation', + ctx, + }); + }, + }), + toTakeaway: llm.tool({ + description: `Called when the user wants to place a takeaway order. + This includes handling orders for pickup, delivery, or when the user wants to + proceed to checkout with their existing order.`, + execute: async (_, { ctx }) => { + return await greeter.transferToAgent({ + name: 'takeaway', + ctx, + }); + }, + }), + }, + }); + return greeter; +} +function createReservationAgent() { + const reservation = new BaseAgent({ + name: 'reservation', + instructions: `You are a reservation agent at a restaurant. Your jobs are to ask for the reservation time, then customer's name, and phone number. Then confirm the reservation details with the customer.`, + tts: new elevenlabs.TTS({ voice: voices.reservation }), + tools: { + updateName, + updatePhone, + toGreeter, + updateReservationTime: llm.tool({ + description: `Called when the user provides their reservation time. + Confirm the time with the user before calling the function.`, + parameters: z.object({ + time: z.string().describe('The reservation time'), + }), + execute: async ({ time }, { ctx }) => { + ctx.userData.reservationTime = time; + return `The reservation time is updated to ${time}`; + }, + }), + confirmReservation: llm.tool({ + description: `Called when the user confirms the reservation.`, + execute: async (_, { ctx }) => { + const userdata = ctx.userData; + if (!userdata.customer.name || !userdata.customer.phone) { + return 'Please provide your name and phone number first.'; + } + if (!userdata.reservationTime) { + return 'Please provide reservation time first.'; + } + return await reservation.transferToAgent({ + name: 'greeter', + ctx, + }); + }, + }), + }, + }); + return reservation; +} +function createTakeawayAgent(menu) { + const takeaway = new BaseAgent({ + name: 'takeaway', + instructions: `Your are a takeaway agent that takes orders from the customer. Our menu is: ${menu}\nClarify special requests and confirm the order with the customer.`, + tts: new elevenlabs.TTS({ voice: voices.takeaway }), + tools: { + toGreeter, + updateOrder: llm.tool({ + description: `Called when the user provides their order.`, + parameters: z.object({ + items: z.array(z.string()).describe('The items of the full order'), + }), + execute: async ({ items }, { ctx }) => { + ctx.userData.order = items; + return `The order is updated to ${items}`; + }, + }), + toCheckout: llm.tool({ + description: `Called when the user confirms the order.`, + execute: async (_, { ctx }) => { + const userdata = ctx.userData; + if (!userdata.order) { + return 'No takeaway order found. Please make an order first.'; + } + return await takeaway.transferToAgent({ + name: 'checkout', + ctx, + }); + }, + }), + }, + }); + return takeaway; +} +function createCheckoutAgent(menu) { + const checkout = new BaseAgent({ + name: 'checkout', + instructions: `You are a checkout agent at a restaurant. The menu is: ${menu}\nYour are responsible for confirming the expense of the order and then collecting customer's name, phone number and credit card information, including the card number, expiry date, and CVV step by step.`, + tts: new elevenlabs.TTS({ voice: voices.checkout }), + tools: { + updateName, + updatePhone, + toGreeter, + confirmExpense: llm.tool({ + description: `Called when the user confirms the expense.`, + parameters: z.object({ + expense: z.number().describe('The expense of the order'), + }), + execute: async ({ expense }, { ctx }) => { + ctx.userData.expense = expense; + return `The expense is confirmed to be ${expense}`; + }, + }), + updateCreditCard: llm.tool({ + description: `Called when the user provides their credit card number, expiry date, and CVV. + Confirm the spelling with the user before calling the function.`, + parameters: z.object({ + number: z.string().describe('The credit card number'), + expiry: z.string().describe('The expiry date of the credit card'), + cvv: z.string().describe('The CVV of the credit card'), + }), + execute: async ({ number, expiry, cvv }, { ctx }) => { + ctx.userData.creditCard = { number, expiry, cvv }; + return `The credit card number is updated to ${number}`; + }, + }), + confirmCheckout: llm.tool({ + description: `Called when the user confirms the checkout.`, + execute: async (_, { ctx }) => { + const userdata = ctx.userData; + if (!userdata.expense) { + return 'Please confirm the expense first.'; + } + if (!userdata.creditCard.number || + !userdata.creditCard.expiry || + !userdata.creditCard.cvv) { + return 'Please provide the credit card information first.'; + } + userdata.checkedOut = true; + return await checkout.transferToAgent({ + name: 'greeter', + ctx, + }); + }, + }), + toTakeaway: llm.tool({ + description: `Called when the user wants to update their order.`, + execute: async (_, { ctx }) => { + return await checkout.transferToAgent({ + name: 'takeaway', + ctx, + }); + }, + }), + }, + }); + return checkout; +} +export default defineAgent({ + prewarm: async (proc) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx) => { + const menu = 'Pizza: $10, Salad: $5, Ice Cream: $3, Coffee: $2'; + const userData = createUserData({ + greeter: createGreeterAgent(menu), + reservation: createReservationAgent(), + takeaway: createTakeawayAgent(menu), + checkout: createCheckoutAgent(menu), + }); + const vad = ctx.proc.userData.vad; + const session = new voice.AgentSession({ + vad, + stt: new deepgram.STT(), + tts: new elevenlabs.TTS(), + llm: new openai.LLM(), + // to use realtime model, replace the stt, llm, tts and vad with the following + // llm: new openai.realtime.RealtimeModel(), + turnDetection: new livekit.turnDetector.EnglishModel(), + userData, + voiceOptions: { + maxToolSteps: 5, + }, + }); + await session.start({ + agent: userData.agents.greeter, + room: ctx.room, + }); + }, +}); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); +//# sourceMappingURL=restaurant_agent.js.map \ No newline at end of file diff --git a/examples/src/restaurant_agent.js.map b/examples/src/restaurant_agent.js.map new file mode 100644 index 000000000..9a1db5e42 --- /dev/null +++ b/examples/src/restaurant_agent.js.map @@ -0,0 +1 @@ +{"version":3,"file":"restaurant_agent.js","sourceRoot":"","sources":["restaurant_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/scripts/copyDeclarationOutput.js b/scripts/copyDeclarationOutput.js index 212b1355e..0f44f3b8e 100644 --- a/scripts/copyDeclarationOutput.js +++ b/scripts/copyDeclarationOutput.js @@ -27,4 +27,4 @@ walkDir('dist', (filePath) => { fs.copyFileSync(filePath, newPath); } }); -console.log('copied declaration .d.ts files to .d.cts files'); \ No newline at end of file +console.log('copied declaration .d.ts files to .d.cts files'); From 9ab554676bf09ebe3d3d4501950badd46edd93cc Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:11:28 +0100 Subject: [PATCH 19/27] update --- examples/src/anam_realtime_agent.js | 48 -- examples/src/anam_realtime_agent.js.map | 1 - examples/src/basic_agent.js | 46 -- examples/src/basic_agent.js.map | 1 - examples/src/basic_eou.js | 38 - examples/src/basic_eou.js.map | 1 - examples/src/basic_tool_call_agent.js | 127 ---- examples/src/basic_tool_call_agent.js.map | 1 - examples/src/cartersia_tts.js | 46 -- examples/src/cartersia_tts.js.map | 1 - examples/src/comprehensive_test.js | 210 ------ examples/src/comprehensive_test.js.map | 1 - examples/src/drive-thru/database.js | 659 ------------------ examples/src/drive-thru/database.js.map | 1 - examples/src/drive-thru/drivethru_agent.js | 294 -------- .../src/drive-thru/drivethru_agent.js.map | 1 - examples/src/drive-thru/order.js | 47 -- examples/src/drive-thru/order.js.map | 1 - examples/src/frontdesk/calendar_api.js | 213 ------ examples/src/frontdesk/calendar_api.js.map | 1 - examples/src/frontdesk/calendar_api.test.js | 367 ---------- .../src/frontdesk/calendar_api.test.js.map | 1 - .../frontdesk/calendar_integration.test.js | 35 - .../calendar_integration.test.js.map | 1 - examples/src/frontdesk/frontdesk_agent.js | 206 ------ examples/src/frontdesk/frontdesk_agent.js.map | 1 - examples/src/gemini_realtime_agent.js | 90 --- examples/src/gemini_realtime_agent.js.map | 1 - examples/src/multi_agent.js | 76 -- examples/src/multi_agent.js.map | 1 - examples/src/push_to_talk.js | 53 -- examples/src/push_to_talk.js.map | 1 - examples/src/raw_function_description.js | 67 -- examples/src/raw_function_description.js.map | 1 - examples/src/realtime_agent.js | 59 -- examples/src/realtime_agent.js.map | 1 - examples/src/realtime_turn_detector.js | 40 -- examples/src/realtime_turn_detector.js.map | 1 - examples/src/restaurant_agent.js | 338 --------- examples/src/restaurant_agent.js.map | 1 - plugins/anam/src/api.ts | 57 +- plugins/anam/src/avatar.ts | 84 +-- plugins/anam/src/index.ts | 3 +- plugins/anam/src/types.ts | 18 +- scripts/copyDeclarationOutput.js | 30 - 45 files changed, 96 insertions(+), 3175 deletions(-) delete mode 100644 examples/src/anam_realtime_agent.js delete mode 100644 examples/src/anam_realtime_agent.js.map delete mode 100644 examples/src/basic_agent.js delete mode 100644 examples/src/basic_agent.js.map delete mode 100644 examples/src/basic_eou.js delete mode 100644 examples/src/basic_eou.js.map delete mode 100644 examples/src/basic_tool_call_agent.js delete mode 100644 examples/src/basic_tool_call_agent.js.map delete mode 100644 examples/src/cartersia_tts.js delete mode 100644 examples/src/cartersia_tts.js.map delete mode 100644 examples/src/comprehensive_test.js delete mode 100644 examples/src/comprehensive_test.js.map delete mode 100644 examples/src/drive-thru/database.js delete mode 100644 examples/src/drive-thru/database.js.map delete mode 100644 examples/src/drive-thru/drivethru_agent.js delete mode 100644 examples/src/drive-thru/drivethru_agent.js.map delete mode 100644 examples/src/drive-thru/order.js delete mode 100644 examples/src/drive-thru/order.js.map delete mode 100644 examples/src/frontdesk/calendar_api.js delete mode 100644 examples/src/frontdesk/calendar_api.js.map delete mode 100644 examples/src/frontdesk/calendar_api.test.js delete mode 100644 examples/src/frontdesk/calendar_api.test.js.map delete mode 100644 examples/src/frontdesk/calendar_integration.test.js delete mode 100644 examples/src/frontdesk/calendar_integration.test.js.map delete mode 100644 examples/src/frontdesk/frontdesk_agent.js delete mode 100644 examples/src/frontdesk/frontdesk_agent.js.map delete mode 100644 examples/src/gemini_realtime_agent.js delete mode 100644 examples/src/gemini_realtime_agent.js.map delete mode 100644 examples/src/multi_agent.js delete mode 100644 examples/src/multi_agent.js.map delete mode 100644 examples/src/push_to_talk.js delete mode 100644 examples/src/push_to_talk.js.map delete mode 100644 examples/src/raw_function_description.js delete mode 100644 examples/src/raw_function_description.js.map delete mode 100644 examples/src/realtime_agent.js delete mode 100644 examples/src/realtime_agent.js.map delete mode 100644 examples/src/realtime_turn_detector.js delete mode 100644 examples/src/realtime_turn_detector.js.map delete mode 100644 examples/src/restaurant_agent.js delete mode 100644 examples/src/restaurant_agent.js.map delete mode 100644 scripts/copyDeclarationOutput.js diff --git a/examples/src/anam_realtime_agent.js b/examples/src/anam_realtime_agent.js deleted file mode 100644 index d9ab3e7eb..000000000 --- a/examples/src/anam_realtime_agent.js +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, voice, } from '@livekit/agents'; -import * as anam from '@livekit/agents-plugin-anam'; -import * as openai from '@livekit/agents-plugin-openai'; -import { fileURLToPath } from 'node:url'; -// Uses OpenAI Advanced Voice (Realtime), so no separate STT/TTS/VAD. -export default defineAgent({ - entry: async (ctx) => { - const agent = new voice.Agent({ - instructions: "You are a helpful assistant. Speak clearly and concisely.", - }); - const session = new voice.AgentSession({ - llm: new openai.realtime.RealtimeModel(), - voiceOptions: { - // allow the model to call multiple tools in a single turn if needed - maxToolSteps: 3, - }, - }); - await session.start({ - agent, - room: ctx.room, - }); - // Join the LiveKit room first (ensures room name and identity available) - await ctx.connect(); - // Configure the Anam avatar persona (requires avatarId) - const personaName = process.env.ANAM_PERSONA_NAME ?? 'Agent'; - const avatarId = process.env.ANAM_AVATAR_ID; - if (!avatarId) { - throw new Error('ANAM_AVATAR_ID is required'); - } - // Start the Anam avatar session and route Agent audio to the avatar - const avatar = new anam.AvatarSession({ - personaConfig: { name: personaName, avatarId }, - // Allow overriding base URL via env - apiUrl: process.env.ANAM_API_URL, - // connOptions: { maxRetry: 5, retryInterval: 2, timeout: 15 }, - }); - await avatar.start(session, ctx.room); - // With Realtime LLM, generateReply will synthesize audio via the model - session.generateReply({ - instructions: 'Greet the user briefly and confirm you are ready.', - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=anam_realtime_agent.js.map \ No newline at end of file diff --git a/examples/src/anam_realtime_agent.js.map b/examples/src/anam_realtime_agent.js.map deleted file mode 100644 index 99cf8724a..000000000 --- a/examples/src/anam_realtime_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"anam_realtime_agent.js","sourceRoot":"","sources":["anam_realtime_agent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAEL,aAAa,EACb,GAAG,EACH,WAAW,EACX,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,6BAA6B,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,qEAAqE;AAErE,eAAe,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,2DAA2D;SAC9D,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE;YACxC,YAAY,EAAE;gBACZ,oEAAoE;gBACpE,YAAY,EAAE,CAAC;aAChB;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,yEAAyE;QACzE,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QAEpB,wDAAwD;QACxD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC;QAC7D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,oEAAoE;QACpE,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC;YACpC,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE;YAC9C,oCAAoC;YACpC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;YAChC,+DAA+D;SAChE,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAEtC,uEAAuE;QACvE,OAAO,CAAC,aAAa,CAAC;YACpB,YAAY,EAAE,mDAAmD;SAClE,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/basic_agent.js b/examples/src/basic_agent.js deleted file mode 100644 index cec51a276..000000000 --- a/examples/src/basic_agent.js +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, metrics, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; -import { fileURLToPath } from 'node:url'; -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const agent = new voice.Agent({ - instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", - }); - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - stt: new deepgram.STT(), - tts: new elevenlabs.TTS(), - llm: new openai.LLM(), - // to use realtime model, replace the stt, llm, tts and vad with the following - // llm: new openai.realtime.RealtimeModel(), - turnDetection: new livekit.turnDetector.MultilingualModel(), - }); - const usageCollector = new metrics.UsageCollector(); - session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { - metrics.logMetrics(ev.metrics); - usageCollector.collect(ev.metrics); - }); - await session.start({ - agent, - room: ctx.room, - inputOptions: { - noiseCancellation: BackgroundVoiceCancellation(), - }, - }); - session.say('Hello, how can I help you today?'); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=basic_agent.js.map \ No newline at end of file diff --git a/examples/src/basic_agent.js.map b/examples/src/basic_agent.js.map deleted file mode 100644 index cde90d353..000000000 --- a/examples/src/basic_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"basic_agent.js","sourceRoot":"","sources":["basic_agent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,OAAO,EACP,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,iFAAiF;SACpF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB,CAAC;QAEjD,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG;YACH,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,8EAA8E;YAC9E,4CAA4C;YAC5C,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE;SAC5D,CAAC,CAAC;QAEH,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAEpD,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YAC/D,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAC/B,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,YAAY,EAAE;gBACZ,iBAAiB,EAAE,2BAA2B,EAAE;aACjD;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/basic_eou.js b/examples/src/basic_eou.js deleted file mode 100644 index fe2e85b83..000000000 --- a/examples/src/basic_eou.js +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, log } from '@livekit/agents'; -import { turnDetector } from '@livekit/agents-plugin-livekit'; -import { fileURLToPath } from 'node:url'; -export default defineAgent({ - entry: async (ctx) => { - const logger = log(); - // Manual connection required since this example doesn't use AgentSession - await ctx.connect(); - // const eouModel = new turnDetector.EnglishModel(); - const eouModel = new turnDetector.MultilingualModel(); - const unlikelyThreshold = await eouModel.unlikelyThreshold('en'); - logger.info({ unlikelyThreshold }, 'unlikelyThreshold'); - const chatCtx = llm.ChatContext.empty(); - chatCtx.addMessage({ - role: 'assistant', - content: 'Hi, how can I help you today?', - }); - const nonEndingTurn = chatCtx.copy(); - nonEndingTurn.addMessage({ - role: 'user', - content: 'What is the weather in', - }); - const nonEndingTurnResult = await eouModel.predictEndOfTurn(nonEndingTurn); - logger.info({ nonEndingTurnResult }, 'nonEndingTurnResult'); - const endingTurn = chatCtx.copy(); - endingTurn.addMessage({ - role: 'user', - content: 'What is the weather in San Francisco?', - }); - const endingTurnResult = await eouModel.predictEndOfTurn(endingTurn); - logger.info({ endingTurnResult }, 'endingTurnResult'); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=basic_eou.js.map \ No newline at end of file diff --git a/examples/src/basic_eou.js.map b/examples/src/basic_eou.js.map deleted file mode 100644 index fb5475a99..000000000 --- a/examples/src/basic_eou.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"basic_eou.js","sourceRoot":"","sources":["basic_eou.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAAmB,aAAa,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC;QAErB,yEAAyE;QACzE,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QAEpB,oDAAoD;QACpD,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,iBAAiB,EAAE,CAAC;QAEtD,MAAM,iBAAiB,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,EAAE,iBAAiB,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAExD,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACxC,OAAO,CAAC,UAAU,CAAC;YACjB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,+BAA+B;SACzC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QACrC,aAAa,CAAC,UAAU,CAAC;YACvB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,wBAAwB;SAClC,CAAC,CAAC;QAEH,MAAM,mBAAmB,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,CAAC,EAAE,mBAAmB,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAClC,UAAU,CAAC,UAAU,CAAC;YACpB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,uCAAuC;SACjD,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACxD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/basic_tool_call_agent.js b/examples/src/basic_tool_call_agent.js deleted file mode 100644 index bba80a55b..000000000 --- a/examples/src/basic_tool_call_agent.js +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as google from '@livekit/agents-plugin-google'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -const roomNameSchema = z.enum(['bedroom', 'living room', 'kitchen', 'bathroom', 'office']); -class RouterAgent extends voice.Agent { - async onEnter() { - this.session.say("Hello, I'm a router agent. I can help you with your tasks."); - } -} -class GameAgent extends voice.Agent { - async onEnter() { - this.session.generateReply({ - userInput: 'Ask the user for a number, then check the stored number', - toolChoice: 'none', - }); - } -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const getWeather = llm.tool({ - description: ' Called when the user asks about the weather.', - parameters: z.object({ - location: z.string().describe('The location to get the weather for'), - }), - execute: async ({ location }, { ctx }) => { - ctx.session.say('Checking the weather, please wait a moment haha...'); - return `The weather in ${location} is sunny today.`; - }, - }); - const toggleLight = llm.tool({ - description: 'Called when the user asks to turn on or off the light.', - parameters: z.object({ - room: roomNameSchema.describe('The room to turn the light in'), - switchTo: z.enum(['on', 'off']).describe('The state to turn the light to'), - }), - execute: async ({ room, switchTo }, { ctx }) => { - ctx.session.generateReply({ - userInput: 'Tell user wait a moment for about 10 seconds', - }); - return `The light in the ${room} is now ${switchTo}.`; - }, - }); - const getNumber = llm.tool({ - description: 'Called when the user wants to get a number value, None if user want a random value', - parameters: z.object({ - value: z.number().nullable().describe('The number value'), - }), - execute: async ({ value }) => { - if (value === null) { - value = Math.floor(Math.random() * 100); - } - return `The number value is ${value}.`; - }, - }); - const checkStoredNumber = llm.tool({ - description: 'Called when the user wants to check the stored number.', - execute: async (_, { ctx }) => { - return `The stored number is ${ctx.userData.number}.`; - }, - }); - const updateStoredNumber = llm.tool({ - description: 'Called when the user wants to update the stored number.', - parameters: z.object({ - number: z.number().describe('The number to update the stored number to'), - }), - execute: async ({ number }, { ctx }) => { - ctx.userData.number = number; - return `The stored number is now ${number}.`; - }, - }); - const routerAgent = new RouterAgent({ - instructions: 'You are a helpful assistant.', - tools: { - getWeather, - toggleLight, - playGame: llm.tool({ - description: 'Called when the user wants to play a game (transfer user to a game agent).', - execute: async () => { - return llm.handoff({ agent: gameAgent, returns: 'The game is now playing.' }); - }, - }), - }, - }); - const gameAgent = new GameAgent({ - instructions: 'You are a game agent. You are playing a game with the user.', - tools: { - getNumber, - checkStoredNumber, - updateStoredNumber, - finishGame: llm.tool({ - description: 'Called when the user wants to finish the game.', - execute: async () => { - return llm.handoff({ agent: routerAgent, returns: 'The game is now finished.' }); - }, - }), - }, - }); - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - stt: new deepgram.STT(), - tts: new elevenlabs.TTS(), - llm: new google.LLM(), - // to use realtime model, replace the stt, llm, tts and vad with the following - // llm: new openai.realtime.RealtimeModel(), - userData: { number: 0 }, - turnDetection: new livekit.turnDetector.EnglishModel(), - }); - await session.start({ - agent: routerAgent, - room: ctx.room, - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=basic_tool_call_agent.js.map \ No newline at end of file diff --git a/examples/src/basic_tool_call_agent.js.map b/examples/src/basic_tool_call_agent.js.map deleted file mode 100644 index ae039aa9e..000000000 --- a/examples/src/basic_tool_call_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"basic_tool_call_agent.js","sourceRoot":"","sources":["basic_tool_call_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/cartersia_tts.js b/examples/src/cartersia_tts.js deleted file mode 100644 index 9812e04aa..000000000 --- a/examples/src/cartersia_tts.js +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, metrics, voice, } from '@livekit/agents'; -import * as cartesia from '@livekit/agents-plugin-cartesia'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; -import { fileURLToPath } from 'node:url'; -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const agent = new voice.Agent({ - instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", - }); - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - stt: new deepgram.STT(), - tts: new cartesia.TTS(), - llm: new openai.LLM(), - // to use realtime model, replace the stt, llm, tts and vad with the following - // llm: new openai.realtime.RealtimeModel(), - turnDetection: new livekit.turnDetector.MultilingualModel(), - }); - const usageCollector = new metrics.UsageCollector(); - session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { - metrics.logMetrics(ev.metrics); - usageCollector.collect(ev.metrics); - }); - await session.start({ - agent, - room: ctx.room, - inputOptions: { - noiseCancellation: BackgroundVoiceCancellation(), - }, - }); - session.say('Hello, how can I help you today?'); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=cartersia_tts.js.map \ No newline at end of file diff --git a/examples/src/cartersia_tts.js.map b/examples/src/cartersia_tts.js.map deleted file mode 100644 index 780907662..000000000 --- a/examples/src/cartersia_tts.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cartersia_tts.js","sourceRoot":"","sources":["cartersia_tts.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,OAAO,EACP,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,iFAAiF;SACpF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB,CAAC;QAEjD,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG;YACH,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,8EAA8E;YAC9E,4CAA4C;YAC5C,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE;SAC5D,CAAC,CAAC;QAEH,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAEpD,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YAC/D,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAC/B,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,YAAY,EAAE;gBACZ,iBAAiB,EAAE,2BAA2B,EAAE;aACjD;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/comprehensive_test.js b/examples/src/comprehensive_test.js deleted file mode 100644 index 1eaa47862..000000000 --- a/examples/src/comprehensive_test.js +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, metrics, voice, } from '@livekit/agents'; -import * as cartesia from '@livekit/agents-plugin-cartesia'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as google from '@livekit/agents-plugin-google'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as neuphonic from '@livekit/agents-plugin-neuphonic'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as resemble from '@livekit/agents-plugin-resemble'; -import * as silero from '@livekit/agents-plugin-silero'; -import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -const sttOptions = { - deepgram: () => new deepgram.STT(), -}; -const ttsOptions = { - cartesia: () => new cartesia.TTS(), - elevenlabs: () => new elevenlabs.TTS(), - openai: () => new openai.TTS(), - gemini: () => new google.beta.TTS(), - neuphonic: () => new neuphonic.TTS(), - resemble: () => new resemble.TTS(), -}; -const eouOptions = { - english: () => new livekit.turnDetector.EnglishModel(), - multilingual: () => new livekit.turnDetector.MultilingualModel(), -}; -const llmOptions = { - openai: () => new openai.LLM(), - gemini: () => new google.LLM(), -}; -const realtimeLlmOptions = { - openai: () => new openai.realtime.RealtimeModel(), - gemini: () => new google.beta.realtime.RealtimeModel(), -}; -const sttChoices = Object.keys(sttOptions); -const ttsChoices = Object.keys(ttsOptions); -const eouChoices = Object.keys(eouOptions); -const llmChoices = Object.keys(llmOptions); -const realtimeLlmChoices = Object.keys(realtimeLlmOptions); -class MainAgent extends voice.Agent { - constructor() { - super({ - instructions: `You are a main route agent, you can test the agent's ability to switch between different agents`, - stt: sttOptions['deepgram'](), - tts: ttsOptions['elevenlabs'](), - llm: llmOptions['openai'](), - turnDetection: eouOptions['multilingual'](), - tools: { - testAgent: llm.tool({ - description: 'Called when user want to test an agent with STT, TTS, EOU, LLM, and optionally realtime LLM configuration', - parameters: z.object({ - sttChoice: z.enum(sttChoices), - ttsChoice: z.enum(ttsChoices), - eouChoice: z.enum(eouChoices), - llmChoice: z.enum(llmChoices), - realtimeLlmChoice: z.enum(realtimeLlmChoices).nullable(), - }), - execute: async (params) => { - const { sttChoice, ttsChoice, eouChoice, llmChoice, realtimeLlmChoice } = params; - return llm.handoff({ - agent: new TestAgent({ - sttChoice: sttChoice, - ttsChoice: ttsChoice, - eouChoice: eouChoice, - llmChoice: llmChoice, - realtimeLlmChoice: realtimeLlmChoice, - }), - returns: 'Transfer to next agent', - }); - }, - }), - }, - }); - } - async onEnter() { - if (this.llm instanceof llm.RealtimeModel) { - this.session.generateReply({ - userInput: `Tell user that you are main route agent, you can test the agent's ability to switch between different agents`, - }); - } - else { - this.session.say(`Hi, I'm a main route agent, you can test the agent's ability to switch between different agents`); - } - } -} -class TestAgent extends voice.Agent { - sttChoice; - ttsChoice; - eouChoice; - llmChoice; - realtimeLlmChoice; - constructor({ sttChoice, ttsChoice, eouChoice, llmChoice, realtimeLlmChoice, }) { - const stt = sttOptions[sttChoice](); - const tts = ttsOptions[ttsChoice](); - const eou = eouOptions[eouChoice](); - const model = llmOptions[llmChoice](); - const realtimeModel = realtimeLlmChoice ? realtimeLlmOptions[realtimeLlmChoice]() : undefined; - const modelName = realtimeModel ? `${realtimeLlmChoice} realtime` : llmChoice; - super({ - instructions: `You are a test voice-based agent, you can hear the user's message and respond to it. User is testing your hearing & speaking abilities. - You are using ${sttChoice} STT, ${ttsChoice} TTS, ${eouChoice} EOU, ${modelName} LLM. - You can use the following tools to test your abilities: - - testTool: Testing agent's tool calling ability - - nextAgent: Called when user confirm current agent is working and want to proceed to next agent`, - stt: stt, - tts: tts, - llm: realtimeModel ?? model, - turnDetection: eou, - tools: { - testTool: llm.tool({ - description: "Testing agent's tool calling ability", - parameters: z - .object({ - randomString: z.string().describe('A random string'), - }) - .describe('Test parameter'), - execute: async (input) => { - return { - result: 'Tool been called with input: ' + JSON.stringify(input), - }; - }, - }), - nextAgent: llm.tool({ - description: 'Called when user confirm current agent is working and want to proceed to next agent', - parameters: z.object({ - sttChoice: z.enum(sttChoices).optional(), - ttsChoice: z.enum(ttsChoices).optional(), - eouChoice: z.enum(eouChoices).optional(), - llmChoice: z.enum(llmChoices).optional(), - realtimeLlmChoice: z.enum(realtimeLlmChoices).optional(), - }), - execute: async (params) => { - const { sttChoice = this.sttChoice, ttsChoice = this.ttsChoice, eouChoice = this.eouChoice, llmChoice = this.llmChoice, realtimeLlmChoice = this.realtimeLlmChoice, } = params; - return llm.handoff({ - agent: new TestAgent({ - sttChoice: sttChoice, - ttsChoice: ttsChoice, - eouChoice: eouChoice, - llmChoice: llmChoice, - realtimeLlmChoice: realtimeLlmChoice, - }), - returns: 'Transfer to next agent', - }); - }, - }), - }, - }); - this.sttChoice = sttChoice; - this.ttsChoice = ttsChoice; - this.eouChoice = eouChoice; - this.llmChoice = llmChoice; - this.realtimeLlmChoice = realtimeLlmChoice; - } - async onEnter() { - if (this.llm instanceof llm.RealtimeModel) { - this.session.generateReply({ - userInput: `Tell user that you are voice agent with ${this.sttChoice} STT, ${this.ttsChoice} TTS, ${this.eouChoice} EOU, ${this.llmChoice} LLM`, - }); - } - else { - this.session.say(`Hi, I'm a voice agent with ${this.sttChoice} STT, ${this.ttsChoice} TTS, ${this.eouChoice} EOU, ${this.llmChoice} LLM. I'm ready to test your hearing & speaking abilities.`); - } - setTimeout(() => { - if (this.session.currentAgent !== this) - return; - this.session.interrupt(); - this.session.generateReply({ - userInput: 'Tell user that this test is over, going back to main agent', - }); - this.session.updateAgent(new MainAgent()); - }, 45 * 1000); - } -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - userData: { - testedSttChoices: new Set(), - testedTtsChoices: new Set(), - testedEouChoices: new Set(), - testedLlmChoices: new Set(), - testedRealtimeLlmChoices: new Set(), - }, - }); - const usageCollector = new metrics.UsageCollector(); - session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { - metrics.logMetrics(ev.metrics); - usageCollector.collect(ev.metrics); - }); - await session.start({ - agent: new MainAgent(), - room: ctx.room, - inputOptions: { - noiseCancellation: BackgroundVoiceCancellation(), - }, - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=comprehensive_test.js.map \ No newline at end of file diff --git a/examples/src/comprehensive_test.js.map b/examples/src/comprehensive_test.js.map deleted file mode 100644 index 006bc5f0d..000000000 --- a/examples/src/comprehensive_test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"comprehensive_test.js","sourceRoot":"","sources":["comprehensive_test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/drive-thru/database.js b/examples/src/drive-thru/database.js deleted file mode 100644 index 413d423ca..000000000 --- a/examples/src/drive-thru/database.js +++ /dev/null @@ -1,659 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -export const COMMON_INSTRUCTIONS = `You are Kelly, a quick and friendly McDonald's drive-thru attendant. -Your job is to guide the customer smoothly through their order, speaking in short, natural voice responses. -This is a voice interaction-assume the customer just pulled up and is speaking to you through a drive-thru speaker. -Respond like you're hearing them, not reading text. -Assume they want food, even if they don't start with a clear request, and help them get what they're looking for. - -If an item comes in different sizes, always ask for the size unless the customer already gave one. -If a customer orders a 'large meal', automatically assume both the fries and the drink should be large. -Do not ask again to confirm the size of the drink or fries. This inference is meant to streamline the interaction. -If the customer clearly indicates a different size for the fries or drink, respect their preference. - -Be fast-keep responses short and snappy. -Sound human-sprinkle in light vocal pauses like 'Mmh…', 'Let me see…', or 'Alright…' at natural moments-but not too often. -Keep everything upbeat and easy to follow. Never overwhelm the customer, don't ask multiple questions at the same time. - -When a customer is confused or asks for something that doesn't exist, let them know politely and suggest something close. -Always confirm what they picked in a warm, clear way, like: 'Alright, one Big Mac Combo!' -If something's unavailable, say so with empathy: 'Ah, we're out of Sweet Tea right now-can I get you a Coke instead?' - -Whenever a customer asks for, changes, or removes something from their order, you MUST use a tool to make it happen. -Don't fake it. Don't pretend something was added - actually **call** the tool and make it real on the ordering system. - -Transcripts often contain speech-to-text errors-don't mention the transcript, don't repeat its mistakes. -Instead treat each user input as a rough draft of what was said. -If you can guess the user's intent and it's safe to do so, infer their meaning and respond naturally. -If the transcript is ambiguous/nonsense and you can't guess their intent, ask the customer to repeat again. -Stay on-topic; if input is nonsensical in a drive-thru context, ask for concise clarification. - -Do not add any item on the user's behalf unless they specifically request it. If the user hasn't asked for an item, NEVER add it. - -When a customer changes an item or meal, make sure to remove the previous version before adding the new one. -Otherwise, the order may contain duplicates. - -Strictly stick to the defined menu, Do not invent or suggest any new sizes or items. -If the item specified by the user is unclear or not **exactly** on the menu, ask for clarification or say you don't have this specific item -E.g: an hamburger isn't a cheeseburger -Do not ask for size unless the item has more than one size option specified. -If an item does not require a size according to the menu, **NEVER** ask the customer to choose one or mention size at all.`; -export class FakeDB { - async listDrinks() { - const drinkData = [ - { - id: 'coca_cola', - name: 'Coca-Cola®', - sizes: { - S: { calories: 200, price: 1.49 }, - M: { calories: 270, price: 1.69 }, - L: { calories: 380, price: 1.89 }, - }, - }, - { - id: 'sprite', - name: 'Sprite®', - sizes: { - S: { calories: 190, price: 1.49 }, - M: { calories: 250, price: 1.69 }, - L: { calories: 350, price: 1.89 }, - }, - }, - { - id: 'diet_coke', - name: 'Diet Coke®', - sizes: { - S: { calories: 0, price: 1.49 }, - M: { calories: 0, price: 1.69 }, - L: { calories: 0, price: 1.89 }, - }, - }, - { - id: 'dr_pepper', - name: 'Dr Pepper®', - sizes: { - S: { calories: 200, price: 1.49 }, - M: { calories: 270, price: 1.69 }, - L: { calories: 380, price: 1.89 }, - }, - }, - { - id: 'fanta_orange', - name: 'Fanta® Orange', - sizes: { - S: { calories: 210, price: 1.49 }, - M: { calories: 280, price: 1.69 }, - L: { calories: 390, price: 1.89 }, - }, - }, - { - id: 'hi_c_orange_lavaburst', - name: 'Hi-C® Orange Lavaburst®', - sizes: { - S: { calories: 210, price: 1.49 }, - M: { calories: 280, price: 1.69 }, - L: { calories: 390, price: 1.89 }, - }, - }, - { - id: 'sweet_tea', - name: 'Sweet Tea', - sizes: { - S: { calories: 140, price: 1.39 }, - M: { calories: 180, price: 1.59 }, - L: { calories: 220, price: 1.79 }, - }, - available: false, - }, - { - id: 'unsweetened_iced_tea', - name: 'Unsweetened Iced Tea', - sizes: { - S: { calories: 0, price: 1.39 }, - M: { calories: 0, price: 1.59 }, - L: { calories: 0, price: 1.79 }, - }, - }, - { - id: 'minute_maid_orange_juice', - name: 'Minute Maid® Premium Orange Juice', - sizes: { - S: { calories: 190, price: 2.59 }, - M: { calories: 240, price: 2.79 }, - L: { calories: 300, price: 2.99 }, - }, - }, - { - id: 'milk', - name: 'Milk', - calories: 100, - price: 1.29, - }, - { - id: 'chocolate_milk', - name: 'Chocolate Milk', - calories: 150, - price: 1.39, - }, - { - id: 'dasani_water', - name: 'DASANI® Water', - calories: 0, - price: 1.59, - }, - ]; - const items = []; - for (const item of drinkData) { - if ('sizes' in item && item.sizes) { - for (const [size, sizeDetails] of Object.entries(item.sizes)) { - items.push({ - id: item.id, - name: item.name, - calories: sizeDetails.calories, - price: sizeDetails.price, - size: size, - available: item.available ?? true, - category: 'drink', - }); - } - } - else { - items.push({ - id: item.id, - name: item.name, - calories: item.calories, - price: item.price, - available: true, - category: 'drink', - }); - } - } - return items; - } - async listComboMeals() { - const rawMeals = [ - { - id: 'combo_big_mac', - name: 'Big Mac® Combo', - alias: '1', - calories: 970, - price: 9.49, - }, - { - id: 'combo_quarter_pounder_2a', - name: 'Quarter Pounder® with Cheese Combo', - alias: '2a', - calories: 840, - price: 9.89, - }, - { - id: 'combo_quarter_pounder_2b', - name: 'Quarter Pounder® with Cheese & Bacon Combo', - alias: '2b', - calories: 950, - price: 10.39, - }, - { - id: 'combo_quarter_pounder_2c', - name: 'Quarter Pounder® Deluxe Combo', - alias: '2c', - calories: 950, - price: 10.39, - }, - { - id: 'combo_double_quarter', - name: 'Double Quarter Pounder® with Cheese Combo', - alias: '3', - calories: 1060, - price: 10.29, - }, - { - id: 'combo_mccrispy_4a', - name: 'McCrispy™ Original Combo', - alias: '4a', - calories: 790, - price: 8.99, - }, - { - id: 'combo_mccrispy_4b', - name: 'McCrispy™ Spicy Combo', - alias: '4b', - calories: 850, - price: 8.99, - }, - { - id: 'combo_mccrispy_4c', - name: 'McCrispy™ Deluxe Combo', - alias: '4c', - calories: 880, - price: 9.89, - }, - { - id: 'combo_mccrispy_4d', - name: 'McCrispy™ Spicy Deluxe Combo', - alias: '4d', - calories: 860, - price: 9.99, - }, - { - id: 'combo_chicken_mcnuggets_10pc', - name: '10 pc. Chicken McNuggets® Combo', - alias: '5', - calories: 740, - price: 9.49, - }, - { - id: 'combo_filet_o_fish', - name: 'Filet-O-Fish® Combo', - alias: '6', - calories: 700, - price: 7.89, - }, - { - id: 'combo_cheeseburgers_2pc', - name: '2 Cheeseburgers Combo', - alias: '7', - calories: 920, - price: 7.89, - }, - ]; - return rawMeals.map((item) => ({ - id: item.id, - name: item.name, - calories: item.calories, - price: item.price, - voiceAlias: item.alias, - category: 'combo_meal', - available: true, - })); - } - async listHappyMeals() { - const rawHappyMeals = [ - { - id: 'happy_meal_4pc_mcnuggets', - name: '4 pc. Chicken McNuggets® Happy Meal', - calories: 430, - price: 5.99, - }, - { - id: 'happy_meal_6pc_mcnuggets', - name: '6 pc. Chicken McNuggets® Happy Meal', - calories: 530, - price: 6.99, - }, - { - id: 'happy_meal_hamburger', - name: 'Hamburger Happy Meal', - calories: 510, - price: 5.59, - }, - ]; - return rawHappyMeals.map((item) => ({ - id: item.id, - name: item.name, - calories: item.calories, - price: item.price, - available: true, - category: 'happy_meal', - })); - } - async listRegulars() { - const rawItems = [ - { - id: 'big_mac', - name: 'Big Mac®', - calories: 590, - price: 5.89, - }, - { - id: 'quarter_pounder_cheese', - name: 'Quarter Pounder® with Cheese', - calories: 520, - price: 6.29, - }, - { - id: 'quarter_pounder_bacon', - name: 'Quarter Pounder® with Cheese & Bacon', - calories: 590, - price: 6.79, - }, - { - id: 'quarter_pounder_deluxe', - name: 'Quarter Pounder® Deluxe', - calories: 530, - price: 6.39, - }, - { - id: 'double_quarter_pounder', - name: 'Double Quarter Pounder® with Cheese', - calories: 740, - price: 7.49, - }, - { - id: 'mccrispy_original', - name: 'McCrispy™ Original', - calories: 470, - price: 5.69, - }, - { - id: 'mccrispy_spicy', - name: 'McCrispy™ Spicy', - calories: 500, - price: 5.69, - }, - { - id: 'mccrispy_deluxe', - name: 'McCrispy™ Deluxe', - calories: 530, - price: 6.39, - }, - { - id: 'mccrispy_spicy_deluxe', - name: 'McCrispy™ Spicy Deluxe', - calories: 530, - price: 6.59, - }, - { - id: 'mcnuggets_10pc', - name: '10 pc. Chicken McNuggets®', - calories: 410, - price: 6.79, - }, - { - id: 'filet_o_fish', - name: 'Filet-O-Fish®', - calories: 390, - price: 5.89, - }, - { - id: 'hamburger', - name: 'Hamburger', - calories: 300, - price: 2.0, - }, - { - id: 'cheeseburger', - name: 'Cheeseburger', - calories: 600, - price: 2.58, - }, - { - id: 'fries', - name: 'Fries', - sizes: { - S: { calories: 230, price: 1.89 }, - M: { calories: 350, price: 3.99 }, - L: { calories: 521, price: 4.75 }, - }, - }, - { - id: 'sweet_sundae', - name: 'Sundae', - calories: 330, - price: 3.69, - }, - { - id: 'sweet_mcflurry_oreo', - name: 'McFlurry® (Oreo)', - calories: 480, - price: 4.89, - }, - { - id: 'shake_vanilla', - name: 'Vanilla Shake', - sizes: { - S: { calories: 510, price: 2.79 }, - M: { calories: 610, price: 3.59 }, - L: { calories: 820, price: 3.89 }, - }, - }, - { - id: 'shake_chocolate', - name: 'Chocolate Shake', - sizes: { - S: { calories: 520, price: 2.79 }, - M: { calories: 620, price: 3.59 }, - L: { calories: 830, price: 3.89 }, - }, - }, - { - id: 'shake_strawberry', - name: 'Strawberry Shake', - sizes: { - S: { calories: 530, price: 2.79 }, - M: { calories: 620, price: 3.59 }, - L: { calories: 840, price: 3.89 }, - }, - }, - { - id: 'sweet_cone', - name: 'Cone', - calories: 200, - price: 3.19, - }, - ]; - const items = []; - for (const item of rawItems) { - if ('sizes' in item && item.sizes) { - for (const [size, sizeDetails] of Object.entries(item.sizes)) { - items.push({ - id: item.id, - name: item.name, - calories: sizeDetails.calories, - price: sizeDetails.price, - size: size, - available: true, - category: 'regular', - }); - } - } - else { - items.push({ - id: item.id, - name: item.name, - calories: item.calories, - price: item.price, - available: true, - category: 'regular', - }); - } - } - return items; - } - async listSauces() { - const rawItems = [ - { - id: 'jalapeno_ranch', - name: 'Jalapeño Ranch', - calories: 70, - price: 0.25, - }, - { - id: 'garlic_sauce', - name: 'Garlic Sauce', - calories: 45, - price: 0.25, - }, - { - id: 'mayonnaise', - name: 'Mayonnaise', - calories: 90, - price: 0.2, - }, - { - id: 'frietsaus', - name: 'Frietsaus', - calories: 100, - price: 0.2, - }, - { - id: 'curry_sauce', - name: 'Curry sauce', - calories: 60, - price: 0.2, - }, - { - id: 'ketchup', - name: 'Ketchup', - calories: 20, - price: 0.1, - }, - { - id: 'barbecue_sauce', - name: 'Barbecue Sauce', - calories: 45, - price: 0.2, - }, - { - id: 'sweet_and_sour_sauce', - name: 'Sweet-and-sour sauce', - calories: 50, - price: 0.4, - }, - { - id: 'honey_mustard_dressing', - name: 'Honey mustard dressing', - calories: 60, - price: 0.2, - }, - ]; - return rawItems.map((item) => ({ - id: item.id, - name: item.name, - calories: item.calories, - price: item.price, - available: true, - category: 'sauce', - })); - } -} -// Helper functions -export function findItemsById(items, itemId, size) { - return items.filter((item) => item.id === itemId && (size === undefined || item.size === size)); -} -export function menuInstructions(category, items) { - switch (category) { - case 'drink': - return drinkMenuInstructions(items); - case 'combo_meal': - return comboMenuInstructions(items); - case 'happy_meal': - return happyMenuInstructions(items); - case 'sauce': - return sauceMenuInstructions(items); - case 'regular': - return regularMenuInstructions(items); - default: - return ''; - } -} -function mapBySizes(items) { - const sized = {}; - const leftovers = []; - for (const item of items) { - if (item.size) { - if (!sized[item.id]) { - sized[item.id] = {}; - } - sized[item.id][item.size] = item; - } - else { - leftovers.push(item); - } - } - return { sized, leftovers }; -} -function drinkMenuInstructions(items) { - const { sized, leftovers } = mapBySizes(items); - const menuLines = []; - for (const [_itemId, sizeMap] of Object.entries(sized)) { - const firstItem = Object.values(sizeMap)[0]; - if (!firstItem) - continue; - menuLines.push(` - ${firstItem.name} (id:${firstItem.id}):`); - for (const [size, item] of Object.entries(sizeMap)) { - let line = ` - Size ${size}: ${item.calories} Cal, $${item.price.toFixed(2)}`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - } - for (const item of leftovers) { - let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id}) - Not size-selectable`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - return '# Drinks:\n' + menuLines.join('\n'); -} -function comboMenuInstructions(items) { - const menuLines = []; - for (const item of items) { - let line = ` **${item.voiceAlias}**. ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id})`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - const instructions = `# Combo Meals: -The user can select a combo meal by saying its voice alias (e.g., '1', '2a', '4c'). Use the alias to identify which combo they chose. -But don't mention the voice alias to the user if not needed.`; - return instructions + '\n' + menuLines.join('\n'); -} -function happyMenuInstructions(items) { - const menuLines = []; - for (const item of items) { - let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id})`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - return `# Happy Meals: -${menuLines.join('\n')} - -Recommended drinks with the Happy Meal: - - Milk chocolate/white - - DASANI Water - - Or any other small drink.`; -} -function sauceMenuInstructions(items) { - const menuLines = []; - for (const item of items) { - let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id})`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - return '# Sauces:\n' + menuLines.join('\n'); -} -function regularMenuInstructions(items) { - const { sized, leftovers } = mapBySizes(items); - const menuLines = []; - for (const [_itemId, sizeMap] of Object.entries(sized)) { - const firstItem = Object.values(sizeMap)[0]; - if (!firstItem) - continue; - menuLines.push(` - ${firstItem.name} (id:${firstItem.id}):`); - for (const [size, item] of Object.entries(sizeMap)) { - let line = ` - Size ${size}: ${item.calories} Cal, $${item.price.toFixed(2)}`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - } - for (const item of leftovers) { - let line = ` - ${item.name}: ${item.calories} Cal, $${item.price.toFixed(2)} (id:${item.id}) - Not size-selectable`; - if (!item.available) { - line += ' UNAVAILABLE'; - } - menuLines.push(line); - } - return '# Regular items/À la carte:\n' + menuLines.join('\n'); -} -//# sourceMappingURL=database.js.map \ No newline at end of file diff --git a/examples/src/drive-thru/database.js.map b/examples/src/drive-thru/database.js.map deleted file mode 100644 index 1b73c87c9..000000000 --- a/examples/src/drive-thru/database.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"database.js","sourceRoot":"","sources":["database.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AAEtC,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2HAqCwF,CAAC;AAgB5H,MAAM,OAAO,MAAM;IACjB,KAAK,CAAC,UAAU;QACd,MAAM,SAAS,GAAG;YAChB;gBACE,EAAE,EAAE,WAAW;gBACf,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,QAAQ;gBACZ,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,WAAW;gBACf,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC/B,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC/B,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;iBAChC;aACF;YACD;gBACE,EAAE,EAAE,WAAW;gBACf,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,uBAAuB;gBAC3B,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,WAAW;gBACf,IAAI,EAAE,WAAW;gBACjB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;gBACD,SAAS,EAAE,KAAK;aACjB;YACD;gBACE,EAAE,EAAE,sBAAsB;gBAC1B,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC/B,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC/B,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;iBAChC;aACF;YACD;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,IAAI,EAAE,mCAAmC;gBACzC,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,MAAM;gBACV,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,gBAAgB;gBACpB,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,eAAe;gBACrB,QAAQ,EAAE,CAAC;gBACX,KAAK,EAAE,IAAI;aACZ;SACF,CAAC;QAEF,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,IAAI,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAClC,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC7D,KAAK,CAAC,IAAI,CAAC;wBACT,EAAE,EAAE,IAAI,CAAC,EAAE;wBACX,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,QAAQ,EAAE,WAAW,CAAC,QAAQ;wBAC9B,KAAK,EAAE,WAAW,CAAC,KAAK;wBACxB,IAAI,EAAE,IAAgB;wBACtB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;wBACjC,QAAQ,EAAE,OAAO;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC;oBACT,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,SAAS,EAAE,IAAI;oBACf,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,QAAQ,GAAG;YACf;gBACE,EAAE,EAAE,eAAe;gBACnB,IAAI,EAAE,gBAAgB;gBACtB,KAAK,EAAE,GAAG;gBACV,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,IAAI,EAAE,oCAAoC;gBAC1C,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,IAAI,EAAE,4CAA4C;gBAClD,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,KAAK;aACb;YACD;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,IAAI,EAAE,+BAA+B;gBACrC,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,KAAK;aACb;YACD;gBACE,EAAE,EAAE,sBAAsB;gBAC1B,IAAI,EAAE,2CAA2C;gBACjD,KAAK,EAAE,GAAG;gBACV,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,KAAK;aACb;YACD;gBACE,EAAE,EAAE,mBAAmB;gBACvB,IAAI,EAAE,0BAA0B;gBAChC,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,mBAAmB;gBACvB,IAAI,EAAE,uBAAuB;gBAC7B,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,mBAAmB;gBACvB,IAAI,EAAE,wBAAwB;gBAC9B,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,mBAAmB;gBACvB,IAAI,EAAE,8BAA8B;gBACpC,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,8BAA8B;gBAClC,IAAI,EAAE,iCAAiC;gBACvC,KAAK,EAAE,GAAG;gBACV,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,oBAAoB;gBACxB,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,GAAG;gBACV,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,yBAAyB;gBAC7B,IAAI,EAAE,uBAAuB;gBAC7B,KAAK,EAAE,GAAG;gBACV,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;SACF,CAAC;QAEF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7B,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,KAAK;YACtB,QAAQ,EAAE,YAA4B;YACtC,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,aAAa,GAAG;YACpB;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,IAAI,EAAE,qCAAqC;gBAC3C,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,0BAA0B;gBAC9B,IAAI,EAAE,qCAAqC;gBAC3C,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,sBAAsB;gBAC1B,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;SACF,CAAC;QAEF,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAClC,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,YAA4B;SACvC,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG;YACf;gBACE,EAAE,EAAE,SAAS;gBACb,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,wBAAwB;gBAC5B,IAAI,EAAE,8BAA8B;gBACpC,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,uBAAuB;gBAC3B,IAAI,EAAE,sCAAsC;gBAC5C,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,wBAAwB;gBAC5B,IAAI,EAAE,yBAAyB;gBAC/B,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,wBAAwB;gBAC5B,IAAI,EAAE,qCAAqC;gBAC3C,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,mBAAmB;gBACvB,IAAI,EAAE,oBAAoB;gBAC1B,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,gBAAgB;gBACpB,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,iBAAiB;gBACrB,IAAI,EAAE,kBAAkB;gBACxB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,uBAAuB;gBAC3B,IAAI,EAAE,wBAAwB;gBAC9B,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,gBAAgB;gBACpB,IAAI,EAAE,2BAA2B;gBACjC,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,eAAe;gBACrB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,WAAW;gBACf,IAAI,EAAE,WAAW;gBACjB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,OAAO;gBACX,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,qBAAqB;gBACzB,IAAI,EAAE,kBAAkB;gBACxB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,eAAe;gBACnB,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,iBAAiB;gBACrB,IAAI,EAAE,iBAAiB;gBACvB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,kBAAkB;gBACtB,IAAI,EAAE,kBAAkB;gBACxB,KAAK,EAAE;oBACL,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;oBACjC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;iBAClC;aACF;YACD;gBACE,EAAE,EAAE,YAAY;gBAChB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,IAAI;aACZ;SACF,CAAC;QAEF,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAClC,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC7D,KAAK,CAAC,IAAI,CAAC;wBACT,EAAE,EAAE,IAAI,CAAC,EAAE;wBACX,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,QAAQ,EAAE,WAAW,CAAC,QAAQ;wBAC9B,KAAK,EAAE,WAAW,CAAC,KAAK;wBACxB,IAAI,EAAE,IAAgB;wBACtB,SAAS,EAAE,IAAI;wBACf,QAAQ,EAAE,SAAS;qBACpB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC;oBACT,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,SAAS,EAAE,IAAI;oBACf,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,QAAQ,GAAG;YACf;gBACE,EAAE,EAAE,gBAAgB;gBACpB,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,IAAI;aACZ;YACD;gBACE,EAAE,EAAE,YAAY;gBAChB,IAAI,EAAE,YAAY;gBAClB,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,WAAW;gBACf,IAAI,EAAE,WAAW;gBACjB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,aAAa;gBACjB,IAAI,EAAE,aAAa;gBACnB,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,SAAS;gBACb,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,gBAAgB;gBACpB,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,sBAAsB;gBAC1B,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,GAAG;aACX;YACD;gBACE,EAAE,EAAE,wBAAwB;gBAC5B,IAAI,EAAE,wBAAwB;gBAC9B,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,GAAG;aACX;SACF,CAAC;QAEF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7B,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,OAAuB;SAClC,CAAC,CAAC,CAAC;IACN,CAAC;CACF;AAED,mBAAmB;AAEnB,MAAM,UAAU,aAAa,CAAC,KAAiB,EAAE,MAAc,EAAE,IAAe;IAC9E,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAsB,EAAE,KAAiB;IACxE,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,OAAO;YACV,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,KAAK,YAAY;YACf,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,KAAK,YAAY;YACf,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,KAAK,OAAO;YACV,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,KAAK,SAAS;YACZ,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC;QACxC;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAiB;IAInC,MAAM,KAAK,GAA+C,EAAE,CAAC;IAC7D,MAAM,SAAS,GAAe,EAAE,CAAC;IAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAgC,CAAC;YACpD,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAiB;IAC9C,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS;YAAE,SAAS;QACzB,SAAS,CAAC,IAAI,CAAC,OAAO,SAAS,CAAC,IAAI,QAAQ,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;QAE9D,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,IAAI,GAAG,cAAc,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACjF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACpB,IAAI,IAAI,cAAc,CAAC;YACzB,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,yBAAyB,CAAC;QACrH,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,IAAI,cAAc,CAAC;QACzB,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAiB;IAC9C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,GAAG,OAAO,IAAI,CAAC,UAAU,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,GAAG,CAAC;QACrH,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,IAAI,cAAc,CAAC;QACzB,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,YAAY,GAAG;;6DAEsC,CAAC;IAE5D,OAAO,YAAY,GAAG,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAiB;IAC9C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,GAAG,CAAC;QAC/F,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,IAAI,cAAc,CAAC;QACzB,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,OAAO;EACP,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;8BAKQ,CAAC;AAC/B,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAiB;IAC9C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,GAAG,CAAC;QAC/F,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,IAAI,cAAc,CAAC;QACzB,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAiB;IAChD,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS;YAAE,SAAS;QACzB,SAAS,CAAC,IAAI,CAAC,OAAO,SAAS,CAAC,IAAI,QAAQ,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;QAE9D,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,IAAI,GAAG,cAAc,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACjF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACpB,IAAI,IAAI,cAAc,CAAC;YACzB,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,EAAE,yBAAyB,CAAC;QACrH,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,IAAI,cAAc,CAAC;QACzB,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,+BAA+B,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC"} \ No newline at end of file diff --git a/examples/src/drive-thru/drivethru_agent.js b/examples/src/drive-thru/drivethru_agent.js deleted file mode 100644 index 7cfa31ddd..000000000 --- a/examples/src/drive-thru/drivethru_agent.js +++ /dev/null @@ -1,294 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -import { COMMON_INSTRUCTIONS, FakeDB, findItemsById, menuInstructions, } from './database.js'; -import { OrderState, createOrderedCombo, createOrderedHappy, createOrderedRegular, } from './order.js'; -class DriveThruAgent extends voice.Agent { - constructor(userdata) { - const instructions = COMMON_INSTRUCTIONS + - '\n\n' + - menuInstructions('drink', userdata.drinkItems) + - '\n\n' + - menuInstructions('combo_meal', userdata.comboItems) + - '\n\n' + - menuInstructions('happy_meal', userdata.happyItems) + - '\n\n' + - menuInstructions('regular', userdata.regularItems) + - '\n\n' + - menuInstructions('sauce', userdata.sauceItems); - super({ - instructions, - tools: { - orderComboMeal: DriveThruAgent.buildComboOrderTool(userdata.comboItems, userdata.drinkItems, userdata.sauceItems), - orderHappyMeal: DriveThruAgent.buildHappyOrderTool(userdata.happyItems, userdata.drinkItems, userdata.sauceItems), - orderRegularItem: DriveThruAgent.buildRegularOrderTool(userdata.regularItems, userdata.drinkItems, userdata.sauceItems), - removeOrderItem: llm.tool({ - description: `Removes one or more items from the user's order using their \`orderId\`s. - -Useful when the user asks to cancel or delete existing items (e.g., "Remove the cheeseburger"). - -If the \`orderId\`s are unknown, call \`listOrderItems\` first to retrieve them.`, - parameters: z.object({ - orderIds: z - .array(z.string()) - .describe('A list of internal `orderId`s of the items to remove. Use `listOrderItems` to look it up if needed.'), - }), - execute: async ({ orderIds }, { ctx }) => { - const notFound = orderIds.filter((oid) => !ctx.userData.order.items[oid]); - if (notFound.length > 0) { - throw new llm.ToolError(`error: no item(s) found with order_id(s): ${notFound.join(', ')}`); - } - const removedItems = await Promise.all(orderIds.map((oid) => ctx.userData.order.remove(oid))); - return 'Removed items:\n' + removedItems.map((item) => JSON.stringify(item)).join('\n'); - }, - }), - listOrderItems: llm.tool({ - description: `Retrieves the current list of items in the user's order, including each item's internal \`orderId\`. - -Helpful when: -- An \`orderId\` is required before modifying or removing an existing item. -- Confirming details or contents of the current order. - -Examples: -- User requests modifying an item, but the item's \`orderId\` is unknown (e.g., "Change the fries from small to large"). -- User requests removing an item, but the item's \`orderId\` is unknown (e.g., "Remove the cheeseburger"). -- User asks about current order details (e.g., "What's in my order so far?").`, - execute: async (_, { ctx }) => { - const items = Object.values(ctx.userData.order.items); - if (items.length === 0) { - return 'The order is empty'; - } - return items.map((item) => JSON.stringify(item)).join('\n'); - }, - }), - }, - }); - } - static buildComboOrderTool(comboItems, drinkItems, sauceItems) { - const availableComboIds = [...new Set(comboItems.map((item) => item.id))]; - const availableDrinkIds = [...new Set(drinkItems.map((item) => item.id))]; - const availableSauceIds = [...new Set(sauceItems.map((item) => item.id))]; - return llm.tool({ - description: `Call this when the user orders a **Combo Meal**, like: "Number 4b with a large Sprite" or "I'll do a medium meal." - -Do not call this tool unless the user clearly refers to a known combo meal by name or number. -Regular items like a single cheeseburger cannot be made into a meal unless such a combo explicitly exists. - -Only call this function once the user has clearly specified both a drink and a sauce — always ask for them if they're missing. - -A meal can only be Medium or Large; Small is not an available option. -Drink and fries sizes can differ (e.g., "large fries but a medium Coke"). - -If the user says just "a large meal," assume both drink and fries are that size.`, - parameters: z.object({ - mealId: z - .enum(availableComboIds) - .describe('The ID of the combo meal the user requested.'), - drinkId: z - .enum(availableDrinkIds) - .describe('The ID of the drink the user requested.'), - drinkSize: z.enum(['M', 'L']).nullable().describe('The size of the drink'), - friesSize: z.enum(['M', 'L']).describe('The size of the fries'), - sauceId: z - .enum(availableSauceIds) - .nullable() - .describe('The ID of the sauce the user requested.'), - }), - execute: async ({ mealId, drinkId, drinkSize, friesSize, sauceId }, { ctx }) => { - if (!findItemsById(comboItems, mealId).length) { - throw new llm.ToolError(`error: the meal ${mealId} was not found`); - } - const drinkSizes = findItemsById(drinkItems, drinkId); - if (!drinkSizes.length) { - throw new llm.ToolError(`error: the drink ${drinkId} was not found`); - } - let actualDrinkSize = drinkSize || undefined; - const actualSauceId = sauceId || undefined; - const availableSizes = [ - ...new Set(drinkSizes.map((item) => item.size).filter((size) => size !== undefined)), - ]; - if (actualDrinkSize === undefined && availableSizes.length > 1) { - throw new llm.ToolError(`error: ${drinkId} comes with multiple sizes: ${availableSizes.join(', ')}. Please clarify which size should be selected.`); - } - if (actualDrinkSize !== undefined && !availableSizes.length) { - throw new llm.ToolError(`error: size should not be specified for item ${drinkId} as it does not support sizing options.`); - } - if (actualDrinkSize && !availableSizes.includes(actualDrinkSize)) { - actualDrinkSize = undefined; - } - if (actualSauceId && !findItemsById(sauceItems, actualSauceId).length) { - throw new llm.ToolError(`error: the sauce ${actualSauceId} was not found`); - } - const item = createOrderedCombo({ - mealId, - drinkId, - drinkSize: actualDrinkSize, - sauceId: actualSauceId, - friesSize, - }); - await ctx.userData.order.add(item); - return `The item was added: ${JSON.stringify(item)}`; - }, - }); - } - static buildHappyOrderTool(happyItems, drinkItems, sauceItems) { - const availableHappyIds = [...new Set(happyItems.map((item) => item.id))]; - const availableDrinkIds = [...new Set(drinkItems.map((item) => item.id))]; - const availableSauceIds = [...new Set(sauceItems.map((item) => item.id))]; - return llm.tool({ - description: `Call this when the user orders a **Happy Meal**, typically for children. These meals come with a main item, a drink, and a sauce. - -The user must clearly specify a valid Happy Meal option (e.g., "Can I get a Happy Meal?"). - -Before calling this tool: -- Ensure the user has provided all required components: a valid meal, drink, drink size, and sauce. -- If any of these are missing, prompt the user for the missing part before proceeding. - -Assume Small as default only if the user says "Happy Meal" and gives no size preference, but always ask for clarification if unsure.`, - parameters: z.object({ - mealId: z - .enum(availableHappyIds) - .describe('The ID of the happy meal the user requested.'), - drinkId: z - .enum(availableDrinkIds) - .describe('The ID of the drink the user requested.'), - drinkSize: z.enum(['S', 'M', 'L']).nullable().describe('The size of the drink'), - sauceId: z - .enum(availableSauceIds) - .nullable() - .describe('The ID of the sauce the user requested.'), - }), - execute: async ({ mealId, drinkId, drinkSize, sauceId }, { ctx }) => { - if (!findItemsById(happyItems, mealId).length) { - throw new llm.ToolError(`error: the meal ${mealId} was not found`); - } - const drinkSizes = findItemsById(drinkItems, drinkId); - if (!drinkSizes.length) { - throw new llm.ToolError(`error: the drink ${drinkId} was not found`); - } - let actualDrinkSize = drinkSize || undefined; - const actualSauceId = sauceId || undefined; - const availableSizes = [ - ...new Set(drinkSizes.map((item) => item.size).filter((size) => size !== undefined)), - ]; - if (actualDrinkSize === undefined && availableSizes.length > 1) { - throw new llm.ToolError(`error: ${drinkId} comes with multiple sizes: ${availableSizes.join(', ')}. Please clarify which size should be selected.`); - } - if (actualDrinkSize !== undefined && !availableSizes.length) { - actualDrinkSize = undefined; - } - if (actualSauceId && !findItemsById(sauceItems, actualSauceId).length) { - throw new llm.ToolError(`error: the sauce ${actualSauceId} was not found`); - } - const item = createOrderedHappy({ - mealId, - drinkId, - drinkSize: actualDrinkSize, - sauceId: actualSauceId, - }); - await ctx.userData.order.add(item); - return `The item was added: ${JSON.stringify(item)}`; - }, - }); - } - static buildRegularOrderTool(regularItems, drinkItems, sauceItems) { - const allItems = [...regularItems, ...drinkItems, ...sauceItems]; - const availableIds = [...new Set(allItems.map((item) => item.id))]; - return llm.tool({ - description: `Call this when the user orders **a single item on its own**, not as part of a Combo Meal or Happy Meal. - -The customer must provide clear and specific input. For example, item variants such as flavor must **always** be explicitly stated. - -The user might say—for example: -- "Just the cheeseburger, no meal" -- "A medium Coke" -- "Can I get some ketchup?" -- "Can I get a McFlurry Oreo?"`, - parameters: z.object({ - itemId: z - .enum(availableIds) - .describe('The ID of the item the user requested.'), - size: z - .enum(['S', 'M', 'L']) - .nullable() - .describe('Size of the item, if applicable (e.g., "S", "M", "L").'), - }), - execute: async ({ itemId, size }, { ctx }) => { - const itemSizes = findItemsById(allItems, itemId); - if (!itemSizes.length) { - throw new llm.ToolError(`error: ${itemId} was not found.`); - } - let actualSize = size || undefined; - const availableSizes = [ - ...new Set(itemSizes.map((item) => item.size).filter((size) => size !== undefined)), - ]; - if (actualSize === undefined && availableSizes.length > 1) { - throw new llm.ToolError(`${itemId} comes with multiple sizes: ${availableSizes.join(', ')}. Please clarify which size should be selected.`); - } - if (actualSize !== undefined && !availableSizes.length) { - actualSize = undefined; - } - if (actualSize && availableSizes.length && !availableSizes.includes(actualSize)) { - throw new llm.ToolError(`error: unknown size ${actualSize} for ${itemId}. Available sizes: ${availableSizes.join(', ')}.`); - } - const item = createOrderedRegular({ - itemId, - size: actualSize, - }); - await ctx.userData.order.add(item); - return `The item was added: ${JSON.stringify(item)}`; - }, - }); - } -} -async function newUserData() { - const fakeDb = new FakeDB(); - const drinkItems = await fakeDb.listDrinks(); - const comboItems = await fakeDb.listComboMeals(); - const happyItems = await fakeDb.listHappyMeals(); - const regularItems = await fakeDb.listRegulars(); - const sauceItems = await fakeDb.listSauces(); - const orderState = new OrderState(); - return { - order: orderState, - drinkItems, - comboItems, - happyItems, - regularItems, - sauceItems, - }; -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const userdata = await newUserData(); - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - stt: new deepgram.STT(), - llm: new openai.LLM({ model: 'gpt-4.1', temperature: 0.45 }), - tts: new elevenlabs.TTS(), - turnDetection: new livekit.turnDetector.MultilingualModel(), - userData: userdata, - voiceOptions: { - maxToolSteps: 10, - }, - }); - await session.start({ - agent: new DriveThruAgent(userdata), - room: ctx.room, - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=drivethru_agent.js.map \ No newline at end of file diff --git a/examples/src/drive-thru/drivethru_agent.js.map b/examples/src/drive-thru/drivethru_agent.js.map deleted file mode 100644 index 1dc76b0ad..000000000 --- a/examples/src/drive-thru/drivethru_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"drivethru_agent.js","sourceRoot":"","sources":["drivethru_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/drive-thru/order.js b/examples/src/drive-thru/order.js deleted file mode 100644 index 4ab07edee..000000000 --- a/examples/src/drive-thru/order.js +++ /dev/null @@ -1,47 +0,0 @@ -export function orderUid() { - const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - let result = 'O_'; - for (let i = 0; i < 6; i++) { - result += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); - } - return result; -} -export class OrderState { - items = {}; - async add(item) { - this.items[item.orderId] = item; - } - async remove(orderId) { - const item = this.items[orderId]; - if (!item) { - throw new Error(`Order item with ID ${orderId} not found`); - } - delete this.items[orderId]; - return item; - } - get(orderId) { - return this.items[orderId]; - } -} -export function createOrderedCombo(params) { - return { - type: 'combo_meal', - orderId: orderUid(), - ...params, - }; -} -export function createOrderedHappy(params) { - return { - type: 'happy_meal', - orderId: orderUid(), - ...params, - }; -} -export function createOrderedRegular(params) { - return { - type: 'regular', - orderId: orderUid(), - ...params, - }; -} -//# sourceMappingURL=order.js.map \ No newline at end of file diff --git a/examples/src/drive-thru/order.js.map b/examples/src/drive-thru/order.js.map deleted file mode 100644 index 9761b3f46..000000000 --- a/examples/src/drive-thru/order.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"order.js","sourceRoot":"","sources":["order.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,QAAQ;IACtB,MAAM,QAAQ,GAAG,sCAAsC,CAAC;IACxD,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AA8BD,MAAM,OAAO,UAAU;IACrB,KAAK,GAAgC,EAAE,CAAC;IAExC,KAAK,CAAC,GAAG,CAAC,IAAiB;QACzB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,YAAY,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,MAMlC;IACC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,QAAQ,EAAE;QACnB,GAAG,MAAM;KACV,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAKlC;IACC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,QAAQ,EAAE;QACnB,GAAG,MAAM;KACV,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAA2C;IAC9E,OAAO;QACL,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE;QACnB,GAAG,MAAM;KACV,CAAC;AACJ,CAAC"} \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.js b/examples/src/frontdesk/calendar_api.js deleted file mode 100644 index 1a172ef79..000000000 --- a/examples/src/frontdesk/calendar_api.js +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { createHash } from 'crypto'; -export class SlotUnavailableError extends Error { - constructor(message) { - super(message); - this.name = 'SlotUnavailableError'; - } -} -export function createAvailableSlot(startTime, durationMin) { - return { startTime, durationMin }; -} -// Base32 alphabet (RFC 4648) -const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; -function toBase32(buffer) { - let result = ''; - let bits = 0; - let value = 0; - for (let i = 0; i < buffer.length; i++) { - value = (value << 8) | buffer[i]; - bits += 8; - while (bits >= 5) { - result += BASE32_ALPHABET[(value >>> (bits - 5)) & 31]; - bits -= 5; - } - } - if (bits > 0) { - result += BASE32_ALPHABET[(value << (5 - bits)) & 31]; - } - return result; -} -export function getUniqueHash(slot) { - // unique id based on the start_time & duration_min - const raw = `${slot.startTime.toISOString()}|${slot.durationMin}`; - const hash = createHash('blake2s256').update(raw).digest(); - const truncated = hash.subarray(0, 5); - // Use base32 encoding like Python version and remove padding, then lowercase - return `ST_${toBase32(truncated).replace(/=/g, '').toLowerCase()}`; -} -export function randomSample(array, size) { - const shuffled = [...array].sort(() => 0.5 - Math.random()); - return shuffled.slice(0, size); -} -export class FakeCalendar { - tz; - _slots; - constructor(options) { - this.tz = options.timezone; - this._slots = []; - if (options.slots) { - this._slots.push(...options.slots); - return; - } - const today = new Date(); - for (let dayOffset = 1; dayOffset <= 90; dayOffset++) { - const currentDay = new Date(today); - currentDay.setDate(today.getDate() + dayOffset); - // Skip weekends (Saturday = 6, Sunday = 0) - if (currentDay.getDay() === 0 || currentDay.getDay() === 6) { - continue; - } - // Build all possible 30-min slots between 09:00 and 17:00 - const dayStart = new Date(currentDay); - dayStart.setHours(9, 0, 0, 0); - const slotsInDay = []; - for (let i = 0; i < 16; i++) { - // (17-9)=8 hours => 16 slots - const slotTime = new Date(dayStart); - slotTime.setMinutes(dayStart.getMinutes() + 30 * i); - slotsInDay.push(slotTime); - } - const numSlots = Math.floor(Math.random() * 4) + 3; // random between 3-6 - const chosen = randomSample(slotsInDay, numSlots); - for (const slotStart of chosen.sort((a, b) => a.getTime() - b.getTime())) { - this._slots.push(createAvailableSlot(slotStart, 30)); - } - } - } - async initialize() { } - async scheduleAppointment(options) { - // Fake it by just removing it from our slots list - this._slots = this._slots.filter((slot) => slot.startTime.getTime() !== options.startTime.getTime()); - } - async listAvailableSlots(options) { - return this._slots.filter((slot) => slot.startTime >= options.startTime && slot.startTime < options.endTime); - } -} -// --- cal.com impl --- -const _CAL_COM_EVENT_TYPE = 'livekit-front-desk'; -const _EVENT_DURATION_MIN = 30; -const _BASE_URL = 'https://api.cal.com/v2/'; -export class CalComCalendar { - tz; - _apiKey; - _lkEventId; - _logger; - constructor(options) { - this.tz = options.timezone; - this._apiKey = options.apiKey; - this._logger = { - info: (message) => console.info(`[cal.com] ${message}`), - }; - } - async initialize() { - const meResponse = await fetch(`${_BASE_URL}me/`, { - headers: this._buildHeaders({ apiVersion: '2024-06-14' }), - }); - if (!meResponse.ok) { - throw new Error(`Failed to get user info: ${meResponse.status} ${meResponse.statusText}`); - } - const meData = await meResponse.json(); - const username = meData.data.username; - this._logger.info(`using cal.com username: ${username}`); - const params = new URLSearchParams({ username }); - const eventTypesResponse = await fetch(`${_BASE_URL}event-types/?${params}`, { - headers: this._buildHeaders({ apiVersion: '2024-06-14' }), - }); - if (!eventTypesResponse.ok) { - throw new Error(`Failed to get event types: ${eventTypesResponse.status} ${eventTypesResponse.statusText}`); - } - const eventTypesData = await eventTypesResponse.json(); - const data = eventTypesData.data; - const lkEventType = data.find((event) => event.slug === _CAL_COM_EVENT_TYPE) || null; - if (lkEventType) { - this._lkEventId = lkEventType.id; - } - else { - const createResponse = await fetch(`${_BASE_URL}event-types`, { - method: 'POST', - headers: { - ...this._buildHeaders({ apiVersion: '2024-06-14' }), - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - lengthInMinutes: _EVENT_DURATION_MIN, - title: 'LiveKit Front-Desk', - slug: _CAL_COM_EVENT_TYPE, - }), - }); - if (!createResponse.ok) { - throw new Error(`Failed to create event type: ${createResponse.status} ${createResponse.statusText}`); - } - this._logger.info(`successfully added ${_CAL_COM_EVENT_TYPE} event type`); - const createData = await createResponse.json(); - this._lkEventId = createData.data.id; - } - this._logger.info(`event type id: ${this._lkEventId}`); - } - async scheduleAppointment(options) { - const startTimeUtc = new Date(options.startTime.getTime()); - const response = await fetch(`${_BASE_URL}bookings`, { - method: 'POST', - headers: { - ...this._buildHeaders({ apiVersion: '2024-08-13' }), - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - start: startTimeUtc.toISOString(), - attendee: { - name: options.attendeeEmail, - email: options.attendeeEmail, - timeZone: this.tz, - }, - eventTypeId: this._lkEventId, - }), - }); - const data = await response.json(); - if (data.error) { - const message = data.error.message; - if (message.includes('User either already has booking at this time or is not available')) { - throw new SlotUnavailableError(data.error.message); - } - } - if (!response.ok) { - throw new Error(`Failed to schedule appointment: ${response.status} ${response.statusText}`); - } - } - async listAvailableSlots(options) { - const startTimeUtc = new Date(options.startTime.getTime()); - const endTimeUtc = new Date(options.endTime.getTime()); - const params = new URLSearchParams({ - eventTypeId: this._lkEventId.toString(), - start: startTimeUtc.toISOString(), - end: endTimeUtc.toISOString(), - }); - const response = await fetch(`${_BASE_URL}slots/?${params}`, { - headers: this._buildHeaders({ apiVersion: '2024-09-04' }), - }); - if (!response.ok) { - throw new Error(`Failed to get available slots: ${response.status} ${response.statusText}`); - } - const rawData = await response.json(); - const availableSlots = []; - for (const [, slots] of Object.entries(rawData.data)) { - for (const slot of slots) { - const startDt = new Date(slot.start.replace('Z', '+00:00')); - availableSlots.push(createAvailableSlot(startDt, _EVENT_DURATION_MIN)); - } - } - return availableSlots; - } - _buildHeaders(options) { - const headers = { - Authorization: `Bearer ${this._apiKey}`, - }; - if (options.apiVersion) { - headers['cal-api-version'] = options.apiVersion; - } - return headers; - } -} -//# sourceMappingURL=calendar_api.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.js.map b/examples/src/frontdesk/calendar_api.js.map deleted file mode 100644 index d5a805e4d..000000000 --- a/examples/src/frontdesk/calendar_api.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"calendar_api.js","sourceRoot":"","sources":["calendar_api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.test.js b/examples/src/frontdesk/calendar_api.test.js deleted file mode 100644 index ec22b1acc..000000000 --- a/examples/src/frontdesk/calendar_api.test.js +++ /dev/null @@ -1,367 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { CalComCalendar, FakeCalendar, SlotUnavailableError, createAvailableSlot, getUniqueHash, } from './calendar_api.js'; -describe('Calendar API', () => { - describe('createAvailableSlot', () => { - it('should create a valid AvailableSlot', () => { - const startTime = new Date('2025-01-20T10:00:00Z'); - const durationMin = 30; - const slot = createAvailableSlot(startTime, durationMin); - expect(slot.startTime).toEqual(startTime); - expect(slot.durationMin).toBe(durationMin); - }); - }); - describe('getUniqueHash', () => { - it('should generate consistent hash for same slot', () => { - const slot = createAvailableSlot(new Date('2025-01-20T10:00:00Z'), 30); - const hash1 = getUniqueHash(slot); - const hash2 = getUniqueHash(slot); - expect(hash1).toBe(hash2); - expect(hash1).toMatch(/^ST_[a-z0-9]+$/); - }); - it('should generate different hashes for different slots', () => { - const slot1 = createAvailableSlot(new Date('2025-01-20T10:00:00Z'), 30); - const slot2 = createAvailableSlot(new Date('2025-01-20T10:30:00Z'), 30); - const hash1 = getUniqueHash(slot1); - const hash2 = getUniqueHash(slot2); - expect(hash1).not.toBe(hash2); - }); - it('should generate different hashes for different durations', () => { - const startTime = new Date('2025-01-20T10:00:00Z'); - const slot1 = createAvailableSlot(startTime, 30); - const slot2 = createAvailableSlot(startTime, 60); - const hash1 = getUniqueHash(slot1); - const hash2 = getUniqueHash(slot2); - expect(hash1).not.toBe(hash2); - }); - }); - describe('FakeCalendar', () => { - let calendar; - beforeEach(() => { - calendar = new FakeCalendar({ timezone: 'America/New_York' }); - }); - it('should initialize without error', async () => { - await expect(calendar.initialize()).resolves.not.toThrow(); - }); - it('should generate weekday slots only', async () => { - await calendar.initialize(); - const startTime = new Date('2025-01-20T00:00:00Z'); - const endTime = new Date('2025-01-27T00:00:00Z'); - const slots = await calendar.listAvailableSlots({ startTime, endTime }); - // Check that all slots are on weekdays - for (const slot of slots) { - const dayOfWeek = slot.startTime.getDay(); - expect(dayOfWeek).toBeGreaterThan(0); - expect(dayOfWeek).toBeLessThan(6); - } - }); - it('should generate slots within business hours', async () => { - await calendar.initialize(); - const startTime = new Date('2025-01-20T00:00:00Z'); - const endTime = new Date('2025-01-21T00:00:00Z'); - const slots = await calendar.listAvailableSlots({ startTime, endTime }); - // Check that all slots are within business hours (9-17) - for (const slot of slots) { - const hour = slot.startTime.getHours(); - expect(hour).toBeGreaterThanOrEqual(9); - expect(hour).toBeLessThan(17); - } - }); - it('should filter slots by date range', async () => { - await calendar.initialize(); - const startTime = new Date('2025-01-20T00:00:00Z'); - const endTime = new Date('2025-01-21T00:00:00Z'); - const slots = await calendar.listAvailableSlots({ startTime, endTime }); - for (const slot of slots) { - expect(slot.startTime.getTime()).toBeGreaterThanOrEqual(startTime.getTime()); - expect(slot.startTime.getTime()).toBeLessThan(endTime.getTime()); - } - }); - it('should schedule appointment and remove slot', async () => { - const predefinedSlots = [ - createAvailableSlot(new Date('2025-01-20T10:00:00Z'), 30), - createAvailableSlot(new Date('2025-01-20T11:00:00Z'), 30), - ]; - calendar = new FakeCalendar({ - timezone: 'America/New_York', - slots: predefinedSlots, - }); - await calendar.initialize(); - await calendar.scheduleAppointment({ - startTime: predefinedSlots[0].startTime, - attendeeEmail: 'test@example.com', - }); - const remainingSlots = await calendar.listAvailableSlots({ - startTime: new Date('2025-01-20T00:00:00Z'), - endTime: new Date('2025-01-21T00:00:00Z'), - }); - expect(remainingSlots).toHaveLength(1); - expect(remainingSlots[0].startTime).toEqual(predefinedSlots[1].startTime); - }); - it('should work with custom slots', async () => { - const customSlots = [ - createAvailableSlot(new Date('2025-01-20T14:00:00Z'), 30), - createAvailableSlot(new Date('2025-01-20T15:30:00Z'), 60), - ]; - calendar = new FakeCalendar({ - timezone: 'UTC', - slots: customSlots, - }); - await calendar.initialize(); - const slots = await calendar.listAvailableSlots({ - startTime: new Date('2025-01-20T00:00:00Z'), - endTime: new Date('2025-01-21T00:00:00Z'), - }); - expect(slots).toHaveLength(2); - expect(slots).toEqual(expect.arrayContaining(customSlots)); - }); - }); - describe('CalComCalendar (mocked)', () => { - let calendar; - beforeEach(() => { - calendar = new CalComCalendar({ - apiKey: 'test-api-key', - timezone: 'America/New_York', - }); - global.fetch = vi.fn(); - }); - afterEach(() => { - vi.restoreAllMocks(); - }); - describe('initialize', () => { - it('should initialize successfully with existing event type', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - data: { username: 'testuser' }, - }), - }); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - data: [{ id: 123, slug: 'livekit-front-desk' }], - }), - }); - const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => { }); - await calendar.initialize(); - expect(mockFetch).toHaveBeenCalledTimes(2); - expect(consoleSpy).toHaveBeenCalledWith('[cal.com] using cal.com username: testuser'); - expect(consoleSpy).toHaveBeenCalledWith('[cal.com] event type id: 123'); - consoleSpy.mockRestore(); - }); - it('should create new event type when not exists', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - data: { username: 'testuser' }, - }), - }); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - data: [], - }), - }); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - data: { id: 456 }, - }), - }); - const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => { }); - await calendar.initialize(); - expect(mockFetch).toHaveBeenCalledTimes(3); - expect(consoleSpy).toHaveBeenCalledWith('[cal.com] using cal.com username: testuser'); - expect(consoleSpy).toHaveBeenCalledWith('[cal.com] successfully added livekit-front-desk event type'); - expect(consoleSpy).toHaveBeenCalledWith('[cal.com] event type id: 456'); - consoleSpy.mockRestore(); - }); - it('should handle API errors', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 401, - statusText: 'Unauthorized', - }); - await expect(calendar.initialize()).rejects.toThrow('Failed to get user info: 401 Unauthorized'); - }); - }); - describe('scheduleAppointment', () => { - beforeEach(async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ data: { username: 'testuser' } }), - }); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ data: [{ id: 123, slug: 'livekit-front-desk' }] }), - }); - vi.spyOn(console, 'info').mockImplementation(() => { }); - await calendar.initialize(); - vi.mocked(console.info).mockRestore(); - mockFetch.mockClear(); - }); - it('should schedule appointment successfully', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ success: true }), - }); - await calendar.scheduleAppointment({ - startTime: new Date('2025-01-20T10:00:00Z'), - attendeeEmail: 'test@example.com', - }); - expect(mockFetch).toHaveBeenCalledWith('https://api.cal.com/v2/bookings', expect.objectContaining({ - method: 'POST', - headers: expect.objectContaining({ - Authorization: 'Bearer test-api-key', - 'cal-api-version': '2024-08-13', - }), - body: JSON.stringify({ - start: '2025-01-20T10:00:00.000Z', - attendee: { - name: 'test@example.com', - email: 'test@example.com', - timeZone: 'America/New_York', - }, - eventTypeId: 123, - }), - })); - }); - it('should throw SlotUnavailableError for booking conflicts', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: false, - json: async () => ({ - error: { - message: 'User either already has booking at this time or is not available', - }, - }), - }); - await expect(calendar.scheduleAppointment({ - startTime: new Date('2025-01-20T10:00:00Z'), - attendeeEmail: 'test@example.com', - })).rejects.toThrow(SlotUnavailableError); - }); - it('should handle other API errors', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 500, - statusText: 'Internal Server Error', - json: async () => ({}), - }); - await expect(calendar.scheduleAppointment({ - startTime: new Date('2025-01-20T10:00:00Z'), - attendeeEmail: 'test@example.com', - })).rejects.toThrow('Failed to schedule appointment: 500 Internal Server Error'); - }); - }); - describe('listAvailableSlots', () => { - beforeEach(async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ data: { username: 'testuser' } }), - }); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ data: [{ id: 123, slug: 'livekit-front-desk' }] }), - }); - vi.spyOn(console, 'info').mockImplementation(() => { }); - await calendar.initialize(); - vi.mocked(console.info).mockRestore(); - mockFetch.mockClear(); - }); - it('should list available slots successfully', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - data: { - '2025-01-20': [{ start: '2025-01-20T10:00:00Z' }, { start: '2025-01-20T11:00:00Z' }], - }, - }), - }); - const startTime = new Date('2025-01-20T00:00:00Z'); - const endTime = new Date('2025-01-21T00:00:00Z'); - const slots = await calendar.listAvailableSlots({ startTime, endTime }); - expect(slots).toHaveLength(2); - expect(slots[0].startTime).toEqual(new Date('2025-01-20T10:00:00Z')); - expect(slots[0].durationMin).toBe(30); - expect(slots[1].startTime).toEqual(new Date('2025-01-20T11:00:00Z')); - expect(slots[1].durationMin).toBe(30); - expect(mockFetch).toHaveBeenCalledWith('https://api.cal.com/v2/slots/?eventTypeId=123&start=2025-01-20T00%3A00%3A00.000Z&end=2025-01-21T00%3A00%3A00.000Z', expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: 'Bearer test-api-key', - 'cal-api-version': '2024-09-04', - }), - })); - }); - it('should handle API errors', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 404, - statusText: 'Not Found', - }); - const startTime = new Date('2025-01-20T00:00:00Z'); - const endTime = new Date('2025-01-21T00:00:00Z'); - await expect(calendar.listAvailableSlots({ startTime, endTime })).rejects.toThrow('Failed to get available slots: 404 Not Found'); - }); - it('should return empty array when no slots available', async () => { - const mockFetch = vi.mocked(fetch); - mockFetch.mockResolvedValueOnce({ - ok: true, - json: async () => ({ data: {} }), - }); - const startTime = new Date('2025-01-20T00:00:00Z'); - const endTime = new Date('2025-01-21T00:00:00Z'); - const slots = await calendar.listAvailableSlots({ startTime, endTime }); - expect(slots).toHaveLength(0); - }); - }); - }); - describe('SlotUnavailableError', () => { - it('should create error with correct name and message', () => { - const message = 'Slot is not available'; - const error = new SlotUnavailableError(message); - expect(error.name).toBe('SlotUnavailableError'); - expect(error.message).toBe(message); - expect(error).toBeInstanceOf(Error); - }); - }); - describe('FakeCalendar end-to-end', () => { - it('should work as a complete calendar system', async () => { - const calendar = new FakeCalendar({ timezone: 'UTC' }); - await calendar.initialize(); - const tomorrow = new Date(); - tomorrow.setDate(tomorrow.getDate() + 1); - tomorrow.setHours(0, 0, 0, 0); - const dayAfter = new Date(tomorrow); - dayAfter.setDate(dayAfter.getDate() + 1); - const slots = await calendar.listAvailableSlots({ - startTime: tomorrow, - endTime: dayAfter, - }); - expect(slots.length).toBeGreaterThan(0); - const firstSlot = slots[0]; - await calendar.scheduleAppointment({ - startTime: firstSlot.startTime, - attendeeEmail: 'test@example.com', - }); - const remainingSlots = await calendar.listAvailableSlots({ - startTime: tomorrow, - endTime: dayAfter, - }); - expect(remainingSlots.length).toBe(slots.length - 1); - expect(remainingSlots.find((slot) => slot.startTime.getTime() === firstSlot.startTime.getTime())).toBeUndefined(); - }); - }); -}); -//# sourceMappingURL=calendar_api.test.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_api.test.js.map b/examples/src/frontdesk/calendar_api.test.js.map deleted file mode 100644 index 20157d4a3..000000000 --- a/examples/src/frontdesk/calendar_api.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"calendar_api.test.js","sourceRoot":"","sources":["calendar_api.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_integration.test.js b/examples/src/frontdesk/calendar_integration.test.js deleted file mode 100644 index 36766fb47..000000000 --- a/examples/src/frontdesk/calendar_integration.test.js +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { describe, expect, it } from 'vitest'; -import { CalComCalendar } from './calendar_api.js'; -describe('Calendar Integration Tests', () => { - describe('CalComCalendar with real API', () => { - it.skipIf(!process.env.CAL_API_KEY)('should initialize with real API key', async () => { - const calendar = new CalComCalendar({ - apiKey: process.env.CAL_API_KEY, - timezone: 'America/New_York', - }); - await expect(calendar.initialize()).resolves.not.toThrow(); - }, 10000); - it.skipIf(!process.env.CAL_API_KEY)('should list available slots with real API key', async () => { - const calendar = new CalComCalendar({ - apiKey: process.env.CAL_API_KEY, - timezone: 'America/New_York', - }); - await calendar.initialize(); - const startTime = new Date(); - startTime.setDate(startTime.getDate() + 1); - const endTime = new Date(startTime); - endTime.setDate(endTime.getDate() + 7); - const slots = await calendar.listAvailableSlots({ startTime, endTime }); - expect(Array.isArray(slots)).toBe(true); - slots.forEach((slot) => { - expect(slot.startTime).toBeInstanceOf(Date); - expect(typeof slot.durationMin).toBe('number'); - expect(slot.durationMin).toBeGreaterThan(0); - }); - }, 10000); - }); -}); -//# sourceMappingURL=calendar_integration.test.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/calendar_integration.test.js.map b/examples/src/frontdesk/calendar_integration.test.js.map deleted file mode 100644 index 83c08f40d..000000000 --- a/examples/src/frontdesk/calendar_integration.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"calendar_integration.test.js","sourceRoot":"","sources":["calendar_integration.test.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CACjC,qCAAqC,EACrC,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC;gBAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAY;gBAChC,QAAQ,EAAE,kBAAkB;aAC7B,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7D,CAAC,EACD,KAAK,CACN,CAAC;QAEF,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CACjC,+CAA+C,EAC/C,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC;gBAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAY;gBAChC,QAAQ,EAAE,kBAAkB;aAC7B,CAAC,CAAC;YAEH,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;YAE5B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;YACpC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAEvC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAExE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBACrB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;gBAC5C,MAAM,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC,EACD,KAAK,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/frontdesk/frontdesk_agent.js b/examples/src/frontdesk/frontdesk_agent.js deleted file mode 100644 index 0bc0d4ea4..000000000 --- a/examples/src/frontdesk/frontdesk_agent.js +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { BackgroundVoiceCancellation } from '@livekit/noise-cancellation-node'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -import { CalComCalendar, FakeCalendar, SlotUnavailableError, getUniqueHash, } from './calendar_api.js'; -export class FrontDeskAgent extends voice.Agent { - tz; - _slotsMap = new Map(); - constructor(options) { - const today = new Date().toLocaleDateString('en-US', { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric', - timeZone: options.timezone, - }); - const instructions = `You are Front-Desk, a helpful and efficient voice assistant. ` + - `Today is ${today}. Your main goal is to schedule an appointment for the user. ` + - `This is a voice conversation — speak naturally, clearly, and concisely. ` + - `When the user says hello or greets you, don't just respond with a greeting — use it as an opportunity to move things forward. ` + - `For example, follow up with a helpful question like: 'Would you like to book a time?' ` + - `When asked for availability, call list_available_slots and offer a few clear, simple options. ` + - `Say things like 'Monday at 2 PM' — avoid timezones, timestamps, and avoid saying 'AM' or 'PM'. ` + - `Use natural phrases like 'in the morning' or 'in the evening', and don't mention the year unless it's different from the current one. ` + - `Offer a few options at a time, pause for a response, then guide the user to confirm. ` + - `If the time is no longer available, let them know gently and offer the next options. ` + - `Always keep the conversation flowing — be proactive, human, and focused on helping the user schedule with ease.`; - super({ - instructions, - tools: { - scheduleAppointment: llm.tool({ - description: 'Schedule an appointment at the given slot.', - parameters: z.object({ - slotId: z - .string() - .describe('The identifier for the selected time slot (as shown in the list of available slots).'), - }), - execute: async ({ slotId }, { ctx }) => { - const slot = this._slotsMap.get(slotId); - if (!slot) { - throw new llm.ToolError(`error: slot ${slotId} was not found`); - } - // Note: The Python version uses beta.workflows.GetEmailTask which is not available in TypeScript yet - // For now, we'll use a placeholder email - const placeholderEmail = 'user@example.com'; - console.warn('Note: Email collection workflow not implemented in TypeScript version yet. Using placeholder email.'); - try { - await ctx.userData.cal.scheduleAppointment({ - startTime: slot.startTime, - attendeeEmail: placeholderEmail, - }); - } - catch (error) { - if (error instanceof SlotUnavailableError) { - throw new llm.ToolError("This slot isn't available anymore"); - } - throw error; - } - const local = new Date(slot.startTime.toLocaleString('en-US', { timeZone: this.tz })); - const formatted = local.toLocaleDateString('en-US', { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - timeZoneName: 'short', - timeZone: this.tz, - }); - return `The appointment was successfully scheduled for ${formatted}.`; - }, - }), - listAvailableSlots: llm.tool({ - description: `Return a plain-text list of available slots, one per line. - - - , , at () - -You must infer the appropriate range implicitly from the conversational context and must not prompt the user to pick a value explicitly.`, - parameters: z.object({ - range: z - .enum(['+2week', '+1month', '+3month', 'default']) - .describe('Determines how far ahead to search for free time slots.'), - }), - execute: async ({ range }, { ctx }) => { - const now = new Date(); - const lines = []; - let rangeDays; - if (range === '+2week' || range === 'default') { - rangeDays = 14; - } - else if (range === '+1month') { - rangeDays = 30; - } - else if (range === '+3month') { - rangeDays = 90; - } - else { - rangeDays = 14; - } - const endTime = new Date(now.getTime() + rangeDays * 24 * 60 * 60 * 1000); - const slots = await ctx.userData.cal.listAvailableSlots({ - startTime: now, - endTime: endTime, - }); - for (const slot of slots) { - const local = new Date(slot.startTime.toLocaleString('en-US', { timeZone: this.tz })); - const delta = local.getTime() - now.getTime(); - const days = Math.floor(delta / (24 * 60 * 60 * 1000)); - const seconds = Math.floor((delta % (24 * 60 * 60 * 1000)) / 1000); - let rel; - if (local.toDateString() === now.toDateString()) { - if (seconds < 3600) { - rel = 'in less than an hour'; - } - else { - rel = 'later today'; - } - } - else if (local.toDateString() === - new Date(now.getTime() + 24 * 60 * 60 * 1000).toDateString()) { - rel = 'tomorrow'; - } - else if (days < 7) { - rel = `in ${days} days`; - } - else if (days < 14) { - rel = 'in 1 week'; - } - else { - rel = `in ${Math.floor(days / 7)} weeks`; - } - const uniqueHash = getUniqueHash(slot); - const formatted = local.toLocaleDateString('en-US', { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - timeZoneName: 'short', - timeZone: this.tz, - }); - lines.push(`${uniqueHash} - ${formatted} (${rel})`); - this._slotsMap.set(uniqueHash, slot); - } - return lines.join('\n') || 'No slots available at the moment.'; - }, - }), - }, - }); - this.tz = options.timezone; - } -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const timezone = 'UTC'; - let cal; - const calApiKey = process.env.CAL_API_KEY; - if (calApiKey) { - console.log('CAL_API_KEY detected, using cal.com calendar'); - cal = new CalComCalendar({ apiKey: calApiKey, timezone }); - } - else { - console.warn('CAL_API_KEY is not set. Falling back to FakeCalendar; set CAL_API_KEY to enable Cal.com integration.'); - cal = new FakeCalendar({ timezone }); - } - await cal.initialize(); - const userdata = { cal }; - const session = new voice.AgentSession({ - vad: ctx.proc.userData.vad, - stt: new deepgram.STT(), - llm: new openai.LLM({ - model: 'gpt-4.1', - }), - tts: new elevenlabs.TTS(), - turnDetection: new livekit.turnDetector.MultilingualModel(), - userData: userdata, - voiceOptions: { - maxToolSteps: 1, - }, - }); - await session.start({ - agent: new FrontDeskAgent({ timezone }), - room: ctx.room, - inputOptions: { - noiseCancellation: BackgroundVoiceCancellation(), - }, - }); - session.generateReply({ - userInput: 'Greet to the user', - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=frontdesk_agent.js.map \ No newline at end of file diff --git a/examples/src/frontdesk/frontdesk_agent.js.map b/examples/src/frontdesk/frontdesk_agent.js.map deleted file mode 100644 index 10fe2f383..000000000 --- a/examples/src/frontdesk/frontdesk_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"frontdesk_agent.js","sourceRoot":"","sources":["frontdesk_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/gemini_realtime_agent.js b/examples/src/gemini_realtime_agent.js deleted file mode 100644 index 789c60df2..000000000 --- a/examples/src/gemini_realtime_agent.js +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as google from '@livekit/agents-plugin-google'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -const roomNameSchema = z.enum(['bedroom', 'living room', 'kitchen', 'bathroom', 'office']); -const getWeather = llm.tool({ - description: ' Called when the user asks about the weather.', - parameters: z.object({ - location: z.string().describe('The location to get the weather for'), - }), - execute: async ({ location }) => { - return `The weather in ${location} is sunny today.`; - }, -}); -const toggleLight = llm.tool({ - description: 'Called when the user asks to turn on or off the light.', - parameters: z.object({ - room: roomNameSchema.describe('The room to turn the light in'), - switchTo: z.enum(['on', 'off']).describe('The state to turn the light to'), - }), - execute: async ({ room, switchTo }) => { - return `The light in the ${room} is now ${switchTo}.`; - }, -}); -// Use inheritance to create agent with custom hooks -class IntroAgent extends voice.Agent { - async onEnter() { - this.session.generateReply({ - instructions: '"greet the user and gather information"', - }); - } - static create() { - return new IntroAgent({ - instructions: `You are a story teller. Your goal is to gather a few pieces of information from the user to make the story personalized and engaging. Ask the user for their name and where they are from.`, - tools: { - informationGathered: llm.tool({ - description: 'Called when the user has provided the information needed to make the story personalized and engaging.', - parameters: z.object({ - name: z.string().describe('The name of the user'), - location: z.string().describe('The location of the user'), - }), - execute: async ({ name, location }, { ctx }) => { - ctx.userData.name = name; - ctx.userData.location = location; - const storyAgent = StoryAgent.create(name, location); - return llm.handoff({ agent: storyAgent, returns: "Let's start the story!" }); - }, - }), - getWeather, - toggleLight, - }, - }); - } -} -class StoryAgent extends voice.Agent { - async onEnter() { - this.session.generateReply(); - } - static create(name, location) { - return new StoryAgent({ - instructions: `You are a storyteller. Use the user's information in order to make the story personalized. - The user's name is ${name}, from ${location}`, - }); - } -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const userdata = {}; - const session = new voice.AgentSession({ - vad: ctx.proc.userData.vad, - llm: new google.beta.realtime.RealtimeModel(), - userData: userdata, - }); - await session.start({ - agent: IntroAgent.create(), - room: ctx.room, - }); - const participant = await ctx.waitForParticipant(); - console.log('participant joined: ', participant.identity); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=gemini_realtime_agent.js.map \ No newline at end of file diff --git a/examples/src/gemini_realtime_agent.js.map b/examples/src/gemini_realtime_agent.js.map deleted file mode 100644 index f9ab6507f..000000000 --- a/examples/src/gemini_realtime_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"gemini_realtime_agent.js","sourceRoot":"","sources":["gemini_realtime_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/multi_agent.js b/examples/src/multi_agent.js deleted file mode 100644 index 7e61bb426..000000000 --- a/examples/src/multi_agent.js +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -// Use inheritance to create agent with custom hooks -class IntroAgent extends voice.Agent { - async onEnter() { - this.session.generateReply({ - instructions: '"greet the user and gather information"', - }); - } - static create() { - return new IntroAgent({ - instructions: `You are a story teller. Your goal is to gather a few pieces of information from the user to make the story personalized and engaging. Ask the user for their name and where they are from.`, - tools: { - informationGathered: llm.tool({ - description: 'Called when the user has provided the information needed to make the story personalized and engaging.', - parameters: z.object({ - name: z.string().describe('The name of the user'), - location: z.string().describe('The location of the user'), - }), - execute: async ({ name, location }, { ctx }) => { - ctx.userData.name = name; - ctx.userData.location = location; - const storyAgent = StoryAgent.create(name, location); - return llm.handoff({ agent: storyAgent, returns: "Let's start the story!" }); - }, - }), - }, - }); - } -} -class StoryAgent extends voice.Agent { - async onEnter() { - this.session.generateReply(); - } - static create(name, location) { - return new StoryAgent({ - instructions: `You are a storyteller. Use the user's information in order to make the story personalized. - The user's name is ${name}, from ${location}`, - }); - } -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const userdata = {}; - const session = new voice.AgentSession({ - vad: ctx.proc.userData.vad, - stt: new deepgram.STT(), - tts: new elevenlabs.TTS(), - llm: new openai.LLM(), - // to use realtime model, replace the stt, llm, tts and vad with the following - // llm: new openai.realtime.RealtimeModel(), - userData: userdata, - turnDetection: new livekit.turnDetector.EnglishModel(), - }); - await session.start({ - agent: IntroAgent.create(), - room: ctx.room, - }); - const participant = await ctx.waitForParticipant(); - console.log('participant joined: ', participant.identity); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=multi_agent.js.map \ No newline at end of file diff --git a/examples/src/multi_agent.js.map b/examples/src/multi_agent.js.map deleted file mode 100644 index 83f64b600..000000000 --- a/examples/src/multi_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"multi_agent.js","sourceRoot":"","sources":["multi_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/examples/src/push_to_talk.js b/examples/src/push_to_talk.js deleted file mode 100644 index e569e6396..000000000 --- a/examples/src/push_to_talk.js +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -class MyAgent extends voice.Agent { - async onUserTurnCompleted(chatCtx, newMessage) { - if (!newMessage.textContent || newMessage.textContent.length === 0) { - console.log('ignore empty user turn'); - throw new voice.StopResponse(); - } - } -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const session = new voice.AgentSession({ - vad: ctx.proc.userData.vad, - stt: new deepgram.STT(), - llm: new openai.LLM(), - tts: new elevenlabs.TTS(), - turnDetection: 'manual', - }); - const agent = new MyAgent({ - instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", - }); - await session.start({ - agent, - room: ctx.room, - }); - ctx.room.localParticipant?.registerRpcMethod('start_turn', async () => { - session.interrupt(); - session.clearUserTurn(); - return 'ok'; - }); - ctx.room.localParticipant?.registerRpcMethod('end_turn', async () => { - session.commitUserTurn(); - return 'ok'; - }); - ctx.room.localParticipant?.registerRpcMethod('cancel_turn', async () => { - session.clearUserTurn(); - return 'ok'; - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=push_to_talk.js.map \ No newline at end of file diff --git a/examples/src/push_to_talk.js.map b/examples/src/push_to_talk.js.map deleted file mode 100644 index bb378f024..000000000 --- a/examples/src/push_to_talk.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"push_to_talk.js","sourceRoot":"","sources":["push_to_talk.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAQ,SAAQ,KAAK,CAAC,KAAK;IAC/B,KAAK,CAAC,mBAAmB,CAAC,OAAoB,EAAE,UAAuB;QACrE,IAAI,CAAC,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;CACF;AAED,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB;YACzC,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,aAAa,EAAE,QAAQ;SACxB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC;YACxB,YAAY,EACV,iFAAiF;SACpF,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YACpE,OAAO,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YAClE,OAAO,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;YACrE,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/raw_function_description.js b/examples/src/raw_function_description.js deleted file mode 100644 index 7e70a7140..000000000 --- a/examples/src/raw_function_description.js +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -function createRawFunctionAgent() { - return new voice.Agent({ - instructions: 'You are a helpful assistant.', - tools: { - openGate: llm.tool({ - description: 'Opens a specified gate from a predefined set of access points.', - parameters: { - type: 'object', - properties: { - gateId: { - type: 'string', - description: 'The ID of the gate to open', - enum: [ - 'main_entrance', - 'north_parking', - 'loading_dock', - 'side_gate', - 'service_entry', - ], - }, - }, - required: ['gateId'], - additionalProperties: false, - }, - execute: async ({ gateId }) => { - return `The gate ${gateId} is now open.`; - }, - }), - }, - }); -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - stt: new deepgram.STT({ - sampleRate: 24000, - }), - tts: new elevenlabs.TTS(), - llm: new openai.LLM(), - // to use realtime model, replace the stt, llm, tts and vad with the following - // llm: new openai.realtime.RealtimeModel(), - userData: { number: 0 }, - turnDetection: new livekit.turnDetector.EnglishModel(), - }); - await session.start({ - agent: createRawFunctionAgent(), - room: ctx.room, - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=raw_function_description.js.map \ No newline at end of file diff --git a/examples/src/raw_function_description.js.map b/examples/src/raw_function_description.js.map deleted file mode 100644 index 521b01cd0..000000000 --- a/examples/src/raw_function_description.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"raw_function_description.js","sourceRoot":"","sources":["raw_function_description.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,GAAG,EACH,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,SAAS,sBAAsB;IAC7B,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC;QACrB,YAAY,EAAE,8BAA8B;QAC5C,KAAK,EAAE;YACL,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC;gBACjB,WAAW,EAAE,gEAAgE;gBAC7E,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,4BAA4B;4BACzC,IAAI,EAAE;gCACJ,eAAe;gCACf,eAAe;gCACf,cAAc;gCACd,WAAW;gCACX,eAAe;6BAChB;yBACF;qBACF;oBACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;oBACpB,oBAAoB,EAAE,KAAK;iBAC5B;gBACD,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;oBAC5B,OAAO,YAAY,MAAM,eAAe,CAAC;gBAC3C,CAAC;aACF,CAAC;SACH;KACF,CAAC,CAAC;AACL,CAAC;AAED,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB,CAAC;QAEjD,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG;YACH,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC;gBACpB,UAAU,EAAE,KAAK;aAClB,CAAC;YACF,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,GAAG,EAAE,IAAI,MAAM,CAAC,GAAG,EAAE;YACrB,8EAA8E;YAC9E,4CAA4C;YAC5C,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;YACvB,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE;SACvD,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK,EAAE,sBAAsB,EAAE;YAC/B,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/realtime_agent.js b/examples/src/realtime_agent.js deleted file mode 100644 index 34f4f6bfa..000000000 --- a/examples/src/realtime_agent.js +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -const roomNameSchema = z.enum(['bedroom', 'living room', 'kitchen', 'bathroom', 'office']); -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const getWeather = llm.tool({ - description: ' Called when the user asks about the weather.', - parameters: z.object({ - location: z.string().describe('The location to get the weather for'), - }), - execute: async ({ location }) => { - return `The weather in ${location} is sunny today.`; - }, - }); - const toggleLight = llm.tool({ - description: 'Called when the user asks to turn on or off the light.', - parameters: z.object({ - room: roomNameSchema.describe('The room to turn the light in'), - switchTo: z.enum(['on', 'off']).describe('The state to turn the light to'), - }), - execute: async ({ room, switchTo }) => { - return `The light in the ${room} is now ${switchTo}.`; - }, - }); - const agent = new voice.Agent({ - instructions: "You are a helpful assistant created by LiveKit, always speaking English, you can hear the user's message and respond to it.", - tools: { - getWeather, - toggleLight, - }, - }); - const session = new voice.AgentSession({ - // llm: new openai.realtime.RealtimeModel(), - llm: new openai.realtime.RealtimeModel(), - // enable to allow chaining of tool calls - voiceOptions: { - maxToolSteps: 5, - }, - }); - await session.start({ - agent, - room: ctx.room, - }); - session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { - console.log('metrics_collected', ev); - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=realtime_agent.js.map \ No newline at end of file diff --git a/examples/src/realtime_agent.js.map b/examples/src/realtime_agent.js.map deleted file mode 100644 index 06d902137..000000000 --- a/examples/src/realtime_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"realtime_agent.js","sourceRoot":"","sources":["realtime_agent.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,GAAG,EACH,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE3F,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;YAC1B,WAAW,EAAE,+CAA+C;YAC5D,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;gBACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;aACrE,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;gBAC9B,OAAO,kBAAkB,QAAQ,kBAAkB,CAAC;YACtD,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;YAC3B,WAAW,EAAE,wDAAwD;YACrE,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;gBACnB,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAA+B,CAAC;gBAC9D,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,gCAAgC,CAAC;aAC3E,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;gBACpC,OAAO,oBAAoB,IAAI,WAAW,QAAQ,GAAG,CAAC;YACxD,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAC5B,YAAY,EACV,6HAA6H;YAC/H,KAAK,EAAE;gBACL,UAAU;gBACV,WAAW;aACZ;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,4CAA4C;YAC5C,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE;YACxC,yCAAyC;YACzC,YAAY,EAAE;gBACZ,YAAY,EAAE,CAAC;aAChB;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK;YACL,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YAC/D,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/realtime_turn_detector.js b/examples/src/realtime_turn_detector.js deleted file mode 100644 index 151eb1801..000000000 --- a/examples/src/realtime_turn_detector.js +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const session = new voice.AgentSession({ - vad: ctx.proc.userData.vad, - stt: new deepgram.STT(), - tts: new elevenlabs.TTS(), - // To use OpenAI Realtime API - llm: new openai.realtime.RealtimeModel({ - voice: 'alloy', - // it's necessary to turn off turn detection in the OpenAI Realtime API in order to use - // LiveKit's turn detection model - turnDetection: null, - inputAudioTranscription: null, - }), - turnDetection: new livekit.turnDetector.EnglishModel(), - }); - await session.start({ - agent: new voice.Agent({ - instructions: "You are a helpful assistant, you can hear the user's message and respond to it.", - }), - room: ctx.room, - }); - session.say('Hello, how can I help you today?'); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=realtime_turn_detector.js.map \ No newline at end of file diff --git a/examples/src/realtime_turn_detector.js.map b/examples/src/realtime_turn_detector.js.map deleted file mode 100644 index a374a46ae..000000000 --- a/examples/src/realtime_turn_detector.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"realtime_turn_detector.js","sourceRoot":"","sources":["realtime_turn_detector.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAGL,aAAa,EACb,GAAG,EACH,WAAW,EACX,KAAK,GACN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,UAAU,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAe,WAAW,CAAC;IACzB,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,KAAK,EAAE,GAAe,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC;YACrC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAkB;YACzC,GAAG,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE;YACvB,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE;YACzB,6BAA6B;YAC7B,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;gBACrC,KAAK,EAAE,OAAO;gBACd,uFAAuF;gBACvF,iCAAiC;gBACjC,aAAa,EAAE,IAAI;gBACnB,uBAAuB,EAAE,IAAI;aAC9B,CAAC;YACF,aAAa,EAAE,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE;SACvD,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC;YAClB,KAAK,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC;gBACrB,YAAY,EACV,iFAAiF;aACpF,CAAC;YACF,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/src/restaurant_agent.js b/examples/src/restaurant_agent.js deleted file mode 100644 index 77ad23c9f..000000000 --- a/examples/src/restaurant_agent.js +++ /dev/null @@ -1,338 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -import { WorkerOptions, cli, defineAgent, llm, voice, } from '@livekit/agents'; -import * as deepgram from '@livekit/agents-plugin-deepgram'; -import * as elevenlabs from '@livekit/agents-plugin-elevenlabs'; -import * as livekit from '@livekit/agents-plugin-livekit'; -import * as openai from '@livekit/agents-plugin-openai'; -import * as silero from '@livekit/agents-plugin-silero'; -import { fileURLToPath } from 'node:url'; -import { z } from 'zod'; -const voices = { - greeter: { - id: '9BWtsMINqrJLrRacOk9x', // Aria - calm, professional female voice - name: 'Aria', - category: 'premade', - }, - reservation: { - id: 'EXAVITQu4vr4xnSDxMaL', // Sarah - warm, reassuring professional tone - name: 'Sarah', - category: 'premade', - }, - takeaway: { - id: 'CwhRBWXzGAHq8TQ4Fs17', // Roger - confident middle-aged male - name: 'Roger', - category: 'premade', - }, - checkout: { - id: '5Q0t7uMcjvnagumLfvZi', // Paul - authoritative middle-aged male - name: 'Paul', - category: 'premade', - }, -}; -function createUserData(agents) { - return { - customer: {}, - creditCard: {}, - agents, - }; -} -function summarize({ customer, reservationTime, order, creditCard, expense, checkedOut, }) { - return JSON.stringify({ - customer: customer.name ?? 'unknown', - customerPhone: customer.phone ?? 'unknown', - reservationTime: reservationTime ?? 'unknown', - order: order ?? 'unknown', - creditCard: creditCard - ? { - number: creditCard.number ?? 'unknown', - expiry: creditCard.expiry ?? 'unknown', - cvv: creditCard.cvv ?? 'unknown', - } - : undefined, - expense: expense ?? 'unknown', - checkedOut: checkedOut ?? false, - }, null, 2); -} -const updateName = llm.tool({ - description: 'Called when the user provides their name. Confirm the spelling with the user before calling the function.', - parameters: z.object({ - name: z.string().describe('The customer name'), - }), - execute: async ({ name }, { ctx }) => { - ctx.userData.customer.name = name; - return `The name is updated to ${name}`; - }, -}); -const updatePhone = llm.tool({ - description: 'Called when the user provides their phone number. Confirm the spelling with the user before calling the function.', - parameters: z.object({ - phone: z.string().describe('The customer phone number'), - }), - execute: async ({ phone }, { ctx }) => { - ctx.userData.customer.phone = phone; - return `The phone number is updated to ${phone}`; - }, -}); -const toGreeter = llm.tool({ - description: 'Called when user asks any unrelated questions or requests any other services not in your job description.', - execute: async (_, { ctx }) => { - const currAgent = ctx.session.currentAgent; - return await currAgent.transferToAgent({ - name: 'greeter', - ctx, - }); - }, -}); -class BaseAgent extends voice.Agent { - name; - constructor(options) { - const { name, ...opts } = options; - super(opts); - this.name = name; - } - async onEnter() { - const userdata = this.session.userData; - const chatCtx = this.chatCtx.copy(); - // add the previous agent's chat history to the current agent - if (userdata.prevAgent) { - const truncatedChatCtx = userdata.prevAgent.chatCtx - .copy({ - excludeInstructions: true, - excludeFunctionCall: false, - }) - .truncate(6); - const existingIds = new Set(chatCtx.items.map((item) => item.id)); - const newItems = truncatedChatCtx.items.filter((item) => !existingIds.has(item.id)); - chatCtx.items.push(...newItems); - } - // add an instructions including the user data as system message - chatCtx.addMessage({ - role: 'system', - content: `You are ${this.name} agent. Current user data is ${summarize(userdata)}`, - }); - await this.updateChatCtx(chatCtx); - this.session.generateReply({ toolChoice: 'none' }); - } - async transferToAgent(options) { - const { name, ctx } = options; - const userdata = ctx.userData; - const currentAgent = ctx.session.currentAgent; - const nextAgent = userdata.agents[name]; - if (!nextAgent) { - throw new Error(`Agent ${name} not found`); - } - userdata.prevAgent = currentAgent; - return llm.handoff({ - agent: nextAgent, - returns: `Transferring to ${name}`, - }); - } -} -function createGreeterAgent(menu) { - const greeter = new BaseAgent({ - name: 'greeter', - instructions: `You are a friendly restaurant receptionist. The menu is: ${menu}\nYour jobs are to greet the caller and understand if they want to make a reservation or order takeaway. Guide them to the right agent using tools.`, - // TODO(brian): support parallel tool calls - tts: new elevenlabs.TTS({ voice: voices.greeter }), - tools: { - toReservation: llm.tool({ - description: `Called when user wants to make or update a reservation. - This function handles transitioning to the reservation agent - who will collect the necessary details like reservation time, - customer name and phone number.`, - execute: async (_, { ctx }) => { - return await greeter.transferToAgent({ - name: 'reservation', - ctx, - }); - }, - }), - toTakeaway: llm.tool({ - description: `Called when the user wants to place a takeaway order. - This includes handling orders for pickup, delivery, or when the user wants to - proceed to checkout with their existing order.`, - execute: async (_, { ctx }) => { - return await greeter.transferToAgent({ - name: 'takeaway', - ctx, - }); - }, - }), - }, - }); - return greeter; -} -function createReservationAgent() { - const reservation = new BaseAgent({ - name: 'reservation', - instructions: `You are a reservation agent at a restaurant. Your jobs are to ask for the reservation time, then customer's name, and phone number. Then confirm the reservation details with the customer.`, - tts: new elevenlabs.TTS({ voice: voices.reservation }), - tools: { - updateName, - updatePhone, - toGreeter, - updateReservationTime: llm.tool({ - description: `Called when the user provides their reservation time. - Confirm the time with the user before calling the function.`, - parameters: z.object({ - time: z.string().describe('The reservation time'), - }), - execute: async ({ time }, { ctx }) => { - ctx.userData.reservationTime = time; - return `The reservation time is updated to ${time}`; - }, - }), - confirmReservation: llm.tool({ - description: `Called when the user confirms the reservation.`, - execute: async (_, { ctx }) => { - const userdata = ctx.userData; - if (!userdata.customer.name || !userdata.customer.phone) { - return 'Please provide your name and phone number first.'; - } - if (!userdata.reservationTime) { - return 'Please provide reservation time first.'; - } - return await reservation.transferToAgent({ - name: 'greeter', - ctx, - }); - }, - }), - }, - }); - return reservation; -} -function createTakeawayAgent(menu) { - const takeaway = new BaseAgent({ - name: 'takeaway', - instructions: `Your are a takeaway agent that takes orders from the customer. Our menu is: ${menu}\nClarify special requests and confirm the order with the customer.`, - tts: new elevenlabs.TTS({ voice: voices.takeaway }), - tools: { - toGreeter, - updateOrder: llm.tool({ - description: `Called when the user provides their order.`, - parameters: z.object({ - items: z.array(z.string()).describe('The items of the full order'), - }), - execute: async ({ items }, { ctx }) => { - ctx.userData.order = items; - return `The order is updated to ${items}`; - }, - }), - toCheckout: llm.tool({ - description: `Called when the user confirms the order.`, - execute: async (_, { ctx }) => { - const userdata = ctx.userData; - if (!userdata.order) { - return 'No takeaway order found. Please make an order first.'; - } - return await takeaway.transferToAgent({ - name: 'checkout', - ctx, - }); - }, - }), - }, - }); - return takeaway; -} -function createCheckoutAgent(menu) { - const checkout = new BaseAgent({ - name: 'checkout', - instructions: `You are a checkout agent at a restaurant. The menu is: ${menu}\nYour are responsible for confirming the expense of the order and then collecting customer's name, phone number and credit card information, including the card number, expiry date, and CVV step by step.`, - tts: new elevenlabs.TTS({ voice: voices.checkout }), - tools: { - updateName, - updatePhone, - toGreeter, - confirmExpense: llm.tool({ - description: `Called when the user confirms the expense.`, - parameters: z.object({ - expense: z.number().describe('The expense of the order'), - }), - execute: async ({ expense }, { ctx }) => { - ctx.userData.expense = expense; - return `The expense is confirmed to be ${expense}`; - }, - }), - updateCreditCard: llm.tool({ - description: `Called when the user provides their credit card number, expiry date, and CVV. - Confirm the spelling with the user before calling the function.`, - parameters: z.object({ - number: z.string().describe('The credit card number'), - expiry: z.string().describe('The expiry date of the credit card'), - cvv: z.string().describe('The CVV of the credit card'), - }), - execute: async ({ number, expiry, cvv }, { ctx }) => { - ctx.userData.creditCard = { number, expiry, cvv }; - return `The credit card number is updated to ${number}`; - }, - }), - confirmCheckout: llm.tool({ - description: `Called when the user confirms the checkout.`, - execute: async (_, { ctx }) => { - const userdata = ctx.userData; - if (!userdata.expense) { - return 'Please confirm the expense first.'; - } - if (!userdata.creditCard.number || - !userdata.creditCard.expiry || - !userdata.creditCard.cvv) { - return 'Please provide the credit card information first.'; - } - userdata.checkedOut = true; - return await checkout.transferToAgent({ - name: 'greeter', - ctx, - }); - }, - }), - toTakeaway: llm.tool({ - description: `Called when the user wants to update their order.`, - execute: async (_, { ctx }) => { - return await checkout.transferToAgent({ - name: 'takeaway', - ctx, - }); - }, - }), - }, - }); - return checkout; -} -export default defineAgent({ - prewarm: async (proc) => { - proc.userData.vad = await silero.VAD.load(); - }, - entry: async (ctx) => { - const menu = 'Pizza: $10, Salad: $5, Ice Cream: $3, Coffee: $2'; - const userData = createUserData({ - greeter: createGreeterAgent(menu), - reservation: createReservationAgent(), - takeaway: createTakeawayAgent(menu), - checkout: createCheckoutAgent(menu), - }); - const vad = ctx.proc.userData.vad; - const session = new voice.AgentSession({ - vad, - stt: new deepgram.STT(), - tts: new elevenlabs.TTS(), - llm: new openai.LLM(), - // to use realtime model, replace the stt, llm, tts and vad with the following - // llm: new openai.realtime.RealtimeModel(), - turnDetection: new livekit.turnDetector.EnglishModel(), - userData, - voiceOptions: { - maxToolSteps: 5, - }, - }); - await session.start({ - agent: userData.agents.greeter, - room: ctx.room, - }); - }, -}); -cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); -//# sourceMappingURL=restaurant_agent.js.map \ No newline at end of file diff --git a/examples/src/restaurant_agent.js.map b/examples/src/restaurant_agent.js.map deleted file mode 100644 index 9a1db5e42..000000000 --- a/examples/src/restaurant_agent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"restaurant_agent.js","sourceRoot":"","sources":["restaurant_agent.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/plugins/anam/src/api.ts b/plugins/anam/src/api.ts index b76da2613..f30a89c0e 100644 --- a/plugins/anam/src/api.ts +++ b/plugins/anam/src/api.ts @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: 2025 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { AnamException, type APIConnectOptions } from './types.js'; import { log } from '@livekit/agents'; +import { type APIConnectOptions, AnamException } from './types.js'; const DEFAULT_API_URL = 'https://api.anam.ai'; @@ -26,11 +26,7 @@ export class AnamAPI { } private get startPath(): string { - return ( - this.paths.startPath || - process.env.ANAM_SESSION_START_PATH || - '/v1/engine/session' - ); + return this.paths.startPath || process.env.ANAM_SESSION_START_PATH || '/v1/engine/session'; } private async postWithHeaders( @@ -69,7 +65,16 @@ export class AnamAPI { return body; })(); - logger.debug({ url, method: 'POST', headers: redactedHeaders, body: redactedBody, attempt: attempt + 1 }, 'calling Anam API'); + logger.debug( + { + url, + method: 'POST', + headers: redactedHeaders, + body: redactedBody, + attempt: attempt + 1, + }, + 'calling Anam API', + ); const res = await fetch(url, { method: 'POST', @@ -79,7 +84,17 @@ export class AnamAPI { }); if (!res.ok) { const text = await res.text(); - logger.error({ url, method: 'POST', headers: redactedHeaders, body: redactedBody, status: res.status, response: text }, 'Anam API request failed'); + logger.error( + { + url, + method: 'POST', + headers: redactedHeaders, + body: redactedBody, + status: res.status, + response: text, + }, + 'Anam API request failed', + ); throw new AnamException(`Anam ${path} failed: ${res.status} ${text}`); } const json = (await res.json()) as T; @@ -88,8 +103,20 @@ export class AnamAPI { } catch (e) { lastErr = e; if (attempt === maxRetry - 1) break; - logger.warn({ url, method: 'POST', body: (body && typeof body === 'object') ? { ...(body as Record), livekitToken: '****' } : body, error: (e as Error)?.message, nextRetrySec: retryInterval }, 'Anam API error, retrying'); - await new Promise(r => setTimeout(r, retryInterval * 1000)); + logger.warn( + { + url, + method: 'POST', + body: + body && typeof body === 'object' + ? { ...(body as Record), livekitToken: '****' } + : body, + error: (e as Error)?.message, + nextRetrySec: retryInterval, + }, + 'Anam API error, retrying', + ); + await new Promise((r) => setTimeout(r, retryInterval * 1000)); } } throw lastErr instanceof Error ? lastErr : new AnamException('Anam API error'); @@ -108,11 +135,11 @@ export class AnamAPI { // Build payload per API spec const pc = params.personaConfig; const personaPayload = { - type: 'ephemeral', - name: pc.name, - avatarId: pc.avatarId, - llmId: 'CUSTOMER_CLIENT_V1', - }; + type: 'ephemeral', + name: pc.name, + avatarId: pc.avatarId, + llmId: 'CUSTOMER_CLIENT_V1', + }; const payload: Record = { personaConfig: personaPayload, diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index af67b08b4..5bc9c8fd8 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -1,38 +1,37 @@ // SPDX-FileCopyrightText: 2025 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { Room, TrackKind } from '@livekit/rtc-node'; -import { voice, log } from '@livekit/agents'; -import { AnamAPI, } from './api.js'; -import { AnamException, type PersonaConfig, type APIConnectOptions } from './types.js'; +import type { Room } from '@livekit/rtc-node'; +import { TrackKind } from '@livekit/rtc-node'; import { AccessToken, type VideoGrant } from 'livekit-server-sdk'; +import { AnamAPI } from './api.js'; +import { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js'; export async function mintAvatarJoinToken({ - roomName, - avatarIdentity, // e.g. `anam:${roomName}` - publishOnBehalf, // your agent's identity - apiKey = process.env.LIVEKIT_API_KEY!, - apiSecret = process.env.LIVEKIT_API_SECRET!, - ttl = '60s', - }: { - roomName: string; - avatarIdentity: string; - publishOnBehalf: string; - apiKey?: string; - apiSecret?: string; - ttl?: string | number; - }): Promise { - const at = new AccessToken(apiKey, apiSecret); - at.identity = avatarIdentity; - at.name = 'Anam Avatar'; - at.kind = 'agent'; // avatar joins as Agent - at.ttl = ttl; - at.attributes = { 'lk.publish_on_behalf': publishOnBehalf }; - - at.addGrant({ roomJoin: true, room: roomName } as VideoGrant); - return at.toJwt(); - } + roomName, + avatarIdentity, // e.g. `anam:${roomName}` + publishOnBehalf, // your agent's identity + apiKey = process.env.LIVEKIT_API_KEY!, + apiSecret = process.env.LIVEKIT_API_SECRET!, + ttl = '60s', +}: { + roomName: string; + avatarIdentity: string; + publishOnBehalf: string; + apiKey?: string; + apiSecret?: string; + ttl?: string | number; +}): Promise { + const at = new AccessToken(apiKey, apiSecret); + at.identity = avatarIdentity; + at.name = 'Anam Avatar'; + at.kind = 'agent'; // avatar joins as Agent + at.ttl = ttl; + at.attributes = { 'lk.publish_on_behalf': publishOnBehalf }; + at.addGrant({ roomJoin: true, room: roomName } as VideoGrant); + return at.toJwt(); +} const AVATAR_IDENTITY = 'anam-avatar-agent'; const AVATAR_NAME = 'anam-avatar-agent'; @@ -48,7 +47,7 @@ export class AvatarSession { avatarParticipantIdentity?: string; avatarParticipantName?: string; connOptions?: APIConnectOptions; - } + }, ) {} async start( @@ -58,7 +57,7 @@ export class AvatarSession { livekitUrl?: string; livekitApiKey?: string; livekitApiSecret?: string; - } + }, ) { const logger = log().child({ module: 'AnamAvatar' }); const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY; @@ -68,7 +67,8 @@ export class AvatarSession { const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL; const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY; const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET; - const devMode = Boolean(apiUrl && apiUrl.includes('anam.dev')) || process.env.ANAM_DEV_MODE === '1'; + const devMode = + Boolean(apiUrl && apiUrl.includes('anam.dev')) || process.env.ANAM_DEV_MODE === '1'; if (!devMode) { if (!livekitUrl || !lkKey || !lkSecret) { @@ -77,17 +77,19 @@ export class AvatarSession { } // who are we publishing on behalf of? - const localIdentity = - (room.localParticipant && room.localParticipant.identity) || 'agent'; + const localIdentity = (room.localParticipant && room.localParticipant.identity) || 'agent'; - logger.debug({ - personaName: this.opts.personaConfig?.name, - avatarId: this.opts.personaConfig?.avatarId, - apiUrl: apiUrl ?? '(default https://api.anam.ai)', - livekitUrl, - avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent', - publishOnBehalf: localIdentity, - }, 'starting Anam avatar session'); + logger.debug( + { + personaName: this.opts.personaConfig?.name, + avatarId: this.opts.personaConfig?.avatarId, + apiUrl: apiUrl ?? '(default https://api.anam.ai)', + livekitUrl, + avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent', + publishOnBehalf: localIdentity, + }, + 'starting Anam avatar session', + ); // build a LiveKit token for the avatar worker (mirrors Python) let jwt: string | undefined = undefined; diff --git a/plugins/anam/src/index.ts b/plugins/anam/src/index.ts index 11e0f59ef..0281ca0fc 100644 --- a/plugins/anam/src/index.ts +++ b/plugins/anam/src/index.ts @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 import { Plugin } from '@livekit/agents'; + export * from './types.js'; export * from './api.js'; export * from './avatar.js'; @@ -15,4 +16,4 @@ class AnamPlugin extends Plugin { }); } } -Plugin.registerPlugin(new AnamPlugin()); \ No newline at end of file +Plugin.registerPlugin(new AnamPlugin()); diff --git a/plugins/anam/src/types.ts b/plugins/anam/src/types.ts index ecc21ecf6..48e849101 100644 --- a/plugins/anam/src/types.ts +++ b/plugins/anam/src/types.ts @@ -2,18 +2,18 @@ // // SPDX-License-Identifier: Apache-2.0 export type PersonaConfig = { - /** Optional display name (prod flow) */ - name?: string; - /** Optional avatar asset id (prod flow) */ - avatarId?: string; - /** Optional persona id (dev flow) */ - personaId?: string; + /** Optional display name (prod flow) */ + name?: string; + /** Optional avatar asset id (prod flow) */ + avatarId?: string; + /** Optional persona id (dev flow) */ + personaId?: string; }; export type APIConnectOptions = { - maxRetry?: number; - retryInterval?: number; // seconds - timeout?: number; // seconds + maxRetry?: number; + retryInterval?: number; // seconds + timeout?: number; // seconds }; export class AnamException extends Error {} diff --git a/scripts/copyDeclarationOutput.js b/scripts/copyDeclarationOutput.js deleted file mode 100644 index 0f44f3b8e..000000000 --- a/scripts/copyDeclarationOutput.js +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2025 LiveKit, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// @ts-check -import fs from 'fs'; -import path from 'path'; - -/** - * @param {string} dir - * @param {(filePath: string) => void} callback - */ -const walkDir = (dir, callback) => { - fs.readdirSync(dir).forEach((f) => { - const dirPath = path.join(dir, f); - const isDirectory = fs.statSync(dirPath).isDirectory(); - isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f)); - }); -}; - -/** - * @param {string} dir - * @param {(filePath: string) => void} callback - */ -walkDir('dist', (filePath) => { - if (filePath.endsWith('.d.ts')) { - const newPath = filePath.replace(/\.d\.ts$/, '.d.cts'); - fs.copyFileSync(filePath, newPath); - } -}); -console.log('copied declaration .d.ts files to .d.cts files'); From 587006dbae6437fd0f04e638e492e0f821bd1f7d Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:16:01 +0100 Subject: [PATCH 20/27] fix --- examples/src/anam_realtime_agent.ts | 11 ++--------- plugins/anam/src/api.ts | 18 +++++------------- plugins/anam/src/avatar.ts | 2 +- turbo.json | 6 +++++- 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/examples/src/anam_realtime_agent.ts b/examples/src/anam_realtime_agent.ts index eac51631c..f631b3684 100644 --- a/examples/src/anam_realtime_agent.ts +++ b/examples/src/anam_realtime_agent.ts @@ -1,13 +1,7 @@ // SPDX-FileCopyrightText: 2025 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { - type JobContext, - WorkerOptions, - cli, - defineAgent, - voice, -} from '@livekit/agents'; +import { type JobContext, WorkerOptions, cli, defineAgent, voice } from '@livekit/agents'; import * as anam from '@livekit/agents-plugin-anam'; import * as openai from '@livekit/agents-plugin-openai'; import { fileURLToPath } from 'node:url'; @@ -17,8 +11,7 @@ import { fileURLToPath } from 'node:url'; export default defineAgent({ entry: async (ctx: JobContext) => { const agent = new voice.Agent({ - instructions: - "You are a helpful assistant. Speak clearly and concisely.", + instructions: 'You are a helpful assistant. Speak clearly and concisely.', }); const session = new voice.AgentSession({ diff --git a/plugins/anam/src/api.ts b/plugins/anam/src/api.ts index f30a89c0e..afdf22741 100644 --- a/plugins/anam/src/api.ts +++ b/plugins/anam/src/api.ts @@ -2,31 +2,23 @@ // // SPDX-License-Identifier: Apache-2.0 import { log } from '@livekit/agents'; -import { type APIConnectOptions, AnamException } from './types.js'; +import { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js'; const DEFAULT_API_URL = 'https://api.anam.ai'; -type APIPaths = { tokenPath?: string; startPath?: string }; - export class AnamAPI { constructor( private apiKey: string, private apiUrl: string = DEFAULT_API_URL, private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 }, - private paths: APIPaths = {}, ) {} private get tokenPath(): string { - return ( - this.paths.tokenPath || - process.env.ANAM_SESSION_TOKEN_PATH || - // Default to the v1 auth endpoint; older '/sessions/token' is deprecated - '/v1/auth/session-token' - ); + return '/v1/auth/session-token'; } private get startPath(): string { - return this.paths.startPath || process.env.ANAM_SESSION_START_PATH || '/v1/engine/session'; + return '/v1/engine/session'; } private async postWithHeaders( @@ -35,7 +27,7 @@ export class AnamAPI { headersIn: Record, ): Promise { const url = `${this.apiUrl}${path}`; - const { maxRetry = 3, retryInterval = 2, timeout = 10 } = this.conn; + const { maxRetry = 3, retryInterval = 2 } = this.conn; let lastErr: unknown; const logger = log().child({ module: 'AnamAPI' }); @@ -128,7 +120,7 @@ export class AnamAPI { } createSessionToken(params: { - personaConfig: import('./types.js').PersonaConfig; + personaConfig: PersonaConfig; livekitUrl?: string; livekitToken?: string; }) { diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index 5bc9c8fd8..da58b42b5 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -34,7 +34,7 @@ export async function mintAvatarJoinToken({ } const AVATAR_IDENTITY = 'anam-avatar-agent'; -const AVATAR_NAME = 'anam-avatar-agent'; +const _AVATAR_NAME = 'anam-avatar-agent'; export class AvatarSession { private sessionId?: string; diff --git a/turbo.json b/turbo.json index df1ac0e64..c0071b58c 100644 --- a/turbo.json +++ b/turbo.json @@ -34,7 +34,11 @@ "GOOGLE_GENAI_API_KEY", "GOOGLE_GENAI_USE_VERTEXAI", "GOOGLE_CLOUD_PROJECT", - "GOOGLE_CLOUD_LOCATION" + "GOOGLE_CLOUD_LOCATION", + "ANAM_API_KEY", + "ANAM_API_URL", + "ANAM_PERSONA_NAME", + "ANAM_AVATAR_ID" ], "pipeline": { "build": { From d738923ca7cf7a6115d16b89ae398c8cdfe06a1e Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:17:48 +0100 Subject: [PATCH 21/27] fix --- plugins/anam/src/avatar.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index da58b42b5..3d8e0e251 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 +import { log, voice } from '@livekit/agents'; import type { Room } from '@livekit/rtc-node'; import { TrackKind } from '@livekit/rtc-node'; import { AccessToken, type VideoGrant } from 'livekit-server-sdk'; From 0c5eb6fa1cee84547b89a69390dbe2b0f0224f17 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:20:12 +0100 Subject: [PATCH 22/27] fix --- scripts/copyDeclarationOutput.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 scripts/copyDeclarationOutput.js diff --git a/scripts/copyDeclarationOutput.js b/scripts/copyDeclarationOutput.js new file mode 100644 index 000000000..0f44f3b8e --- /dev/null +++ b/scripts/copyDeclarationOutput.js @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// @ts-check +import fs from 'fs'; +import path from 'path'; + +/** + * @param {string} dir + * @param {(filePath: string) => void} callback + */ +const walkDir = (dir, callback) => { + fs.readdirSync(dir).forEach((f) => { + const dirPath = path.join(dir, f); + const isDirectory = fs.statSync(dirPath).isDirectory(); + isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f)); + }); +}; + +/** + * @param {string} dir + * @param {(filePath: string) => void} callback + */ +walkDir('dist', (filePath) => { + if (filePath.endsWith('.d.ts')) { + const newPath = filePath.replace(/\.d\.ts$/, '.d.cts'); + fs.copyFileSync(filePath, newPath); + } +}); +console.log('copied declaration .d.ts files to .d.cts files'); From 53b1dca59dc831def75f66528f25003b2b539ac5 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:22:05 +0100 Subject: [PATCH 23/27] fix --- plugins/anam/src/avatar.ts | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index 3d8e0e251..8811c142b 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -68,13 +68,9 @@ export class AvatarSession { const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL; const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY; const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET; - const devMode = - Boolean(apiUrl && apiUrl.includes('anam.dev')) || process.env.ANAM_DEV_MODE === '1'; - if (!devMode) { - if (!livekitUrl || !lkKey || !lkSecret) { - throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set'); - } + if (!livekitUrl || !lkKey || !lkSecret) { + throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set'); } // who are we publishing on behalf of? @@ -93,19 +89,16 @@ export class AvatarSession { ); // build a LiveKit token for the avatar worker (mirrors Python) - let jwt: string | undefined = undefined; - if (!devMode) { - jwt = await mintAvatarJoinToken({ - roomName: room.name!, - avatarIdentity: this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY, - publishOnBehalf: localIdentity, - apiKey: lkKey, - apiSecret: lkSecret, - }); - } + const jwt = await mintAvatarJoinToken({ + roomName: room.name!, + avatarIdentity: this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY, + publishOnBehalf: localIdentity, + apiKey: lkKey, + apiSecret: lkSecret, + }); const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions); - logger.debug({ livekitUrl, devMode }, 'requesting Anam session token'); + logger.debug({ livekitUrl }, 'requesting Anam session token'); const { sessionToken } = await anam.createSessionToken({ personaConfig: { From 91b9fcb6fae15161de65268c25fde6241aaa0349 Mon Sep 17 00:00:00 2001 From: karlson lee Date: Fri, 12 Sep 2025 14:52:43 +0100 Subject: [PATCH 24/27] fix --- examples/src/anam_realtime_agent.ts | 17 ++++++++++++++++- examples/tsconfig.json | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/examples/src/anam_realtime_agent.ts b/examples/src/anam_realtime_agent.ts index f631b3684..432c8ca82 100644 --- a/examples/src/anam_realtime_agent.ts +++ b/examples/src/anam_realtime_agent.ts @@ -1,14 +1,25 @@ // SPDX-FileCopyrightText: 2025 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { type JobContext, WorkerOptions, cli, defineAgent, voice } from '@livekit/agents'; +import { + type JobContext, + type JobProcess, + WorkerOptions, + cli, + defineAgent, + voice, +} from '@livekit/agents'; import * as anam from '@livekit/agents-plugin-anam'; import * as openai from '@livekit/agents-plugin-openai'; +import * as silero from '@livekit/agents-plugin-silero'; import { fileURLToPath } from 'node:url'; // Uses OpenAI Advanced Voice (Realtime), so no separate STT/TTS/VAD. export default defineAgent({ + prewarm: async (proc: JobProcess) => { + proc.userData.vad = await silero.VAD.load(); + }, entry: async (ctx: JobContext) => { const agent = new voice.Agent({ instructions: 'You are a helpful assistant. Speak clearly and concisely.', @@ -46,6 +57,10 @@ export default defineAgent({ }); await avatar.start(session, ctx.room); + session.on(voice.AgentSessionEventTypes.MetricsCollected, (ev) => { + console.log('metrics_collected', ev); + }); + // With Realtime LLM, generateReply will synthesize audio via the model session.generateReply({ instructions: 'Greet the user briefly and confirm you are ready.', diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 80eeb2e0e..eb44cef99 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -4,6 +4,7 @@ "compilerOptions": { // match output dir to input dir. e.g. dist/index instead of dist/src/index "rootDir": "./src", - "declarationDir": "./dist" + "declarationDir": "./dist", + "outDir": "./dist" } } From e3a89b5cf8c1aa4ceea55e7c30bcf4ba8e9e454b Mon Sep 17 00:00:00 2001 From: karlson lee Date: Tue, 23 Sep 2025 07:10:00 +0100 Subject: [PATCH 25/27] fix --- pnpm-lock.yaml | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78c260896..b4e53b412 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -222,7 +222,7 @@ importers: dependencies: livekit-server-sdk: specifier: ^2.9.2 - version: 2.9.2 + version: 2.13.3 ws: specifier: ^8.16.0 version: 8.18.3 @@ -1243,92 +1243,78 @@ packages: resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.0': resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.0': resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.0': resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.0': resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.0': resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.0': resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.34.3': resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.34.3': resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-ppc64@0.34.3': resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.34.3': resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.34.3': resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.3': resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.34.3': resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.34.3': resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} @@ -1456,14 +1442,12 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@livekit/rtc-node-linux-x64-gnu@0.13.13': resolution: {integrity: sha512-B/SgbeBRobpA5LqmDEoBJHpRXePpoF4RO4F0zJf9BdkDhOR0j77p6hD0ZiOuPTRoBzUqukpsTszp+lZnHoNmiA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@livekit/rtc-node-win32-x64-msvc@0.13.13': resolution: {integrity: sha512-ygVYV4eHczs3QdaW/p0ADhhm7InUDhFaCYk8OzzIn056ZibZPXzvPizCThZqs8VsDniA01MraZF3qhZZb8IyRg==} @@ -1604,121 +1588,101 @@ packages: resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-gnueabihf@4.40.0': resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.17.2': resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm-musleabihf@4.40.0': resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.17.2': resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-gnu@4.40.0': resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.17.2': resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-musl@4.40.0': resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.40.0': resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.17.2': resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.17.2': resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.40.0': resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.40.0': resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.17.2': resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.40.0': resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.17.2': resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.40.0': resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.17.2': resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-linux-x64-musl@4.40.0': resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.17.2': resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} From 7b5dfd4cb2c286ef6eab35a20d4c247a5e7d194d Mon Sep 17 00:00:00 2001 From: karlson lee Date: Tue, 23 Sep 2025 07:13:45 +0100 Subject: [PATCH 26/27] fix --- plugins/anam/src/api.ts | 5 ----- plugins/anam/src/avatar.ts | 9 +++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/plugins/anam/src/api.ts b/plugins/anam/src/api.ts index afdf22741..242198d4d 100644 --- a/plugins/anam/src/api.ts +++ b/plugins/anam/src/api.ts @@ -38,7 +38,6 @@ export class AnamAPI { ...headersIn, }; - // redact token for logs const redactedHeaders: Record = { ...headers }; if (redactedHeaders.Authorization) { redactedHeaders.Authorization = 'Bearer ****'; @@ -115,7 +114,6 @@ export class AnamAPI { } private async post(path: string, body: unknown): Promise { - // Default auth with API key return this.postWithHeaders(path, body, { Authorization: `Bearer ${this.apiKey}` }); } @@ -124,7 +122,6 @@ export class AnamAPI { livekitUrl?: string; livekitToken?: string; }) { - // Build payload per API spec const pc = params.personaConfig; const personaPayload = { type: 'ephemeral', @@ -145,8 +142,6 @@ export class AnamAPI { } startEngineSession(params: { sessionToken: string }) { - // Per API, startEngineSession must authorize with the Session Token, - // not the API key. return this.postWithHeaders<{ sessionId: string }>( this.startPath, {}, diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index 8811c142b..b592c7dd8 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -10,8 +10,8 @@ import { type APIConnectOptions, AnamException, type PersonaConfig } from './typ export async function mintAvatarJoinToken({ roomName, - avatarIdentity, // e.g. `anam:${roomName}` - publishOnBehalf, // your agent's identity + avatarIdentity, + publishOnBehalf, apiKey = process.env.LIVEKIT_API_KEY!, apiSecret = process.env.LIVEKIT_API_SECRET!, ttl = '60s', @@ -26,7 +26,7 @@ export async function mintAvatarJoinToken({ const at = new AccessToken(apiKey, apiSecret); at.identity = avatarIdentity; at.name = 'Anam Avatar'; - at.kind = 'agent'; // avatar joins as Agent + at.kind = 'agent'; at.ttl = ttl; at.attributes = { 'lk.publish_on_behalf': publishOnBehalf }; @@ -73,7 +73,6 @@ export class AvatarSession { throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set'); } - // who are we publishing on behalf of? const localIdentity = (room.localParticipant && room.localParticipant.identity) || 'agent'; logger.debug( @@ -88,7 +87,6 @@ export class AvatarSession { 'starting Anam avatar session', ); - // build a LiveKit token for the avatar worker (mirrors Python) const jwt = await mintAvatarJoinToken({ roomName: room.name!, avatarIdentity: this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY, @@ -112,7 +110,6 @@ export class AvatarSession { const started = await anam.startEngineSession({ sessionToken }); this.sessionId = started.sessionId; - // route the agent audio to the avatar via data channel & wait for video agentSession.output.audio = new voice.DataStreamAudioOutput({ room, destinationIdentity: this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY, From ea108827620921c66207d12d0d9ece38c2c469ab Mon Sep 17 00:00:00 2001 From: karlson lee Date: Tue, 23 Sep 2025 07:41:39 +0100 Subject: [PATCH 27/27] fix --- plugins/anam/src/avatar.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/anam/src/avatar.ts b/plugins/anam/src/avatar.ts index b592c7dd8..ab19cba88 100644 --- a/plugins/anam/src/avatar.ts +++ b/plugins/anam/src/avatar.ts @@ -10,8 +10,8 @@ import { type APIConnectOptions, AnamException, type PersonaConfig } from './typ export async function mintAvatarJoinToken({ roomName, - avatarIdentity, - publishOnBehalf, + avatarIdentity, + publishOnBehalf, apiKey = process.env.LIVEKIT_API_KEY!, apiSecret = process.env.LIVEKIT_API_SECRET!, ttl = '60s', @@ -26,7 +26,7 @@ export async function mintAvatarJoinToken({ const at = new AccessToken(apiKey, apiSecret); at.identity = avatarIdentity; at.name = 'Anam Avatar'; - at.kind = 'agent'; + at.kind = 'agent'; at.ttl = ttl; at.attributes = { 'lk.publish_on_behalf': publishOnBehalf };