@h-ai/cli 0.1.0-alpha.35 → 0.1.0-alpha.36

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/cli",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.35",
4
+ "version": "0.1.0-alpha.36",
5
5
  "description": "Hai Framework CLI for project scaffolding and code generation.",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.1.0",
35
35
  "prompts": "^2.4.2",
36
36
  "yaml": "^2.7.0",
37
- "@h-ai/core": "0.1.0-alpha.35"
37
+ "@h-ai/core": "0.1.0-alpha.36"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/fs-extra": "^11.0.4",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "{{projectName}}",
3
- "version": "0.1.0-alpha.35",
3
+ "version": "0.1.0-alpha.36",
4
4
  "private": true,
5
5
  "description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
6
6
  "keywords": ["hai", {{#unless isSvelteOnlyApp}}"sveltekit", {{/unless}}"svelte5", "{{appType}}"],
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: hai-ai
3
- description: 使用 @h-ai/ai 进行 LLM 调用(OpenAI 兼容)、MCP 服务器创建、工具定义与注册、流式处理、记忆管理(objectId 隔离)、上下文压缩与会话持久化;当需求涉及 AI 对话、工具调用、MCP 协议、流式响应、记忆提取注入、上下文摘要压缩、会话管理或 AI 客户端时使用。
3
+ description: 使用 @h-ai/ai 进行 LLM 调用、工具与 MCP、流式处理、记忆与上下文、RAG、语音识别与合成、A2A;当需求涉及 AI 对话、Audio、会话或 AI 客户端时使用。
4
4
  ---
5
5
 
6
6
  # hai-ai — @h-ai/ai 快速指南
7
7
 
8
- `@h-ai/ai` 统一提供 LLM、工具、MCP、Embedding、Memory、Retrieval/RAG、Knowledge、Context、File、Rerank 与 A2A 能力。
8
+ `@h-ai/ai` 统一提供 LLM、工具、MCP、Embedding、Memory、Retrieval/RAG、Knowledge、Context、File、Rerank、Audio 与 A2A 能力。
9
9
 
10
10
  > 详细 API 表、错误码与长示例见同目录 `reference.md`。只有需要完整契约或边界用例时再读取,避免把长参考塞进上下文。
11
11
 
12
12
  ## 使用边界
13
13
 
14
14
  - `ai.tools`、`ai.stream` 是纯函数能力,无需 `ai.init()`。
15
- - `ai.llm`、`ai.embedding`、`ai.memory`、`ai.retrieval`、`ai.rag`、`ai.knowledge`、`ai.context`、`ai.a2a` 需要先 `await ai.init(...)`。
15
+ - `ai.llm`、`ai.embedding`、`ai.memory`、`ai.retrieval`、`ai.rag`、`ai.knowledge`、`ai.context`、`ai.audio`、`ai.a2a` 需要先 `await ai.init(...)`。
16
16
  - 默认 DB Provider 需要在 `ai.init()` 前完成 `reldb.init()` 与 `vecdb.init()`;自定义 `AIStoreProvider` 可跳过这两个依赖。
17
17
  - 浏览器端不要直接调用 Node-only 能力;通过 `@h-ai/api-client` 或应用自定义 API/SSE endpoint 代理。
18
18
  - 公共 API 返回 `HaiResult<T>` 时按 `if (!result.success) return result` 处理;不要用 `try/catch` 包裹正常业务错误。
@@ -20,6 +20,7 @@ description: 使用 @h-ai/ai 进行 LLM 调用(OpenAI 兼容)、MCP 服务
20
20
  ## 初始化与关闭
21
21
 
22
22
  ```ts
23
+ import { core } from '@h-ai/core'
23
24
  import { ai } from '@h-ai/ai'
24
25
  import { reldb } from '@h-ai/reldb'
25
26
  import { vecdb } from '@h-ai/vecdb'
@@ -97,11 +98,15 @@ memory:
97
98
  | Retrieval/RAG | `ai.retrieval.retrieve` / `ai.rag.query` | Retrieval source 先注册或由配置预置 |
98
99
  | Knowledge | `ai.knowledge.setup/ingest/ask` | 入库前会调用 datapipe 清洗分块 |
99
100
  | Context | `ai.context.createManager` | 编排 LLM + Memory + RAG + 压缩;`manager.consolidate()` 把会话固化为长期记忆 |
101
+ | 语音识别 | `ai.audio.transcribe` / `transcribeStream` | 完整或持续音频输入 |
102
+ | 语音合成 | `ai.audio.synthesize` / `synthesizeStream` | 带稳定 ID 的文本段映射为结构化音频事件 |
100
103
  | A2A | `ai.a2a.registerExecutor/handleRequest` | 延迟初始化 SDK handler |
101
104
 
102
105
  ## LLM + 工具调用
103
106
 
104
107
  ```ts
108
+ import { z } from 'zod'
109
+
105
110
  const registry = ai.tools.createRegistry()
106
111
  registry.register(ai.tools.define({
107
112
  name: 'get_weather',
@@ -242,6 +247,31 @@ for await (const ev of m.chatStream('展开讲讲')) {
242
247
  - 只有 `committed` 的内容进入上下文与记忆;`getTurns()` 可观测 `generated` / `committed` / `status`。
243
248
 
244
249
 
250
+ ## 语音(Audio)
251
+
252
+ 模型必须声明允许的操作;框架会在厂商调用前拒绝 ASR/TTS 模型混用:
253
+
254
+ ```yaml
255
+ audio:
256
+ models:
257
+ - { id: asr, provider: qwen, model: qwen3-asr-flash-realtime, operations: [transcribe] }
258
+ - { id: tts, provider: qwen, model: qwen3-tts-flash-realtime, operations: [synthesize] }
259
+ transcribeModel: asr
260
+ synthesizeModel: tts
261
+ ```
262
+
263
+ ```ts
264
+ for await (const event of ai.audio.synthesizeStream({ text: { id: 'seg-1', text: '欢迎。' } })) {
265
+ if (event.type === 'audio')
266
+ await player.write(event.data)
267
+ }
268
+
269
+ const caps = ai.audio.getCapabilities({ operation: 'synthesize', model: 'tts' })
270
+ ```
271
+
272
+ 浏览器客户端配置 `audio: { url, getTicket }`;`getTicket` 通过已登录 HTTP 请求获取短期、一次性 ticket,禁止把 IAM access token 放入 WebSocket URL。
273
+
274
+
245
275
  ## SvelteKit API 端点模式
246
276
 
247
277
  ```ts
@@ -257,6 +287,8 @@ export const POST = kit.handler(async ({ request }) => {
257
287
 
258
288
  - API Key 只从配置 / 环境变量读取,禁止写死。
259
289
  - 工具 handler 必须校验输入;用户可见错误走 i18n/错误码。
290
+ - 模型输出、Prompt 和检索内容均不可信;不要把用户内容拼入不可覆盖的系统规则。Zod 只保证参数形状,高权限工具还必须在 handler 内校验身份、租户、资源归属和配额。
291
+ - 只向模型注册允许自动执行的工具;写操作默认要求业务侧确认或幂等键,不能把“模型请求调用”当作授权。
260
292
  - 批量导入、批量 embedding 用批量 API,避免 await-in-loop。
261
293
  - 文件解析/OCR 失败按 `HaiAIError.FILE_*` 返回,不抛业务异常。
262
- - A2A 认证失败返回 `A2A_AUTH_FAILED`;不要记录 token、apiKey、私钥等敏感字段。
294
+ - A2A 认证失败返回 `A2A_AUTH_FAILED`;`callRemoteAgent` 只依赖 `ai.init()`,无需注册本地 executor。远端 URL 仅接受 HTTP(S) 且不得内嵌凭据,应用仍须配置 origin 白名单与出口网络策略。不要记录 token、apiKey、私钥、完整 headers 或带 query 的 URL。
@@ -92,10 +92,10 @@ Embedding OpenAI 客户端按 `apiKey + baseURL + timeout` 缓存;同一 key
92
92
  | --- | --- |
93
93
  | `extract(messages, options?)` | 从对话提取记忆并存储 |
94
94
  | `add(entry)` | 手动添加记忆 |
95
- | `get(memoryId)` / `update(memoryId, updates)` | 读取/更新 |
95
+ | `get(memoryId, accessScope?)` / `update(memoryId, updates, accessScope?)` | 读取/更新(传 `accessScope: { objectId, scope? }` 做归属校验,不匹配→ `MEMORY_NOT_FOUND`) |
96
96
  | `recall(query, options?)` | 检索相关记忆 |
97
97
  | `injectMemories(messages, options?)` | 注入 system 记忆上下文 |
98
- | `remove(memoryId)` / `clear(options?)` | 删除 |
98
+ | `remove(memoryId, accessScope?)` / `clear(options?)` | 删除(`accessScope` 同上) |
99
99
  | `list(options?)` / `listPage(options?)` | 列表/分页 |
100
100
 
101
101
  推荐所有记忆操作带 `objectId`,避免不同用户或 Agent 混写。
@@ -163,6 +163,19 @@ const result = await ai.rag.query('核心架构是什么?', {
163
163
  - `ai.rerank.rerank(request)` / `rerankTexts(query, texts, topN?)`:相关性重排。
164
164
  - `ai.reasoning.run(query, options?)` / `runStream(query, options?)`:`react`、`cot`、`plan-execute`。
165
165
 
166
+ ## Audio
167
+
168
+ | 方法 | 返回 | 说明 |
169
+ | --- | --- | --- |
170
+ | `transcribe(request)` | `Promise<HaiResult<TranscriptionResult>>` | 完整音频 → 完整文本 |
171
+ | `transcribeStream(request)` | `AsyncIterable<TranscriptionEvent>` | 完整或持续音频 → 识别/VAD 领域事件 |
172
+ | `synthesize(request)` | `Promise<HaiResult<SynthesisResult>>` | 完整文本 → 完整音频 |
173
+ | `synthesizeStream(request)` | `AsyncIterable<SynthesisEvent>` | 带 ID 文本段 → `segment_started → audio* → segment_done` |
174
+ | `getCapabilities({ operation, model? })` | `HaiResult<AudioModelCapabilities>` | 按操作查询能力,并拒绝操作不匹配模型 |
175
+
176
+ - 模型配置必须包含 `operations: ['transcribe'] | ['synthesize'] | ['transcribe','synthesize']`。
177
+ - 浏览器端使用 `audio: { url, getTicket }`;ticket 由已登录 HTTP 请求签发且一次性消费,IAM access token 不进入 WebSocket URL。
178
+
166
179
  ## A2A
167
180
 
168
181
  | 方法 | 说明 |
@@ -171,9 +184,11 @@ const result = await ai.rag.query('核心架构是什么?', {
171
184
  | `getAgentCard()` | 获取 Agent Card |
172
185
  | `handleRequest(body, context?)` | 处理 A2A JSON-RPC 请求 |
173
186
  | `listMessages(filter)` | 查询 A2A 消息记录 |
174
- | `callRemoteAgent(remoteUrl, message, options?)` | 调用远端 Agent |
187
+ | `callRemoteAgent(remoteUrl, message, options?)` | 调用远端 Agent;只依赖 `ai.init()`,不要求本地 executor |
188
+
189
+ A2A 服务端 SDK handler 在注册 executor 时延迟创建;远端客户端调用不依赖服务端配置。
175
190
 
176
- A2A 的 SDK handler 延迟创建;注册 executor 或首次处理请求时才装配运行时对象。
191
+ `callRemoteAgent` 只接受 HTTP(S) 且拒绝 URL 内嵌凭据。该校验不是完整 SSRF 防护:remote URL 若来自外部输入,应用必须先按 origin 白名单过滤,并通过出口代理限制 DNS 重绑定、重定向到私网与云元数据地址。
177
192
 
178
193
  ## 错误码速查
179
194
 
@@ -182,6 +197,7 @@ A2A 的 SDK handler 延迟创建;注册 executor 或首次处理请求时才
182
197
  | `hai:ai:000` | `INTERNAL_ERROR` |
183
198
  | `hai:ai:010-012` | 初始化:`NOT_INITIALIZED` / `CONFIGURATION_ERROR` / `INIT_IN_PROGRESS` |
184
199
  | `hai:ai:020-033` | Rerank / File |
200
+ | `hai:ai:050-059` | Audio:`AUDIO_INVALID_REQUEST` / `MODEL_NOT_FOUND` / `PROVIDER_NOT_FOUND` / `UNSUPPORTED_INPUT` / `UPSTREAM_ERROR` / `PROTOCOL_ERROR` / `CONNECTION_FAILED` / `TIMEOUT` / `INPUT_TOO_LARGE` / `CANCELLED` |
185
201
  | `hai:ai:100-107` | LLM / 历史记录 |
186
202
  | `hai:ai:200-204` | MCP |
187
203
  | `hai:ai:300-302` | Embedding |
@@ -189,7 +205,7 @@ A2A 的 SDK handler 延迟创建;注册 executor 或首次处理请求时才
189
205
  | `hai:ai:500-502` | Reasoning |
190
206
  | `hai:ai:600-701` | Retrieval / RAG |
191
207
  | `hai:ai:800-805` | Knowledge |
192
- | `hai:ai:900-904` | Memory |
208
+ | `hai:ai:900-905` | Memory |
193
209
  | `hai:ai:950-971` | Context / Store / Session |
194
210
  | `hai:ai:980-984` | A2A |
195
211
 
@@ -28,6 +28,7 @@ description: 使用 @h-ai/serv 将 oRPC contract 挂载为最小 HTTP App 抽象
28
28
  - 通过 `config/_serv.yml` 管理 API 前缀、OpenAPI、docs、health、rpc 等 HTTP 挂载配置
29
29
  - 启用传输加密:`serv.createApp({ transport: { crypto } })`
30
30
  - 高级场景通过 `createApp({ middlewares })` 自定义 HTTP middleware,或通过共享类型自定义 procedure wrapper
31
+ - 为 Audio WebSocket 配置一次性 ticket 校验、IAM/模型/配额授权与并发释放钩子
31
32
 
32
33
  ---
33
34
 
@@ -80,6 +81,26 @@ serv.listen(app, {
80
81
  })
81
82
  ```
82
83
 
84
+ ### 2.1 Audio WebSocket 安全接入
85
+
86
+ ```typescript
87
+ const app = serv.createApp({
88
+ contract,
89
+ procedures,
90
+ iam,
91
+ audio: {
92
+ ai,
93
+ verifyTicket: consumeAudioTicket, // 校验用途/时效并原子消费,返回 ServSession
94
+ authorize: (session, request) => authorizeAudioRequest(session, request),
95
+ onSessionEnd: (session, request) => releaseAudioConcurrency(session.userId, request.operation),
96
+ },
97
+ })
98
+ ```
99
+
100
+ - ticket 由已登录 HTTP 请求签发,短期有效且只能消费一次;普通 IAM access token 禁止进入 WebSocket URL。
101
+ - `authorize` 返回的 `AuthorizedAudioRequest` 是唯一会传给 `ai.audio` 的模型、音色与格式配置;未提供时客户端的付费参数会被忽略。
102
+ - IAM 权限、Persona、套餐配额和并发计数属于应用策略;框架提供校验与结束钩子,不硬编码业务权限名或存储。
103
+
83
104
  ### 3. 扩展应用自有 contract + procedures
84
105
 
85
106
  `apiContract.create({...})` 接受任意 key——除了框架提供的 `iam/storage/ai`,应用可以挂入自己的领域 contract,
@@ -258,7 +258,7 @@ const p = usePlatform()
258
258
  | `Spinner` | `size`, `variant` | 加载动画 |
259
259
  | `Progress` | `value`, `max`, `striped`, `animated` | 进度条 |
260
260
 
261
- ### 组合组件(Compounds,33 个)
261
+ ### 组合组件(Compounds,36 个)
262
262
 
263
263
  由原子组件 + Bits UI headless 交互组合。
264
264
 
@@ -275,7 +275,7 @@ const p = usePlatform()
275
275
  | `Calendar` | `value`, `minValue`, `maxValue` | 独立日历 |
276
276
  | `DatePicker` | `value`, `minValue`, `maxValue`, `error` | 日期输入+弹出 |
277
277
  | `Tabs` | `items: TabItem[]`, `active`, `type` | 标签页 |
278
- | `Pagination` | `page`, `total`, `pageSize`, `showTotal`, `showJumper`, `showSizeChanger`, `onchange` | 分页(统一为 shadcn table 风格) |
278
+ | `Pagination` | `page`, `total`, `pageSize`, `showTotal`, `showJumper`, `showSizeChanger`, `showPageInfo`, `showFirstLast`, `onchange` | 分页(统一为 shadcn table 风格) |
279
279
  | `Dropdown` | `items: DropdownItem[]`, `trigger` | 下拉菜单 |
280
280
  | `Accordion` | `items: AccordionItem[]` | 折叠面板 |
281
281
  | `Skeleton` | `variant`, `count`, `animation` | 骨架屏 |
@@ -375,11 +375,13 @@ const p = usePlatform()
375
375
 
376
376
  | 组件 | Props 要点 | 说明 |
377
377
  | ------------- | ---------------------------------------------- | -------- |
378
- | `FileUpload` | `accept`, `maxSize`, `uploadUrl`, `autoUpload` | 文件上传 |
379
- | `ImageUpload` | `value`, `uploadUrl`, `aspectRatio` | 图片上传 |
380
- | `AvatarUpload` | `value`, `size`, `maxSize`, `fallback` | 头像上传 |
378
+ | `FileUpload` | `accept`, `maxSize`, `uploadHandler`, `autoUpload` | 文件上传 |
379
+ | `ImageUpload` | `value`, `uploadHandler`, `aspectRatio` | 图片上传 |
380
+ | `AvatarUpload` | `value`, `uploadHandler`, `size`, `fallback` | 头像上传 |
381
381
  | `FileList` | `files: FileItem[]`, `layout`, `showPreview` | 文件列表 |
382
382
 
383
+ 上传协议属于应用 service:组件通过 `uploadHandler(file, { signal, onProgress })` 注入实现,并接收 `{ url?, response? }`。不要把 presign、认证头或 PUT/POST 细节塞进 UI 组件;图片与头像 handler 必须返回安全的 `url`。未提供 handler 时组件只负责文件选择或本地预览。
384
+
383
385
  #### AI 场景组件
384
386
 
385
387
  | 组件 | 说明 |
@@ -454,6 +456,7 @@ const p = usePlatform()
454
456
  | `pagination.showSizeChanger` | 每页条数选择器(默认开启) |
455
457
  | `pagination.pageSizeOptions` | 每页条数候选项(默认 `[10, 20, 50, 100]`) |
456
458
  | `pagination.showJumper` / `pagination.showTotal` | 跳页输入 / 总数(默认开启) |
459
+ | `pagination.showPageInfo` / `pagination.showFirstLast` | 页码文案 / 首页末页按钮(默认开启;紧凑视图可关闭) |
457
460
  | `density` | 列表密度:`'normal'`(默认)或 `'compact'`;仅影响列表行、行内操作、分页与创建/编辑/详情表单 |
458
461
 
459
462
  注意:分页栏会始终显示,不再因数据量小而自动隐藏。