diff --git a/.changeset/add-anam-plugin.md b/.changeset/add-anam-plugin.md new file mode 100644 index 00000000..26fc1a0b --- /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. diff --git a/examples/package.json b/examples/package.json index b5ae131d..3ac34554 100644 --- a/examples/package.json +++ b/examples/package.json @@ -22,15 +22,16 @@ }, "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", "livekit-server-sdk": "^2.13.3", diff --git a/examples/src/anam_realtime_agent.ts b/examples/src/anam_realtime_agent.ts new file mode 100644 index 00000000..432c8ca8 --- /dev/null +++ b/examples/src/anam_realtime_agent.ts @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +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.', + }); + + 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); + + 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.', + }); + }, +}); + +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) })); diff --git a/plugins/anam/CHANGELOG.md b/plugins/anam/CHANGELOG.md new file mode 100644 index 00000000..47f5c942 --- /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 00000000..3b7cc417 --- /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 00000000..1f75e070 --- /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 00000000..cef91059 --- /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 00000000..242198d4 --- /dev/null +++ b/plugins/anam/src/api.ts @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { log } from '@livekit/agents'; +import { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js'; + +const DEFAULT_API_URL = 'https://api.anam.ai'; + +export class AnamAPI { + constructor( + private apiKey: string, + private apiUrl: string = DEFAULT_API_URL, + private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 }, + ) {} + + private get tokenPath(): string { + return '/v1/auth/session-token'; + } + + private get startPath(): string { + return '/v1/engine/session'; + } + + private async postWithHeaders( + path: string, + body: unknown, + headersIn: Record, + ): Promise { + const url = `${this.apiUrl}${path}`; + const { maxRetry = 3, retryInterval = 2 } = 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, + }; + + 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 { + return this.postWithHeaders(path, body, { Authorization: `Bearer ${this.apiKey}` }); + } + + createSessionToken(params: { + personaConfig: PersonaConfig; + livekitUrl?: string; + livekitToken?: string; + }) { + const pc = params.personaConfig; + const personaPayload = { + type: 'ephemeral', + name: pc.name, + avatarId: pc.avatarId, + llmId: 'CUSTOMER_CLIENT_V1', + }; + + const payload: Record = { + personaConfig: personaPayload, + }; + payload.environment = { + livekitUrl: params.livekitUrl, + livekitToken: params.livekitToken, + }; + + return this.post<{ sessionToken: string }>(this.tokenPath, payload); + } + + startEngineSession(params: { sessionToken: string }) { + 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 00000000..ab19cba8 --- /dev/null +++ b/plugins/anam/src/avatar.ts @@ -0,0 +1,119 @@ +// 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'; +import { AnamAPI } from './api.js'; +import { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js'; + +export async function mintAvatarJoinToken({ + roomName, + avatarIdentity, + publishOnBehalf, + 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'; + 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; + + if (!livekitUrl || !lkKey || !lkSecret) { + throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set'); + } + + 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', + ); + + 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 }, '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; + + 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 00000000..0281ca0f --- /dev/null +++ b/plugins/anam/src/index.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +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()); diff --git a/plugins/anam/src/types.ts b/plugins/anam/src/types.ts new file mode 100644 index 00000000..48e84910 --- /dev/null +++ b/plugins/anam/src/types.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2025 LiveKit, Inc. +// +// 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; +}; + +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 00000000..dacffdfe --- /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 00000000..8ca20961 --- /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 90406faf..b4e53b41 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 @@ -215,6 +218,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.13.3 + 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: @@ -1206,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==} @@ -1419,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==} @@ -1567,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==} diff --git a/turbo.json b/turbo.json index df1ac0e6..c0071b58 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": {