@autoark-ai/eva-client-sdk-ts 1.0.3 → 1.0.5

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/README.md CHANGED
@@ -20,7 +20,8 @@ npm install @autoark-ai/eva-client-sdk-ts
20
20
 
21
21
  ## 版本与 Gateway
22
22
 
23
- package version 仅使用普通 SemVer `X.Y.Z`。每个完整 version 只对应一份确定制品,并固定连接正式 EVA Gateway;Gateway 地址不能通过 Agent 配置改写。
23
+ 公网正式版本使用普通 SemVer `X.Y.Z`。SDK 使用内置的固定 EVA Gateway;Gateway 地址不能通过
24
+ Agent 配置或任意 URL 改写。
24
25
 
25
26
  包只提供三个入口:
26
27
 
@@ -64,7 +65,6 @@ import type { MediaTransportsConfig } from "@autoark-ai/eva-client-sdk-ts/spi";
64
65
  import {
65
66
  createBrowserAudioInputSource,
66
67
  createBrowserAudioOutputSink,
67
- createBrowserCameraSnapshotSource,
68
68
  createPassthroughAecProcessor,
69
69
  } from "@autoark-ai/eva-client-sdk-ts/browser";
70
70
 
@@ -77,44 +77,27 @@ const transports: MediaTransportsConfig = {
77
77
  }),
78
78
  output: createBrowserAudioOutputSink(),
79
79
  aec: createPassthroughAecProcessor(),
80
- camera: createBrowserCameraSnapshotSource({
81
- video: { facingMode: { ideal: "environment" } },
82
- mimeType: "image/png",
83
- }),
84
80
  };
85
81
 
86
82
  const agent = createEvaVoiceDialogueAgent({
87
83
  apiKey: applicationManagedApiKey,
88
84
  asr: {
89
- model: "fun_asr",
90
- sampleRate: 48_000,
85
+ model: "ark-asr-plus",
86
+ sampleRate: 16_000,
91
87
  },
92
88
  llm: {
93
- model: "doubao-seed-2-0-mini-nothink",
89
+ model: "volcengine-doubao-seed-2.0-lite",
94
90
  },
95
91
  tts: {
96
- model: "cosyvoice_tts",
97
- voice: "longjielidou_v3",
98
- sampleRate: 48_000,
92
+ model: "ark-tts-flash",
93
+ voice: "zh_en_male_evan",
94
+ sampleRate: 44100,
99
95
  },
100
96
  vad: {
101
97
  sensitivity: 0.6,
102
98
  silenceThresholdMs: 400,
103
99
  },
104
- bargeIn: {
105
- initialPlaybackGuardMs: 3000, // 单位:ms(毫秒);3000 ms = 3 秒
106
- },
107
100
  transports,
108
- history: { maxTurns: 10 },
109
- camera: { captureTimeoutMs: 1500 },
110
- emotion: {
111
- enabled: true,
112
- // custom labels 会完整替换默认业务标签;SDK 会自动补一个 unknown。
113
- labels: ["happy", "sad"],
114
- // 这里只填业务补充说明,不是完整提示词,也不能替换 SDK 内置规则。
115
- instructions: "这是儿童陪伴场景,重点区分害怕、难过和开心。",
116
- maxInputChars: 2000,
117
- },
118
101
  });
119
102
 
120
103
  const unsubscribe = agent.onEvent((event: AgentEvent) => {
@@ -125,8 +108,6 @@ const unsubscribe = agent.onEvent((event: AgentEvent) => {
125
108
 
126
109
  await agent.start();
127
110
  await agent.setAudioInputEnabled(true);
128
- // 摄像头默认关闭;需要时显式开启,开启期间持续持有一个 session。
129
- await agent.setCameraCaptureEnabled(true);
130
111
  await agent.submitText("你好");
131
112
 
132
113
  console.log(agent.getMessages());
@@ -136,6 +117,24 @@ await agent.stop();
136
117
  unsubscribe();
137
118
  ```
138
119
 
120
+ ## 可选:增加摄像头
121
+
122
+ 需要图片输入时,在上例的 `transports` 中增加 browser camera source:
123
+
124
+ ```ts
125
+ import { createBrowserCameraSnapshotSource } from "@autoark-ai/eva-client-sdk-ts/browser";
126
+
127
+ const cameraTransports: MediaTransportsConfig = {
128
+ ...transports,
129
+ camera: createBrowserCameraSnapshotSource(),
130
+ };
131
+ ```
132
+
133
+ 创建 Agent 时使用 `cameraTransports`。摄像头默认关闭;调用
134
+ `await agent.setCameraCaptureEnabled(true)` 后才会申请权限并持有 camera session,关闭后释放。
135
+ 启用期间,每个 speech turn 至多采集一张图片供当前 LLM 请求使用;不开启或不提供 camera source
136
+ 都不影响语音和文本对话。
137
+
139
138
  ## Agent Facade
140
139
 
141
140
  `createEvaVoiceDialogueAgent(config)` 返回的 `EvaVoiceDialogueAgent` 只有八个方法:
@@ -168,218 +167,126 @@ Agent 一旦开始停止,就不再接受新的 turn 或事件订阅。需要
168
167
 
169
168
  ## 配置
170
169
 
171
- `EvaVoiceDialogueAgentConfig` 的公共字段如下:
172
-
173
- | 字段 | 必填 | 说明 |
174
- | -------------------------------- | ---: | ----------------------------------------------------------------------------- |
175
- | `apiKey` | 是 | 应用提供并管理的 EVA Gateway AK |
176
- | `asr.model` | | ASR model 标识 |
177
- | `asr.sampleRate` | | ASR 接收的目标 PCM 采样率,必须为正整数 |
178
- | `llm.model` | | LLM model 标识 |
179
- | `llm.temperature` | | 采样温度 |
180
- | `llm.maxTokens` | | 最大生成 token 数 |
181
- | `llm.extraParameters` | | 平铺到 Gateway LLM JSON body 顶层的 model-specific `JsonObject` |
182
- | `tts.model` | | TTS model 标识 |
183
- | `tts.voice` | | voice 标识 |
184
- | `tts.speakingRate` | | 语速 |
185
- | `tts.pitch` | | 音调倍率 |
186
- | `tts.sampleRate` | | TTS 输出采样率,默认 `16000` |
187
- | `vad.sensitivity` | | 语音概率阈值,默认 `0.5` |
188
- | `vad.silenceThresholdMs` | | 判定停止说话所需的连续静音时间,默认 `200` ms |
189
- | `bargeIn.initialPlaybackGuardMs` | | 首次 playback 语音保护窗口时长,单位为 `ms`(毫秒);默认 `0`(禁用) |
190
- | `systemPrompt` | | 每次 LLM 请求使用的系统指令 |
191
- | `greeting` | | `disabled`、`static` `dynamic` greeting |
192
- | `history.maxTurns` | | LLM 上下文保留的已完成轮数 |
193
- | `camera.captureTimeoutMs` | | 单次采图等待上限,默认 `1500` ms |
194
- | `emotion.enabled` | 否 | 是否启用 emotion 旁路识别,默认 `false`;这是构造配置,不是运行时开关 |
195
- | `emotion.labels` | 否 | 需要识别的完整 custom code 集;省略时使用默认标签,提供时完整替换默认业务标签 |
196
- | `emotion.instructions` | 否 | 给内置分类 prompt 的业务补充说明,不是完整提示词 |
197
- | `emotion.maxInputChars` | 否 | 送入 emotion 分类的 utterance 上限,默认 `2000` 个 Unicode code point |
198
- | `commands.registrations` | 否 | 构造期成对注册的 command definition + handler;空数组等同关闭 |
199
- | `commands.maxCallsPerTurn` | 否 | 单 turn 完整 raw tool-call 回合上限,默认 `3` |
200
- | `metadata` | 否 | JSON-compatible Agent metadata |
201
- | `transports` | 否 | 完整的 `input`、`output`、`aec` 与可选 `camera` 组合;省略时为纯文本 Agent |
202
-
203
- 当配置 `transports.input` 时,应同时提供 `vad`。`submitText()` 可通过 `SubmitTextOptions` 指定 `turnId` 和当前 turn 的 `metadata`。
170
+ README 只介绍顶层配置及其用途。字段级的必填性、默认值、范围、单位、调节方向、代价和校验规则,
171
+ 请查看对应 TypeScript declaration 的双语 JSDoc(IDE hover 或随包 `.d.ts`)。
172
+
173
+ | 顶层配置 / 入口 | 控制什么 |
174
+ | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
175
+ | `createEvaVoiceDialogueAgent(config)` | 创建一个 Agent;`config` provider 工作前完成校验和快照。 |
176
+ | `EvaVoiceDialogueAgentConfig.apiKey` | 控制 Agent 以哪个 Gateway 凭证访问 ASR、LLM TTS。 |
177
+ | `EvaVoiceDialogueAgentConfig.asr` | 控制语音转写使用的 model 和目标 PCM 采样率。 |
178
+ | `EvaVoiceDialogueAgentConfig.llm` | 控制回复生成使用的 model 及其生成参数。 |
179
+ | `EvaVoiceDialogueAgentConfig.tts` | 控制语音合成使用的 model、音色及合成参数。 |
180
+ | `EvaVoiceDialogueAgentConfig.vad` | 控制本地语音起止检测。 |
181
+ | `EvaVoiceDialogueAgentConfig.systemPrompt` | 控制每次 LLM 请求使用的 system instruction。 |
182
+ | `EvaVoiceDialogueAgentConfig.greeting` | 控制 Agent 启动时是否问候,以及问候如何生成。 |
183
+ | `EvaVoiceDialogueAgentConfig.history` | 控制后续 LLM 请求是否携带历史问答,以及最多携带多少轮。 |
184
+ | `EvaVoiceDialogueAgentConfig.camera` | 控制单次图片采集的等待时限;不会自动启用摄像头。 |
185
+ | `EvaVoiceDialogueAgentConfig.emotion` | 控制是否为用户输入执行情绪识别,以及分类目录和输入上限。 |
186
+ | `EvaVoiceDialogueAgentConfig.bargeIn` | 控制播放期间何时允许新语音进入打断判定。 |
187
+ | `EvaVoiceDialogueAgentConfig.commands` | 控制 Agent 可调用的 command 及每个 turn 的调用预算。 |
188
+ | `EvaVoiceDialogueAgentConfig.metadata` | 控制附加到 Agent 事件和消息的稳定业务 metadata。 |
189
+ | `EvaVoiceDialogueAgentConfig.transports` | 控制 Agent 使用哪些 audio、AEC 和 camera media roles;省略时为 text-only。 |
190
+ | `SubmitTextOptions` | 控制一次手动文本 turn identity 和 metadata。 |
191
+ | `MediaTransportsConfig` | 定义注入 Agent 的完整 media role 集合。 |
192
+ | `AecProcessorConfiguration` | 保留 AEC processor 的配置入口;当前版本没有可设置字段。 |
193
+ | `BrowserAudioInputOptions` / `createBrowserAudioInputSource(options)` | 控制默认浏览器麦克风 source 的采集约束和缓冲。 |
194
+ | `BrowserCameraSnapshotOptions` / `createBrowserCameraSnapshotSource(options)` | 控制默认浏览器 camera source 的设备约束和图片编码。 |
204
195
 
205
196
  `llm.extraParameters` 用于 SDK 尚未预定义的模型参数。例如:
206
197
 
207
198
  ```ts
208
199
  const agent = createEvaVoiceDialogueAgent({
209
200
  apiKey: applicationManagedApiKey,
210
- asr: { model: "fun_asr", sampleRate: 48_000 },
201
+ asr: { model: "ark-asr-plus", sampleRate: 16_000 },
211
202
  llm: {
212
203
  model: "volcengine-doubao-seed-2.0-lite",
213
204
  extraParameters: {
214
- thinking: { type: "enabled" },
215
- enable_thinking: true,
205
+ thinking: {
206
+ type: "disabled",
207
+ },
216
208
  },
217
209
  },
218
- tts: { model: "cosyvoice_tts" },
210
+ tts: {
211
+ model: "ark-tts-flash",
212
+ voice: "zh_en_male_evan",
213
+ sampleRate: 44100,
214
+ },
219
215
  });
220
216
  ```
221
217
 
222
- 扩展字段会保持 key、大小写和嵌套结构,直接出现在最终 LLM request body 顶层;不会放入
223
- `extra_body` `extraBody`。SDK 不维护模型参数 allowlist,也不定义 `thinking.type` 等未知
224
- 参数的值域;具体支持情况由 Gateway 和所选模型裁定。省略 `extraParameters` 或传 `{}` 不会
225
- 增加任何字段,也不会补模型默认值。
218
+ `extraParameters` 中的字段会直接添加到 LLM request body 顶层,且不能覆盖 SDK 管理的请求字段;
219
+ 具体 model 参数见下方外部配置参考。
226
220
 
227
- 为避免覆盖 SDK 自己构造的 request,`extraParameters` 顶层禁止 `model`、`stream`、
228
- `messages`、`temperature`、`max_tokens`、`tools`、`tool_choice`。这些 key 或非 JSON-compatible
229
- 值会在 Agent 构造期报 `EvaSdkError`;检查区分大小写且不递归,因此顶层 `Model` 和嵌套
230
- `model` 仍是普通扩展字段。构造后修改调用方原对象不会改变请求快照。若 Gateway 拒绝合法未知
231
- 参数,错误沿既有 `GatewayAccessError` 浮现,SDK 不会删除该参数重试。
221
+ ## 外部配置参考 / External configuration references
232
222
 
233
- `bargeIn.initialPlaybackGuardMs` 的数值单位固定为 `ms`(毫秒):`3000` 表示 `3000 ms`,即 3 秒。该值必须是有限的非负整数。非零值会忽略完全落在窗口内的短语音;`3000 ms` 只是当前实验支持的业务验证起始建议,不是 SDK 默认值,也不代表所有设备的推荐阈值。请在目标麦克风、扬声器、AEC 和 greeting 条件下自行回归;设为 `0` 或省略即可保持现有行为。
223
+ - 用户可在 [控制台](https://eva.autoarkai.com/console/api-keys) 创建并获取 API Key,格式如
224
+ `ak-xxxxxxxx...`;也可使用 [EVA Skill](https://eva-dev.autoarkai.com/api-docs/guide/eva-skill),
225
+ 或参考 [EVA CLI 文档](https://eva-dev.autoarkai.com/api-docs/guide/eva-cli)获取 API Key。
234
226
 
235
- `DEFAULT_EMOTION_CODES` 是一个冻结的 readonly tuple,顺序固定为 `neutral`、`happy`、`sad`、`angry`、`anxious`、`confused`、`excited`、`frustrated`、`unknown`;`DefaultEmotionCode` 是由该 tuple 派生的类型联合。custom code 必须匹配 `^[a-z][a-z0-9_-]{0,63}$`,数组不能为空或重复。custom labels 不会补全默认业务标签:它会完整替换它们,并在缺失时由 SDK 追加唯一的 `unknown`;显式提供一次 `unknown` 也合法。即使 `enabled: false`,显式非法的 labels 或 `maxInputChars` 仍会在构造 Agent 时失败。
227
+ ASR、LLM、TTS model、TTS voice,以及采样率和模型参数的支持范围、准确值与默认值见
228
+ [EVA Models](https://eva.autoarkai.com/api-docs/guide/gateway-model-list.md)。选择 model 时,相关采样率、voice、生成参数与
229
+ model-specific 参数必须配套使用。`temperature`、token 上限等可选调音参数可以省略,由 Gateway
230
+ 或 model 使用默认行为;带 SDK 默认值的字段仍以对应 JSDoc 为准。SDK 只校验字段形状和基础
231
+ 数值,不维护动态 model 兼容矩阵。
236
232
 
237
- `instructions` 只用于补充场景、语气或业务判断背景,SDK 会把它作为内置固定分类 prompt 的一部分;不要在这里填写一份完整 prompt。它不会新增 labels,也不能改变固定输出约束。例如:
233
+ 当前 SDK 使用 Chat Completions(`/llm/v1/chat/completions`)。查看 EVA Models 的 LLM 参数时,
234
+ 请使用 **Chat Completions** 部分。
235
+
236
+ 当前 SDK 的托管 ASR 和 TTS 链路只支持 PCM(`pcm_s16le`):ASR model 必须支持 PCM 输入,
237
+ TTS model 必须支持 PCM 输出。不支持 PCM 的 model 无法用于当前 SDK。
238
+
239
+ ## Emotion
240
+
241
+ 通过 `emotion.enabled` 开启情绪识别后,SDK 会同时处理麦克风转写和 `submitText()` 提交的用户文本,
242
+ 并通过 `emotion.detected` 事件返回分类结果。例如:
238
243
 
239
244
  ```ts
240
245
  const emotion: EmotionConfig = {
241
246
  enabled: true,
242
247
  labels: ["happy", "sad"],
243
248
  instructions: "这是儿童陪伴场景,重点区分害怕、难过和开心。",
244
- maxInputChars: 2000,
245
249
  };
246
250
  ```
247
251
 
248
- ## Command
249
-
250
- Command 只能通过 `commands.registrations` Agent 构造时成对注册。每个 `CommandRegistration` 在同一对象里提供一份纯 definition 与一个 handler;没有 `definitions` / `handlers` 双表,也没有运行中热注册。下面提供两份完整、可复制的 browser command registration 用法;它们不是 SDK 内置 command,接入方可以按自己的页面和业务实现 registration。
251
-
252
- `CommandCall.id` 是 Gateway/model 给出的不透明标识,只用于同一 turn 内关联结果与 SDK 幂等;下一 turn 可以再次出现相同 id。handler 应尽快响应 `CommandContext.signal`。`command.called` 一旦可见,handler 已承诺入场一次,因此同步 listener 立即打断时 handler 仍可能以预取消 signal 入场。SDK 会抑制取消后的事件、result回填、LLM与媒体输出,但不能撤回 handler 已提交到外部系统的业务副作用;跨进程幂等或补偿仍由接入方负责。
253
-
254
- `maxCallsPerTurn` 统计 Gateway 已聚合完成的 raw tool call,默认 `3`。`executable`、`rejected`、`replay` 与 `conflict` 都各消耗一次额度,尚未收全的 arguments fragment 不计。同一 turn 内 id、name 与原始 `argumentsJson` 字节完全一致的 replay 会复用已收束 result,不重复调用 handler 或发 command lifecycle event;相同 id 但内容不同属于 conflict。未知 command、非法 JSON 或 schema 不匹配等 rejection 不调用 handler,也不产生 command lifecycle/error event,而是把安全失败 result 回填给模型。最后一个 result 后只允许一次不带 tools 的 closing completion;如果模型仍返回 tool call,当前 turn 按 LLM failure 结束,不再执行或请求。
255
-
256
- handler 返回 `ok: false`、throw/reject 或返回运行时无效值都会规整为脱敏的 `command.failed`;原始异常、cause、AK 与 provider raw body 不会进入公共事件或 tool result。成功或失败 result 都会回填给模型,用于生成后续自然语言回复。
252
+ `emotionCode` 默认来自 `DEFAULT_EMOTION_CODES`;custom `labels` 会完整替换默认业务标签,SDK
253
+ 仍会保留 `unknown`。`confidence` 是模型自报的可选信心度,不是经过统计校准的概率;
254
+ `source`、`textPreview` `latencyMs` 分别表示输入来源、用户内容预览和分类耗时,记录事件时应按
255
+ 应用的隐私规则处理 `textPreview`。
257
256
 
258
- <!-- command-example:show-current-time:start -->
257
+ 情绪识别与正常回复并发,不改写 reply、history、messages 或 TTS,也不保证相对
258
+ `reply.final` 的到达顺序。分类请求失败会发出非致命 `error`,但不会中断正常回复;
259
+ `instructions` 只补充业务语境,不需要自定义分类时可以省略。字段默认值和校验规则见
260
+ `EmotionConfig` JSDoc。
259
261
 
260
- ```ts
261
- import type { CommandRegistration } from "@autoark-ai/eva-client-sdk-ts";
262
-
263
- export interface ShowCurrentTimeCommandOptions {
264
- readonly clock?: () => Date;
265
- readonly showAlert?: (message: string) => void;
266
- }
267
-
268
- export function createShowCurrentTimeCommand(
269
- options: ShowCurrentTimeCommandOptions = {},
270
- ): CommandRegistration {
271
- const clock = options.clock ?? (() => new Date());
272
- const showAlert = options.showAlert ?? ((message: string) => window.alert(message));
273
- return {
274
- definition: {
275
- name: "show_current_time",
276
- description: "在当前页面弹窗展示用户本地时间。",
277
- },
278
- handler(_call, context) {
279
- if (context.signal.aborted) {
280
- return { ok: false, message: "Command cancelled" };
281
- }
282
- const now = clock();
283
- showAlert(`当前时间:${now.toLocaleTimeString()}`);
284
- return {
285
- ok: true,
286
- message: "已显示当前时间",
287
- data: { isoTime: now.toISOString() },
288
- };
289
- },
290
- };
291
- }
292
- ```
293
-
294
- <!-- command-example:show-current-time:end -->
295
-
296
- <!-- command-example:set-page-theme:start -->
297
-
298
- ```ts
299
- import type { CommandRegistration } from "@autoark-ai/eva-client-sdk-ts";
300
-
301
- export type PageTheme = "light" | "dark";
302
-
303
- export interface SetPageThemeCommandOptions {
304
- readonly applyTheme?: (theme: PageTheme) => void;
305
- }
306
-
307
- export function createSetPageThemeCommand(
308
- options: SetPageThemeCommandOptions = {},
309
- ): CommandRegistration {
310
- const applyTheme = options.applyTheme ?? applyPageTheme;
311
- return {
312
- definition: {
313
- name: "set_page_theme",
314
- description: "切换当前浏览器页面的明暗主题。",
315
- parameters: [{
316
- name: "theme",
317
- description: "要应用的页面主题。",
318
- type: "string",
319
- required: true,
320
- enum: ["light", "dark"],
321
- example: "dark",
322
- }],
323
- },
324
- handler(call, context) {
325
- if (context.signal.aborted) {
326
- return { ok: false, message: "Command cancelled" };
327
- }
328
- const theme = call.arguments.theme;
329
- if (theme !== "light" && theme !== "dark") {
330
- return { ok: false, message: "Unsupported page theme" };
331
- }
332
- applyTheme(theme);
333
- return {
334
- ok: true,
335
- message: `页面主题已切换为 ${theme}`,
336
- data: { theme },
337
- };
338
- },
339
- };
340
- }
341
-
342
- function applyPageTheme(theme: PageTheme): void {
343
- const root = document.documentElement;
344
- const dark = theme === "dark";
345
- root.dataset.theme = theme;
346
- root.style.colorScheme = theme;
347
- root.style.backgroundColor = dark ? "#171717" : "#ffffff";
348
- root.style.color = dark ? "#f5f5f5" : "#171717";
349
- document.body.style.backgroundColor = root.style.backgroundColor;
350
- document.body.style.color = root.style.color;
351
- }
352
- ```
353
-
354
- <!-- command-example:set-page-theme:end -->
262
+ ## Command
355
263
 
356
- 把两份完整 registration 放入一个 `CommandsConfig`,再作为 Agent config 的 `commands` 字段传入:
264
+ Command 通过 `commands.registrations` Agent 构造时注册。每项同时包含提供给模型的
265
+ `definition` 和执行具体业务的 `handler`:
357
266
 
358
267
  ```ts
359
- import { createEvaVoiceDialogueAgent, type CommandsConfig } from "@autoark-ai/eva-client-sdk-ts";
268
+ import type { CommandsConfig } from "@autoark-ai/eva-client-sdk-ts";
360
269
 
361
270
  const commandConfig: CommandsConfig = {
362
- registrations: [createShowCurrentTimeCommand(), createSetPageThemeCommand()],
363
- maxCallsPerTurn: 3,
271
+ registrations: [
272
+ {
273
+ definition: {
274
+ name: "get_current_time",
275
+ description: "获取当前时间。",
276
+ },
277
+ handler(_call, context) {
278
+ if (context.signal.aborted) {
279
+ return { ok: false, message: "Command cancelled" };
280
+ }
281
+ return { ok: true, data: { isoTime: new Date().toISOString() } };
282
+ },
283
+ },
284
+ ],
364
285
  };
365
-
366
- const agent = createEvaVoiceDialogueAgent({
367
- apiKey: applicationManagedApiKey,
368
- asr: {
369
- model: "fun_asr",
370
- sampleRate: 48_000,
371
- },
372
- llm: {
373
- model: "doubao-seed-2-0-mini-nothink",
374
- },
375
- tts: {
376
- model: "cosyvoice_tts",
377
- voice: "longjielidou_v3",
378
- },
379
- commands: commandConfig,
380
- });
381
286
  ```
382
287
 
288
+ 将 `commandConfig` 传给 Agent config 的 `commands` 字段。完整字段和行为说明请查看对应 JSDoc。
289
+
383
290
  ## 消息
384
291
 
385
292
  `getMessages()` 同步返回按提交顺序排列的只读快照。每条 `ConversationMessage` 包含:
@@ -427,10 +334,6 @@ user 与 assistant 的最终文本才会进入消息列表。同一轮的两条
427
334
 
428
335
  所有事件都有 `streamId`、`partial`、`final` 和只读 `metadata`;除无法定位 turn 的错误外都有 `turnId`。还可能包含 `sequence`、`timestamp` 和 `frameId`。
429
336
 
430
- `emotion.detected` 是只上报的旁路结果,不参与 reply、history、messages 或 TTS 控制。`emotionCode` 一定在当前有效 code 空间内,无法解析或越界时为 `unknown`。`confidence` 若存在,会被归一化到 `[0,1]`;它只是模型自报值,不是经过校准的概率或准确率承诺。`textPreview` 含用户内容:不超过 100 个 Unicode code point 时保留原文,超过时取前 100 个后追加 ASCII `...`,应用仍应按自身隐私策略处理事件和日志。
431
-
432
- 分类复用当前 Agent 的 LLM stage,但不会接收正常 reply 的 system prompt、history 或 camera。分类请求失败时,同 turn 发出 `fatal: false` 的脱敏 `error`,不伪造 `unknown` 结果;新输入或 `stop()` 取消的分类不会发出 emotion 事件或错误。分类可能晚于 `reply.final` 到达。
433
-
434
337
  建议用穷尽 switch 消费事件:
435
338
 
436
339
  ```ts
@@ -573,18 +476,17 @@ const softwareAecTransports: MediaTransportsConfig = {
573
476
  | 输入 | `createBrowserAudioInputSource()` | 使用浏览器麦克风采集音频 |
574
477
  | 输出 | `createBrowserAudioOutputSink()` | 使用 Web Audio 播放 TTS 音频 |
575
478
  | AEC | `createPassthroughAecProcessor()` | 不做软件回声处理,由浏览器和操作系统负责 AEC |
576
- | 摄像头 | `createBrowserCameraSnapshotSource()` | 持续持有 video session,并在语音开始时采一张 PNG/JPEG |
479
+ | 摄像头 | `createBrowserCameraSnapshotSource()` | 提供可选的浏览器静态图片采集 |
577
480
 
578
481
  ```ts
579
482
  const defaultTransports: MediaTransportsConfig = {
580
483
  input: createBrowserAudioInputSource({ echoCancellation: true }),
581
484
  output: createBrowserAudioOutputSink(),
582
485
  aec: createPassthroughAecProcessor(),
583
- camera: createBrowserCameraSnapshotSource(),
584
486
  };
585
487
  ```
586
488
 
587
- 摄像头 factory 只是 `CameraSnapshotSource` 的默认浏览器实现,不会被 agent factory 自动补齐。不开启 camera 时不会申请摄像头权限;开启后可以直接问“图片里有什么”。图片只参与当前 LLM 请求,不进入 `getMessages()` 或后续 history。摄像头权限、设备、超时或本地采图失败会通过结构化 `media` error 可观测,并以纯文本继续当前 turn;已经发出的多模态请求若被模型拒绝,则按普通 gateway/provider error 结束,不会移除图片再做纯文本重试。
489
+ 摄像头按前文的可选小节加入,不会由 agent factory 自动补齐。
588
490
 
589
491
  默认组合开启浏览器 input 的 `echoCancellation`,同时使用 passthrough AEC。如果接入自己的软件 AEC,应将 `echoCancellation` 设为 `false`,避免平台 AEC 与软件 AEC 重复处理。
590
492
 
package/dist/browser.d.ts CHANGED
@@ -2,33 +2,39 @@ import type { AecProcessor, AudioInputSource, AudioOutputSink, CameraSnapshotSou
2
2
  /** Options for the SDK-provided browser microphone input. */
3
3
  export interface BrowserAudioInputOptions {
4
4
  /**
5
- * Maximum encoded chunks buffered before the current capture session fails.
5
+ * 当前 capture session 失败前最多缓冲的已编码音频块数;必须为正整数,越大越能吸收短暂消费抖动,但占用更多内存并可能增加排队延迟。
6
+ * English: Maximum encoded chunks buffered before capture fails; it must be a positive integer, and larger values absorb brief consumer stalls at the cost of memory and queueing latency.
6
7
  * @defaultValue 128
7
8
  * @remarks Must be a positive integer.
8
9
  */
9
10
  maxBufferedChunks?: number;
10
11
  /**
11
- * Requests platform echo cancellation through `getUserMedia`.
12
+ * 通过 `getUserMedia` 请求平台 echo cancellation;启用通常降低扬声器回声,但与 active software AEC 同开可能重复处理。
13
+ * English: Requests platform echo cancellation through `getUserMedia`; it usually reduces speaker echo but may double-process audio with active software AEC.
12
14
  * @defaultValue true
13
15
  * @remarks The browser or device may ignore this constraint. Disable it when using active software AEC.
14
16
  */
15
17
  echoCancellation?: boolean;
16
18
  /**
17
- * Requests platform noise suppression through `getUserMedia`.
19
+ * 通过 `getUserMedia` 请求平台 noise suppression;启用通常降低背景噪声,但可能改变音色,且平台可忽略该 constraint。
20
+ * English: Requests platform noise suppression through `getUserMedia`; it usually reduces background noise but may color speech, and the platform may ignore it.
18
21
  * @defaultValue true
19
22
  * @remarks The browser or device may ignore this constraint.
20
23
  */
21
24
  noiseSuppression?: boolean;
22
25
  /**
23
- * Requests platform automatic gain control through `getUserMedia`.
26
+ * 通过 `getUserMedia` 请求平台 automatic gain control;启用通常均衡响度,但可能放大底噪,且平台可忽略该 constraint。
27
+ * English: Requests platform automatic gain control through `getUserMedia`; it usually evens loudness but may amplify noise, and the platform may ignore it.
24
28
  * @defaultValue true
25
29
  * @remarks The browser or device may ignore this constraint.
26
30
  */
27
31
  autoGainControl?: boolean;
28
32
  }
29
33
  /**
30
- * Creates the SDK-provided browser microphone implementation of `AudioInputSource`.
34
+ * 创建 SDK 提供的 browser microphone `AudioInputSource`;factory 本身不采集,start 后资源由 agent lifecycle 独占。
35
+ * English: Creates the SDK-provided browser microphone `AudioInputSource`; the factory does not capture immediately, and the agent owns resources after start.
31
36
  * The dialogue runtime owns `start()` / `frames()` / `stop()` after agent construction.
37
+ * @param options - 可选的 queue 与平台 audio constraints;省略时使用已记录默认值。 English: Optional queue and platform audio constraints; omission uses the documented defaults.
32
38
  */
33
39
  export declare function createBrowserAudioInputSource(options?: BrowserAudioInputOptions): AudioInputSource;
34
40
  /**
@@ -39,24 +45,28 @@ export declare function createBrowserAudioOutputSink(): AudioOutputSink;
39
45
  /** Options for the SDK-provided browser still-camera source. */
40
46
  export interface BrowserCameraSnapshotOptions {
41
47
  /**
42
- * Native video track constraints passed to `getUserMedia`.
48
+ * 传给 `getUserMedia` 的原生 video track constraints;更严格的 constraints 可能提高画面要求,也更容易被设备拒绝或适配。
49
+ * English: Native video-track constraints passed to `getUserMedia`; stricter constraints may request better capture but are more likely to be adapted or rejected.
43
50
  * @defaultValue `true`
44
51
  * @remarks The browser may adapt or ignore individual ideal constraints.
45
52
  */
46
53
  video?: MediaTrackConstraints;
47
54
  /**
48
- * Requested still-image encoding.
55
+ * 请求的静态图片编码;PNG 通常无损但字节更大,JPEG 通常更小但有损。
56
+ * English: Requested still-image encoding; PNG is usually lossless and larger, while JPEG is usually smaller and lossy.
49
57
  * @defaultValue `image/png`
50
58
  */
51
59
  mimeType?: "image/png" | "image/jpeg";
52
60
  /**
53
- * JPEG encoder quality from 0 through 1.
61
+ * JPEG encoder质量范围 `0-1`;值越大通常细节更多、文件也更大。
62
+ * English: JPEG encoder quality in `0-1`; larger values usually preserve more detail and produce larger files.
54
63
  * @remarks Used only when `mimeType` is `image/jpeg`. Omit to use the browser default.
55
64
  */
56
65
  jpegQuality?: number;
57
66
  }
58
67
  /**
59
- * Creates the SDK-provided browser implementation of `CameraSnapshotSource`.
68
+ * 创建 SDK 提供的 browser `CameraSnapshotSource`,不在 factory 调用时申请权限。
69
+ * English: Creates the SDK-provided browser `CameraSnapshotSource` without requesting permission at factory call time.
60
70
  *
61
71
  * @remarks
62
72
  * The factory does not request permission. After composition, the runtime exclusively owns
@@ -71,8 +81,7 @@ export interface BrowserCameraSnapshotOptions {
71
81
  * faults the camera role and later camera controls reject deterministically. A successful `stop`
72
82
  * permits a later sequential restart.
73
83
  *
74
- * @param options - Browser video constraints and still-image encoding preferences. Omit to use
75
- * the default video device and PNG encoding at the captured frame's native dimensions.
84
+ * @param options - Browser video constraints 与静态图片编码选项;省略时使用默认 video device、原始尺寸和 PNG。 English: Browser video constraints and still-image encoding preferences; omit for the default device, native dimensions, and PNG.
76
85
  * @returns A reusable `CameraSnapshotSource` to pass as `MediaTransportsConfig.camera`.
77
86
  */
78
87
  export declare function createBrowserCameraSnapshotSource(options?: BrowserCameraSnapshotOptions): CameraSnapshotSource;
package/dist/browser.js CHANGED
@@ -498,7 +498,10 @@ function workletNodeOf(dependencies, context) {
498
498
  if (dependencies.audioWorkletNodeFactory !== void 0) {
499
499
  return dependencies.audioWorkletNodeFactory(context, WORKLET_NAME);
500
500
  }
501
- return new AudioWorkletNode(context, WORKLET_NAME);
501
+ return new AudioWorkletNode(
502
+ context,
503
+ WORKLET_NAME
504
+ );
502
505
  }
503
506
  function objectUrlOf(dependencies) {
504
507
  return dependencies.objectUrl ?? {
@@ -685,7 +688,9 @@ var DefaultBrowserCameraSnapshotSource = class {
685
688
  start(signal) {
686
689
  if (signal.aborted) return Promise.reject(abortError());
687
690
  if (this.active !== void 0 || this.pendingStart !== void 0 || this.teardownPromise !== void 0) {
688
- return Promise.reject(new EvaSdkError("Browser camera session already exists", { fatal: true }));
691
+ return Promise.reject(
692
+ new EvaSdkError("Browser camera session already exists", { fatal: true })
693
+ );
689
694
  }
690
695
  const generation = ++this.generation;
691
696
  const cancellation = cancellable();
@@ -716,9 +721,11 @@ var DefaultBrowserCameraSnapshotSource = class {
716
721
  return Promise.reject(new EvaSdkError("Browser camera is not started", { fatal: true }));
717
722
  }
718
723
  if (this.pendingCapture !== void 0) {
719
- return Promise.reject(new EvaSdkError("Browser camera capture is already in progress", {
720
- fatal: true
721
- }));
724
+ return Promise.reject(
725
+ new EvaSdkError("Browser camera capture is already in progress", {
726
+ fatal: true
727
+ })
728
+ );
722
729
  }
723
730
  const cancellation = cancellable();
724
731
  const pending = {
@@ -880,13 +887,17 @@ function createCanvasOf(dependencies) {
880
887
  },
881
888
  encode(type, quality) {
882
889
  return new Promise((resolve, reject) => {
883
- canvas.toBlob((blob) => {
884
- if (blob === null) {
885
- reject(new EvaSdkError("Browser camera image encoding failed", { fatal: true }));
886
- } else {
887
- resolve(blob);
888
- }
889
- }, type, quality);
890
+ canvas.toBlob(
891
+ (blob) => {
892
+ if (blob === null) {
893
+ reject(new EvaSdkError("Browser camera image encoding failed", { fatal: true }));
894
+ } else {
895
+ resolve(blob);
896
+ }
897
+ },
898
+ type,
899
+ quality
900
+ );
890
901
  });
891
902
  }
892
903
  };