@livekit/agents-plugin-anam 1.4.9 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.cjs CHANGED
@@ -121,11 +121,14 @@ class AnamAPI {
121
121
  }
122
122
  createSessionToken(params) {
123
123
  const pc = params.personaConfig;
124
- const personaPayload = {
124
+ const personaPayload = pc.personaId ? { personaId: pc.personaId } : {
125
125
  type: "ephemeral",
126
126
  name: pc.name,
127
127
  avatarId: pc.avatarId,
128
- llmId: "CUSTOMER_CLIENT_V1"
128
+ llmId: "CUSTOMER_CLIENT_V1",
129
+ // Only forward the avatar model version when set; otherwise let Anam
130
+ // fall back to the avatar's default model.
131
+ ...pc.avatarModel ? { avatarModel: pc.avatarModel } : {}
129
132
  };
130
133
  const payload = {
131
134
  personaConfig: personaPayload
@@ -134,6 +137,17 @@ class AnamAPI {
134
137
  livekitUrl: params.livekitUrl,
135
138
  livekitToken: params.livekitToken
136
139
  };
140
+ if (params.sessionOptions && (params.sessionOptions.videoWidth !== void 0 || params.sessionOptions.videoHeight !== void 0)) {
141
+ if (params.sessionOptions.videoWidth === void 0 || params.sessionOptions.videoHeight === void 0) {
142
+ throw new import_types.AnamException(
143
+ "videoWidth and videoHeight must be set together (both or neither)"
144
+ );
145
+ }
146
+ payload.sessionOptions = {
147
+ videoWidth: params.sessionOptions.videoWidth,
148
+ videoHeight: params.sessionOptions.videoHeight
149
+ };
150
+ }
137
151
  return this.post(this.tokenPath, payload);
138
152
  }
139
153
  startEngineSession(params) {
package/dist/api.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '@livekit/agents';\nimport { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js';\n\nconst DEFAULT_API_URL = 'https://api.anam.ai';\n\nexport class AnamAPI {\n constructor(\n private apiKey: string,\n private apiUrl: string = DEFAULT_API_URL,\n private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 },\n ) {}\n\n private get tokenPath(): string {\n return '/v1/auth/session-token';\n }\n\n private get startPath(): string {\n return '/v1/engine/session';\n }\n\n private async postWithHeaders<T>(\n path: string,\n body: unknown,\n headersIn: Record<string, string>,\n ): Promise<T> {\n const url = `${this.apiUrl}${path}`;\n const { maxRetry = 3, retryInterval = 2 } = this.conn;\n let lastErr: unknown;\n const logger = log().child({ module: 'AnamAPI' });\n\n for (let attempt = 0; attempt < maxRetry; attempt++) {\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headersIn,\n };\n\n const redactedHeaders: Record<string, string> = { ...headers };\n if (redactedHeaders.Authorization) {\n redactedHeaders.Authorization = 'Bearer ****';\n }\n const redactedBody = (() => {\n if (body && typeof body === 'object') {\n try {\n const clone = { ...(body as Record<string, unknown>) } as Record<string, unknown>;\n if ('livekitToken' in clone) clone.livekitToken = '****';\n if ('sessionToken' in clone) clone.sessionToken = '****' as unknown as never;\n return clone;\n } catch {\n return { note: 'unserializable body' };\n }\n }\n return body;\n })();\n\n logger.debug(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n attempt: attempt + 1,\n },\n 'calling Anam API',\n );\n\n const res = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n // simple timeout: rely on AbortController in real impl\n });\n if (!res.ok) {\n const text = await res.text();\n logger.error(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n status: res.status,\n response: text,\n },\n 'Anam API request failed',\n );\n throw new AnamException(`Anam ${path} failed: ${res.status} ${text}`);\n }\n const json = (await res.json()) as T;\n logger.debug({ url }, 'Anam API request succeeded');\n return json;\n } catch (e) {\n lastErr = e;\n if (attempt === maxRetry - 1) break;\n logger.warn(\n {\n url,\n method: 'POST',\n body:\n body && typeof body === 'object'\n ? { ...(body as Record<string, unknown>), livekitToken: '****' }\n : body,\n error: (e as Error)?.message,\n nextRetrySec: retryInterval,\n },\n 'Anam API error, retrying',\n );\n await new Promise((r) => setTimeout(r, retryInterval * 1000));\n }\n }\n throw lastErr instanceof Error ? lastErr : new AnamException('Anam API error');\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n return this.postWithHeaders<T>(path, body, { Authorization: `Bearer ${this.apiKey}` });\n }\n\n createSessionToken(params: {\n personaConfig: PersonaConfig;\n livekitUrl?: string;\n livekitToken?: string;\n }) {\n const pc = params.personaConfig;\n const personaPayload = {\n type: 'ephemeral',\n name: pc.name,\n avatarId: pc.avatarId,\n llmId: 'CUSTOMER_CLIENT_V1',\n };\n\n const payload: Record<string, unknown> = {\n personaConfig: personaPayload,\n };\n payload.environment = {\n livekitUrl: params.livekitUrl,\n livekitToken: params.livekitToken,\n };\n\n return this.post<{ sessionToken: string }>(this.tokenPath, payload);\n }\n\n startEngineSession(params: { sessionToken: string }) {\n return this.postWithHeaders<{ sessionId: string }>(\n this.startPath,\n {},\n { Authorization: `Bearer ${params.sessionToken}` },\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/api.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '@livekit/agents';\nimport {\n type APIConnectOptions,\n AnamException,\n type PersonaConfig,\n type SessionOptions,\n} from './types.js';\n\nconst DEFAULT_API_URL = 'https://api.anam.ai';\n\n/** @public */\nexport class AnamAPI {\n constructor(\n private apiKey: string,\n private apiUrl: string = DEFAULT_API_URL,\n private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 },\n ) {}\n\n private get tokenPath(): string {\n return '/v1/auth/session-token';\n }\n\n private get startPath(): string {\n return '/v1/engine/session';\n }\n\n private async postWithHeaders<T>(\n path: string,\n body: unknown,\n headersIn: Record<string, string>,\n ): Promise<T> {\n const url = `${this.apiUrl}${path}`;\n const { maxRetry = 3, retryInterval = 2 } = this.conn;\n let lastErr: unknown;\n const logger = log().child({ module: 'AnamAPI' });\n\n for (let attempt = 0; attempt < maxRetry; attempt++) {\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headersIn,\n };\n\n const redactedHeaders: Record<string, string> = { ...headers };\n if (redactedHeaders.Authorization) {\n redactedHeaders.Authorization = 'Bearer ****';\n }\n const redactedBody = (() => {\n if (body && typeof body === 'object') {\n try {\n const clone = { ...(body as Record<string, unknown>) } as Record<string, unknown>;\n if ('livekitToken' in clone) clone.livekitToken = '****';\n if ('sessionToken' in clone) clone.sessionToken = '****' as unknown as never;\n return clone;\n } catch {\n return { note: 'unserializable body' };\n }\n }\n return body;\n })();\n\n logger.debug(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n attempt: attempt + 1,\n },\n 'calling Anam API',\n );\n\n const res = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n // simple timeout: rely on AbortController in real impl\n });\n if (!res.ok) {\n const text = await res.text();\n logger.error(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n status: res.status,\n response: text,\n },\n 'Anam API request failed',\n );\n throw new AnamException(`Anam ${path} failed: ${res.status} ${text}`);\n }\n const json = (await res.json()) as T;\n logger.debug({ url }, 'Anam API request succeeded');\n return json;\n } catch (e) {\n lastErr = e;\n if (attempt === maxRetry - 1) break;\n logger.warn(\n {\n url,\n method: 'POST',\n body:\n body && typeof body === 'object'\n ? { ...(body as Record<string, unknown>), livekitToken: '****' }\n : body,\n error: (e as Error)?.message,\n nextRetrySec: retryInterval,\n },\n 'Anam API error, retrying',\n );\n await new Promise((r) => setTimeout(r, retryInterval * 1000));\n }\n }\n throw lastErr instanceof Error ? lastErr : new AnamException('Anam API error');\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n return this.postWithHeaders<T>(path, body, { Authorization: `Bearer ${this.apiKey}` });\n }\n\n createSessionToken(params: {\n personaConfig: PersonaConfig;\n livekitUrl?: string;\n livekitToken?: string;\n sessionOptions?: SessionOptions;\n }) {\n const pc = params.personaConfig;\n // Anam's personaConfig is a `oneOf`: reference a previously created\n // (stateful) persona by `personaId` — the \"dev flow\" — or configure an\n // ephemeral persona inline with name/avatarId/llmId. The two are mutually\n // exclusive, so when a personaId is given we must not also send the\n // ephemeral fields.\n const personaPayload: Record<string, unknown> = pc.personaId\n ? { personaId: pc.personaId }\n : {\n type: 'ephemeral',\n name: pc.name,\n avatarId: pc.avatarId,\n llmId: 'CUSTOMER_CLIENT_V1',\n // Only forward the avatar model version when set; otherwise let Anam\n // fall back to the avatar's default model.\n ...(pc.avatarModel ? { avatarModel: pc.avatarModel } : {}),\n };\n\n const payload: Record<string, unknown> = {\n personaConfig: personaPayload,\n };\n payload.environment = {\n livekitUrl: params.livekitUrl,\n livekitToken: params.livekitToken,\n };\n\n if (\n params.sessionOptions &&\n (params.sessionOptions.videoWidth !== undefined ||\n params.sessionOptions.videoHeight !== undefined)\n ) {\n if (\n params.sessionOptions.videoWidth === undefined ||\n params.sessionOptions.videoHeight === undefined\n ) {\n throw new AnamException(\n 'videoWidth and videoHeight must be set together (both or neither)',\n );\n }\n payload.sessionOptions = {\n videoWidth: params.sessionOptions.videoWidth,\n videoHeight: params.sessionOptions.videoHeight,\n };\n }\n\n return this.post<{ sessionToken: string }>(this.tokenPath, payload);\n }\n\n startEngineSession(params: { sessionToken: string }) {\n return this.postWithHeaders<{ sessionId: string }>(\n this.startPath,\n {},\n { Authorization: `Bearer ${params.sessionToken}` },\n );\n }\n}\n"],"mappings":"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","names":[]}
package/dist/api.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- import { type APIConnectOptions, type PersonaConfig } from './types.js';
1
+ import { type APIConnectOptions, type PersonaConfig, type SessionOptions } from './types.js';
2
+ /** @public */
2
3
  export declare class AnamAPI {
3
4
  private apiKey;
4
5
  private apiUrl;
@@ -12,6 +13,7 @@ export declare class AnamAPI {
12
13
  personaConfig: PersonaConfig;
13
14
  livekitUrl?: string;
14
15
  livekitToken?: string;
16
+ sessionOptions?: SessionOptions;
15
17
  }): Promise<{
16
18
  sessionToken: string;
17
19
  }>;
package/dist/api.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type APIConnectOptions, type PersonaConfig } from './types.js';
1
+ import { type APIConnectOptions, type PersonaConfig, type SessionOptions } from './types.js';
2
+ /** @public */
2
3
  export declare class AnamAPI {
3
4
  private apiKey;
4
5
  private apiUrl;
@@ -12,6 +13,7 @@ export declare class AnamAPI {
12
13
  personaConfig: PersonaConfig;
13
14
  livekitUrl?: string;
14
15
  livekitToken?: string;
16
+ sessionOptions?: SessionOptions;
15
17
  }): Promise<{
16
18
  sessionToken: string;
17
19
  }>;
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAIvF,qBAAa,OAAO;IAEhB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,IAAI;gBAFJ,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAwB,EAChC,IAAI,GAAE,iBAAkE;IAGlF,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,KAAK,SAAS,GAEpB;YAEa,eAAe;YA4Ff,IAAI;IAIlB,kBAAkB,CAAC,MAAM,EAAE;QACzB,aAAa,EAAE,aAAa,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;sBAiBkC,MAAM;;IAGzC,kBAAkB,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE;mBACR,MAAM;;CAMlD"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAIpB,cAAc;AACd,qBAAa,OAAO;IAEhB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,IAAI;gBAFJ,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAwB,EAChC,IAAI,GAAE,iBAAkE;IAGlF,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,KAAK,SAAS,GAEpB;YAEa,eAAe;YA4Ff,IAAI;IAIlB,kBAAkB,CAAC,MAAM,EAAE;QACzB,aAAa,EAAE,aAAa,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,cAAc,CAAC,EAAE,cAAc,CAAC;KACjC;sBA8CkC,MAAM;;IAGzC,kBAAkB,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE;mBACR,MAAM;;CAMlD"}
package/dist/api.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { log } from "@livekit/agents";
2
- import { AnamException } from "./types.js";
2
+ import {
3
+ AnamException
4
+ } from "./types.js";
3
5
  const DEFAULT_API_URL = "https://api.anam.ai";
4
6
  class AnamAPI {
5
7
  constructor(apiKey, apiUrl = DEFAULT_API_URL, conn = { maxRetry: 3, retryInterval: 2, timeout: 10 }) {
@@ -98,11 +100,14 @@ class AnamAPI {
98
100
  }
99
101
  createSessionToken(params) {
100
102
  const pc = params.personaConfig;
101
- const personaPayload = {
103
+ const personaPayload = pc.personaId ? { personaId: pc.personaId } : {
102
104
  type: "ephemeral",
103
105
  name: pc.name,
104
106
  avatarId: pc.avatarId,
105
- llmId: "CUSTOMER_CLIENT_V1"
107
+ llmId: "CUSTOMER_CLIENT_V1",
108
+ // Only forward the avatar model version when set; otherwise let Anam
109
+ // fall back to the avatar's default model.
110
+ ...pc.avatarModel ? { avatarModel: pc.avatarModel } : {}
106
111
  };
107
112
  const payload = {
108
113
  personaConfig: personaPayload
@@ -111,6 +116,17 @@ class AnamAPI {
111
116
  livekitUrl: params.livekitUrl,
112
117
  livekitToken: params.livekitToken
113
118
  };
119
+ if (params.sessionOptions && (params.sessionOptions.videoWidth !== void 0 || params.sessionOptions.videoHeight !== void 0)) {
120
+ if (params.sessionOptions.videoWidth === void 0 || params.sessionOptions.videoHeight === void 0) {
121
+ throw new AnamException(
122
+ "videoWidth and videoHeight must be set together (both or neither)"
123
+ );
124
+ }
125
+ payload.sessionOptions = {
126
+ videoWidth: params.sessionOptions.videoWidth,
127
+ videoHeight: params.sessionOptions.videoHeight
128
+ };
129
+ }
114
130
  return this.post(this.tokenPath, payload);
115
131
  }
116
132
  startEngineSession(params) {
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '@livekit/agents';\nimport { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js';\n\nconst DEFAULT_API_URL = 'https://api.anam.ai';\n\nexport class AnamAPI {\n constructor(\n private apiKey: string,\n private apiUrl: string = DEFAULT_API_URL,\n private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 },\n ) {}\n\n private get tokenPath(): string {\n return '/v1/auth/session-token';\n }\n\n private get startPath(): string {\n return '/v1/engine/session';\n }\n\n private async postWithHeaders<T>(\n path: string,\n body: unknown,\n headersIn: Record<string, string>,\n ): Promise<T> {\n const url = `${this.apiUrl}${path}`;\n const { maxRetry = 3, retryInterval = 2 } = this.conn;\n let lastErr: unknown;\n const logger = log().child({ module: 'AnamAPI' });\n\n for (let attempt = 0; attempt < maxRetry; attempt++) {\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headersIn,\n };\n\n const redactedHeaders: Record<string, string> = { ...headers };\n if (redactedHeaders.Authorization) {\n redactedHeaders.Authorization = 'Bearer ****';\n }\n const redactedBody = (() => {\n if (body && typeof body === 'object') {\n try {\n const clone = { ...(body as Record<string, unknown>) } as Record<string, unknown>;\n if ('livekitToken' in clone) clone.livekitToken = '****';\n if ('sessionToken' in clone) clone.sessionToken = '****' as unknown as never;\n return clone;\n } catch {\n return { note: 'unserializable body' };\n }\n }\n return body;\n })();\n\n logger.debug(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n attempt: attempt + 1,\n },\n 'calling Anam API',\n );\n\n const res = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n // simple timeout: rely on AbortController in real impl\n });\n if (!res.ok) {\n const text = await res.text();\n logger.error(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n status: res.status,\n response: text,\n },\n 'Anam API request failed',\n );\n throw new AnamException(`Anam ${path} failed: ${res.status} ${text}`);\n }\n const json = (await res.json()) as T;\n logger.debug({ url }, 'Anam API request succeeded');\n return json;\n } catch (e) {\n lastErr = e;\n if (attempt === maxRetry - 1) break;\n logger.warn(\n {\n url,\n method: 'POST',\n body:\n body && typeof body === 'object'\n ? { ...(body as Record<string, unknown>), livekitToken: '****' }\n : body,\n error: (e as Error)?.message,\n nextRetrySec: retryInterval,\n },\n 'Anam API error, retrying',\n );\n await new Promise((r) => setTimeout(r, retryInterval * 1000));\n }\n }\n throw lastErr instanceof Error ? lastErr : new AnamException('Anam API error');\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n return this.postWithHeaders<T>(path, body, { Authorization: `Bearer ${this.apiKey}` });\n }\n\n createSessionToken(params: {\n personaConfig: PersonaConfig;\n livekitUrl?: string;\n livekitToken?: string;\n }) {\n const pc = params.personaConfig;\n const personaPayload = {\n type: 'ephemeral',\n name: pc.name,\n avatarId: pc.avatarId,\n llmId: 'CUSTOMER_CLIENT_V1',\n };\n\n const payload: Record<string, unknown> = {\n personaConfig: personaPayload,\n };\n payload.environment = {\n livekitUrl: params.livekitUrl,\n livekitToken: params.livekitToken,\n };\n\n return this.post<{ sessionToken: string }>(this.tokenPath, payload);\n }\n\n startEngineSession(params: { sessionToken: string }) {\n return this.postWithHeaders<{ sessionId: string }>(\n this.startPath,\n {},\n { Authorization: `Bearer ${params.sessionToken}` },\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/api.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '@livekit/agents';\nimport {\n type APIConnectOptions,\n AnamException,\n type PersonaConfig,\n type SessionOptions,\n} from './types.js';\n\nconst DEFAULT_API_URL = 'https://api.anam.ai';\n\n/** @public */\nexport class AnamAPI {\n constructor(\n private apiKey: string,\n private apiUrl: string = DEFAULT_API_URL,\n private conn: APIConnectOptions = { maxRetry: 3, retryInterval: 2, timeout: 10 },\n ) {}\n\n private get tokenPath(): string {\n return '/v1/auth/session-token';\n }\n\n private get startPath(): string {\n return '/v1/engine/session';\n }\n\n private async postWithHeaders<T>(\n path: string,\n body: unknown,\n headersIn: Record<string, string>,\n ): Promise<T> {\n const url = `${this.apiUrl}${path}`;\n const { maxRetry = 3, retryInterval = 2 } = this.conn;\n let lastErr: unknown;\n const logger = log().child({ module: 'AnamAPI' });\n\n for (let attempt = 0; attempt < maxRetry; attempt++) {\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headersIn,\n };\n\n const redactedHeaders: Record<string, string> = { ...headers };\n if (redactedHeaders.Authorization) {\n redactedHeaders.Authorization = 'Bearer ****';\n }\n const redactedBody = (() => {\n if (body && typeof body === 'object') {\n try {\n const clone = { ...(body as Record<string, unknown>) } as Record<string, unknown>;\n if ('livekitToken' in clone) clone.livekitToken = '****';\n if ('sessionToken' in clone) clone.sessionToken = '****' as unknown as never;\n return clone;\n } catch {\n return { note: 'unserializable body' };\n }\n }\n return body;\n })();\n\n logger.debug(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n attempt: attempt + 1,\n },\n 'calling Anam API',\n );\n\n const res = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n // simple timeout: rely on AbortController in real impl\n });\n if (!res.ok) {\n const text = await res.text();\n logger.error(\n {\n url,\n method: 'POST',\n headers: redactedHeaders,\n body: redactedBody,\n status: res.status,\n response: text,\n },\n 'Anam API request failed',\n );\n throw new AnamException(`Anam ${path} failed: ${res.status} ${text}`);\n }\n const json = (await res.json()) as T;\n logger.debug({ url }, 'Anam API request succeeded');\n return json;\n } catch (e) {\n lastErr = e;\n if (attempt === maxRetry - 1) break;\n logger.warn(\n {\n url,\n method: 'POST',\n body:\n body && typeof body === 'object'\n ? { ...(body as Record<string, unknown>), livekitToken: '****' }\n : body,\n error: (e as Error)?.message,\n nextRetrySec: retryInterval,\n },\n 'Anam API error, retrying',\n );\n await new Promise((r) => setTimeout(r, retryInterval * 1000));\n }\n }\n throw lastErr instanceof Error ? lastErr : new AnamException('Anam API error');\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n return this.postWithHeaders<T>(path, body, { Authorization: `Bearer ${this.apiKey}` });\n }\n\n createSessionToken(params: {\n personaConfig: PersonaConfig;\n livekitUrl?: string;\n livekitToken?: string;\n sessionOptions?: SessionOptions;\n }) {\n const pc = params.personaConfig;\n // Anam's personaConfig is a `oneOf`: reference a previously created\n // (stateful) persona by `personaId` — the \"dev flow\" — or configure an\n // ephemeral persona inline with name/avatarId/llmId. The two are mutually\n // exclusive, so when a personaId is given we must not also send the\n // ephemeral fields.\n const personaPayload: Record<string, unknown> = pc.personaId\n ? { personaId: pc.personaId }\n : {\n type: 'ephemeral',\n name: pc.name,\n avatarId: pc.avatarId,\n llmId: 'CUSTOMER_CLIENT_V1',\n // Only forward the avatar model version when set; otherwise let Anam\n // fall back to the avatar's default model.\n ...(pc.avatarModel ? { avatarModel: pc.avatarModel } : {}),\n };\n\n const payload: Record<string, unknown> = {\n personaConfig: personaPayload,\n };\n payload.environment = {\n livekitUrl: params.livekitUrl,\n livekitToken: params.livekitToken,\n };\n\n if (\n params.sessionOptions &&\n (params.sessionOptions.videoWidth !== undefined ||\n params.sessionOptions.videoHeight !== undefined)\n ) {\n if (\n params.sessionOptions.videoWidth === undefined ||\n params.sessionOptions.videoHeight === undefined\n ) {\n throw new AnamException(\n 'videoWidth and videoHeight must be set together (both or neither)',\n );\n }\n payload.sessionOptions = {\n videoWidth: params.sessionOptions.videoWidth,\n videoHeight: params.sessionOptions.videoHeight,\n };\n }\n\n return this.post<{ sessionToken: string }>(this.tokenPath, payload);\n }\n\n startEngineSession(params: { sessionToken: string }) {\n return this.postWithHeaders<{ sessionId: string }>(\n this.startPath,\n {},\n { Authorization: `Bearer ${params.sessionToken}` },\n );\n }\n}\n"],"mappings":"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","names":[]}
package/dist/avatar.cjs CHANGED
@@ -59,7 +59,7 @@ class AvatarSession extends import_agents.voice.AvatarSession {
59
59
  return "anam";
60
60
  }
61
61
  async start(agentSession, room, params) {
62
- var _a, _b, _c, _d;
62
+ var _a, _b, _c;
63
63
  await super.start(agentSession, room);
64
64
  const logger = (0, import_agents.log)().child({ module: "AnamAvatar" });
65
65
  const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY;
@@ -76,6 +76,7 @@ class AvatarSession extends import_agents.voice.AvatarSession {
76
76
  {
77
77
  personaName: (_a = this.opts.personaConfig) == null ? void 0 : _a.name,
78
78
  avatarId: (_b = this.opts.personaConfig) == null ? void 0 : _b.avatarId,
79
+ personaId: (_c = this.opts.personaConfig) == null ? void 0 : _c.personaId,
79
80
  apiUrl: apiUrl ?? "(default https://api.anam.ai)",
80
81
  livekitUrl,
81
82
  avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? "anam-avatar-agent",
@@ -93,12 +94,10 @@ class AvatarSession extends import_agents.voice.AvatarSession {
93
94
  const anam = new import_api.AnamAPI(apiKey, apiUrl, this.opts.connOptions);
94
95
  logger.debug({ livekitUrl }, "requesting Anam session token");
95
96
  const { sessionToken } = await anam.createSessionToken({
96
- personaConfig: {
97
- name: (_c = this.opts.personaConfig) == null ? void 0 : _c.name,
98
- avatarId: (_d = this.opts.personaConfig) == null ? void 0 : _d.avatarId
99
- },
97
+ personaConfig: this.opts.personaConfig,
100
98
  livekitUrl,
101
- livekitToken: jwt
99
+ livekitToken: jwt,
100
+ sessionOptions: this.opts.sessionOptions
102
101
  });
103
102
  logger.debug("starting Anam engine session");
104
103
  const started = await anam.startEngineSession({ sessionToken });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/avatar.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log, voice } from '@livekit/agents';\nimport type { Room } from '@livekit/rtc-node';\nimport { TrackKind } from '@livekit/rtc-node';\nimport { AccessToken, type VideoGrant } from 'livekit-server-sdk';\nimport { AnamAPI } from './api.js';\nimport { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js';\n\nexport async function mintAvatarJoinToken({\n roomName,\n avatarIdentity,\n publishOnBehalf,\n apiKey = process.env.LIVEKIT_API_KEY!,\n apiSecret = process.env.LIVEKIT_API_SECRET!,\n ttl = '60s',\n}: {\n roomName: string;\n avatarIdentity: string;\n publishOnBehalf: string;\n apiKey?: string;\n apiSecret?: string;\n ttl?: string | number;\n}): Promise<string> {\n const at = new AccessToken(apiKey, apiSecret);\n at.identity = avatarIdentity;\n at.name = 'Anam Avatar';\n at.kind = 'agent';\n at.ttl = ttl;\n at.attributes = { 'lk.publish_on_behalf': publishOnBehalf };\n\n at.addGrant({ roomJoin: true, room: roomName } as VideoGrant);\n return at.toJwt();\n}\n\nconst AVATAR_IDENTITY = 'anam-avatar-agent';\nconst _AVATAR_NAME = 'anam-avatar-agent';\n\nexport class AvatarSession extends voice.AvatarSession {\n private sessionId?: string;\n\n constructor(\n private opts: {\n personaConfig: PersonaConfig;\n apiUrl?: string;\n apiKey?: string;\n avatarParticipantIdentity?: string;\n avatarParticipantName?: string;\n connOptions?: APIConnectOptions;\n },\n ) {\n super();\n }\n\n override get avatarIdentity(): string {\n return this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY;\n }\n\n override get provider(): string {\n return 'anam';\n }\n\n async start(\n agentSession: voice.AgentSession,\n room: Room,\n params?: {\n livekitUrl?: string;\n livekitApiKey?: string;\n livekitApiSecret?: string;\n },\n ) {\n await super.start(agentSession, room);\n\n const logger = log().child({ module: 'AnamAvatar' });\n const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY;\n if (!apiKey) throw new AnamException('ANAM_API_KEY is required');\n\n const apiUrl = this.opts.apiUrl ?? process.env.ANAM_API_URL;\n const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL;\n const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY;\n const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET;\n\n if (!livekitUrl || !lkKey || !lkSecret) {\n throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set');\n }\n\n const localIdentity = (room.localParticipant && room.localParticipant.identity) || 'agent';\n\n logger.debug(\n {\n personaName: this.opts.personaConfig?.name,\n avatarId: this.opts.personaConfig?.avatarId,\n apiUrl: apiUrl ?? '(default https://api.anam.ai)',\n livekitUrl,\n avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent',\n publishOnBehalf: localIdentity,\n },\n 'starting Anam avatar session',\n );\n\n const jwt = await mintAvatarJoinToken({\n roomName: room.name!,\n avatarIdentity: this.avatarIdentity,\n publishOnBehalf: localIdentity,\n apiKey: lkKey,\n apiSecret: lkSecret,\n });\n\n const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions);\n logger.debug({ livekitUrl }, 'requesting Anam session token');\n\n const { sessionToken } = await anam.createSessionToken({\n personaConfig: {\n name: this.opts.personaConfig?.name,\n avatarId: this.opts.personaConfig?.avatarId,\n },\n livekitUrl,\n livekitToken: jwt,\n });\n logger.debug('starting Anam engine session');\n const started = await anam.startEngineSession({ sessionToken });\n this.sessionId = started.sessionId;\n\n agentSession.output.audio = new voice.DataStreamAudioOutput({\n room,\n destinationIdentity: this.avatarIdentity,\n waitRemoteTrack: TrackKind.KIND_VIDEO,\n });\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/avatar.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log, voice } from '@livekit/agents';\nimport type { Room } from '@livekit/rtc-node';\nimport { TrackKind } from '@livekit/rtc-node';\nimport { AccessToken, type VideoGrant } from 'livekit-server-sdk';\nimport { AnamAPI } from './api.js';\nimport {\n type APIConnectOptions,\n AnamException,\n type PersonaConfig,\n type SessionOptions,\n} from './types.js';\n\n/** @public */\nexport async function mintAvatarJoinToken({\n roomName,\n avatarIdentity,\n publishOnBehalf,\n apiKey = process.env.LIVEKIT_API_KEY!,\n apiSecret = process.env.LIVEKIT_API_SECRET!,\n ttl = '60s',\n}: {\n roomName: string;\n avatarIdentity: string;\n publishOnBehalf: string;\n apiKey?: string;\n apiSecret?: string;\n ttl?: string | number;\n}): Promise<string> {\n const at = new AccessToken(apiKey, apiSecret);\n at.identity = avatarIdentity;\n at.name = 'Anam Avatar';\n at.kind = 'agent';\n at.ttl = ttl;\n at.attributes = { 'lk.publish_on_behalf': publishOnBehalf };\n\n at.addGrant({ roomJoin: true, room: roomName } as VideoGrant);\n return at.toJwt();\n}\n\nconst AVATAR_IDENTITY = 'anam-avatar-agent';\nconst _AVATAR_NAME = 'anam-avatar-agent';\n\n/** @public */\nexport class AvatarSession extends voice.AvatarSession {\n private sessionId?: string;\n\n constructor(\n private opts: {\n personaConfig: PersonaConfig;\n sessionOptions?: SessionOptions;\n apiUrl?: string;\n apiKey?: string;\n avatarParticipantIdentity?: string;\n avatarParticipantName?: string;\n connOptions?: APIConnectOptions;\n },\n ) {\n super();\n }\n\n override get avatarIdentity(): string {\n return this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY;\n }\n\n override get provider(): string {\n return 'anam';\n }\n\n async start(\n agentSession: voice.AgentSession,\n room: Room,\n params?: {\n livekitUrl?: string;\n livekitApiKey?: string;\n livekitApiSecret?: string;\n },\n ) {\n await super.start(agentSession, room);\n\n const logger = log().child({ module: 'AnamAvatar' });\n const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY;\n if (!apiKey) throw new AnamException('ANAM_API_KEY is required');\n\n const apiUrl = this.opts.apiUrl ?? process.env.ANAM_API_URL;\n const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL;\n const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY;\n const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET;\n\n if (!livekitUrl || !lkKey || !lkSecret) {\n throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set');\n }\n\n const localIdentity = (room.localParticipant && room.localParticipant.identity) || 'agent';\n\n logger.debug(\n {\n personaName: this.opts.personaConfig?.name,\n avatarId: this.opts.personaConfig?.avatarId,\n personaId: this.opts.personaConfig?.personaId,\n apiUrl: apiUrl ?? '(default https://api.anam.ai)',\n livekitUrl,\n avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent',\n publishOnBehalf: localIdentity,\n },\n 'starting Anam avatar session',\n );\n\n const jwt = await mintAvatarJoinToken({\n roomName: room.name!,\n avatarIdentity: this.avatarIdentity,\n publishOnBehalf: localIdentity,\n apiKey: lkKey,\n apiSecret: lkSecret,\n });\n\n const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions);\n logger.debug({ livekitUrl }, 'requesting Anam session token');\n\n const { sessionToken } = await anam.createSessionToken({\n personaConfig: this.opts.personaConfig,\n livekitUrl,\n livekitToken: jwt,\n sessionOptions: this.opts.sessionOptions,\n });\n logger.debug('starting Anam engine session');\n const started = await anam.startEngineSession({ sessionToken });\n this.sessionId = started.sessionId;\n\n agentSession.output.audio = new voice.DataStreamAudioOutput({\n room,\n destinationIdentity: this.avatarIdentity,\n waitRemoteTrack: TrackKind.KIND_VIDEO,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAA2B;AAE3B,sBAA0B;AAC1B,gCAA6C;AAC7C,iBAAwB;AACxB,mBAKO;AAGP,eAAsB,oBAAoB;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,QAAQ,IAAI;AAAA,EACrB,YAAY,QAAQ,IAAI;AAAA,EACxB,MAAM;AACR,GAOoB;AAClB,QAAM,KAAK,IAAI,sCAAY,QAAQ,SAAS;AAC5C,KAAG,WAAW;AACd,KAAG,OAAO;AACV,KAAG,OAAO;AACV,KAAG,MAAM;AACT,KAAG,aAAa,EAAE,wBAAwB,gBAAgB;AAE1D,KAAG,SAAS,EAAE,UAAU,MAAM,MAAM,SAAS,CAAe;AAC5D,SAAO,GAAG,MAAM;AAClB;AAEA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAGd,MAAM,sBAAsB,oBAAM,cAAc;AAAA,EAGrD,YACU,MASR;AACA,UAAM;AAVE;AAAA,EAWV;AAAA,EAdQ;AAAA,EAgBR,IAAa,iBAAyB;AACpC,WAAO,KAAK,KAAK,6BAA6B;AAAA,EAChD;AAAA,EAEA,IAAa,WAAmB;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MACJ,cACA,MACA,QAKA;AA/EJ;AAgFI,UAAM,MAAM,MAAM,cAAc,IAAI;AAEpC,UAAM,aAAS,mBAAI,EAAE,MAAM,EAAE,QAAQ,aAAa,CAAC;AACnD,UAAM,SAAS,KAAK,KAAK,UAAU,QAAQ,IAAI;AAC/C,QAAI,CAAC,OAAQ,OAAM,IAAI,2BAAc,0BAA0B;AAE/D,UAAM,SAAS,KAAK,KAAK,UAAU,QAAQ,IAAI;AAC/C,UAAM,cAAa,iCAAQ,eAAc,QAAQ,IAAI;AACrD,UAAM,SAAQ,iCAAQ,kBAAiB,QAAQ,IAAI;AACnD,UAAM,YAAW,iCAAQ,qBAAoB,QAAQ,IAAI;AAEzD,QAAI,CAAC,cAAc,CAAC,SAAS,CAAC,UAAU;AACtC,YAAM,IAAI,2BAAc,4CAA4C;AAAA,IACtE;AAEA,UAAM,gBAAiB,KAAK,oBAAoB,KAAK,iBAAiB,YAAa;AAEnF,WAAO;AAAA,MACL;AAAA,QACE,cAAa,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACtC,WAAU,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACnC,YAAW,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACpC,QAAQ,UAAU;AAAA,QAClB;AAAA,QACA,2BAA2B,KAAK,KAAK,6BAA6B;AAAA,QAClE,iBAAiB;AAAA,MACnB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,oBAAoB;AAAA,MACpC,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,iBAAiB;AAAA,MACjB,QAAQ;AAAA,MACR,WAAW;AAAA,IACb,CAAC;AAED,UAAM,OAAO,IAAI,mBAAQ,QAAQ,QAAQ,KAAK,KAAK,WAAW;AAC9D,WAAO,MAAM,EAAE,WAAW,GAAG,+BAA+B;AAE5D,UAAM,EAAE,aAAa,IAAI,MAAM,KAAK,mBAAmB;AAAA,MACrD,eAAe,KAAK,KAAK;AAAA,MACzB;AAAA,MACA,cAAc;AAAA,MACd,gBAAgB,KAAK,KAAK;AAAA,IAC5B,CAAC;AACD,WAAO,MAAM,8BAA8B;AAC3C,UAAM,UAAU,MAAM,KAAK,mBAAmB,EAAE,aAAa,CAAC;AAC9D,SAAK,YAAY,QAAQ;AAEzB,iBAAa,OAAO,QAAQ,IAAI,oBAAM,sBAAsB;AAAA,MAC1D;AAAA,MACA,qBAAqB,KAAK;AAAA,MAC1B,iBAAiB,0BAAU;AAAA,IAC7B,CAAC;AAAA,EACH;AACF;","names":[]}
package/dist/avatar.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { voice } from '@livekit/agents';
2
2
  import type { Room } from '@livekit/rtc-node';
3
- import { type APIConnectOptions, type PersonaConfig } from './types.js';
3
+ import { type APIConnectOptions, type PersonaConfig, type SessionOptions } from './types.js';
4
+ /** @public */
4
5
  export declare function mintAvatarJoinToken({ roomName, avatarIdentity, publishOnBehalf, apiKey, apiSecret, ttl, }: {
5
6
  roomName: string;
6
7
  avatarIdentity: string;
@@ -9,11 +10,13 @@ export declare function mintAvatarJoinToken({ roomName, avatarIdentity, publishO
9
10
  apiSecret?: string;
10
11
  ttl?: string | number;
11
12
  }): Promise<string>;
13
+ /** @public */
12
14
  export declare class AvatarSession extends voice.AvatarSession {
13
15
  private opts;
14
16
  private sessionId?;
15
17
  constructor(opts: {
16
18
  personaConfig: PersonaConfig;
19
+ sessionOptions?: SessionOptions;
17
20
  apiUrl?: string;
18
21
  apiKey?: string;
19
22
  avatarParticipantIdentity?: string;
package/dist/avatar.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { voice } from '@livekit/agents';
2
2
  import type { Room } from '@livekit/rtc-node';
3
- import { type APIConnectOptions, type PersonaConfig } from './types.js';
3
+ import { type APIConnectOptions, type PersonaConfig, type SessionOptions } from './types.js';
4
+ /** @public */
4
5
  export declare function mintAvatarJoinToken({ roomName, avatarIdentity, publishOnBehalf, apiKey, apiSecret, ttl, }: {
5
6
  roomName: string;
6
7
  avatarIdentity: string;
@@ -9,11 +10,13 @@ export declare function mintAvatarJoinToken({ roomName, avatarIdentity, publishO
9
10
  apiSecret?: string;
10
11
  ttl?: string | number;
11
12
  }): Promise<string>;
13
+ /** @public */
12
14
  export declare class AvatarSession extends voice.AvatarSession {
13
15
  private opts;
14
16
  private sessionId?;
15
17
  constructor(opts: {
16
18
  personaConfig: PersonaConfig;
19
+ sessionOptions?: SessionOptions;
17
20
  apiUrl?: string;
18
21
  apiKey?: string;
19
22
  avatarParticipantIdentity?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../src/avatar.ts"],"names":[],"mappings":"AAGA,OAAO,EAAO,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,OAAO,EAAE,KAAK,iBAAiB,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEvF,wBAAsB,mBAAmB,CAAC,EACxC,QAAQ,EACR,cAAc,EACd,eAAe,EACf,MAAqC,EACrC,SAA2C,EAC3C,GAAW,GACZ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlB;AAKD,qBAAa,aAAc,SAAQ,KAAK,CAAC,aAAa;IAIlD,OAAO,CAAC,IAAI;IAHd,OAAO,CAAC,SAAS,CAAC,CAAS;gBAGjB,IAAI,EAAE;QACZ,aAAa,EAAE,aAAa,CAAC;QAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,WAAW,CAAC,EAAE,iBAAiB,CAAC;KACjC;IAKH,IAAa,cAAc,IAAI,MAAM,CAEpC;IAED,IAAa,QAAQ,IAAI,MAAM,CAE9B;IAEK,KAAK,CACT,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,IAAI,EAAE,IAAI,EACV,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B;CA4DJ"}
1
+ {"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../src/avatar.ts"],"names":[],"mappings":"AAGA,OAAO,EAAO,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAEpB,cAAc;AACd,wBAAsB,mBAAmB,CAAC,EACxC,QAAQ,EACR,cAAc,EACd,eAAe,EACf,MAAqC,EACrC,SAA2C,EAC3C,GAAW,GACZ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlB;AAKD,cAAc;AACd,qBAAa,aAAc,SAAQ,KAAK,CAAC,aAAa;IAIlD,OAAO,CAAC,IAAI;IAHd,OAAO,CAAC,SAAS,CAAC,CAAS;gBAGjB,IAAI,EAAE;QACZ,aAAa,EAAE,aAAa,CAAC;QAC7B,cAAc,CAAC,EAAE,cAAc,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,WAAW,CAAC,EAAE,iBAAiB,CAAC;KACjC;IAKH,IAAa,cAAc,IAAI,MAAM,CAEpC;IAED,IAAa,QAAQ,IAAI,MAAM,CAE9B;IAEK,KAAK,CACT,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,IAAI,EAAE,IAAI,EACV,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B;CA2DJ"}
package/dist/avatar.js CHANGED
@@ -2,7 +2,9 @@ import { log, voice } from "@livekit/agents";
2
2
  import { TrackKind } from "@livekit/rtc-node";
3
3
  import { AccessToken } from "livekit-server-sdk";
4
4
  import { AnamAPI } from "./api.js";
5
- import { AnamException } from "./types.js";
5
+ import {
6
+ AnamException
7
+ } from "./types.js";
6
8
  async function mintAvatarJoinToken({
7
9
  roomName,
8
10
  avatarIdentity,
@@ -35,7 +37,7 @@ class AvatarSession extends voice.AvatarSession {
35
37
  return "anam";
36
38
  }
37
39
  async start(agentSession, room, params) {
38
- var _a, _b, _c, _d;
40
+ var _a, _b, _c;
39
41
  await super.start(agentSession, room);
40
42
  const logger = log().child({ module: "AnamAvatar" });
41
43
  const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY;
@@ -52,6 +54,7 @@ class AvatarSession extends voice.AvatarSession {
52
54
  {
53
55
  personaName: (_a = this.opts.personaConfig) == null ? void 0 : _a.name,
54
56
  avatarId: (_b = this.opts.personaConfig) == null ? void 0 : _b.avatarId,
57
+ personaId: (_c = this.opts.personaConfig) == null ? void 0 : _c.personaId,
55
58
  apiUrl: apiUrl ?? "(default https://api.anam.ai)",
56
59
  livekitUrl,
57
60
  avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? "anam-avatar-agent",
@@ -69,12 +72,10 @@ class AvatarSession extends voice.AvatarSession {
69
72
  const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions);
70
73
  logger.debug({ livekitUrl }, "requesting Anam session token");
71
74
  const { sessionToken } = await anam.createSessionToken({
72
- personaConfig: {
73
- name: (_c = this.opts.personaConfig) == null ? void 0 : _c.name,
74
- avatarId: (_d = this.opts.personaConfig) == null ? void 0 : _d.avatarId
75
- },
75
+ personaConfig: this.opts.personaConfig,
76
76
  livekitUrl,
77
- livekitToken: jwt
77
+ livekitToken: jwt,
78
+ sessionOptions: this.opts.sessionOptions
78
79
  });
79
80
  logger.debug("starting Anam engine session");
80
81
  const started = await anam.startEngineSession({ sessionToken });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/avatar.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log, voice } from '@livekit/agents';\nimport type { Room } from '@livekit/rtc-node';\nimport { TrackKind } from '@livekit/rtc-node';\nimport { AccessToken, type VideoGrant } from 'livekit-server-sdk';\nimport { AnamAPI } from './api.js';\nimport { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js';\n\nexport async function mintAvatarJoinToken({\n roomName,\n avatarIdentity,\n publishOnBehalf,\n apiKey = process.env.LIVEKIT_API_KEY!,\n apiSecret = process.env.LIVEKIT_API_SECRET!,\n ttl = '60s',\n}: {\n roomName: string;\n avatarIdentity: string;\n publishOnBehalf: string;\n apiKey?: string;\n apiSecret?: string;\n ttl?: string | number;\n}): Promise<string> {\n const at = new AccessToken(apiKey, apiSecret);\n at.identity = avatarIdentity;\n at.name = 'Anam Avatar';\n at.kind = 'agent';\n at.ttl = ttl;\n at.attributes = { 'lk.publish_on_behalf': publishOnBehalf };\n\n at.addGrant({ roomJoin: true, room: roomName } as VideoGrant);\n return at.toJwt();\n}\n\nconst AVATAR_IDENTITY = 'anam-avatar-agent';\nconst _AVATAR_NAME = 'anam-avatar-agent';\n\nexport class AvatarSession extends voice.AvatarSession {\n private sessionId?: string;\n\n constructor(\n private opts: {\n personaConfig: PersonaConfig;\n apiUrl?: string;\n apiKey?: string;\n avatarParticipantIdentity?: string;\n avatarParticipantName?: string;\n connOptions?: APIConnectOptions;\n },\n ) {\n super();\n }\n\n override get avatarIdentity(): string {\n return this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY;\n }\n\n override get provider(): string {\n return 'anam';\n }\n\n async start(\n agentSession: voice.AgentSession,\n room: Room,\n params?: {\n livekitUrl?: string;\n livekitApiKey?: string;\n livekitApiSecret?: string;\n },\n ) {\n await super.start(agentSession, room);\n\n const logger = log().child({ module: 'AnamAvatar' });\n const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY;\n if (!apiKey) throw new AnamException('ANAM_API_KEY is required');\n\n const apiUrl = this.opts.apiUrl ?? process.env.ANAM_API_URL;\n const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL;\n const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY;\n const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET;\n\n if (!livekitUrl || !lkKey || !lkSecret) {\n throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set');\n }\n\n const localIdentity = (room.localParticipant && room.localParticipant.identity) || 'agent';\n\n logger.debug(\n {\n personaName: this.opts.personaConfig?.name,\n avatarId: this.opts.personaConfig?.avatarId,\n apiUrl: apiUrl ?? '(default https://api.anam.ai)',\n livekitUrl,\n avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent',\n publishOnBehalf: localIdentity,\n },\n 'starting Anam avatar session',\n );\n\n const jwt = await mintAvatarJoinToken({\n roomName: room.name!,\n avatarIdentity: this.avatarIdentity,\n publishOnBehalf: localIdentity,\n apiKey: lkKey,\n apiSecret: lkSecret,\n });\n\n const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions);\n logger.debug({ livekitUrl }, 'requesting Anam session token');\n\n const { sessionToken } = await anam.createSessionToken({\n personaConfig: {\n name: this.opts.personaConfig?.name,\n avatarId: this.opts.personaConfig?.avatarId,\n },\n livekitUrl,\n livekitToken: jwt,\n });\n logger.debug('starting Anam engine session');\n const started = await anam.startEngineSession({ sessionToken });\n this.sessionId = started.sessionId;\n\n agentSession.output.audio = new voice.DataStreamAudioOutput({\n room,\n destinationIdentity: this.avatarIdentity,\n waitRemoteTrack: TrackKind.KIND_VIDEO,\n });\n }\n}\n"],"mappings":"AAGA,SAAS,KAAK,aAAa;AAE3B,SAAS,iBAAiB;AAC1B,SAAS,mBAAoC;AAC7C,SAAS,eAAe;AACxB,SAAiC,qBAAyC;AAE1E,eAAsB,oBAAoB;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,QAAQ,IAAI;AAAA,EACrB,YAAY,QAAQ,IAAI;AAAA,EACxB,MAAM;AACR,GAOoB;AAClB,QAAM,KAAK,IAAI,YAAY,QAAQ,SAAS;AAC5C,KAAG,WAAW;AACd,KAAG,OAAO;AACV,KAAG,OAAO;AACV,KAAG,MAAM;AACT,KAAG,aAAa,EAAE,wBAAwB,gBAAgB;AAE1D,KAAG,SAAS,EAAE,UAAU,MAAM,MAAM,SAAS,CAAe;AAC5D,SAAO,GAAG,MAAM;AAClB;AAEA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAEd,MAAM,sBAAsB,MAAM,cAAc;AAAA,EAGrD,YACU,MAQR;AACA,UAAM;AATE;AAAA,EAUV;AAAA,EAbQ;AAAA,EAeR,IAAa,iBAAyB;AACpC,WAAO,KAAK,KAAK,6BAA6B;AAAA,EAChD;AAAA,EAEA,IAAa,WAAmB;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MACJ,cACA,MACA,QAKA;AAvEJ;AAwEI,UAAM,MAAM,MAAM,cAAc,IAAI;AAEpC,UAAM,SAAS,IAAI,EAAE,MAAM,EAAE,QAAQ,aAAa,CAAC;AACnD,UAAM,SAAS,KAAK,KAAK,UAAU,QAAQ,IAAI;AAC/C,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,0BAA0B;AAE/D,UAAM,SAAS,KAAK,KAAK,UAAU,QAAQ,IAAI;AAC/C,UAAM,cAAa,iCAAQ,eAAc,QAAQ,IAAI;AACrD,UAAM,SAAQ,iCAAQ,kBAAiB,QAAQ,IAAI;AACnD,UAAM,YAAW,iCAAQ,qBAAoB,QAAQ,IAAI;AAEzD,QAAI,CAAC,cAAc,CAAC,SAAS,CAAC,UAAU;AACtC,YAAM,IAAI,cAAc,4CAA4C;AAAA,IACtE;AAEA,UAAM,gBAAiB,KAAK,oBAAoB,KAAK,iBAAiB,YAAa;AAEnF,WAAO;AAAA,MACL;AAAA,QACE,cAAa,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACtC,WAAU,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACnC,QAAQ,UAAU;AAAA,QAClB;AAAA,QACA,2BAA2B,KAAK,KAAK,6BAA6B;AAAA,QAClE,iBAAiB;AAAA,MACnB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,oBAAoB;AAAA,MACpC,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,iBAAiB;AAAA,MACjB,QAAQ;AAAA,MACR,WAAW;AAAA,IACb,CAAC;AAED,UAAM,OAAO,IAAI,QAAQ,QAAQ,QAAQ,KAAK,KAAK,WAAW;AAC9D,WAAO,MAAM,EAAE,WAAW,GAAG,+BAA+B;AAE5D,UAAM,EAAE,aAAa,IAAI,MAAM,KAAK,mBAAmB;AAAA,MACrD,eAAe;AAAA,QACb,OAAM,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QAC/B,WAAU,UAAK,KAAK,kBAAV,mBAAyB;AAAA,MACrC;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,WAAO,MAAM,8BAA8B;AAC3C,UAAM,UAAU,MAAM,KAAK,mBAAmB,EAAE,aAAa,CAAC;AAC9D,SAAK,YAAY,QAAQ;AAEzB,iBAAa,OAAO,QAAQ,IAAI,MAAM,sBAAsB;AAAA,MAC1D;AAAA,MACA,qBAAqB,KAAK;AAAA,MAC1B,iBAAiB,UAAU;AAAA,IAC7B,CAAC;AAAA,EACH;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/avatar.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log, voice } from '@livekit/agents';\nimport type { Room } from '@livekit/rtc-node';\nimport { TrackKind } from '@livekit/rtc-node';\nimport { AccessToken, type VideoGrant } from 'livekit-server-sdk';\nimport { AnamAPI } from './api.js';\nimport {\n type APIConnectOptions,\n AnamException,\n type PersonaConfig,\n type SessionOptions,\n} from './types.js';\n\n/** @public */\nexport async function mintAvatarJoinToken({\n roomName,\n avatarIdentity,\n publishOnBehalf,\n apiKey = process.env.LIVEKIT_API_KEY!,\n apiSecret = process.env.LIVEKIT_API_SECRET!,\n ttl = '60s',\n}: {\n roomName: string;\n avatarIdentity: string;\n publishOnBehalf: string;\n apiKey?: string;\n apiSecret?: string;\n ttl?: string | number;\n}): Promise<string> {\n const at = new AccessToken(apiKey, apiSecret);\n at.identity = avatarIdentity;\n at.name = 'Anam Avatar';\n at.kind = 'agent';\n at.ttl = ttl;\n at.attributes = { 'lk.publish_on_behalf': publishOnBehalf };\n\n at.addGrant({ roomJoin: true, room: roomName } as VideoGrant);\n return at.toJwt();\n}\n\nconst AVATAR_IDENTITY = 'anam-avatar-agent';\nconst _AVATAR_NAME = 'anam-avatar-agent';\n\n/** @public */\nexport class AvatarSession extends voice.AvatarSession {\n private sessionId?: string;\n\n constructor(\n private opts: {\n personaConfig: PersonaConfig;\n sessionOptions?: SessionOptions;\n apiUrl?: string;\n apiKey?: string;\n avatarParticipantIdentity?: string;\n avatarParticipantName?: string;\n connOptions?: APIConnectOptions;\n },\n ) {\n super();\n }\n\n override get avatarIdentity(): string {\n return this.opts.avatarParticipantIdentity ?? AVATAR_IDENTITY;\n }\n\n override get provider(): string {\n return 'anam';\n }\n\n async start(\n agentSession: voice.AgentSession,\n room: Room,\n params?: {\n livekitUrl?: string;\n livekitApiKey?: string;\n livekitApiSecret?: string;\n },\n ) {\n await super.start(agentSession, room);\n\n const logger = log().child({ module: 'AnamAvatar' });\n const apiKey = this.opts.apiKey ?? process.env.ANAM_API_KEY;\n if (!apiKey) throw new AnamException('ANAM_API_KEY is required');\n\n const apiUrl = this.opts.apiUrl ?? process.env.ANAM_API_URL;\n const livekitUrl = params?.livekitUrl ?? process.env.LIVEKIT_URL;\n const lkKey = params?.livekitApiKey ?? process.env.LIVEKIT_API_KEY;\n const lkSecret = params?.livekitApiSecret ?? process.env.LIVEKIT_API_SECRET;\n\n if (!livekitUrl || !lkKey || !lkSecret) {\n throw new AnamException('LIVEKIT_URL/API_KEY/API_SECRET must be set');\n }\n\n const localIdentity = (room.localParticipant && room.localParticipant.identity) || 'agent';\n\n logger.debug(\n {\n personaName: this.opts.personaConfig?.name,\n avatarId: this.opts.personaConfig?.avatarId,\n personaId: this.opts.personaConfig?.personaId,\n apiUrl: apiUrl ?? '(default https://api.anam.ai)',\n livekitUrl,\n avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent',\n publishOnBehalf: localIdentity,\n },\n 'starting Anam avatar session',\n );\n\n const jwt = await mintAvatarJoinToken({\n roomName: room.name!,\n avatarIdentity: this.avatarIdentity,\n publishOnBehalf: localIdentity,\n apiKey: lkKey,\n apiSecret: lkSecret,\n });\n\n const anam = new AnamAPI(apiKey, apiUrl, this.opts.connOptions);\n logger.debug({ livekitUrl }, 'requesting Anam session token');\n\n const { sessionToken } = await anam.createSessionToken({\n personaConfig: this.opts.personaConfig,\n livekitUrl,\n livekitToken: jwt,\n sessionOptions: this.opts.sessionOptions,\n });\n logger.debug('starting Anam engine session');\n const started = await anam.startEngineSession({ sessionToken });\n this.sessionId = started.sessionId;\n\n agentSession.output.audio = new voice.DataStreamAudioOutput({\n room,\n destinationIdentity: this.avatarIdentity,\n waitRemoteTrack: TrackKind.KIND_VIDEO,\n });\n }\n}\n"],"mappings":"AAGA,SAAS,KAAK,aAAa;AAE3B,SAAS,iBAAiB;AAC1B,SAAS,mBAAoC;AAC7C,SAAS,eAAe;AACxB;AAAA,EAEE;AAAA,OAGK;AAGP,eAAsB,oBAAoB;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,QAAQ,IAAI;AAAA,EACrB,YAAY,QAAQ,IAAI;AAAA,EACxB,MAAM;AACR,GAOoB;AAClB,QAAM,KAAK,IAAI,YAAY,QAAQ,SAAS;AAC5C,KAAG,WAAW;AACd,KAAG,OAAO;AACV,KAAG,OAAO;AACV,KAAG,MAAM;AACT,KAAG,aAAa,EAAE,wBAAwB,gBAAgB;AAE1D,KAAG,SAAS,EAAE,UAAU,MAAM,MAAM,SAAS,CAAe;AAC5D,SAAO,GAAG,MAAM;AAClB;AAEA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAGd,MAAM,sBAAsB,MAAM,cAAc;AAAA,EAGrD,YACU,MASR;AACA,UAAM;AAVE;AAAA,EAWV;AAAA,EAdQ;AAAA,EAgBR,IAAa,iBAAyB;AACpC,WAAO,KAAK,KAAK,6BAA6B;AAAA,EAChD;AAAA,EAEA,IAAa,WAAmB;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MACJ,cACA,MACA,QAKA;AA/EJ;AAgFI,UAAM,MAAM,MAAM,cAAc,IAAI;AAEpC,UAAM,SAAS,IAAI,EAAE,MAAM,EAAE,QAAQ,aAAa,CAAC;AACnD,UAAM,SAAS,KAAK,KAAK,UAAU,QAAQ,IAAI;AAC/C,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,0BAA0B;AAE/D,UAAM,SAAS,KAAK,KAAK,UAAU,QAAQ,IAAI;AAC/C,UAAM,cAAa,iCAAQ,eAAc,QAAQ,IAAI;AACrD,UAAM,SAAQ,iCAAQ,kBAAiB,QAAQ,IAAI;AACnD,UAAM,YAAW,iCAAQ,qBAAoB,QAAQ,IAAI;AAEzD,QAAI,CAAC,cAAc,CAAC,SAAS,CAAC,UAAU;AACtC,YAAM,IAAI,cAAc,4CAA4C;AAAA,IACtE;AAEA,UAAM,gBAAiB,KAAK,oBAAoB,KAAK,iBAAiB,YAAa;AAEnF,WAAO;AAAA,MACL;AAAA,QACE,cAAa,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACtC,WAAU,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACnC,YAAW,UAAK,KAAK,kBAAV,mBAAyB;AAAA,QACpC,QAAQ,UAAU;AAAA,QAClB;AAAA,QACA,2BAA2B,KAAK,KAAK,6BAA6B;AAAA,QAClE,iBAAiB;AAAA,MACnB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,oBAAoB;AAAA,MACpC,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,iBAAiB;AAAA,MACjB,QAAQ;AAAA,MACR,WAAW;AAAA,IACb,CAAC;AAED,UAAM,OAAO,IAAI,QAAQ,QAAQ,QAAQ,KAAK,KAAK,WAAW;AAC9D,WAAO,MAAM,EAAE,WAAW,GAAG,+BAA+B;AAE5D,UAAM,EAAE,aAAa,IAAI,MAAM,KAAK,mBAAmB;AAAA,MACrD,eAAe,KAAK,KAAK;AAAA,MACzB;AAAA,MACA,cAAc;AAAA,MACd,gBAAgB,KAAK,KAAK;AAAA,IAC5B,CAAC;AACD,WAAO,MAAM,8BAA8B;AAC3C,UAAM,UAAU,MAAM,KAAK,mBAAmB,EAAE,aAAa,CAAC;AAC9D,SAAK,YAAY,QAAQ;AAEzB,iBAAa,OAAO,QAAQ,IAAI,MAAM,sBAAsB;AAAA,MAC1D;AAAA,MACA,qBAAqB,KAAK;AAAA,MAC1B,iBAAiB,UAAU;AAAA,IAC7B,CAAC;AAAA,EACH;AACF;","names":[]}
package/dist/index.cjs CHANGED
@@ -23,7 +23,7 @@ class AnamPlugin extends import_agents.Plugin {
23
23
  constructor() {
24
24
  super({
25
25
  title: "anam",
26
- version: "1.4.9",
26
+ version: "1.5.0",
27
27
  package: "@livekit/agents-plugin-anam"
28
28
  });
29
29
  }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ class AnamPlugin extends Plugin {
6
6
  constructor() {
7
7
  super({
8
8
  title: "anam",
9
- version: "1.4.9",
9
+ version: "1.5.0",
10
10
  package: "@livekit/agents-plugin-anam"
11
11
  });
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nexport type PersonaConfig = {\n /** Optional display name (prod flow) */\n name?: string;\n /** Optional avatar asset id (prod flow) */\n avatarId?: string;\n /** Optional persona id (dev flow) */\n personaId?: string;\n};\n\nexport type APIConnectOptions = {\n maxRetry?: number;\n retryInterval?: number; // seconds\n timeout?: number; // seconds\n};\n\nexport class AnamException extends Error {}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBO,MAAM,sBAAsB,MAAM;AAAC;","names":[]}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n/** @public */\nexport type PersonaConfig = {\n /** Optional display name (prod flow) */\n name?: string;\n /** Optional avatar asset id (prod flow) */\n avatarId?: string;\n /** Optional avatar model version, e.g. \"cara-3\" or \"cara-4-latest\" (prod flow) */\n avatarModel?: string;\n /** Optional persona id (dev flow) */\n personaId?: string;\n};\n\n/** @public */\nexport type SessionOptions = {\n /** Output video frame width in pixels. Provide together with videoHeight. */\n videoWidth?: number;\n /** Output video frame height in pixels. Provide together with videoWidth. */\n videoHeight?: number;\n};\n\n/** @public */\nexport type APIConnectOptions = {\n maxRetry?: number;\n retryInterval?: number; // seconds\n timeout?: number; // seconds\n};\n\n/** @public */\nexport class AnamException extends Error {}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BO,MAAM,sBAAsB,MAAM;AAAC;","names":[]}
package/dist/types.d.cts CHANGED
@@ -1,16 +1,28 @@
1
+ /** @public */
1
2
  export type PersonaConfig = {
2
3
  /** Optional display name (prod flow) */
3
4
  name?: string;
4
5
  /** Optional avatar asset id (prod flow) */
5
6
  avatarId?: string;
7
+ /** Optional avatar model version, e.g. "cara-3" or "cara-4-latest" (prod flow) */
8
+ avatarModel?: string;
6
9
  /** Optional persona id (dev flow) */
7
10
  personaId?: string;
8
11
  };
12
+ /** @public */
13
+ export type SessionOptions = {
14
+ /** Output video frame width in pixels. Provide together with videoHeight. */
15
+ videoWidth?: number;
16
+ /** Output video frame height in pixels. Provide together with videoWidth. */
17
+ videoHeight?: number;
18
+ };
19
+ /** @public */
9
20
  export type APIConnectOptions = {
10
21
  maxRetry?: number;
11
22
  retryInterval?: number;
12
23
  timeout?: number;
13
24
  };
25
+ /** @public */
14
26
  export declare class AnamException extends Error {
15
27
  }
16
28
  //# sourceMappingURL=types.d.ts.map
package/dist/types.d.ts CHANGED
@@ -1,16 +1,28 @@
1
+ /** @public */
1
2
  export type PersonaConfig = {
2
3
  /** Optional display name (prod flow) */
3
4
  name?: string;
4
5
  /** Optional avatar asset id (prod flow) */
5
6
  avatarId?: string;
7
+ /** Optional avatar model version, e.g. "cara-3" or "cara-4-latest" (prod flow) */
8
+ avatarModel?: string;
6
9
  /** Optional persona id (dev flow) */
7
10
  personaId?: string;
8
11
  };
12
+ /** @public */
13
+ export type SessionOptions = {
14
+ /** Output video frame width in pixels. Provide together with videoHeight. */
15
+ videoWidth?: number;
16
+ /** Output video frame height in pixels. Provide together with videoWidth. */
17
+ videoHeight?: number;
18
+ };
19
+ /** @public */
9
20
  export type APIConnectOptions = {
10
21
  maxRetry?: number;
11
22
  retryInterval?: number;
12
23
  timeout?: number;
13
24
  };
25
+ /** @public */
14
26
  export declare class AnamException extends Error {
15
27
  }
16
28
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG;IAC1B,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qBAAa,aAAc,SAAQ,KAAK;CAAG"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,cAAc;AACd,MAAM,MAAM,aAAa,GAAG;IAC1B,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,cAAc;AACd,MAAM,MAAM,cAAc,GAAG;IAC3B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,cAAc;AACd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,cAAc;AACd,qBAAa,aAAc,SAAQ,KAAK;CAAG"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nexport type PersonaConfig = {\n /** Optional display name (prod flow) */\n name?: string;\n /** Optional avatar asset id (prod flow) */\n avatarId?: string;\n /** Optional persona id (dev flow) */\n personaId?: string;\n};\n\nexport type APIConnectOptions = {\n maxRetry?: number;\n retryInterval?: number; // seconds\n timeout?: number; // seconds\n};\n\nexport class AnamException extends Error {}\n"],"mappings":"AAkBO,MAAM,sBAAsB,MAAM;AAAC;","names":[]}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n/** @public */\nexport type PersonaConfig = {\n /** Optional display name (prod flow) */\n name?: string;\n /** Optional avatar asset id (prod flow) */\n avatarId?: string;\n /** Optional avatar model version, e.g. \"cara-3\" or \"cara-4-latest\" (prod flow) */\n avatarModel?: string;\n /** Optional persona id (dev flow) */\n personaId?: string;\n};\n\n/** @public */\nexport type SessionOptions = {\n /** Output video frame width in pixels. Provide together with videoHeight. */\n videoWidth?: number;\n /** Output video frame height in pixels. Provide together with videoWidth. */\n videoHeight?: number;\n};\n\n/** @public */\nexport type APIConnectOptions = {\n maxRetry?: number;\n retryInterval?: number; // seconds\n timeout?: number; // seconds\n};\n\n/** @public */\nexport class AnamException extends Error {}\n"],"mappings":"AA+BO,MAAM,sBAAsB,MAAM;AAAC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-anam",
3
- "version": "1.4.9",
3
+ "version": "1.5.0",
4
4
  "description": "Anam plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -25,22 +25,22 @@
25
25
  "README.md"
26
26
  ],
27
27
  "devDependencies": {
28
- "@livekit/rtc-node": "^0.13.29",
28
+ "@livekit/rtc-node": "^0.13.30",
29
29
  "@microsoft/api-extractor": "^7.35.0",
30
30
  "@types/ws": "^8.5.10",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.0.0",
33
- "@livekit/agents": "1.4.9",
34
- "@livekit/agents-plugins-test": "1.4.9",
35
- "@livekit/agents-plugin-openai": "1.4.9"
33
+ "@livekit/agents": "1.5.0",
34
+ "@livekit/agents-plugins-test": "1.5.0",
35
+ "@livekit/agents-plugin-openai": "1.5.0"
36
36
  },
37
37
  "dependencies": {
38
38
  "livekit-server-sdk": "^2.9.2",
39
39
  "ws": "^8.18.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "@livekit/rtc-node": "^0.13.29",
43
- "@livekit/agents": "1.4.9"
42
+ "@livekit/rtc-node": "^0.13.30",
43
+ "@livekit/agents": "1.5.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup --onSuccess \"pnpm build:types\"",
package/src/api.ts CHANGED
@@ -2,10 +2,16 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { log } from '@livekit/agents';
5
- import { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js';
5
+ import {
6
+ type APIConnectOptions,
7
+ AnamException,
8
+ type PersonaConfig,
9
+ type SessionOptions,
10
+ } from './types.js';
6
11
 
7
12
  const DEFAULT_API_URL = 'https://api.anam.ai';
8
13
 
14
+ /** @public */
9
15
  export class AnamAPI {
10
16
  constructor(
11
17
  private apiKey: string,
@@ -121,14 +127,25 @@ export class AnamAPI {
121
127
  personaConfig: PersonaConfig;
122
128
  livekitUrl?: string;
123
129
  livekitToken?: string;
130
+ sessionOptions?: SessionOptions;
124
131
  }) {
125
132
  const pc = params.personaConfig;
126
- const personaPayload = {
127
- type: 'ephemeral',
128
- name: pc.name,
129
- avatarId: pc.avatarId,
130
- llmId: 'CUSTOMER_CLIENT_V1',
131
- };
133
+ // Anam's personaConfig is a `oneOf`: reference a previously created
134
+ // (stateful) persona by `personaId` — the "dev flow" — or configure an
135
+ // ephemeral persona inline with name/avatarId/llmId. The two are mutually
136
+ // exclusive, so when a personaId is given we must not also send the
137
+ // ephemeral fields.
138
+ const personaPayload: Record<string, unknown> = pc.personaId
139
+ ? { personaId: pc.personaId }
140
+ : {
141
+ type: 'ephemeral',
142
+ name: pc.name,
143
+ avatarId: pc.avatarId,
144
+ llmId: 'CUSTOMER_CLIENT_V1',
145
+ // Only forward the avatar model version when set; otherwise let Anam
146
+ // fall back to the avatar's default model.
147
+ ...(pc.avatarModel ? { avatarModel: pc.avatarModel } : {}),
148
+ };
132
149
 
133
150
  const payload: Record<string, unknown> = {
134
151
  personaConfig: personaPayload,
@@ -138,6 +155,25 @@ export class AnamAPI {
138
155
  livekitToken: params.livekitToken,
139
156
  };
140
157
 
158
+ if (
159
+ params.sessionOptions &&
160
+ (params.sessionOptions.videoWidth !== undefined ||
161
+ params.sessionOptions.videoHeight !== undefined)
162
+ ) {
163
+ if (
164
+ params.sessionOptions.videoWidth === undefined ||
165
+ params.sessionOptions.videoHeight === undefined
166
+ ) {
167
+ throw new AnamException(
168
+ 'videoWidth and videoHeight must be set together (both or neither)',
169
+ );
170
+ }
171
+ payload.sessionOptions = {
172
+ videoWidth: params.sessionOptions.videoWidth,
173
+ videoHeight: params.sessionOptions.videoHeight,
174
+ };
175
+ }
176
+
141
177
  return this.post<{ sessionToken: string }>(this.tokenPath, payload);
142
178
  }
143
179
 
package/src/avatar.ts CHANGED
@@ -6,8 +6,14 @@ import type { Room } from '@livekit/rtc-node';
6
6
  import { TrackKind } from '@livekit/rtc-node';
7
7
  import { AccessToken, type VideoGrant } from 'livekit-server-sdk';
8
8
  import { AnamAPI } from './api.js';
9
- import { type APIConnectOptions, AnamException, type PersonaConfig } from './types.js';
10
-
9
+ import {
10
+ type APIConnectOptions,
11
+ AnamException,
12
+ type PersonaConfig,
13
+ type SessionOptions,
14
+ } from './types.js';
15
+
16
+ /** @public */
11
17
  export async function mintAvatarJoinToken({
12
18
  roomName,
13
19
  avatarIdentity,
@@ -37,12 +43,14 @@ export async function mintAvatarJoinToken({
37
43
  const AVATAR_IDENTITY = 'anam-avatar-agent';
38
44
  const _AVATAR_NAME = 'anam-avatar-agent';
39
45
 
46
+ /** @public */
40
47
  export class AvatarSession extends voice.AvatarSession {
41
48
  private sessionId?: string;
42
49
 
43
50
  constructor(
44
51
  private opts: {
45
52
  personaConfig: PersonaConfig;
53
+ sessionOptions?: SessionOptions;
46
54
  apiUrl?: string;
47
55
  apiKey?: string;
48
56
  avatarParticipantIdentity?: string;
@@ -91,6 +99,7 @@ export class AvatarSession extends voice.AvatarSession {
91
99
  {
92
100
  personaName: this.opts.personaConfig?.name,
93
101
  avatarId: this.opts.personaConfig?.avatarId,
102
+ personaId: this.opts.personaConfig?.personaId,
94
103
  apiUrl: apiUrl ?? '(default https://api.anam.ai)',
95
104
  livekitUrl,
96
105
  avatarParticipantIdentity: this.opts.avatarParticipantIdentity ?? 'anam-avatar-agent',
@@ -111,12 +120,10 @@ export class AvatarSession extends voice.AvatarSession {
111
120
  logger.debug({ livekitUrl }, 'requesting Anam session token');
112
121
 
113
122
  const { sessionToken } = await anam.createSessionToken({
114
- personaConfig: {
115
- name: this.opts.personaConfig?.name,
116
- avatarId: this.opts.personaConfig?.avatarId,
117
- },
123
+ personaConfig: this.opts.personaConfig,
118
124
  livekitUrl,
119
125
  livekitToken: jwt,
126
+ sessionOptions: this.opts.sessionOptions,
120
127
  });
121
128
  logger.debug('starting Anam engine session');
122
129
  const started = await anam.startEngineSession({ sessionToken });
package/src/types.ts CHANGED
@@ -1,19 +1,32 @@
1
1
  // SPDX-FileCopyrightText: 2025 LiveKit, Inc.
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
+ /** @public */
4
5
  export type PersonaConfig = {
5
6
  /** Optional display name (prod flow) */
6
7
  name?: string;
7
8
  /** Optional avatar asset id (prod flow) */
8
9
  avatarId?: string;
10
+ /** Optional avatar model version, e.g. "cara-3" or "cara-4-latest" (prod flow) */
11
+ avatarModel?: string;
9
12
  /** Optional persona id (dev flow) */
10
13
  personaId?: string;
11
14
  };
12
15
 
16
+ /** @public */
17
+ export type SessionOptions = {
18
+ /** Output video frame width in pixels. Provide together with videoHeight. */
19
+ videoWidth?: number;
20
+ /** Output video frame height in pixels. Provide together with videoWidth. */
21
+ videoHeight?: number;
22
+ };
23
+
24
+ /** @public */
13
25
  export type APIConnectOptions = {
14
26
  maxRetry?: number;
15
27
  retryInterval?: number; // seconds
16
28
  timeout?: number; // seconds
17
29
  };
18
30
 
31
+ /** @public */
19
32
  export class AnamException extends Error {}