@henjicc/ai-sdk 0.6.0 → 0.6.2

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.
Files changed (28) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +16 -3
  3. package/dist/capabilities/client.js +103 -86
  4. package/dist/capabilities/speech-recognition/bailian/realtime/module.js +42 -7
  5. package/dist/capabilities/speech-recognition/bailian/realtime/protocol.js +61 -19
  6. package/dist/capabilities/speech-recognition/volcengine/realtime/module.js +42 -10
  7. package/dist/capabilities/translation/bailian/module.js +41 -13
  8. package/dist/capabilities/translation/bailian/sse.d.ts +1 -1
  9. package/dist/capabilities/translation/bailian/sse.js +82 -54
  10. package/dist/capabilities/types.d.ts +4 -0
  11. package/dist/llm/chat.js +6 -1
  12. package/dist/llm/streaming.js +131 -91
  13. package/dist/protocols/sse-events.d.ts +4 -0
  14. package/dist/protocols/sse-events.js +31 -0
  15. package/docs/consumers.md +102 -86
  16. package/docs/llm-adaptation/README.md +26 -1
  17. package/docs/llm-adaptation//344/276/233/345/272/224/345/225/206//346/231/272/350/260/261GLM.md +3 -1
  18. package/docs/model-adaptation/Fun-ASR-Realtime/Fun-ASR-Realtime_/347/231/276/347/202/274.md +22 -2
  19. package/docs/model-adaptation/Qwen-MT-Flash/Qwen-MT-Flash_/347/231/276/347/202/274.md +24 -3
  20. package/docs/model-adaptation/Qwen-MT-Lite/Qwen-MT-Lite_/347/231/276/347/202/274.md +3 -2
  21. package/docs/model-adaptation/Qwen-MT-Plus/Qwen-MT-Plus_/347/231/276/347/202/274.md +3 -2
  22. package/docs/model-adaptation/Qwen3-ASR-Flash-Realtime/Qwen3-ASR-Flash-Realtime_/347/231/276/347/202/274.md +25 -2
  23. package/docs/model-adaptation/SeedASR-2.0-Realtime/SeedASR-2.0-Realtime_/347/201/253/345/261/261/345/274/225/346/223/216.md +7 -3
  24. package/docs/model-adaptation//344/276/233/345/272/224/345/225/206//347/231/276/347/202/274.md +12 -0
  25. package/examples/llm-chat/README.md +1 -1
  26. package/examples/llm-chat/index.ts +1 -1
  27. package/examples/llm-chat/package.json +1 -1
  28. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { drainSseEvents } from '../protocols/sse-events.js';
2
+ import { AiRuntimeError } from '../runtime/AiRuntimeError.js';
1
3
  import { fetchProvider } from '../providers/provider-fetch.js';
2
4
  import { applyProviderRequestBodyQuirks, resolveProviderExtraAuthHeaders, } from './providerProtocolCore.js';
3
5
  import { applyProviderReasoningRequestBody } from './providerReasoningRequest.js';
@@ -78,7 +80,7 @@ export async function streamOpenAiCompatibleChat(options) {
78
80
  if (!response.body) {
79
81
  throw new Error('LLM streaming response body is empty');
80
82
  }
81
- return await readSseStream(response.body, options.emit);
83
+ return await readSseStream(response.body, options);
82
84
  }
83
85
  export function serializeMessage(message) {
84
86
  const serialized = { role: message.role };
@@ -138,7 +140,8 @@ function serializeContentPart(part) {
138
140
  }
139
141
  return next;
140
142
  }
141
- async function readSseStream(body, emit) {
143
+ async function readSseStream(body, options) {
144
+ const { emit, signal } = options;
142
145
  const reader = body.getReader();
143
146
  const decoder = createUtf8StreamDecoder();
144
147
  let pending = '';
@@ -147,68 +150,84 @@ async function readSseStream(body, emit) {
147
150
  let usage = null;
148
151
  let finishReason = null;
149
152
  const toolCalls = new Map();
150
- let streamDone = false;
151
- while (!streamDone) {
152
- const result = await reader.read();
153
- if (result.done) {
154
- streamDone = true;
155
- continue;
156
- }
157
- pending += decoder.decode(result.value, { stream: true });
158
- const parsed = drainSseEvents(pending);
159
- pending = parsed.remaining;
160
- for (const event of parsed.events) {
161
- const chunk = parseSseData(event);
162
- if (chunk.done) {
163
- return {
164
- output,
165
- reasoningOutput,
166
- usage,
167
- finishReason,
168
- truncated: isOutputTruncated(finishReason),
169
- toolCalls: [...toolCalls.values()],
170
- };
171
- }
172
- usage = chunk.usage ?? usage;
173
- finishReason = chunk.finishReason ?? finishReason;
174
- mergeToolCallDeltas(toolCalls, chunk.toolCalls);
175
- if (chunk.reasoning) {
176
- reasoningOutput += chunk.reasoning;
177
- emit({ type: 'ReasoningToken', data: chunk.reasoning });
153
+ let eof = false;
154
+ let stage = 'read';
155
+ let cancellation;
156
+ const cancelReader = () => {
157
+ // Cleanup may reject on an already errored stream. It must not replace the
158
+ // original protocol/transport failure or delay settlement on a custom host.
159
+ cancellation ??= reader.cancel().catch(() => undefined);
160
+ };
161
+ const checkAbort = () => {
162
+ if (signal.aborted)
163
+ throw Object.assign(new Error('LLM stream cancelled'), { name: 'AbortError' });
164
+ };
165
+ const complete = () => {
166
+ stage = 'completion';
167
+ checkAbort();
168
+ if (!finishReason)
169
+ throw new AiRuntimeError('STREAM_INCOMPLETE', 'Chat stream ended without finish_reason');
170
+ return { output, reasoningOutput, usage, finishReason,
171
+ truncated: isOutputTruncated(finishReason, options.providerId), toolCalls: [...toolCalls.values()] };
172
+ };
173
+ signal.addEventListener('abort', cancelReader, { once: true });
174
+ try {
175
+ checkAbort();
176
+ for (;;) {
177
+ stage = 'read';
178
+ const result = await reader.read();
179
+ checkAbort();
180
+ if (result.done) {
181
+ eof = true;
182
+ // SSE dispatch requires a blank line. Do not manufacture a final event
183
+ // from an unterminated block at EOF.
184
+ return complete();
178
185
  }
179
- if (chunk.content) {
180
- output += chunk.content;
181
- emit({ type: 'Token', data: chunk.content });
186
+ pending += decoder.decode(result.value, { stream: true });
187
+ const parsed = drainSseEvents(pending);
188
+ pending = parsed.remaining;
189
+ for (const event of parsed.events) {
190
+ checkAbort();
191
+ stage = 'parse';
192
+ const chunk = parseSseData(event, options.providerId);
193
+ if (chunk.done)
194
+ return complete();
195
+ if (finishReason && (chunk.content || chunk.reasoning || chunk.toolCalls?.length
196
+ || (chunk.finishReason && chunk.finishReason !== finishReason))) {
197
+ throw new AiRuntimeError('INVALID_STREAM_RESPONSE', 'Chat result changed after finish_reason');
198
+ }
199
+ usage = chunk.usage ?? usage;
200
+ finishReason = chunk.finishReason ?? finishReason;
201
+ mergeToolCallDeltas(toolCalls, chunk.toolCalls);
202
+ stage = 'emit';
203
+ if (chunk.reasoning) {
204
+ reasoningOutput += chunk.reasoning;
205
+ emit({ type: 'ReasoningToken', data: chunk.reasoning });
206
+ checkAbort();
207
+ }
208
+ if (chunk.content) {
209
+ output += chunk.content;
210
+ emit({ type: 'Token', data: chunk.content });
211
+ }
182
212
  }
183
213
  }
184
214
  }
185
- const flushed = decoder.decode();
186
- if (flushed) {
187
- pending += flushed;
188
- }
189
- const parsed = drainSseEvents(`${pending}\n\n`);
190
- for (const event of parsed.events) {
191
- const chunk = parseSseData(event);
192
- usage = chunk.usage ?? usage;
193
- finishReason = chunk.finishReason ?? finishReason;
194
- mergeToolCallDeltas(toolCalls, chunk.toolCalls);
195
- if (chunk.reasoning) {
196
- reasoningOutput += chunk.reasoning;
197
- emit({ type: 'ReasoningToken', data: chunk.reasoning });
198
- }
199
- if (chunk.content) {
200
- output += chunk.content;
201
- emit({ type: 'Token', data: chunk.content });
215
+ catch (error) {
216
+ if (error instanceof AiRuntimeError) {
217
+ throw new AiRuntimeError(error.code, 'Chat streaming response failed', {
218
+ providerId: options.providerId, modelId: options.payload.model,
219
+ protocol: 'openai-chat-sse', stage, receivedFinish: finishReason !== null,
220
+ ...(error.details?.finishReason === 'network_error' ? { finishReason: 'network_error' } : {}),
221
+ });
202
222
  }
223
+ throw error;
224
+ }
225
+ finally {
226
+ signal.removeEventListener('abort', cancelReader);
227
+ if (!eof)
228
+ cancelReader();
229
+ reader.releaseLock();
203
230
  }
204
- return {
205
- output,
206
- reasoningOutput,
207
- usage,
208
- finishReason,
209
- truncated: isOutputTruncated(finishReason),
210
- toolCalls: [...toolCalls.values()],
211
- };
212
231
  }
213
232
  function applyStructuredOutputRequestBody(request, providerId, body) {
214
233
  const output = request.structuredOutput;
@@ -282,47 +301,68 @@ function resolveMaxOutputTokens(request) {
282
301
  function invalidRequest(code, message) {
283
302
  return Object.assign(new Error(message), { code, statusCode: 400 });
284
303
  }
285
- function isOutputTruncated(finishReason) {
286
- return finishReason === 'length' || finishReason === 'max_tokens' || finishReason === 'max_output_tokens';
287
- }
288
- function drainSseEvents(input) {
289
- const events = [];
290
- let remaining = input;
291
- let hasSeparator = true;
292
- while (hasSeparator) {
293
- const separator = findNextSeparator(remaining);
294
- if (!separator) {
295
- hasSeparator = false;
296
- continue;
297
- }
298
- if (separator) {
299
- events.push(remaining.slice(0, separator.index));
300
- remaining = remaining.slice(separator.index + separator.length);
301
- }
302
- }
303
- return { events, remaining };
304
- }
305
- function findNextSeparator(input) {
306
- const crlf = input.indexOf('\r\n\r\n');
307
- const lf = input.indexOf('\n\n');
308
- if (crlf === -1 && lf === -1)
309
- return undefined;
310
- if (crlf !== -1 && (lf === -1 || crlf < lf))
311
- return { index: crlf, length: 4 };
312
- return { index: lf, length: 2 };
304
+ function isOutputTruncated(finishReason, providerId) {
305
+ return finishReason === 'length' || finishReason === 'max_tokens' || finishReason === 'max_output_tokens'
306
+ || (providerId === 'bigmodel' && finishReason === 'model_context_window_exceeded');
313
307
  }
314
- function parseSseData(event) {
308
+ function parseSseData(event, providerId) {
309
+ const eventName = event.split(/\r\n|\r|\n/)
310
+ .filter(line => line.startsWith('event:')).at(-1)?.slice(6).trim();
315
311
  const data = event
316
- .split(/\r?\n/)
317
- .map((line) => line.trim())
312
+ .split(/\r\n|\r|\n/)
318
313
  .filter((line) => line.startsWith('data:'))
319
314
  .map((line) => line.slice(5).trim())
320
315
  .join('\n');
316
+ if (eventName === 'error' && !data)
317
+ throw new AiRuntimeError('PROVIDER_STREAM_ERROR', 'Server error event');
321
318
  if (!data)
322
319
  return { done: false };
323
- if (data === '[DONE]')
320
+ if (data === '[DONE]' && (!eventName || eventName === 'message'))
324
321
  return { done: true };
325
- const json = JSON.parse(data);
322
+ if (eventName && !['message', 'chat.completion.chunk', 'error'].includes(eventName))
323
+ return { done: false };
324
+ let json;
325
+ try {
326
+ json = JSON.parse(data);
327
+ }
328
+ catch {
329
+ if (eventName === 'error')
330
+ throw new AiRuntimeError('PROVIDER_STREAM_ERROR', 'Server error event');
331
+ throw new AiRuntimeError('INVALID_STREAM_RESPONSE', 'Invalid SSE JSON');
332
+ }
333
+ if (eventName === 'error' || (isRecord(json) && json.error != null)) {
334
+ const error = isRecord(json) && isRecord(json.error) ? json.error : json;
335
+ const code = isRecord(error) && typeof error.code === 'string' && /^[\w.-]{1,80}$/.test(error.code)
336
+ ? error.code : 'PROVIDER_STREAM_ERROR';
337
+ throw new AiRuntimeError(code, 'Server error event');
338
+ }
339
+ // BigModel's documented SDK consumer permits status-only chunks without
340
+ // choices. This does not make them completion evidence or allow wrong types.
341
+ const statusOnlyAllowed = providerId === 'bigmodel';
342
+ if (statusOnlyAllowed && isRecord(json) && json.choices === undefined) {
343
+ return { done: false, usage: readUsage(json) };
344
+ }
345
+ if (!isRecord(json) || !Array.isArray(json.choices)) {
346
+ throw new AiRuntimeError('INVALID_STREAM_RESPONSE', 'Chat chunk has no choices array');
347
+ }
348
+ if (json.choices.length === 0) {
349
+ if (!statusOnlyAllowed && !isRecord(json.usage))
350
+ throw new AiRuntimeError('INVALID_STREAM_RESPONSE', 'Empty choices without usage');
351
+ return { done: false, usage: readUsage(json) };
352
+ }
353
+ const first = json.choices[0];
354
+ if (!isRecord(first) || !isRecord(first.delta)
355
+ || (first.finish_reason != null && (typeof first.finish_reason !== 'string' || !first.finish_reason.trim()))) {
356
+ throw new AiRuntimeError('INVALID_STREAM_RESPONSE', 'Invalid Chat choice');
357
+ }
358
+ if (providerId === 'bigmodel' && first.finish_reason === 'network_error') {
359
+ throw new AiRuntimeError('PROVIDER_STREAM_ERROR', 'Model inference failed', { finishReason: 'network_error' });
360
+ }
361
+ for (const field of ['content', 'reasoning_content', 'reasoning']) {
362
+ if (first.delta[field] != null && typeof first.delta[field] !== 'string') {
363
+ throw new AiRuntimeError('INVALID_STREAM_RESPONSE', 'Invalid Chat text delta');
364
+ }
365
+ }
326
366
  const delta = readDelta(json);
327
367
  return {
328
368
  done: false,
@@ -0,0 +1,4 @@
1
+ export declare function drainSseEvents(input: string): {
2
+ events: string[];
3
+ remaining: string;
4
+ };
@@ -0,0 +1,31 @@
1
+ // Dispatch only complete SSE events; an EOF does not imply a blank line.
2
+ export function drainSseEvents(input) {
3
+ const events = [];
4
+ let remaining = input;
5
+ let hasSeparator = true;
6
+ while (hasSeparator) {
7
+ const separator = findNextSeparator(remaining);
8
+ if (!separator) {
9
+ hasSeparator = false;
10
+ continue;
11
+ }
12
+ if (separator) {
13
+ events.push(remaining.slice(0, separator.index));
14
+ remaining = remaining.slice(separator.index + separator.length);
15
+ }
16
+ }
17
+ return { events, remaining };
18
+ }
19
+ function findNextSeparator(input) {
20
+ // CRLF is one line ending, including when it crosses transport chunks.
21
+ const endings = /\r\n|\r|\n/g;
22
+ let previous = null;
23
+ let current;
24
+ while ((current = endings.exec(input)) !== null) {
25
+ if (previous && previous.index + previous[0].length === current.index) {
26
+ return { index: previous.index, length: previous[0].length + current[0].length };
27
+ }
28
+ previous = current;
29
+ }
30
+ return undefined;
31
+ }
package/docs/consumers.md CHANGED
@@ -1,17 +1,33 @@
1
- # SDK 消费项目清单
2
-
3
- 本清单是 `@henjicc/ai-sdk` 消费方的唯一维护入口,用于 SDK 发布后的跨仓升级协调。
4
- 绝对路径仅描述当前开发机上的仓库位置,不进入 SDK 运行时代码、发布包或用户配置。
5
-
1
+ # SDK 消费项目清单
2
+
3
+ 本清单是 `@henjicc/ai-sdk` 消费方的唯一维护入口,用于 SDK 发布后的跨仓升级协调。
4
+ 绝对路径仅描述当前开发机上的仓库位置,不进入 SDK 运行时代码、发布包或用户配置。
5
+
6
6
  最后核对日期:2026-09-24
7
7
 
8
- `0.6.0` 候选版本:新增 MiMo 2.6、GLM-5.3-FlashX、KIE HappyHorse 1.1、火山 Seedream 5.0 Flash 和百炼 Qwen Audio 3.1 ASR;补齐 MiMo Responses 思考事件与 ASR JSON/说话人解析。SDK 79 个测试文件 / 933 项测试、类型构建、可移植性、49 个公开入口和受限宿主验证通过;痕迹 AI 目录、参数组合、模型发现与旧配置兼容验证通过。MiMo 思考事件、ASR JSON 回退、HappyHorse 图生字段的修复撤销验证均检测到失败,恢复后通过。未执行付费请求。等待发布提交 CI 和 npm 身份恢复,尚未公共发布。
8
+ `0.6.2` 候选验证中,尚未发布:聊天/翻译流终态、百炼/火山实时 ASR、共享会话终止与脱敏诊断已完成修复及撤销修复后的失败验证。SDK 83 文件 / 1023 项测试、可移植性、构建、49 个公开入口、严格类型和受限宿主门禁通过,Henji-AI 8 项相关宿主测试与主进程类型检查通过。下一步验证固定候选包并等待对应发布提交的必需 CI;未执行本轮真实付费请求。Henji-AI workspace 和 llm-chat 示例准备使用 `0.6.2`,minimal-node/form-renderer 保持 `0.6.0`。说吧当前仍为 `0.6.1`,本次不修改外部仓库;Photoshop 未在本机验证,不能声称已升级。
9
+
10
+ `0.6.1` 已发布:修复百炼 Fun Duplex 实时识别将无句首标记的空中间帧误判为 `invalid_response`;缺失 text、空 final 和无最终结果仍明确失败。发布源码提交 `6dd90b22` 的必需 CI [35917044506](https://github.com/henjicc/Henji-AI/actions/runs/35917044506) 全部成功;SDK 79 文件 / 933 项测试、可移植性、类型构建、49 个公开入口和受限宿主验证通过。两项事件序列回归撤销修复后失败、恢复后通过;合成语音最小真实请求成功。固定候选包发布后,隔离配置、缓存和令牌的仓外匿名回装及 Vite / 受限宿主验证通过。
11
+
12
+ - `0.6.1` tarball:[公共 npm 包](https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.6.1.tgz)
13
+ - `0.6.1` shasum:`48a48e4692dd30695393b0c7f7432b994ede5ef1`
14
+ - `0.6.1` integrity:`sha512-0gY7crODwieU82r5XOgrmKVQIzLdJqFf8gAjsz8xv0wIpJe4aL8SeBM59qVVVSGpjckaVqzFe9NryhA/hjQw8Q==`
15
+
16
+ 外部 `say-it` 已在 `D:/VibeCode/说吧` 完成公共 npm `0.6.1` 精确升级,提交 `18bad6d`;manifest、lock、bundle 版本与完整性校验一致。QuickJS/WS 空中间帧回归、SDK 宿主 35 项本地测试、runtime 类型检查通过;另有听写 48 项、历史 12 项、历史 UI 6 项及前端类型检查/构建通过。开发桌面应用已重启,实际麦克风交互由用户验证。当前宿主已含流式/原生文件上传桥,不再受下方旧版迁移待办限制。三个仓内示例仅消费 generation/LLM,保持已验证的 `0.6.0`;Photoshop 未消费此次 ASR 修复,不机械升级。
17
+
18
+ 以下 `0.6.0` 及更早记录用于追溯历史发布。
19
+
20
+ `0.6.0` 已发布:新增 MiMo 2.6、GLM-5.3-FlashX、KIE HappyHorse 1.1、火山 Seedream 5.0 Flash 和百炼 Qwen Audio 3.1 ASR;补齐 MiMo Responses 思考事件与 ASR JSON/说话人解析。SDK 79 个测试文件 / 933 项测试、类型构建、可移植性、49 个公开入口和受限宿主验证通过;痕迹 AI 目录、参数组合、模型发现与旧配置兼容验证通过。MiMo 思考事件、ASR JSON 回退、HappyHorse 图生字段的修复撤销验证均检测到失败,恢复后通过。未执行付费请求。发布提交 `d08ea8fa` 的必需 CI [35904531000](https://github.com/henjicc/Henji-AI/actions/runs/35904531000) 全部通过,npm 网页认证后发布固定候选包;公共索引可读后,隔离配置、缓存与令牌的仓外匿名安装、包校验、新模型请求探针、Vite 49 入口和受限宿主验证全部通过。
21
+
22
+ 本轮消费影响:Henji-AI 自动组合新增 generation pack,无需新增助手工具;三个仓内示例精确依赖同步到 `0.6.0`,均在仓外从公共 npm 匿名安装,minimal-node dry-run、llm-chat dry-run / bundle 边界和 form-renderer 构建 / 五类模型表单输出通过。外部 `say-it` 仍需先完成 `0.5.0` 文件上传桥迁移;当前未引入这批模型,不机械升级。`henji-ai-ps` 本机未定位,不声称已完成外部宿主升级。
23
+
24
+ - `0.6.0` tarball:[公共 npm 包](https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.6.0.tgz)
25
+ - `0.6.0` shasum:`2d02f956d6addc9fb92580cda323bc9d0904607e`
26
+ - `0.6.0` integrity:`sha512-GzhWnVkdv2NNZjf0diI9dRBRkc/cH0O5nCjbiQrast54f+ik4Q4LbKjytrMhyMawxaSkCoHVeNQuEUwnCDkcnA==`
9
27
 
10
- 本轮消费影响:Henji-AI 自动组合新增 generation pack,无需新增助手工具;三个仓内示例的精确依赖同步到 `0.6.0`,公共发布后的独立安装验证待完成。外部 `say-it` 仍需先完成 `0.5.0` 文件上传桥迁移;当前未引入这批模型,不机械升级。`henji-ai-ps` 本机未定位,不声称已完成外部宿主升级。
11
-
12
- Henji-AI 安装包的 GPT Image 2.5 高分辨率报错归属:宿主智能比例预处理曾忽略联动过滤,将 `smart` 转成 KIE 仅限 1K 的 `27:16`,SDK 在请求前正确拒绝。修复位于应用公共预处理,按当前分辨率/渠道的合法选项匹配;Flare/Sunburst 的 1K/2K/4K 及 APIMart、Grsai 同类筛选已有定向覆盖。无需放宽 SDK 契约或发布 SDK;安装包需随应用更新才包含修复,现有版本可手动选合法比例规避。
13
-
14
- 当前仓内候选 SDK:`0.6.0`;最近已确认公共 npm SDK:`0.5.2`(2026-09-21 已发布并完成公共匿名回装)。以下为历史发布证据。
28
+ Henji-AI 安装包的 GPT Image 2.5 高分辨率报错归属:宿主智能比例预处理曾忽略联动过滤,将 `smart` 转成 KIE 仅限 1K 的 `27:16`,SDK 在请求前正确拒绝。修复位于应用公共预处理,按当前分辨率/渠道的合法选项匹配;Flare/Sunburst 的 1K/2K/4K 及 APIMart、Grsai 同类筛选已有定向覆盖。无需放宽 SDK 契约或发布 SDK;安装包需随应用更新才包含修复,现有版本可手动选合法比例规避。
29
+
30
+ 当前仓内 SDK:`0.6.2` 候选;公共 npm 仍为 `0.6.1`(已发布并完成公共匿名回装)。以下为历史发布证据。
15
31
 
16
32
  `0.5.2` 完善轻量 `@henjicc/ai-sdk/llm/streaming`:正式类型支持文本、JSON Object 与 JSON Schema,Chat Completions 的最终请求体写入 `response_format`,OpenAI 输出上限写入 `max_completion_tokens`;结构化输出、思考模式和模型能力的非法组合会在请求前返回结构化错误。流式正文与思考继续分离,保留外部 `AbortSignal`,不设置默认总时限;未显式配置输出上限时不再注入 4096,服务端以 `length` 等原因结束时返回 `truncated: true`。发布前 77 个测试文件 / 909 项测试、可移植性、构建、49 个 Vite 公开入口和受限宿主门禁通过;故意撤销结构化参数、输出上限、OpenAI 字段映射和截断标记后,8 项定向断言失败,恢复后通过。公共索引可读后,已使用隔离 npm 配置和缓存匿名安装精确版本,并从已发布的轻量入口验证最终请求体包含 JSON Schema、思考强度和 24,000 token 上限,SSE 正文/思考事件分离且未截断。
17
33
 
@@ -26,78 +42,78 @@ Henji-AI 安装包的 GPT Image 2.5 高分辨率报错归属:宿主智能比
26
42
  - `0.5.1` integrity:`sha512-+L/mAmFU6zgJ0gdsIwy0T1Z9TDOABaxO3HgLsEvMUNbRQhs9K55golF1472EgSJsIDysScnUulFJfBVg0xsNBg==`
27
43
 
28
44
  `0.5.0` 发布运行时代码提交 `1333422e`,补充边界回归提交 `da37e4bc`;后者必需 CI `35535140722` 全部成功。12组修复撤销验证均检测到失败,恢复后定向测试通过。固定候选包经仓外回装后发布,公共 registry 匿名安装的 shasum/integrity 与候选包一致;Vite公开入口和无TextEncoder/TextDecoder的受限宿主消费通过。npm网页登录与发布二次验证已完成;发布后等待公共索引可读才执行匿名验证,没有重复发布。
29
-
30
- - tarball:https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.5.0.tgz
31
- - shasum:`1b66a7c54f4f86a01b73fdca68b1c63e53dcbe10`
32
- - integrity:`sha512-MaPS87wDnvGdeAkGZOQHV+q/XPsQv9WZSPKYTYhqYrDhLbGUKS+m6h1sQ2oPM0GVXuZXKsos9BTJH61NSHjzag==`
33
-
34
- 本轮按用户SDK范围交付可安装包及宿主调整依据;未升级或修改外部say-it/Photoshop仓库。say-it必须实现新流式/原生文件上传桥并完成Tauri集成验收后再升级;不能只改10 MiB常量。
35
-
36
- `0.5.0` 文件 ASR:四族九模型已核对。SDK 新增 describe/readChunk + fetchStream 的有界请求体消费,百炼异步 media-ref 必须通过 uploadFile 原生直传 OSS;旧宿主 read 兼容路径不得提升原内存阈值。structured media_too_large 保留实际字节、上限与可得时长。官方 Qwen 编码后10 MB对应原始7,500,000字节,Fun Flash 2,000,000,000字节/5分钟,Groq附件25,000,000,硅基流动50,000,000,百炼临时上传取min(1,000,000,000,凭证MB×1,000,000)。
37
-
38
- 本次完成SDK精确回归、12组修复撤销反向验证、全量发布门禁与真实QuickJS 64 MiB堆探针(50 MB multipart、7.5/12 MB JSON);未执行付费请求。say-it 当前 Host API 虽有分块,适配器仍拼回完整Uint8Array,请求体也仍缓冲,必须接入新契约后才可调整分块总文件上限;原生OSS单独受凭证限制。当前任务交付SDK及宿主调整依据,未改写该外部仓库或声称完成Tauri验收。Henji-AI/Photoshop现有生成媒体读取未迁移到新ASR接口,不机械增加未使用能力。
39
-
40
- 2026-09-14 候选增量:安全网络失败在端点尝试耗尽后按 1/3/8 秒退避,每次失败重新判断重放安全性,取消立即结束退避。SDK 873 项测试、主进程定向测试与类型检查、可移植性及仓外 Vite/受限宿主消费通过;故意移除重试安全复核后,未知提交状态测试失败,恢复后通过。真实 Electron 主进程对本地服务及 KIE 无凭据只读查询,Node 与 Chromium 两栈均得到 HTTP 200;只证明当时连通,不代表付费生成成功,也未复现之前断连的外部原因。宿主增加关联原请求的脱敏 DNS/代理诊断,不更换网络栈。本机 npm 身份仍返回 401,本次候选变更尚未公共发布或同步外部消费者;下方旧候选包校验值仅对应此前版本内容。
41
-
42
- `0.4.2`:共享 transport 将 Node 明确的 TLS 建立前 ECONNRESET 识别为未发送,允许备用端点切换;GET/HEAD 的瞬态断连也允许切换,提交状态不明的写请求仍禁止重放。错误附带脱敏端点、阶段、耗时和提交状态,Henji-AI 生成与继续查询日志保留这些字段。SDK 870 项测试、宿主 7 项定向测试、可移植性、类型构建、仓外 Node ESM / 严格 TypeScript / Vite 49 入口与受限宿主回装通过;故意放行普通 ECONNRESET 重放会被 3 项写请求测试检测,恢复后全量通过。未执行真实付费请求。外部宿主使用不同 transport,需按其错误契约核对影响,不能将 Node 连接前分类视为所有宿主均已实测。
43
-
44
- 修复提交 `5bf36dce` 已推送,CI `34758299265` 已启动。本机 npm 身份检查返回 `401 Unauthorized`,认证恢复且必需 CI 通过前不得发布;未声称完成公共回装或外部消费者升级。候选包 shasum 为 `dafaae4787798c3f22e5615fe8f5d60f4545c0a2`。宿主 Electron 类型检查与重启通过。上一提交 `45bb50b5` 的 CI 已存在两项画布能力引用相等断言失败,与本次网络修复无关,未夹带修改。
45
-
46
- `0.4.1`:修复 KIE GPT Image 2 / 2.5 的视频参考误标签,图片生成、编辑、多图输入及请求契约保持不变。109 个生成模型的跨模态视频标签检查、67 项定向测试、SDK 862 项全量测试、可移植性、类型构建及仓外公开入口/受限宿主回装通过。外部消费者登记版本为 `0.2.8`:say-it 不使用图片模型,不受影响;henji-ai-ps 的 KIE GPT Image 2 标签消费情况需在该项目可访问时核实,尚未升级。
47
-
48
- 修复提交 `ef2be83f` 的必需 CI `34575686946` 已全部通过。正式包与固定候选包 shasum 一致:`32b8dd00f3e320822999b626b8e95a50b6a8ff2f`。发布后在仓外隔离 npm 配置并移除令牌环境变量,已从公共 npm 匿名安装精确版本;Node ESM、严格 TypeScript、49 个 Vite 入口及无 TextEncoder/TextDecoder 的受限宿主验证通过。仓内 workspace 与三个示例 manifest 已锁定 `0.4.1`;未执行真实付费生成或外部消费项目升级。
49
-
50
- - `0.4.1` tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.1.tgz`
51
- - `0.4.1` integrity:`sha512-HtOoOlJWGR6m15wIvqI2VMJFk+ijHntXl2oZAqEA6Ri12gWqaa3JhPthisTj+epO7GwGcuGX4gW3a8qIDd4vAw==`
52
-
53
- `0.4.0`:新增硅基流动聊天预设与分类模型发现,提交 `a678dab6` 已推送,必需 CI `34565921443` 全部通过。SDK 861 项测试、宿主配置 15 项测试、类型构建、49 个公开入口及受限宿主候选包回装通过;未执行真实付费推理。移除服务端分类参数的断牙验证使 4 项测试失败,恢复后全量通过。
54
-
55
- 账号网页二次验证已完成,正式包与候选包校验值一致。已在仓外隔离 npm 配置并移除令牌环境变量,从公共 npm 匿名安装精确版本:Node ESM、严格 TypeScript、Vite 49 个入口及无 TextEncoder/TextDecoder 的受限宿主验证通过,包含硅基流动动态模型发现。仓内 workspace 与三个示例 manifest 已锁定 `0.4.0`;现有外部消费者未新增硅基流动聊天使用,暂不升级。
56
-
57
- - tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.0.tgz`
58
- - shasum:`29a267d8a9f8375ac08b5af94569939473ab6a17`
59
- - integrity:`sha512-4D2gHakuo7Fhhh3a987qrObqJd78k8mIhXPL9dgE3A5uywBi4ebHj7U4XVLUcbjv/HO34wH/+8kFlE5JMOGD0g==`
60
-
61
- `0.3.0` 已发布:新增四家 GPT Image 2.5 pack、五家文本 embedding / 四家 rerank,DeepSeek 官方默认模型更新为 `deepseek-flash`。发布提交 `a2dc5bc4` 的必需 CI 门禁全部通过(运行 `34532267703`),SDK 全量 839 项测试和候选包回装通过。正式包已在隔离 npm 配置、无用户令牌的仓外环境从公共 npm 安装,标准 Vite 48 个入口与受限宿主验证通过;正式包校验值与候选包一致。未运行真实付费模型请求。
62
-
63
- Henji-AI workspace 与三个仓内示例 manifest 均锁定 `0.4.0`;下表原有示例运行记录仅代表 `0.2.8` 历史证据,本次未完成三个示例的独立全套回装复验。`say-it` 已在 `D:/VibeCode/说吧` 定位,仍锁定公共 npm `0.2.8`,实际按需使用 ASR、translation 与 LLM modules,未使用本次新增硅基流动聊天能力,无需机械升级;未运行其真实宿主验收。`henji-ai-ps` 的下表路径为另一台开发机记录,本机未定位该路径,不声称本次完成外部升级。
64
-
65
- ## 判定口径
66
-
67
- - **消费者**:package manifest / lockfile 声明 SDK,或源码、构建入口实际导入 SDK。
68
- - **内部消费验证面**:位于 SDK 主仓库内,但有独立 manifest 和可运行构建的示例;它们不是外部仓库,仍需跟随发布版本验证。
69
- - **非消费者证据**:README、任务交接、迁移记录中的文字引用,以及没有进入实际构建的声明,不单独算消费者。
70
- - 扫描范围为 `/Users/henji/Documents/VibeCode` 下一级项目,排除了 `.git`、`node_modules`、`dist`、`target`、`build`、`coverage` 和缓存目录。
71
-
72
- ## 独立宿主
73
-
74
- | 仓库 / 开发路径 | 宿主类型 | 当前精确版本 | SDK 入口与构建方式 | 凭据 / transport 责任 | 需同步的变更类型 | 验证命令 | 同步证据与边界 | 最后核对 |
75
- |---|---|---|---|---|---|---|---|---|
76
- | `Henji-AI`<br>`/Users/henji/Documents/VibeCode/Henji-AI` | Electron 42 主进程 + React/Vite;SDK 主开发、首发验证宿主 | workspace 源码与公共 npm 均为 `0.2.8` | 包根、`provider-packs/*`、`tool-packs/*`;根构建先执行 `build:sdk`,再构建 Electron | Electron 主进程注入 HTTP transport、凭据、媒体读取、日志、trace、取消与落盘;渲染层不直接持有密钥 | 公共类型/目录、provider preset、凭据坐标、transport、媒体、包导出、LLM/生成执行协议 | `npm run check:sdk`;相关 Vitest;改主进程后 `npm run electron:build` | `e74fdf09`;公共发布 prepublish 全门禁通过,72 个测试文件 / 766 项测试;正式 tarball 已在无 npm/GitHub 凭据的仓外目录完成安装与 ESM 导入;全新安装后的 workspace 解析已修复 | 2026-08-31 |
77
- | `henji-ai-ps`<br>`/Users/henji/Documents/VibeCode/henji-ai-ps` | Photoshop UXP 插件 + React/Vite IIFE(pnpm) | manifest/lock 均精确锁定公共 npm `0.2.8`;lock integrity 与上方正式产物一致 | `generation/core`、单模型/供应商 pack、LLM streaming;Vite 构建与 UXP smoke bundle | UXP 宿主注入受限 `fetch`、provider 凭据、媒体编码读取和脱敏日志;SDK 不读取 Node/文件系统 | 生成 pack/exports、受限环境可移植性、RuntimeContext、凭据 scope、媒体与流式 LLM;不因版本同步自动引入 GLM | `pnpm typecheck:uxp-smoke && pnpm check:uxp-sdk && pnpm smoke:uxp:build && pnpm check:uxp-smoke`;完整 `pnpm check` | `e509716`;无用户 npm 凭据的 frozen lock 安装通过;Node 22 下四项门禁通过,正式产品仍仅 39 个 generation/erase packs,网络调用与受限环境风险均为 0 | 2026-08-31 |
78
- | `say-it`<br>`/Users/henji/Documents/VibeCode/say-it` | Tauri 2 + Rust 管理 QuickJS;WebView 不运行 SDK | manifest/lock 均精确锁定公共 npm `0.2.8`;resolved、shasum、integrity 与上方正式产物一致 | 按需打包 capability、Bailian/火山实时 ASR、SiliconFlow/Groq 文件 ASR、translation、LLM modules 为相互隔离 IIFE;Rust 加载 bundle | Rust Host API 注入 HTTP 字节流、WS、media-ref、CredentialStore、日志/trace、Abort/timeout/cancel;QuickJS/插件/WebView 不直取密钥 | capability/LLM 协议、按需 exports、descriptor source/坐标、QuickJS 可移植性、bundle 隔离;不因版本同步自动增加未采用模型 | `npm run sdk-runtime:typecheck && npm run sdk-runtime:build`;`npm run test:ui`;Rust 定向/全量测试;`npm run ui:build` | SDK 迁移提交 `5eb4c8b`,0.2.8 基础验证 `df3107e`,Rust→QuickJS 可选字段省略修复 `9e70d32`;QuickJS multipart 内存修复 `3f00ea9` 属宿主传输实现问题,改为原生二进制句柄且保持 64 MiB 沙箱上限,10 MiB 音频边界回归、重定向/资源清理测试、SDK Runtime 类型检查与构建通过 | 2026-09-01 |
79
-
80
- ## SDK 仓内消费验证面
81
-
82
- 这些目录是可独立安装、构建的真实示例,但与 SDK 同属 `Henji-AI` 仓库,不重复算外部仓库。
83
- 它们的 manifest 均精确声明待发布的 `0.2.8`,且没有锁文件;公共 npm 发布后再从远端重新安装并回填验证证据。
84
-
85
- | 路径 | 用途 / 入口 | 当前版本 | 宿主责任 | 验证命令 | 同步证据 | 最后核对 |
86
- |---|---|---|---|---|---|---|
87
- | `/Users/henji/Documents/VibeCode/Henji-AI/packages/ai-sdk/examples/minimal-node` | `generation` + `runtime` 按需入口;完整 generation catalog、KIE dry-run/live 闸门,不带 LLM/BigModel | `0.2.8`(精确 manifest,无 lock、无 workspace alias) | Node transport、环境凭据、文件媒体读取、日志 | 在目录内 `npm install && npm run dry-run` | 已在无用户 npm 凭据的临时副本中从公共 npm 回装;dry-run 网络调用为 0 | 2026-08-31 |
88
- | `/Users/henji/Documents/VibeCode/Henji-AI/packages/ai-sdk/examples/llm-chat` | `llm/streaming` 按需入口;OpenAI-compatible SSE 对话,不带 generation、BigModel preset/models/pricing、Groq 或 LLM modules(保留通用身份解析所需 profiles) | `0.2.8`(精确 manifest,无 lock、无 workspace alias) | Node transport、环境凭据、流事件与取消 | 在目录内 `npm install && npm run dry-run && npm run check:bundle` | 已从公共 npm 回装;dry-run 网络调用为 0,bundle 38 个模块、无越界模块 | 2026-08-31 |
89
- | `/Users/henji/Documents/VibeCode/Henji-AI/packages/ai-sdk/examples/form-renderer` | `generation` + `catalog` + `runtime` 按需入口;完整 generation catalog/参数契约与最小 renderer,不带 LLM/BigModel | `0.2.8`(精确 manifest,无 lock、无 workspace alias) | 零网络 RuntimeContext;仅目录与 renderer | 在目录内 `npm install && npm run build && npm start` | 已从公共 npm 回装;构建与五类代表模型表单渲染通过 | 2026-08-31 |
90
-
91
- ## 非消费者证据
92
-
93
- - `Henji-AI`、`henji-ai-ps`、`say-it` 的 README、任务文件和交接文档含历史版本或迁移描述;它们用于追溯,不形成额外消费者。
94
- - `/Users/henji/Documents/VibeCode` 下的 `ai-roundtable`、`fast-install`、`henji`、`henji-dev`、`henjicc.github.io`、`mySkills`、`test` 未发现 `@henjicc/ai-sdk` 的 manifest、lockfile、源码 import 或构建脚本引用。
95
- - SDK 自身的测试、fixture、导出检查和发布脚本属于生产者验证,不作为独立消费项目。
96
-
97
- ## 发布后维护规则
98
-
99
- 1. SDK 必须先在 `Henji-AI` 完成精确测试、全量/可移植性/包验证与远端回装,再发布。
100
- 2. 发布后按变更影响逐个同步本清单中的实际消费者;外部消费者必须精确锁版本并核对 lock integrity,再回写 commit、验证状态、边界和日期。
101
- 3. 只升级消费项目实际使用的按需入口。新增 provider/model 不等于所有宿主都要增加对应 bundle 或业务入口。
102
- 4. 涉及 provider 身份、endpoint profile、credentialId、transport、media 或协议事件时,逐项检查表中宿主责任边界,禁止在消费方复制 SDK 执行内核。
103
- 5. 消费项目升级完成后,将验证结果写入对应任务交接;本清单只维护稳定事实,不复制每次执行日志。
45
+
46
+ - tarball:https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.5.0.tgz
47
+ - shasum:`1b66a7c54f4f86a01b73fdca68b1c63e53dcbe10`
48
+ - integrity:`sha512-MaPS87wDnvGdeAkGZOQHV+q/XPsQv9WZSPKYTYhqYrDhLbGUKS+m6h1sQ2oPM0GVXuZXKsos9BTJH61NSHjzag==`
49
+
50
+ 本轮按用户SDK范围交付可安装包及宿主调整依据;未升级或修改外部say-it/Photoshop仓库。say-it必须实现新流式/原生文件上传桥并完成Tauri集成验收后再升级;不能只改10 MiB常量。
51
+
52
+ `0.5.0` 文件 ASR:四族九模型已核对。SDK 新增 describe/readChunk + fetchStream 的有界请求体消费,百炼异步 media-ref 必须通过 uploadFile 原生直传 OSS;旧宿主 read 兼容路径不得提升原内存阈值。structured media_too_large 保留实际字节、上限与可得时长。官方 Qwen 编码后10 MB对应原始7,500,000字节,Fun Flash 2,000,000,000字节/5分钟,Groq附件25,000,000,硅基流动50,000,000,百炼临时上传取min(1,000,000,000,凭证MB×1,000,000)。
53
+
54
+ 本次完成SDK精确回归、12组修复撤销反向验证、全量发布门禁与真实QuickJS 64 MiB堆探针(50 MB multipart、7.5/12 MB JSON);未执行付费请求。say-it 当前 Host API 虽有分块,适配器仍拼回完整Uint8Array,请求体也仍缓冲,必须接入新契约后才可调整分块总文件上限;原生OSS单独受凭证限制。当前任务交付SDK及宿主调整依据,未改写该外部仓库或声称完成Tauri验收。Henji-AI/Photoshop现有生成媒体读取未迁移到新ASR接口,不机械增加未使用能力。
55
+
56
+ 2026-09-14 候选增量:安全网络失败在端点尝试耗尽后按 1/3/8 秒退避,每次失败重新判断重放安全性,取消立即结束退避。SDK 873 项测试、主进程定向测试与类型检查、可移植性及仓外 Vite/受限宿主消费通过;故意移除重试安全复核后,未知提交状态测试失败,恢复后通过。真实 Electron 主进程对本地服务及 KIE 无凭据只读查询,Node 与 Chromium 两栈均得到 HTTP 200;只证明当时连通,不代表付费生成成功,也未复现之前断连的外部原因。宿主增加关联原请求的脱敏 DNS/代理诊断,不更换网络栈。本机 npm 身份仍返回 401,本次候选变更尚未公共发布或同步外部消费者;下方旧候选包校验值仅对应此前版本内容。
57
+
58
+ `0.4.2`:共享 transport 将 Node 明确的 TLS 建立前 ECONNRESET 识别为未发送,允许备用端点切换;GET/HEAD 的瞬态断连也允许切换,提交状态不明的写请求仍禁止重放。错误附带脱敏端点、阶段、耗时和提交状态,Henji-AI 生成与继续查询日志保留这些字段。SDK 870 项测试、宿主 7 项定向测试、可移植性、类型构建、仓外 Node ESM / 严格 TypeScript / Vite 49 入口与受限宿主回装通过;故意放行普通 ECONNRESET 重放会被 3 项写请求测试检测,恢复后全量通过。未执行真实付费请求。外部宿主使用不同 transport,需按其错误契约核对影响,不能将 Node 连接前分类视为所有宿主均已实测。
59
+
60
+ 修复提交 `5bf36dce` 已推送,CI `34758299265` 已启动。本机 npm 身份检查返回 `401 Unauthorized`,认证恢复且必需 CI 通过前不得发布;未声称完成公共回装或外部消费者升级。候选包 shasum 为 `dafaae4787798c3f22e5615fe8f5d60f4545c0a2`。宿主 Electron 类型检查与重启通过。上一提交 `45bb50b5` 的 CI 已存在两项画布能力引用相等断言失败,与本次网络修复无关,未夹带修改。
61
+
62
+ `0.4.1`:修复 KIE GPT Image 2 / 2.5 的视频参考误标签,图片生成、编辑、多图输入及请求契约保持不变。109 个生成模型的跨模态视频标签检查、67 项定向测试、SDK 862 项全量测试、可移植性、类型构建及仓外公开入口/受限宿主回装通过。外部消费者登记版本为 `0.2.8`:say-it 不使用图片模型,不受影响;henji-ai-ps 的 KIE GPT Image 2 标签消费情况需在该项目可访问时核实,尚未升级。
63
+
64
+ 修复提交 `ef2be83f` 的必需 CI `34575686946` 已全部通过。正式包与固定候选包 shasum 一致:`32b8dd00f3e320822999b626b8e95a50b6a8ff2f`。发布后在仓外隔离 npm 配置并移除令牌环境变量,已从公共 npm 匿名安装精确版本;Node ESM、严格 TypeScript、49 个 Vite 入口及无 TextEncoder/TextDecoder 的受限宿主验证通过。仓内 workspace 与三个示例 manifest 已锁定 `0.4.1`;未执行真实付费生成或外部消费项目升级。
65
+
66
+ - `0.4.1` tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.1.tgz`
67
+ - `0.4.1` integrity:`sha512-HtOoOlJWGR6m15wIvqI2VMJFk+ijHntXl2oZAqEA6Ri12gWqaa3JhPthisTj+epO7GwGcuGX4gW3a8qIDd4vAw==`
68
+
69
+ `0.4.0`:新增硅基流动聊天预设与分类模型发现,提交 `a678dab6` 已推送,必需 CI `34565921443` 全部通过。SDK 861 项测试、宿主配置 15 项测试、类型构建、49 个公开入口及受限宿主候选包回装通过;未执行真实付费推理。移除服务端分类参数的断牙验证使 4 项测试失败,恢复后全量通过。
70
+
71
+ 账号网页二次验证已完成,正式包与候选包校验值一致。已在仓外隔离 npm 配置并移除令牌环境变量,从公共 npm 匿名安装精确版本:Node ESM、严格 TypeScript、Vite 49 个入口及无 TextEncoder/TextDecoder 的受限宿主验证通过,包含硅基流动动态模型发现。仓内 workspace 与三个示例 manifest 已锁定 `0.4.0`;现有外部消费者未新增硅基流动聊天使用,暂不升级。
72
+
73
+ - tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.0.tgz`
74
+ - shasum:`29a267d8a9f8375ac08b5af94569939473ab6a17`
75
+ - integrity:`sha512-4D2gHakuo7Fhhh3a987qrObqJd78k8mIhXPL9dgE3A5uywBi4ebHj7U4XVLUcbjv/HO34wH/+8kFlE5JMOGD0g==`
76
+
77
+ `0.3.0` 已发布:新增四家 GPT Image 2.5 pack、五家文本 embedding / 四家 rerank,DeepSeek 官方默认模型更新为 `deepseek-flash`。发布提交 `a2dc5bc4` 的必需 CI 门禁全部通过(运行 `34532267703`),SDK 全量 839 项测试和候选包回装通过。正式包已在隔离 npm 配置、无用户令牌的仓外环境从公共 npm 安装,标准 Vite 48 个入口与受限宿主验证通过;正式包校验值与候选包一致。未运行真实付费模型请求。
78
+
79
+ Henji-AI workspace 与三个仓内示例 manifest 均锁定 `0.4.0`;下表原有示例运行记录仅代表 `0.2.8` 历史证据,本次未完成三个示例的独立全套回装复验。`say-it` 已在 `D:/VibeCode/说吧` 定位,仍锁定公共 npm `0.2.8`,实际按需使用 ASR、translation 与 LLM modules,未使用本次新增硅基流动聊天能力,无需机械升级;未运行其真实宿主验收。`henji-ai-ps` 的下表路径为另一台开发机记录,本机未定位该路径,不声称本次完成外部升级。
80
+
81
+ ## 判定口径
82
+
83
+ - **消费者**:package manifest / lockfile 声明 SDK,或源码、构建入口实际导入 SDK。
84
+ - **内部消费验证面**:位于 SDK 主仓库内,但有独立 manifest 和可运行构建的示例;它们不是外部仓库,仍需跟随发布版本验证。
85
+ - **非消费者证据**:README、任务交接、迁移记录中的文字引用,以及没有进入实际构建的声明,不单独算消费者。
86
+ - 扫描范围为 `/Users/henji/Documents/VibeCode` 下一级项目,排除了 `.git`、`node_modules`、`dist`、`target`、`build`、`coverage` 和缓存目录。
87
+
88
+ ## 独立宿主
89
+
90
+ | 仓库 / 开发路径 | 宿主类型 | 当前精确版本 | SDK 入口与构建方式 | 凭据 / transport 责任 | 需同步的变更类型 | 验证命令 | 同步证据与边界 | 最后核对 |
91
+ |---|---|---|---|---|---|---|---|---|
92
+ | `Henji-AI`<br>`D:/VibeCode/Henji-AI` | Electron 42 主进程 + React/Vite;SDK 主开发、首发验证宿主 | workspace 源码 `0.6.2` 候选;公共 npm `0.6.1` | 包根、`provider-packs/*`、`tool-packs/*`;根构建先执行 `build:sdk`,再构建 Electron | Electron 主进程注入 HTTP transport、凭据、媒体读取、日志、trace、取消与落盘;渲染层不直接持有密钥 | 公共类型/目录、provider preset、凭据坐标、transport、媒体、包导出、LLM/生成执行协议 | SDK 发布门禁、相关宿主 Vitest、渲染层/主进程类型检查与模型 i18n | `6dd90b22`;必需 CI `35917044506` 成功,SDK 933 项测试、公开入口及受限宿主验证通过;公共包匿名回装通过,合成语音最小真实请求成功 | 2026-09-24 |
93
+ | `henji-ai-ps`<br>`/Users/henji/Documents/VibeCode/henji-ai-ps` | Photoshop UXP 插件 + React/Vite IIFE(pnpm) | manifest/lock 均精确锁定公共 npm `0.2.8`;lock integrity 与上方正式产物一致 | `generation/core`、单模型/供应商 pack、LLM streaming;Vite 构建与 UXP smoke bundle | UXP 宿主注入受限 `fetch`、provider 凭据、媒体编码读取和脱敏日志;SDK 不读取 Node/文件系统 | 生成 pack/exports、受限环境可移植性、RuntimeContext、凭据 scope、媒体与流式 LLM;不因版本同步自动引入 GLM | `pnpm typecheck:uxp-smoke && pnpm check:uxp-sdk && pnpm smoke:uxp:build && pnpm check:uxp-smoke`;完整 `pnpm check` | `e509716`;无用户 npm 凭据的 frozen lock 安装通过;Node 22 下四项门禁通过,正式产品仍仅 39 个 generation/erase packs,网络调用与受限环境风险均为 0 | 2026-08-31 |
94
+ | `say-it`<br>`D:/VibeCode/说吧` | Tauri 2 + Rust 管理 QuickJS;WebView 不运行 SDK | manifest/lock 精确锁定公共 npm `0.6.1`;bundle 版本、resolved、shasum、integrity 一致 | 按需打包 capability、Bailian/火山实时 ASR、SiliconFlow/Groq 文件 ASR、translation、LLM modules 为相互隔离 IIFE;Rust 加载 bundle | Rust Host API 注入 HTTP 字节流、WS、media-ref、CredentialStore、日志/trace、Abort/timeout/cancel;QuickJS/插件/WebView 不直取密钥 | capability/LLM 协议、按需 exports、descriptor source/坐标、QuickJS 可移植性、bundle 隔离;不因版本同步自动增加未采用模型 | `npm run sdk-runtime:typecheck && npm run sdk-runtime:build`;`npm run test:ui`;Rust 定向/全量测试;`npm run ui:build` | `18bad6d`;QuickJS/WS 空中间帧回归及 35 项 SDK 宿主本地测试通过,runtime 类型检查通过;听写/历史/UI 定向回归与前端类型构建通过,桌面开发应用已重启,实际麦克风交互待用户验证 | 2026-09-24 |
95
+
96
+ ## SDK 仓内消费验证面
97
+
98
+ 这些目录是可独立安装、构建的真实示例,但与 SDK 同属 `Henji-AI` 仓库,不重复算外部仓库。
99
+ 示例 manifest 使用精确版本,仓内不维护锁文件。llm-chat 准备升级到 `0.6.2`,其余两个示例保持 `0.6.0`;下表正式消费证据仍对应已发布版本,候选验证不冒充公共 npm 安装。
100
+
101
+ | 路径 | 用途 / 入口 | 当前版本 | 宿主责任 | 验证命令 | 同步证据 | 最后核对 |
102
+ |---|---|---|---|---|---|---|
103
+ | `D:/VibeCode/Henji-AI/packages/ai-sdk/examples/minimal-node` | `generation` + `runtime` 按需入口;完整 generation catalog、KIE dry-run/live 闸门,不带 LLM/BigModel | `0.6.0`(精确 manifest,仓内无 lock) | Node transport、环境凭据、文件媒体读取、日志 | 在目录内 `npm install && npm run dry-run` | 公共 npm 匿名安装、类型构建与 dry-run 通过,模型网络调用为 0 | 2026-09-24 |
104
+ | `D:/VibeCode/Henji-AI/packages/ai-sdk/examples/llm-chat` | `llm/streaming` 按需入口;OpenAI-compatible SSE 对话,不带 generation、BigModel preset/models/pricing、Groq 或 LLM modules(保留通用身份解析所需 profiles) | `0.6.2` 候选(上次公共验证为 `0.6.0`) | Node transport、环境凭据、流事件与取消 | 在目录内 `npm install && npm run dry-run && npm run check:bundle` | 公共 npm 匿名安装、类型构建、dry-run 和 bundle 边界检查通过 | 2026-09-24 |
105
+ | `D:/VibeCode/Henji-AI/packages/ai-sdk/examples/form-renderer` | `generation` + `catalog` + `runtime` 按需入口;完整 generation catalog/参数契约与最小 renderer,不带 LLM/BigModel | `0.6.0`(精确 manifest,仓内无 lock) | 零网络 RuntimeContext;仅目录与 renderer | 在目录内 `npm install && npm run build && npm start` | 公共 npm 匿名安装、类型构建与五类代表模型表单输出通过 | 2026-09-24 |
106
+
107
+ ## 非消费者证据
108
+
109
+ - `Henji-AI`、`henji-ai-ps`、`say-it` 的 README、任务文件和交接文档含历史版本或迁移描述;它们用于追溯,不形成额外消费者。
110
+ - `/Users/henji/Documents/VibeCode` 下的 `ai-roundtable`、`fast-install`、`henji`、`henji-dev`、`henjicc.github.io`、`mySkills`、`test` 未发现 `@henjicc/ai-sdk` 的 manifest、lockfile、源码 import 或构建脚本引用。
111
+ - SDK 自身的测试、fixture、导出检查和发布脚本属于生产者验证,不作为独立消费项目。
112
+
113
+ ## 发布后维护规则
114
+
115
+ 1. SDK 必须先在 `Henji-AI` 完成精确测试、全量/可移植性/包验证与远端回装,再发布。
116
+ 2. 发布后按变更影响逐个同步本清单中的实际消费者;外部消费者必须精确锁版本并核对 lock integrity,再回写 commit、验证状态、边界和日期。
117
+ 3. 只升级消费项目实际使用的按需入口。新增 provider/model 不等于所有宿主都要增加对应 bundle 或业务入口。
118
+ 4. 涉及 provider 身份、endpoint profile、credentialId、transport、media 或协议事件时,逐项检查表中宿主责任边界,禁止在消费方复制 SDK 执行内核。
119
+ 5. 消费项目升级完成后,将验证结果写入对应任务交接;本清单只维护稳定事实,不复制每次执行日志。
@@ -76,7 +76,32 @@ Responses 请求统一下发 `store: false`:助手自己的会话存储仍是
76
76
 
77
77
  所以模型能力目录里的 `input` 记的是"**本项目当前请求路径下真实可用**"的模态,不是模型宣传的模态。DeepSeek 视觉模型已经因 Responses 接通而开放图片;智能助手的视频输入仍会在协议边界明确阻断,不会降级成文本地址造成静默失效。
78
78
 
79
- ## 三、本项目的协议接入优先级(产品决策,不是能力强弱排序)
79
+ ### 2.5 原生 Chat Completions 事件矩阵(2026-09-24 核对)
80
+
81
+ 适用 `llm/streaming` 的 `runLlmChatStream`,与 Responses / Vercel model-step 路径分别验证。来源:[OpenAI 字段契约](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)、[官方 SDK 错误事件处理](https://raw.githubusercontent.com/openai/openai-node/master/src/core/streaming.ts)、[WHATWG SSE 分帧](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation)。字段表构造样本及采集日期见 [chat-stream-lifecycle.json](../../tests/fixtures/llm/chat-stream-lifecycle.json),截断、删除字段和改类型的负例在对应 lifecycle 测试中独立标记。
82
+
83
+ | 事件/状态 | 前置状态与字段契约 | 空值/缺字段语义 | 下一状态 / 对外输出 | 终态与资源 |
84
+ |---|---|---|---|---|
85
+ | SSE 注释、无 data 通知 | 等待/生成;SSE 字段可省略 | 不含结果 | 状态不变,无 token | 继续读取 |
86
+ | 角色/内容增量 | choices 数组;选中项 delta 对象;content 可省略/null/字符串 | 空 content、空 delta 合法;有值但错类型拒绝 | 等待或部分结果;仅非空文本发 token | 继续读取 |
87
+ | reasoning / tool_calls | Chat 兼容扩展;按已接入供应商契约 | 无 content 不代表失败 | 累加推理或工具参数 | 仍需完成标记 |
88
+ | finish_reason | null 表示继续;非空字符串表示模型结束(stop/length/tool_calls/content_filter/function_call 等) | 不能拿空结果、任意对象替代完成标记 | 保存结束原因;length 保持 truncated | 等待 usage / DONE / EOF |
89
+ | usage | choices 可为空数组;usage 对象 | 无文本合法;本身不能证明完成 | 更新用量,无 token | 继续读取 |
90
+ | SSE error / JSON error | 任一读取状态;官方 SDK 将两种形状都作为错误 | 即使前面已收到文字仍失败 | provider error;不发 completed | 取消剩余流,释放 reader |
91
+ | 未知命名事件 | 任一读取状态;保留扩展兼容空间 | 不把扩展通知当完成;含 error 仍失败 | 不输出、不改变完成状态 | 等待已知完成证据 |
92
+ | 无名/message 数据缺 choices 或 delta | 已知 Chat 数据路径 | 必要结果结构缺失,不能按空通知处理 | INVALID_STREAM_RESPONSE | 取消并释放 |
93
+ | DONE / EOF | 已收到有效 finish_reason | 未收到则 STREAM_INCOMPLETE;usage、partial 不能替代 | 完成,或明确失败 | 释放 reader;DONE 取消剩余流 |
94
+ | 智谱 network_error / model_context_window_exceeded | 已开始流 | 前者为推理异常,后者为窗口限制 | 不能把异常当正常 stop | 前者 PROVIDER_STREAM_ERROR;后者 truncated=true,保留部分输出和结束原因 | 不自动重试 |
95
+ | 未完成 SSE 块后 EOF | 空行才派发事件;官方 SSE 不派发残留块 | 不补空行伪造 final | 残块不产生结果;依据此前终态判断 | 释放 reader |
96
+ | 取消 / 读取异常 | 任一非终态 | 不能当正常 EOF | 取消或失败,不调用 completed | 移除监听、取消并释放 reader |
97
+
98
+ 兼容边界:不新增非空文本要求,工具调用、拒绝或空模型结果可以带合法完成原因。官方未承诺所有兼容供应商都发送 DONE,因此 SDK 保留“已收到 finish_reason 后正常 EOF 可完成”的策略;无 finish_reason 的 DONE 不足以证明生成完成。未知命名事件不会自行成功或失败,但无名 Chat 数据必须具有可识别结构。[智谱契约第 6 节](供应商/智谱GLM.md) 允许缺省/空 choices 的状态块,按 provider family 保留该差异,这些块不改变完成状态;choices 错类型仍拒绝。新增此处校验不要求所有网关补齐无关的 id/created/model 元数据。SSE 连接不复用;SDK 不在断流后重放已可能计费的请求。
99
+
100
+ 未知具名扩展通知的 data 可以是普通文本,不强行 JSON 解析;已知 error 事件不论正文格式都失败,合法 Chat 数据仍严格校验 JSON。
101
+
102
+ 回调也是取消边界:同一 Chat 帧同时含思考与正文时,思考回调取消后不得继续发正文;对应受控变异回放进入生命周期测试。
103
+
104
+ ## 三、本项目的协议接入优先级(产品决策,不是能力强弱排序)
80
105
 
81
106
  新写协议适配器、给某个供应商挑协议时,按下面顺序取舍——这是本项目的既定优先级,**不因某家供应商官方推荐哪个协议就单独提高它的顺序**(MiniMax 官方推荐 Anthropic,但本项目仍按下表顺序执行,见 [MiniMax.md 第 6 节](供应商/MiniMax.md)):
82
107
 
@@ -144,7 +144,9 @@ schema 未暴露 `tool_stream`,通用字段说明列出的支持模型也未
144
144
 
145
145
  `finish_reason` 的 schema 枚举列出 `stop`、`length`、`tool_calls`、`sensitive`、`network_error`,
146
146
  但同页字段说明还提到 `model_context_window_exceeded`。这是官方页面内部不一致;解析器必须接受并保留
147
- 未知字符串,不能用封闭枚举拒绝服务器返回。
147
+ 未知字符串,不能用封闭枚举拒绝服务器返回。
148
+
149
+ 2026-09-24 复核:开放字符串不代表所有结束原因都成功。官方明确 `network_error` 为模型推理异常,SDK 原生流必须失败并保留该结束原因,不执行自动付费重放;`model_context_window_exceeded` 表示上下文窗口上限,沿用部分输出但标 `truncated=true`。`stop/tool_calls/sensitive` 及未知扩展字符串继续保留原值,供调用方依据语义处理。对应字段表 fixture 与负例位于 `tests/fixtures/llm/chat-stream-lifecycle.json` 和 `tests/llm/streaming-lifecycle.test.ts`;没有将这些构造样本声称为真实模型返回。
148
150
 
149
151
  ## 7. 结构化输出与流式能力
150
152