@h-ai/ai 0.1.0-alpha.35 → 0.1.0-alpha.37
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 +114 -27
- package/dist/{ai-audio-ws-protocol-C-V8N8YL.d.ts → ai-audio-ws-protocol-BK5753o1.d.ts} +166 -22
- package/dist/{ai-reasoning-types-BGf-x2Qj.d.ts → ai-reasoning-types-CslaY4xX.d.ts} +298 -60
- package/dist/browser.d.ts +4 -3
- package/dist/browser.js +3 -2
- package/dist/{chunk-URXCNMJW.js → chunk-5XL4QDIJ.js} +47 -105
- package/dist/chunk-5XL4QDIJ.js.map +1 -0
- package/dist/{chunk-AXIZBZMV.js → chunk-ELH2IPOE.js} +79 -38
- package/dist/chunk-ELH2IPOE.js.map +1 -0
- package/dist/chunk-P3GND76X.js +107 -0
- package/dist/chunk-P3GND76X.js.map +1 -0
- package/dist/client/index.d.ts +20 -7
- package/dist/client/index.js +2 -1
- package/dist/index.d.ts +58 -4
- package/dist/index.js +1222 -607
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
- package/dist/chunk-AXIZBZMV.js.map +0 -1
- package/dist/chunk-URXCNMJW.js.map +0 -1
package/README.md
CHANGED
|
@@ -18,13 +18,36 @@ AI 能力模块,提供统一的 `ai` 服务对象,覆盖 LLM 对话、工具
|
|
|
18
18
|
- `ai.audio`:语音识别(ASR)与语音合成(TTS),支持完整与流式调用,覆盖 OpenAI / MiMo / Qwen / 豆包平台。
|
|
19
19
|
- `ai.a2a`:Agent-to-Agent 请求处理与远端调用。
|
|
20
20
|
- `@h-ai/ai/client`:前端轻量客户端(配合 API 服务)。
|
|
21
|
-
- `AIStoreProvider
|
|
21
|
+
- `AIStoreProvider`:统一存储抽象;无数据库时默认使用进程内临时 Store,已初始化 reldb + vecdb 时自动使用持久化 DB Provider。
|
|
22
22
|
|
|
23
23
|
更完整的方法清单、错误码与长示例见 [REFERENCE.md](./REFERENCE.md)。
|
|
24
24
|
|
|
25
25
|
## 快速开始
|
|
26
26
|
|
|
27
|
-
###
|
|
27
|
+
### LLM-only(零数据库依赖)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { ai } from '@h-ai/ai'
|
|
31
|
+
|
|
32
|
+
const init = await ai.init({
|
|
33
|
+
llm: {
|
|
34
|
+
model: 'gpt-4o-mini',
|
|
35
|
+
apiKey: process.env.HAI_AI_LLM_API_KEY,
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
if (!init.success)
|
|
39
|
+
return init
|
|
40
|
+
|
|
41
|
+
const result = await ai.llm.chat({
|
|
42
|
+
messages: [{ role: 'user', content: '你好!' }],
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
await ai.close()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
未初始化 reldb/vecdb 时,AI 使用进程内临时 Store 保存会话等运行时状态;`ai.close()`、进程退出或多实例切换后数据不会保留。需要 Memory、Context、Persona 或会话跨重启持久化时,使用下面的 DB Provider。
|
|
49
|
+
|
|
50
|
+
### 持久化 DB Provider(reldb + vecdb)
|
|
28
51
|
|
|
29
52
|
```ts
|
|
30
53
|
import { ai } from '@h-ai/ai'
|
|
@@ -76,9 +99,9 @@ await ai.close()
|
|
|
76
99
|
|
|
77
100
|
- 对外只通过 `ai` 服务对象和少量独立工厂(如 `createMcpServer`)访问。
|
|
78
101
|
- 生命周期为 `await ai.init(config, options?)` / `await ai.close()`;关闭会等待自定义 `AIStoreProvider.close()`。
|
|
79
|
-
-
|
|
102
|
+
- 领域方法返回 `HaiResult<T>` 或 `Promise<HaiResult<T>>`;业务失败通过 `result.success === false` 和 `result.error.code` 表达。流式 `AsyncIterable`、客户端传输和第三方回调在建立或迭代期间可能抛异常。
|
|
80
103
|
- `ai.tools` 与 `ai.stream` 是纯函数子系统,无需初始化即可使用。
|
|
81
|
-
-
|
|
104
|
+
- 未初始化 reldb/vecdb 时默认使用进程内临时 Store;两者均已初始化时自动使用 DB Provider。自定义 Provider 可隐藏其他存储后端。
|
|
82
105
|
|
|
83
106
|
## API 概览
|
|
84
107
|
|
|
@@ -115,13 +138,31 @@ const temp = await ai.llm.chat({
|
|
|
115
138
|
### 工具调用
|
|
116
139
|
|
|
117
140
|
```ts
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
import { z } from 'zod'
|
|
142
|
+
|
|
143
|
+
const registry = ai.tools.createRegistry({
|
|
144
|
+
// 统一授权在 Zod 校验后、handler 前执行;false/异常均 fail-closed
|
|
145
|
+
authorize: ({ toolName, context }) => canExecuteTool(context.objectId, toolName),
|
|
146
|
+
})
|
|
147
|
+
const registered = registry.register(ai.tools.define({
|
|
120
148
|
name: 'get_weather',
|
|
121
149
|
description: '获取天气',
|
|
122
150
|
parameters: z.object({ city: z.string() }),
|
|
123
|
-
handler
|
|
151
|
+
// handler 第二参为执行上下文:可响应取消(打断/超时)、感知截止时间与交互主体
|
|
152
|
+
handler: async ({ city }, { signal }) => {
|
|
153
|
+
const res = await fetch(`https://api.example.com/weather?city=${city}`, { signal })
|
|
154
|
+
return res.json()
|
|
155
|
+
},
|
|
156
|
+
timeoutMs: 10_000, // 本工具默认超时(可被 execute 的 deadline / timeoutMs 覆盖)
|
|
124
157
|
}))
|
|
158
|
+
if (!registered.success)
|
|
159
|
+
return registered
|
|
160
|
+
|
|
161
|
+
// 执行时可传入取消信号 / 超时 / 作用域;取消或超时返回 TOOL_TIMEOUT,且不再等待未响应的 handler
|
|
162
|
+
const result = await registry.execute(toolCall, { signal: controller.signal, objectId: 'user-001', timeoutMs: 30_000 })
|
|
163
|
+
|
|
164
|
+
// 批量调用默认串行,避免副作用工具并发启动;纯读取且确认安全时才显式并行
|
|
165
|
+
const batch = await registry.executeAll(toolCalls)
|
|
125
166
|
|
|
126
167
|
const chat = await ai.llm.chat({ messages, tools: registry.getDefinitions() })
|
|
127
168
|
```
|
|
@@ -130,6 +171,7 @@ const chat = await ai.llm.chat({ messages, tools: registry.getDefinitions() })
|
|
|
130
171
|
|
|
131
172
|
```ts
|
|
132
173
|
import { createMcpServer, StreamableHTTPServerTransport } from '@h-ai/ai'
|
|
174
|
+
import { z } from 'zod'
|
|
133
175
|
|
|
134
176
|
const server = createMcpServer({ name: 'my-server', version: '1.0.0' })
|
|
135
177
|
server.registerTool('search', {
|
|
@@ -149,6 +191,15 @@ if (!enriched.success) {
|
|
|
149
191
|
return enriched
|
|
150
192
|
}
|
|
151
193
|
|
|
194
|
+
// 多租户推荐入口:scoped() 绑定主体与作用域,所有操作自动携带 objectId / scope(含归属校验),杜绝「忘记传 objectId」越权
|
|
195
|
+
const memory = ai.memory.scoped({ objectId: 'user-001', scope: { topicId: 't-1', personaId: 'p-1' } })
|
|
196
|
+
await memory.add({ content: '用户偏好中文', type: 'preference' })
|
|
197
|
+
const recalled = await memory.recall('语言偏好')
|
|
198
|
+
|
|
199
|
+
// clear 拒绝空过滤(防误清全局);全局清空只能显式走管理接口
|
|
200
|
+
await memory.clear({ types: ['event'] }) // 仅清该作用域内的 event
|
|
201
|
+
await ai.memory.admin.clearAll({ confirm: true }) // 危险:清空整个记忆后端,需显式确认
|
|
202
|
+
|
|
152
203
|
const rag = await ai.rag.query('核心架构是什么?', { sources: ['docs'], topK: 5 })
|
|
153
204
|
|
|
154
205
|
const setup = await ai.knowledge.setup()
|
|
@@ -165,8 +216,8 @@ if (setup.success) {
|
|
|
165
216
|
await ai.init({
|
|
166
217
|
audio: {
|
|
167
218
|
models: [
|
|
168
|
-
{ id: 'asr', provider: 'qwen', model: 'qwen3-asr-flash-realtime' },
|
|
169
|
-
{ id: 'tts', provider: 'qwen', model: 'qwen3-tts-flash-realtime' },
|
|
219
|
+
{ id: 'asr', provider: 'qwen', model: 'qwen3-asr-flash-realtime', operations: ['transcribe'] },
|
|
220
|
+
{ id: 'tts', provider: 'qwen', model: 'qwen3-tts-flash-realtime', operations: ['synthesize'] },
|
|
170
221
|
],
|
|
171
222
|
transcribeModel: 'asr',
|
|
172
223
|
synthesizeModel: 'tts',
|
|
@@ -189,16 +240,32 @@ for await (const event of ai.audio.transcribeStream({
|
|
|
189
240
|
updateTranscript(event.text, event.final)
|
|
190
241
|
}
|
|
191
242
|
|
|
192
|
-
//
|
|
243
|
+
// 流式合成:调用方为文本段分配稳定 ID,事件可精确关联文本与音频;signal 可随时打断
|
|
193
244
|
const controller = new AbortController()
|
|
194
|
-
for await (const
|
|
195
|
-
|
|
245
|
+
for await (const event of ai.audio.synthesizeStream({
|
|
246
|
+
text: { id: 'answer-1', text: '欢迎参加访谈。' },
|
|
247
|
+
voice: 'Cherry',
|
|
248
|
+
instruction: '用轻快的语气',
|
|
249
|
+
signal: controller.signal,
|
|
250
|
+
})) {
|
|
251
|
+
if (event.type === 'segment_started')
|
|
252
|
+
prepareDecoder(event.format, event.sampleRate, event.channels) // 真实输出格式来自服务端解析后的 Provider 输出
|
|
253
|
+
else if (event.type === 'audio')
|
|
254
|
+
await player.write(event.data)
|
|
255
|
+
else if (event.type === 'segment_done')
|
|
256
|
+
markSegmentReadyToCommit(event.segmentId)
|
|
196
257
|
}
|
|
258
|
+
|
|
259
|
+
// 实时会话启动前按操作校验模型能力
|
|
260
|
+
const caps = ai.audio.getCapabilities({ operation: 'synthesize', model: 'tts' })
|
|
261
|
+
if (caps.success && caps.data.synthesize?.streamingAudioOutput) { /* 可实时 TTS */ }
|
|
197
262
|
```
|
|
198
263
|
|
|
199
|
-
|
|
264
|
+
> `synthesizeStream` 严格按 `segment_started → audio* → segment_done` 产出事件。`segment_started` 携带服务端解析 Provider 后的**真实输出音频参数**(`format` / `sampleRate` / `channels`),播放器据此正确解码,不应按请求参数猜测格式。播放器只有在对应音频真正播放完成后才应把该段文本计入 `spokenText`;播放状态仍由应用管理。
|
|
200
265
|
|
|
201
|
-
|
|
266
|
+
取消/超时/连接错误统一为领域错误:`AbortSignal` 触发 → `AUDIO_CANCELLED`(超时 → `AUDIO_TIMEOUT`),连接失败或 `end` 前异常断连 → `AUDIO_CONNECTION_FAILED`。实时连接时长受 `audio.maxStreamDurationMs`(默认 5 分钟)限制。
|
|
267
|
+
|
|
268
|
+
浏览器 / 移动端通过 `@h-ai/serv` 暴露的统一语音 WebSocket 入口访问,`@h-ai/ai/client` 提供与 Node 端一致的 `audio.*` API(传输细节内部隐藏)。浏览器客户端严格区分正常结束、取消(`AUDIO_CANCELLED`)与异常断连(`AUDIO_CONNECTION_FAILED`):取消或在 `end` 前断连会抛出对应领域错误码,`synthesize` 不会把未完成的部分音频当作成功结果返回。
|
|
202
269
|
|
|
203
270
|
### Context 管理器
|
|
204
271
|
|
|
@@ -207,32 +274,43 @@ const manager = ai.context.createManager({
|
|
|
207
274
|
scope: { objectId: 'user-001', sessionId: 'sess-001' },
|
|
208
275
|
compress: { auto: true, strategy: 'hybrid', maxTokens: 8000 },
|
|
209
276
|
memory: { enable: true, enableExtract: true },
|
|
277
|
+
concurrency: 'reject', // 单活动生成(默认):活动生成期间的新 chat 返回 CONTEXT_BUSY;'queue' 则排队
|
|
210
278
|
})
|
|
211
279
|
if (manager.success) {
|
|
212
280
|
const reply = await manager.data.chat('你好')
|
|
213
281
|
await manager.data.save()
|
|
282
|
+
// reset 生命周期完整:终止活动生成、清空消息/摘要/轮次,默认保留系统提示词
|
|
283
|
+
await manager.data.reset() // 可传 { preserveSystemPrompt, cancelActiveTurn, waitForMemoryTasks }
|
|
214
284
|
}
|
|
215
285
|
```
|
|
216
286
|
|
|
287
|
+
同一管理器默认实行**单活动生成**,避免「上一轮 AI 尚未退出,下一轮 user 消息先写入」导致的消息乱序;`reset()` 现为异步并会终止活动生成、释放并发屏障、清空轮次并默认重新写入 Persona/System Prompt。
|
|
288
|
+
|
|
217
289
|
#### 真实对话状态(Conversation Commit Layer)
|
|
218
290
|
|
|
219
291
|
默认(`turnCommit: 'auto'`)下,`chat` / `chatStream` 会把**模型生成的完整文本**写入上下文。但在「模型生成 → TTS 合成 → 实际播放」链路中,AI 可能说到一半就被打断——此时进入下一轮所有参与者可见的对话状态,应当是**实际播放出去的部分**,而非模型本想说完的全文。
|
|
220
292
|
|
|
221
|
-
设置 `turnCommit: 'manual'` 后,生成结果不会自动写入上下文,而是返回一个 `turnId
|
|
293
|
+
设置 `turnCommit: 'manual'` 后,生成结果不会自动写入上下文,而是返回一个 `turnId`;由调用方在确定「实际发生了什么」后显式提交真实文本。
|
|
294
|
+
|
|
295
|
+
`chatStream` 在**调用上游模型前**就登记轮次并产出 `turn_started`(事件序列 `turn_started → delta* → done`,中途取消时 `turn_started → delta* → cancelled`)。因此即使生成到一半被 `AbortSignal` 取消,也能拿到 `turnId` 与已生成文本,用真实内容提交:
|
|
222
296
|
|
|
223
297
|
```ts
|
|
224
298
|
const m = ai.context.createManager({ turnCommit: 'manual' /* ... */ }).data
|
|
299
|
+
const controller = new AbortController()
|
|
225
300
|
|
|
226
|
-
for await (const ev of m.chatStream('请展开讲讲')) {
|
|
227
|
-
if (ev.type === '
|
|
228
|
-
feedTts(ev.text)
|
|
229
|
-
} // 边生成边合成播放
|
|
230
|
-
else if (ev.type === 'done') {
|
|
301
|
+
for await (const ev of m.chatStream('请展开讲讲', { signal: controller.signal })) {
|
|
302
|
+
if (ev.type === 'turn_started') {
|
|
231
303
|
m.markTurnSpeaking(ev.turnId) // 可选:标记进入播放
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
304
|
+
}
|
|
305
|
+
else if (ev.type === 'delta') {
|
|
306
|
+
feedTts(ev.text) // 边生成边合成播放
|
|
307
|
+
}
|
|
308
|
+
else if (ev.type === 'done') {
|
|
309
|
+
await m.commitTurn(ev.turnId) // 完整提交
|
|
310
|
+
}
|
|
311
|
+
else if (ev.type === 'cancelled') {
|
|
312
|
+
// 生成被 controller.abort() 取消:轮次保留,只提交实际播放出去的部分
|
|
313
|
+
await m.interruptTurn(ev.turnId, { text: actuallySpokenText })
|
|
236
314
|
}
|
|
237
315
|
}
|
|
238
316
|
|
|
@@ -243,6 +321,7 @@ const turns = m.getTurns()
|
|
|
243
321
|
- `commitTurn(turnId, { text? })` — 提交真实文本(缺省用完整生成文本),状态转 `completed`。
|
|
244
322
|
- `interruptTurn(turnId, { text? })` — 只写入实际表达出去的部分(缺省视为未表达,不写入),状态转 `interrupted`。
|
|
245
323
|
- 只有 `committed` 的内容进入上下文与记忆提取;未提交/被打断丢弃的部分不会污染后续轮次。
|
|
324
|
+
- 若轮次在流完成前已被 `interruptTurn` 打断(如主持人抢话,同时上游模型恰好正常结束),`chatStream` **不会再产出 `done`**,避免业务层误判为正常完成后继续提交文本。
|
|
246
325
|
|
|
247
326
|
#### 会话固化(Memory 生命周期)
|
|
248
327
|
|
|
@@ -333,9 +412,9 @@ memory:
|
|
|
333
412
|
```
|
|
334
413
|
|
|
335
414
|
- **`native`(默认,推荐)**:HAI 原生引擎,复用同一套 vecdb(向量库)、reldb(关系库)、LLM 与 Embedding。`extract` 采用 **Mem0 式批量合并**——一次 LLM 调用对整批抽取事实与相关既有记忆做 ADD / UPDATE / DELETE / NONE 决策,实现增量更新、跨条去重与矛盾删除,并支持 `category` 主题标签。`maxEntriesPerObject`、`maxEntriesGlobal`、`recencyDecay`、`embeddingEnabled`、`writebackRelatedTopK` 均作用于此后端;淘汰按 `objectId` 分区触发,不会因某一主体写入过多而淘汰其他主体的记忆。native 后端的 `scope` 过滤:候选集已被 `objectId` 索引收窄(≤ `maxEntriesPerObject`),PostgreSQL 上还会把 scope 下推为 `data @> '{"scope":...}'::jsonb` 包含查询并命中 JSONB **GIN 索引**(SQLite / MySQL 退回内存匹配,结果一致)。
|
|
336
|
-
- **`mem0`(真·mem0ai/oss)**:直接使用 `mem0ai/oss` 的 `Memory` 引擎(嵌入式,无云服务)。LLM / Embedder 从 `llm`
|
|
415
|
+
- **`mem0`(真·mem0ai/oss)**:直接使用 `mem0ai/oss` 的 `Memory` 引擎(嵌入式,无云服务)。LLM / Embedder 从 `llm` 配置提取;`qdrant` / `pgvector` 可复用底层 vecdb。无法映射 `lancedb` / `chroma` 等后端时默认 fail-fast,只有显式设置 `memory.allowEphemeralFallback: true` 才使用 mem0 in-memory,避免重启后静默丢失记忆。历史记录默认禁用。
|
|
337
416
|
|
|
338
|
-
两个 Provider 对外 `ai.memory.*` API 完全一致(`extract` / `recall` / `injectMemories` / `add` / `update` / `get` / `remove` / `list` / `listPage` / `clear`),均支持 `objectId`(主体隔离)与 `scope`(业务作用域 key-value 过滤,如 `{ topicId, personaId }`)。`recall` / `list` / `listPage` / `clear` 均按 `scope` 严格过滤,`clear` 在传入 `types` / `scope`
|
|
417
|
+
两个 Provider 对外 `ai.memory.*` API 完全一致(`extract` / `recall` / `injectMemories` / `add` / `update` / `get` / `remove` / `list` / `listPage` / `clear`),均支持 `objectId`(主体隔离)与 `scope`(业务作用域 key-value 过滤,如 `{ topicId, personaId }`)。`recall` / `list` / `listPage` / `clear` 均按 `scope` 严格过滤,`clear` 在传入 `types` / `scope` 时仅删除同时匹配项(避免误删)。mem0 后端的 `extract` 在框架层用统一提取器完成分类与打分(honor `types` / `model` / `minImportance` / `systemPrompt`)后以 `infer:false` 写入,保留 `hai_type` / `hai_importance`;`recall` 同样支持 `types` 过滤与 `recencyWeight` 时间衰减——二者行为与 native 一致。一个差异:mem0 后端在 `update` 涉及 type/importance/metadata 时会重建记忆并重新分配 `id`(native 后端保持 id 稳定)。
|
|
339
418
|
|
|
340
419
|
**候选池与 scope 漏召回**:`scope` 过滤在内存中完成,若先按 `topK` 截断再过滤,同一主体下相关度较高的其它主题/角色记忆会把目标 scope 的记忆挤出候选池,导致「明明有却召回 0 条」。为此 `recall` / `injectMemories` 先取回 `topK × candidateMultiplier`(默认 5)条候选,过滤后再截取 `topK`。scope 隔离越细(如按 `topicId` + `personaId`),可将 `candidateMultiplier` 调大:
|
|
341
420
|
|
|
@@ -350,6 +429,13 @@ const memories = await ai.memory.recall('经济发展', {
|
|
|
350
429
|
|
|
351
430
|
`ai.config` 返回脱敏后的配置快照;`apiKey`、`privateKey`、URL 内嵌凭证等敏感字段不会原样暴露。
|
|
352
431
|
|
|
432
|
+
## 安全边界
|
|
433
|
+
|
|
434
|
+
- Prompt、检索文档和模型输出都按不可信输入处理;不要把用户内容拼进不可覆盖的系统规则。
|
|
435
|
+
- Zod 只校验工具参数形状,不代表调用者有权限。高权限工具必须在 handler 内再次校验身份、租户、资源归属与配额,并只把允许自动执行的工具注册给模型。
|
|
436
|
+
- `callRemoteAgent()` 是独立客户端能力,只依赖 `ai.init()`,不要求配置 Agent Card 或注册本地 executor。它拒绝非 HTTP(S) 和 URL 内嵌凭据,但应用仍必须对远端 origin 配置白名单,并在出口代理处限制 DNS 重绑定、重定向到私网和云元数据地址。
|
|
437
|
+
- 不记录完整 Prompt、工具参数、A2A headers、临时模型凭据或带 query 的远端 URL;确需审计时仅保存脱敏摘要。
|
|
438
|
+
|
|
353
439
|
## 错误处理
|
|
354
440
|
|
|
355
441
|
```ts
|
|
@@ -373,7 +459,8 @@ if (!result.success) {
|
|
|
373
459
|
- `hai:ai:300-302`:Embedding。
|
|
374
460
|
- `hai:ai:600-701`:Retrieval/RAG。
|
|
375
461
|
- `hai:ai:800-805`:Knowledge。
|
|
376
|
-
- `hai:ai:
|
|
462
|
+
- `hai:ai:050-059`:Audio。
|
|
463
|
+
- `hai:ai:900-905`:Memory。
|
|
377
464
|
- `hai:ai:980-984`:A2A。
|
|
378
465
|
|
|
379
466
|
## 测试
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as _h_ai_core from '@h-ai/core';
|
|
2
2
|
import { HaiResult } from '@h-ai/core';
|
|
3
|
-
import {
|
|
3
|
+
import { aG as ChatMessage, af as InteractionScope, O as MemoryType, br as RagOptions, bv as ReasoningOptions, c2 as ToolRegistryOperations, bd as MemoryEntry, bK as SessionInfo, b6 as LLMOperations, bj as MemoryOperations, bq as RagOperations, bu as ReasoningOperations, c as AIConfig, d as AIConfigInput, av as AIStoreProvider, c4 as ToolsOperations, bO as StreamOperations, bB as RetrievalOperations, b0 as KnowledgeOperations, ap as A2AOperations, q as AudioOperations, l as AudioFormat } from './ai-reasoning-types-CslaY4xX.js';
|
|
4
|
+
import * as zod from 'zod';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { Buffer } from 'node:buffer';
|
|
7
|
+
import * as zod_v4_core from 'zod/v4/core';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* @h-ai/ai — Compress 子功能类型
|
|
@@ -279,6 +281,20 @@ interface ContextManagerOptions {
|
|
|
279
281
|
* 部分才应进入下一轮所有参与者可见的对话状态,而不是模型本想说完的全文。
|
|
280
282
|
*/
|
|
281
283
|
turnCommit?: 'auto' | 'manual';
|
|
284
|
+
/**
|
|
285
|
+
* 并发生成策略(默认 `reject`)
|
|
286
|
+
*
|
|
287
|
+
* ContextManager 默认实行**单活动生成**:同一管理器同一时刻只允许一个未完成的
|
|
288
|
+
* `chat` / `chatStream` 轮次,避免「上一轮 AI 尚未退出,下一轮 user 消息先写入」
|
|
289
|
+
* 导致的消息乱序(user1 → user2 → assistant1)。
|
|
290
|
+
*
|
|
291
|
+
* - `reject`:已有活动生成时,新的 `chat` / `chatStream` 立即返回 `CONTEXT_BUSY` 错误。
|
|
292
|
+
* - `queue`:新请求排队,等待前一轮次进入终态(`completed` / `interrupted`)后再开始。
|
|
293
|
+
*
|
|
294
|
+
* `manual` 提交模式下,屏障持续到 `commitTurn` / `interruptTurn` 提交真实文本为止,
|
|
295
|
+
* 因此「打断当前轮次 → 立即发起下一轮」也能保证顺序正确。
|
|
296
|
+
*/
|
|
297
|
+
concurrency?: 'reject' | 'queue';
|
|
282
298
|
/**
|
|
283
299
|
* 压缩配置(覆盖全局 compress 配置)
|
|
284
300
|
*
|
|
@@ -345,6 +361,32 @@ interface ContextManagerOptions {
|
|
|
345
361
|
*/
|
|
346
362
|
maxToolRounds?: number;
|
|
347
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* 重置管理器选项(`ContextManager.reset` 使用)
|
|
366
|
+
*/
|
|
367
|
+
interface ContextResetOptions {
|
|
368
|
+
/**
|
|
369
|
+
* 是否保留系统提示词(默认 `true`)
|
|
370
|
+
*
|
|
371
|
+
* 系统提示词(Persona / System Prompt)只在创建时追加一次;重置若不保留,
|
|
372
|
+
* 会连同对话历史一起清空。默认重新写入系统提示词,避免 Persona 语义丢失。
|
|
373
|
+
*/
|
|
374
|
+
preserveSystemPrompt?: boolean;
|
|
375
|
+
/**
|
|
376
|
+
* 是否终止活动轮次(默认 `true`)
|
|
377
|
+
*
|
|
378
|
+
* 为 `true` 时:中断内部生成信号,使所有非终态轮次进入 `interrupted` 终态,
|
|
379
|
+
* 释放并发屏障,并阻止这些旧轮次在重置后被再次 `commitTurn` / `interruptTurn`。
|
|
380
|
+
*/
|
|
381
|
+
cancelActiveTurn?: boolean;
|
|
382
|
+
/**
|
|
383
|
+
* 是否等待后台记忆提取任务完成后再清空(默认 `false`)
|
|
384
|
+
*
|
|
385
|
+
* 为 `true` 时先 `flush()` 等待正在运行的记忆提取写入完成;否则不等待
|
|
386
|
+
* (已在途的提取任务仍会自行写入记忆后端,但其结果不再影响本管理器状态)。
|
|
387
|
+
*/
|
|
388
|
+
waitForMemoryTasks?: boolean;
|
|
389
|
+
}
|
|
348
390
|
/**
|
|
349
391
|
* 单次 chat/chatStream 请求的覆盖选项
|
|
350
392
|
*/
|
|
@@ -358,7 +400,7 @@ interface ContextChatOptions {
|
|
|
358
400
|
/**
|
|
359
401
|
* 请求取消信号
|
|
360
402
|
*
|
|
361
|
-
* 透传给底层 LLM
|
|
403
|
+
* 透传给底层 LLM 调用;打断、用户切换等场景可 `abortController.abort()`
|
|
362
404
|
* 立即停止上游生成与计费。
|
|
363
405
|
*/
|
|
364
406
|
signal?: AbortSignal;
|
|
@@ -387,8 +429,15 @@ interface ContextChatResult {
|
|
|
387
429
|
}
|
|
388
430
|
/**
|
|
389
431
|
* chatStream() 产出的事件
|
|
432
|
+
*
|
|
433
|
+
* 事件序列:`turn_started` → `delta`* → `done`;中途取消(AbortSignal)时为
|
|
434
|
+
* `turn_started` → `delta`* → `cancelled`。`cancelled` 保留 turnId 与已生成文本,
|
|
435
|
+
* 调用方可用真实内容调用 `commitTurn` / `interruptTurn` 提交。
|
|
390
436
|
*/
|
|
391
437
|
type ContextStreamEvent = {
|
|
438
|
+
type: 'turn_started';
|
|
439
|
+
turnId: string;
|
|
440
|
+
} | {
|
|
392
441
|
type: 'delta';
|
|
393
442
|
text: string;
|
|
394
443
|
} | {
|
|
@@ -410,6 +459,10 @@ type ContextStreamEvent = {
|
|
|
410
459
|
completion_tokens: number;
|
|
411
460
|
total_tokens: number;
|
|
412
461
|
};
|
|
462
|
+
} | {
|
|
463
|
+
type: 'cancelled';
|
|
464
|
+
turnId: string;
|
|
465
|
+
generated: string;
|
|
413
466
|
};
|
|
414
467
|
/**
|
|
415
468
|
* 有状态上下文管理器接口
|
|
@@ -496,9 +549,15 @@ interface ContextManager {
|
|
|
496
549
|
*/
|
|
497
550
|
readonly pendingMemoryTasks: number;
|
|
498
551
|
/**
|
|
499
|
-
*
|
|
552
|
+
* 重置管理器
|
|
553
|
+
*
|
|
554
|
+
* 默认行为:终止活动轮次(进入终态并释放并发屏障)、清空消息 / 摘要 / 轮次 /
|
|
555
|
+
* 待提交轮次,并重新写入系统提示词。可通过选项调整。
|
|
556
|
+
*
|
|
557
|
+
* @param options - 重置选项(保留系统提示词 / 终止活动轮次 / 等待记忆任务)
|
|
558
|
+
* @returns 成功返回 ok(undefined)
|
|
500
559
|
*/
|
|
501
|
-
reset: () => void
|
|
560
|
+
reset: (options?: ContextResetOptions) => Promise<HaiResult<void>>;
|
|
502
561
|
/**
|
|
503
562
|
* 获取对话轮次列表(Conversation Commit Layer)
|
|
504
563
|
*
|
|
@@ -563,7 +622,7 @@ interface ContextManager {
|
|
|
563
622
|
/**
|
|
564
623
|
* 流式发送消息并获取回复(需 deps.llm 可用)
|
|
565
624
|
*
|
|
566
|
-
* 产出事件序列:delta* → done
|
|
625
|
+
* 产出事件序列:turn_started → delta* → done(中途取消时 → cancelled)
|
|
567
626
|
*
|
|
568
627
|
* @param message - 用户消息文本
|
|
569
628
|
* @param options - 单次请求覆盖选项
|
|
@@ -622,18 +681,18 @@ interface ContextOperations {
|
|
|
622
681
|
/**
|
|
623
682
|
* 重命名会话
|
|
624
683
|
*
|
|
625
|
-
* @param
|
|
684
|
+
* @param scope - 交互作用域(objectId + sessionId,用于多租户隔离)
|
|
626
685
|
* @param title - 新标题
|
|
627
686
|
* @returns 成功返回 ok(undefined)
|
|
628
687
|
*/
|
|
629
|
-
renameSession: (
|
|
688
|
+
renameSession: (scope: InteractionScope, title: string) => Promise<HaiResult<void>>;
|
|
630
689
|
/**
|
|
631
690
|
* 删除会话(删除会话元数据和对应的上下文数据)
|
|
632
691
|
*
|
|
633
|
-
* @param
|
|
692
|
+
* @param scope - 交互作用域(objectId + sessionId,用于多租户隔离)
|
|
634
693
|
* @returns 成功返回 ok(undefined)
|
|
635
694
|
*/
|
|
636
|
-
removeSession: (
|
|
695
|
+
removeSession: (scope: InteractionScope) => Promise<HaiResult<void>>;
|
|
637
696
|
}
|
|
638
697
|
|
|
639
698
|
/**
|
|
@@ -1316,6 +1375,8 @@ declare const HaiAIError: {
|
|
|
1316
1375
|
readonly TOOL_VALIDATION_FAILED: _h_ai_core.HaiErrorDef;
|
|
1317
1376
|
readonly TOOL_EXECUTION_FAILED: _h_ai_core.HaiErrorDef;
|
|
1318
1377
|
readonly TOOL_TIMEOUT: _h_ai_core.HaiErrorDef;
|
|
1378
|
+
readonly TOOL_ALREADY_REGISTERED: _h_ai_core.HaiErrorDef;
|
|
1379
|
+
readonly TOOL_FORBIDDEN: _h_ai_core.HaiErrorDef;
|
|
1319
1380
|
readonly REASONING_FAILED: _h_ai_core.HaiErrorDef;
|
|
1320
1381
|
readonly REASONING_MAX_ROUNDS: _h_ai_core.HaiErrorDef;
|
|
1321
1382
|
readonly REASONING_STRATEGY_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
@@ -1357,6 +1418,7 @@ declare const HaiAIError: {
|
|
|
1357
1418
|
readonly CONTEXT_BUDGET_EXCEEDED: _h_ai_core.HaiErrorDef;
|
|
1358
1419
|
readonly CONTEXT_TURN_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
1359
1420
|
readonly CONTEXT_TURN_INVALID_STATE: _h_ai_core.HaiErrorDef;
|
|
1421
|
+
readonly CONTEXT_BUSY: _h_ai_core.HaiErrorDef;
|
|
1360
1422
|
readonly STORE_FAILED: _h_ai_core.HaiErrorDef;
|
|
1361
1423
|
readonly STORE_NOT_AVAILABLE: _h_ai_core.HaiErrorDef;
|
|
1362
1424
|
readonly SESSION_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
@@ -1377,8 +1439,8 @@ interface AIInitOptions {
|
|
|
1377
1439
|
/**
|
|
1378
1440
|
* 自定义存储 Provider
|
|
1379
1441
|
*
|
|
1380
|
-
* 提供后 AI 模块将使用此 Provider
|
|
1381
|
-
*
|
|
1442
|
+
* 提供后 AI 模块将使用此 Provider,而不采用自动选择的存储实现。
|
|
1443
|
+
* 未提供时:reldb + vecdb 已初始化则使用持久化 DB Provider,否则使用进程内临时 Provider。
|
|
1382
1444
|
*/
|
|
1383
1445
|
storeProvider?: AIStoreProvider;
|
|
1384
1446
|
}
|
|
@@ -1460,15 +1522,6 @@ interface AIFunctions {
|
|
|
1460
1522
|
readonly audio: AudioOperations;
|
|
1461
1523
|
}
|
|
1462
1524
|
|
|
1463
|
-
/**
|
|
1464
|
-
* @h-ai/ai — 统一语音 WebSocket 协议
|
|
1465
|
-
*
|
|
1466
|
-
* 定义浏览器 / 远程客户端与 `@h-ai/serv` 语音入口之间的统一 WebSocket 消息协议。
|
|
1467
|
-
* 客户端与服务端共享此协议,客户端不接收任何厂商原生事件;音频以二进制帧传输,
|
|
1468
|
-
* 控制与文本以 JSON 帧传输。
|
|
1469
|
-
* @module audio/ai-audio-ws-protocol
|
|
1470
|
-
*/
|
|
1471
|
-
|
|
1472
1525
|
/** 统一语音入口的默认路径(相对 API 前缀) */
|
|
1473
1526
|
declare const AUDIO_WS_PATH = "/ai/audio";
|
|
1474
1527
|
/**
|
|
@@ -1507,6 +1560,8 @@ interface AudioWsStartMessage {
|
|
|
1507
1560
|
/** 文本输入帧(合成操作时携带待合成文本) */
|
|
1508
1561
|
interface AudioWsTextMessage {
|
|
1509
1562
|
type: 'text';
|
|
1563
|
+
/** 调用方分配的稳定文本段 ID */
|
|
1564
|
+
segmentId: string;
|
|
1510
1565
|
/** 待合成文本片段 */
|
|
1511
1566
|
text: string;
|
|
1512
1567
|
}
|
|
@@ -1516,6 +1571,74 @@ interface AudioWsDoneMessage {
|
|
|
1516
1571
|
}
|
|
1517
1572
|
/** 客户端 JSON 控制消息(音频输入以二进制帧发送,不走 JSON) */
|
|
1518
1573
|
type AudioWsClientMessage = AudioWsStartMessage | AudioWsTextMessage | AudioWsDoneMessage;
|
|
1574
|
+
/** 合法音频格式 */
|
|
1575
|
+
declare const AudioFormatSchema: zod.ZodEnum<{
|
|
1576
|
+
pcm16: "pcm16";
|
|
1577
|
+
wav: "wav";
|
|
1578
|
+
mp3: "mp3";
|
|
1579
|
+
opus: "opus";
|
|
1580
|
+
}>;
|
|
1581
|
+
/** 单个字符串控制字段最大长度(model / language / voice / instruction 等) */
|
|
1582
|
+
/** 会话起始帧 Schema */
|
|
1583
|
+
declare const AudioWsStartMessageSchema: zod.ZodObject<{
|
|
1584
|
+
type: zod.ZodLiteral<"start">;
|
|
1585
|
+
operation: zod.ZodEnum<{
|
|
1586
|
+
transcribe: "transcribe";
|
|
1587
|
+
synthesize: "synthesize";
|
|
1588
|
+
}>;
|
|
1589
|
+
stream: zod.ZodOptional<zod.ZodBoolean>;
|
|
1590
|
+
model: zod.ZodOptional<zod.ZodString>;
|
|
1591
|
+
language: zod.ZodOptional<zod.ZodString>;
|
|
1592
|
+
contextHints: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
|
|
1593
|
+
voice: zod.ZodOptional<zod.ZodString>;
|
|
1594
|
+
instruction: zod.ZodOptional<zod.ZodString>;
|
|
1595
|
+
format: zod.ZodOptional<zod.ZodEnum<{
|
|
1596
|
+
pcm16: "pcm16";
|
|
1597
|
+
wav: "wav";
|
|
1598
|
+
mp3: "mp3";
|
|
1599
|
+
opus: "opus";
|
|
1600
|
+
}>>;
|
|
1601
|
+
sampleRate: zod.ZodOptional<zod.ZodNumber>;
|
|
1602
|
+
channels: zod.ZodOptional<zod.ZodUnion<readonly [zod.ZodLiteral<1>, zod.ZodLiteral<2>]>>;
|
|
1603
|
+
}, zod_v4_core.$strip>;
|
|
1604
|
+
/** 文本输入帧 Schema(segmentId 非空且长度受限) */
|
|
1605
|
+
declare const AudioWsTextMessageSchema: zod.ZodObject<{
|
|
1606
|
+
type: zod.ZodLiteral<"text">;
|
|
1607
|
+
segmentId: zod.ZodString;
|
|
1608
|
+
text: zod.ZodString;
|
|
1609
|
+
}, zod_v4_core.$strip>;
|
|
1610
|
+
/** 输入结束帧 Schema */
|
|
1611
|
+
declare const AudioWsDoneMessageSchema: zod.ZodObject<{
|
|
1612
|
+
type: zod.ZodLiteral<"done">;
|
|
1613
|
+
}, zod_v4_core.$strip>;
|
|
1614
|
+
/** 客户端 JSON 控制消息 Schema(按 type 判别) */
|
|
1615
|
+
declare const AudioWsClientMessageSchema: zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
1616
|
+
type: zod.ZodLiteral<"start">;
|
|
1617
|
+
operation: zod.ZodEnum<{
|
|
1618
|
+
transcribe: "transcribe";
|
|
1619
|
+
synthesize: "synthesize";
|
|
1620
|
+
}>;
|
|
1621
|
+
stream: zod.ZodOptional<zod.ZodBoolean>;
|
|
1622
|
+
model: zod.ZodOptional<zod.ZodString>;
|
|
1623
|
+
language: zod.ZodOptional<zod.ZodString>;
|
|
1624
|
+
contextHints: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
|
|
1625
|
+
voice: zod.ZodOptional<zod.ZodString>;
|
|
1626
|
+
instruction: zod.ZodOptional<zod.ZodString>;
|
|
1627
|
+
format: zod.ZodOptional<zod.ZodEnum<{
|
|
1628
|
+
pcm16: "pcm16";
|
|
1629
|
+
wav: "wav";
|
|
1630
|
+
mp3: "mp3";
|
|
1631
|
+
opus: "opus";
|
|
1632
|
+
}>>;
|
|
1633
|
+
sampleRate: zod.ZodOptional<zod.ZodNumber>;
|
|
1634
|
+
channels: zod.ZodOptional<zod.ZodUnion<readonly [zod.ZodLiteral<1>, zod.ZodLiteral<2>]>>;
|
|
1635
|
+
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
1636
|
+
type: zod.ZodLiteral<"text">;
|
|
1637
|
+
segmentId: zod.ZodString;
|
|
1638
|
+
text: zod.ZodString;
|
|
1639
|
+
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
1640
|
+
type: zod.ZodLiteral<"done">;
|
|
1641
|
+
}, zod_v4_core.$strip>], "type">;
|
|
1519
1642
|
/** 语音起止事件(识别操作时服务端 VAD 检测到语音开始 / 结束) */
|
|
1520
1643
|
interface AudioWsSpeechMessage {
|
|
1521
1644
|
type: 'speech_started' | 'speech_stopped';
|
|
@@ -1528,6 +1651,27 @@ interface AudioWsTranscriptMessage {
|
|
|
1528
1651
|
/** 是否为该语句的最终结果 */
|
|
1529
1652
|
final: boolean;
|
|
1530
1653
|
}
|
|
1654
|
+
/**
|
|
1655
|
+
* 合成文本段开始;后续二进制帧均属于该段,直到收到对应的 `segment_done`。
|
|
1656
|
+
*
|
|
1657
|
+
* 携带服务端解析 Provider 后的真实输出音频参数,供浏览器正确标注音频格式。
|
|
1658
|
+
*/
|
|
1659
|
+
interface AudioWsSegmentStartedMessage {
|
|
1660
|
+
type: 'segment_started';
|
|
1661
|
+
segmentId: string;
|
|
1662
|
+
text: string;
|
|
1663
|
+
/** 真实输出音频格式(来自服务端解析后的 Provider 输出,非客户端请求参数) */
|
|
1664
|
+
format: AudioFormat;
|
|
1665
|
+
/** 采样率(Hz);pcm16 等裸音频必填 */
|
|
1666
|
+
sampleRate?: number;
|
|
1667
|
+
/** 声道数(默认单声道) */
|
|
1668
|
+
channels?: 1 | 2;
|
|
1669
|
+
}
|
|
1670
|
+
/** 合成文本段的音频已全部发送。 */
|
|
1671
|
+
interface AudioWsSegmentDoneMessage {
|
|
1672
|
+
type: 'segment_done';
|
|
1673
|
+
segmentId: string;
|
|
1674
|
+
}
|
|
1531
1675
|
/** 错误帧(领域语义错误码,不暴露厂商协议细节) */
|
|
1532
1676
|
interface AudioWsErrorMessage {
|
|
1533
1677
|
type: 'error';
|
|
@@ -1541,6 +1685,6 @@ interface AudioWsEndMessage {
|
|
|
1541
1685
|
type: 'end';
|
|
1542
1686
|
}
|
|
1543
1687
|
/** 服务端 JSON 消息(合成音频以二进制帧返回,不走 JSON) */
|
|
1544
|
-
type AudioWsServerMessage = AudioWsSpeechMessage | AudioWsTranscriptMessage | AudioWsErrorMessage | AudioWsEndMessage;
|
|
1688
|
+
type AudioWsServerMessage = AudioWsSpeechMessage | AudioWsTranscriptMessage | AudioWsSegmentStartedMessage | AudioWsSegmentDoneMessage | AudioWsErrorMessage | AudioWsEndMessage;
|
|
1545
1689
|
|
|
1546
|
-
export { type
|
|
1690
|
+
export { type MCPPromptArgument as $, type AIFunctions as A, type ContextChatOptions as B, type CompressionStrategy as C, type ContextChatResult as D, type ContextDeps as E, type ContextManager as F, type ContextManagerOptions as G, HaiAIError as H, type ContextOperations as I, type ContextResetOptions as J, type ContextStreamEvent as K, type ConversationTurn as L, type McpServerOptions as M, type ConversationTurnStatus as N, type EmbeddingItem as O, type EmbeddingOperations as P, type EmbeddingProvider as Q, type EmbeddingRequest as R, type EmbeddingResponse as S, type FileOperations as T, type FileParseMethod as U, type FileParseOptions as V, type FileParseRequest as W, type FileParseResult as X, type MCPContext as Y, type MCPOperations as Z, type MCPPrompt as _, type AIInitOptions as a, type MCPPromptContent as a0, type MCPPromptMessage as a1, type MCPProvider as a2, type MCPResource as a3, type MCPResourceContent as a4, type MCPToolDefinition as a5, type MCPToolHandler as a6, type OutputFormat as a7, type PersonaOperations as a8, type PersonaProfile as a9, type PersonaProfileInput as aa, type PersonaProfileUpdate as ab, type PersonaScopeOptions as ac, type RerankDocument as ad, type RerankItem as ae, type RerankOperations as af, type RerankRequest as ag, type RerankResponse as ah, type SummaryOperations as ai, type SummaryOptions as aj, type SummaryResult as ak, type TokenOperations as al, AUDIO_WS_PATH as b, AudioFormatSchema as c, type AudioWsClientMessage as d, AudioWsClientMessageSchema as e, type AudioWsDoneMessage as f, AudioWsDoneMessageSchema as g, type AudioWsEndMessage as h, type AudioWsErrorMessage as i, type AudioWsSegmentDoneMessage as j, type AudioWsSegmentStartedMessage as k, type AudioWsServerMessage as l, type AudioWsSpeechMessage as m, type AudioWsStartMessage as n, AudioWsStartMessageSchema as o, type AudioWsTextMessage as p, AudioWsTextMessageSchema as q, type AudioWsTranscriptMessage as r, CompressionStrategySchema as s, type AIMCPFunctionsDeps as t, type CommitTurnInput as u, type CompressOperations as v, type CompressOptions as w, type CompressResult as x, type ConsolidateOptions as y, type ConsolidateResult as z };
|