Anam AI JavaScript SDK 软件
这是JavaScript SDK的官方版本,用于将Anam AI实时数字人物集成到你的产品中. 它提供了一个简单而直观的API,与Anam AI的服务交互.
导言
Anam AI JavaScript SDK旨在帮助开发者将Anam AI的数字人格集成到他们的JavaScript应用中. SDK提供一套API和公用设施,以方便在实时环境中创建,管理和与数字人物互动.
文档
完整的文件可在 翻译:。 。 。 .
实例
看看我们 实例项目 用于执行样本.
先决条件
Anam AI 账户
创建自由账户头到 阿南实验室 并注册.
Anam API 密钥
要使用 SDK 您首先需要 API 密钥 。 遵守指示 这里 来创造一个.
开始
首先,在您的工程中安装 SDK
npm install @anam-ai/js-sdk
折旧通知
重要编号: brainType 输入 PersonaConfig 已贬值,并将在未来版本被删除。 请使用 llmId 换句话说 如果您正在使用 brainType在控制台上你会看到堕落警告 在过渡时期,这两个领域都得到了支持.
地方发展
最快速的开始测试 SDK 是直接使用您的 API 密钥与我们的 SDK 和下面显示的示例人物配置 .
要使用 SDK 您首先需要创建实例 AnamClient为本地开发,您可以使用 unsafe_createClientWithApiKey 方法.
import { unsafe_createClientWithApiKey } from '@anam-ai/js-sdk';
const anamClient = unsafe_createClientWithApiKey('your-api-key', {
personaId: '<PERSONA ID HERE>',
name: 'Cara',
avatarId: '30fa96d0-26c4-4e55-94a0-517025942e18',
voiceId: '6bfbe25a-979d-40f3-a92b-5394170af54b',
brainType: 'ANAM_GPT_4O_MINI_V1',
systemPrompt:
"[STYLE] Reply in natural speech without formatting. Add pauses using '...' and very occasionally a disfluency. [PERSONALITY] You are Cara, a helpful assistant.",
});
页:1: 方法 unsafe_createClientWithApiKey 对生产使用来说是不安全的,因为它需要将您的 api 密钥暴露给客户端。 部署到生产时见 生产用途 先说.
一旦您有 Anam 客户端实例 已关闭, 您可以通过流到 DOM 中的音频和视频元素开始会话 .
await anamClient.streamToVideoElement('video-element-id');
这将使用预配置的人物a id开始一个新的会话,并在 DOM 中以匹配的元素 id 开始流视频元素.
要停止会话使用 stopStreaming 方法.
anamClient.stopStreaming();
生产用途
当部署到生产时,重要的是不要公开曝光您的API密钥. 为避免这个问题, 您应该先将您的 API 密钥交换为服务器侧的短长会话符 。 然后会话符号可以传递给客户端, 并用于 Evolution Anam SDK .
从服务器
const response = await fetch(`https://api.anam.ai/v1/auth/session-token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
personaConfig: {
name: 'Cara',
avatarId: '30fa96d0-26c4-4e55-94a0-517025942e18',
voiceId: '6bfbe25a-979d-40f3-a92b-5394170af54b',
llmId: '<LLM ID HERE>',
systemPrompt:
"[STYLE] Reply in natural speech without formatting. Add pauses using '...' and very occasionally a disfluency. [PERSONALITY] You are Cara, a helpful assistant.",
},
}),
});
const data = await response.json();
const sessionToken = data.sessionToken;
一旦您有会话符号,您就可以使用 createClient 将 Anam SDK 程序用于初始化 Anam 客户端实例.
import { createClient } from '@anam-ai/js-sdk';
const anamClient = createClient('your-session-token');
不论您是否使用 API 密钥或会话符号将客户端曝光同一套可用的流化方法 .
看这里 例如,在生产环境中开始会话的序列图.
聊天流
createTalkMessageStream 直接发送文字给 TTS 供人发言,一次一个块。 溪流中的所有块都有一个相通的ID,这是中断相关联的.
const stream = anamClient.createTalkMessageStream();
for (const chunk of llmChunks) {
await stream.streamMessageChunk(chunk.text, chunk.isLast);
}
你可以马上开始谈话 streamToVideoElement() 决定。 在信号连接首次打开前发送的块被排出队列,并在完成时发送.
交付失败
对话流块通过会话的信号连接。 在回答心跳的引擎上(见 旧引擎 在下面,SDK检查与心跳的关联, 以及一个块确认块到达后发出的心跳的答案。 SDK 不重新发送块 。 如果一个块不能被发送,因为连接已经下降或停止回答,或者它在大约3秒内没有得到确认,流停止接受块, stream.getState() 返回时 TalkMessageStreamState.DELIVERY_FAILED,客户端会发出 MESSAGE_DELIVERY_FAILED 与流的关联 ID。 SDK自己重新连接;决定如何处理答案由你决定. 一个选择是回到 talk() 全文为:
import { AnamEvent } from '@anam-ai/js-sdk';
interface Answer {
correlationId: string;
text: string;
complete: boolean;
failed: boolean;
}
// Only the latest answer falls back: once a new one starts, the old one is stale.
let latest: Answer | null = null;
// Register once per client, not once per stream.
anamClient.addListener(AnamEvent.MESSAGE_DELIVERY_FAILED, (failure) => {
const answer = latest;
if (!answer || failure.correlationId !== answer.correlationId) return;
answer.failed = true;
// If the failure arrives after the answer is complete, speak it now.
if (answer.complete) anamClient.talk(answer.text).catch(console.error);
});
async function speak(llmChunks: AsyncIterable<{ text: string; isLast: boolean }>) {
const stream = anamClient.createTalkMessageStream();
const answer: Answer = {
correlationId: stream.getCorrelationId(),
text: '',
complete: false,
failed: false,
};
latest = answer;
for await (const chunk of llmChunks) {
answer.text += chunk.text;
// A failed or interrupted stream rejects further chunks, so keep collecting the
// answer instead. This also covers a stale answer, whose failure the listener ignores.
if (stream.isActive()) await stream.streamMessageChunk(chunk.text, chunk.isLast);
}
answer.complete = true;
if (answer.failed && latest === answer) await anamClient.talk(answer.text);
}
故障意味着发动机没有确认块,而不是它从来没有收到过块. 有些答案可能已经说过了 talk() 复说. 一旦客户收到 TALK_STREAM_INTERRUPTED 对于一溪,它不会发射 MESSAGE_DELIVERY_FAILED 对于它。 连接被关闭时发送的中断可以被错过,不过,因此倒计时仍然可以重复用户中断的答案. 反向也可能发生:引擎可以在客户端重新连接后交付中断,所以 TALK_STREAM_INTERRUPTED 可以在你开始倒计时后到达已经失败的溪流.
结束会话不是执行失败。 之后 stopStreaming(),或者当客户端发出 CONNECTION_CLOSED 因为服务器结束会话或媒体连接失败, 未确认块会丢失 MESSAGE_DELIVERY_FAILED,之后发送的块被丢弃。 处理 CONNECTION_CLOSED 作为每个开口的溪流的尽头。 唯一的例外是信号连接本身就永远关闭:未经证实的块在之前失败了 CONNECTION_CLOSED。 。 。 .
只确认谈话流 。 音频发送通过 createAgentAudioInputStream() 而不是,所以一个停止工作而不关闭的连接 可能会失去它不发生任何事件.
旧引擎
确认块需要一个能回答心跳的引擎. 当会话准备就绪时会这样说的引擎 sendsHeartbeatAcks: true 输入 sessionready 留言。 在更古老的引擎上,SDK在会话准备好后会停止跟踪块并像以前一样发送,在连接重新连接时排队. 还发作 MESSAGE_DELIVERY_FAILED 在三种情况下:
- 连接关闭后发送块 .
- 一个块在连接首次打开前被排入队列,它永远不会打开.
- 在会话准备好之前发送一个块,会话需要3秒多的时间才能准备好. 在此之前,SDK无法从已经停止响应的引擎中分辨出一个更老的引擎,因此它会使流失效并重新连接. 等待时间
AnamEvent.SESSION_READY在第一个块避免这一点之前.
跨工具呼叫的演讲
当您的应用程序运行工具时, 谈话流可以保持开放 。 在工具之前和之后给出演讲,呼叫单独的发音ids,第二个等待第一个完成播放.
[注] Utterance ids需要一个Cara 4相机. Cara 3相机运行一个更古老的引擎, 静悄悄地丢出字段: 文字仍然被说出
utteranceId。没有错误或警告,所以请检查身份证明是否回来了而不是等待失败.
[大
utteranceId表示语音的开始,而不是单个文本块。 把它放在第一个块上,然后从该词中的剩余块中省去。 仅在下一个语句开始时设置新的 id .
const stream = anamClient.createTalkMessageStream();
// Utterance A can begin playing while the tool runs.
await stream.streamMessageChunk('Let me check ', false, crypto.randomUUID());
await stream.streamMessageChunk('that for you.', false); // Continues utterance A.
const toolResultText = await runToolCall();
// Utterance B waits for A to finish, then continues with the result.
await stream.streamMessageChunk(toolResultText, true, crypto.randomUUID());
工具必须迅速完成. 服务器关闭一个15秒的谈话流, 却没有收到一个带文字的块, 结束转弯, 并拒绝随后发送到这个关联ID上的任何信息 。 空块不重设计时器 。 对于一个可能跑得更长的工具,让第一句话结束流出,并在结果到来时开始一个新的流出.
同样的命令适用于立即准备好的两种言论。 寄出不同的代号,第二个则等待第一个完成游戏:
const stream = anamClient.createTalkMessageStream();
await stream.streamMessageChunk('First utterance.', false, crypto.randomUUID());
await stream.streamMessageChunk('Second utterance.', true, crypto.randomUUID());
Ids必须是小写 Canonical UUID v4 字符串,这就是什么 crypto.randomUUID() 返回; 其它任何投掷。 他们回来的人 一个消息事件作为 utteranceId,所以你可以匹配自己的身份 与人说话.
司长说明(第4节)
在Cara 4相机上您可以添加 司长 指导阿凡达的表演. 提供内置 presetStyle 或者说 自由形式 customStylePrompt ——两者是相互排斥的(由《联合国宪章》和《联合国宪章》所强化) DirectorNotes ——加可选 expressivity 值,从 0 改为 1,控制样式的表达性(较低值比较稳定;较高值增加样式并一起进行语音驱动运动;省略后使用引擎默认值)。 Director Notes没有被转发到会话脚本创建中,并且只被应用到Cara 4 avatars上;对于更古老的模型,服务器忽略它们,会话在没有它们的情况下进行.
import { unsafe_createClientWithApiKey } from '@anam-ai/js-sdk';
const anamClient = unsafe_createClientWithApiKey('your-api-key', {
personaId: '<PERSONA ID HERE>',
name: 'Cara',
// Use a Cara 4 avatar — Director Notes are ignored on older models.
avatarId: '30fa96d0-26c4-4e55-94a0-517025942e18',
voiceId: '6bfbe25a-979d-40f3-a92b-5394170af54b',
llmId: '<LLM ID HERE>',
systemPrompt:
'[STYLE] Reply in natural speech without formatting. [PERSONALITY] You are Cara, a helpful assistant.',
directorNotes: {
presetStyle: 'warm',
expressivity: 0.5,
},
});
要使用自由形式样式而不是预设,请提供 customStylePrompt数字 :
directorNotes: {
customStylePrompt:
'Warm smile, composed, slightly amused, looking directly at camera',
expressivity: 0.5,
}
您也可以设置 expressivity 曲调如何表达阿凡达的默认风格:
directorNotes: {
expressivity: 0.2,
}
发送主任 Note 提示会中段
在卡拉4号,你可以使用 sendDirectorNoteCue 以在对话中改变表演。 Cues使用引擎的专用提示路径,因此不会清洗缓冲音频或视频. 数据通道提示主要用于音频通过会话;对于Twitchey会话,偏好在人物语音文本中内置提示标记.
import { AnamEvent, type DirectorNoteCueTag } from '@anam-ai/js-sdk';
let cueChannelOpen = false;
anamClient.addListener(AnamEvent.DATA_CHANNEL_OPEN, () => {
cueChannelOpen = true;
});
anamClient.addListener(AnamEvent.CONNECTION_CLOSED, () => {
cueChannelOpen = false;
});
// Call this later from your TTS timing callback for an active persona turn.
function onTtsCue(tag: DirectorNoteCueTag, atSeconds: number) {
if (!cueChannelOpen) {
throw new Error('Director Note cue channel is not open');
}
anamClient.sendDirectorNoteCue(tag, { atSeconds });
}
// Wire onTtsCue to your TTS provider's cue/word-timing callback. For example,
// report `warm` at 0 seconds and `surprised` at 1.25 seconds for that turn.
在启动流前注册听众, 使其不能错过事件 。 发送前 DATA_CHANNEL_OPEN 而不是悄悄地放弃提示。 发信号时间立即应用提示. inSeconds 是一个从现在开始的延迟,在积极反应,同时 atSeconds 是一个绝对的相抵,从个人的语音开始,在对齐给生成的音频提示时首选;最多提供一个。 运行时提示标记还包括只显示提示的样式,例如 laughter, (中文(简体) ), curious, (中文(简体) ), concerned,以及 surprised 无法作为会话启动的 presetStyle 数值.