@codehz/ai 0.9.0 → 0.9.1

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/dist/index.mjs CHANGED
@@ -5,8 +5,10 @@
5
5
  * KnownAdapterKind 覆盖内置 adapter;
6
6
  * AdapterKind 额外接受任意 string,便于自定义 backend 扩展(无需改库联合类型)。
7
7
  */
8
+ /** 内置 adapter kind 列表。 */
8
9
  const KNOWN_ADAPTER_KINDS = [
9
10
  "chat-completions",
11
+ "delta-completions",
10
12
  "messages",
11
13
  "responses",
12
14
  "ollama",
@@ -34,6 +36,7 @@ const REASONING_LEVEL_SET = new Set(REASONING_LEVELS);
34
36
  *
35
37
  * runtime.errors 与 events 均从此导出,避免手工双表漂移。
36
38
  */
39
+ /** 标准 warning 代码常量;事件和响应均可携带这些代码。 */
37
40
  const WarningCode = {
38
41
  /** replay fidelity 低于预期 */
39
42
  REPLAY_FIDELITY_LOW: "REPLAY_FIDELITY_LOW",
@@ -87,7 +90,7 @@ function streamWarningKey(warning) {
87
90
  }
88
91
  //#endregion
89
92
  //#region src/types/compress.ts
90
- /** 探测 adapter 是否实现 compress(不依赖 kind 硬编码)。 */
93
+ /** 探测 adapter 是否实现上下文压缩能力。 */
91
94
  function supportsContextCompress(adapter) {
92
95
  return typeof adapter.compress === "function";
93
96
  }
@@ -99,11 +102,7 @@ const DEFAULT_INCLUDE = {
99
102
  providerMetadata: "best_effort"
100
103
  };
101
104
  /**
102
- * 归一化请求:
103
- * 1. 合并 defaults
104
- * 2. 填充 include 默认值
105
- * 3. 生成 requestId
106
- * 4. 返回 provider 映射所需的归一化请求
105
+ * 归一化请求:合并 defaults、填充 include 默认值并生成 requestId。
107
106
  */
108
107
  function normalizeRequest(request, options) {
109
108
  const { model, defaults } = options;
@@ -121,6 +120,7 @@ function normalizeRequest(request, options) {
121
120
  }
122
121
  //#endregion
123
122
  //#region src/runtime/client.ts
123
+ /** 创建统一 AI 客户端;请求级字段覆盖 defaults,signal 可在客户端级设置。 */
124
124
  function createAIClient(options) {
125
125
  const { adapter, model, defaults, signal: defaultSignal } = options;
126
126
  return { stream(request) {
@@ -147,6 +147,7 @@ function mergeAbortSignals(...signals) {
147
147
  }
148
148
  //#endregion
149
149
  //#region src/runtime/errors.ts
150
+ /** 所有运行时错误的基类。 */
150
151
  var AIError = class extends Error {
151
152
  code;
152
153
  name;
@@ -220,24 +221,28 @@ var AIRecoverableError = class extends AIError {
220
221
  };
221
222
  //#endregion
222
223
  //#region src/canonical/content.ts
224
+ /** 创建文本内容块。 */
223
225
  function textBlock(text) {
224
226
  return {
225
227
  type: "text",
226
228
  text
227
229
  };
228
230
  }
231
+ /** 创建 JSON 内容块。 */
229
232
  function jsonBlock(json) {
230
233
  return {
231
234
  type: "json",
232
235
  json
233
236
  };
234
237
  }
238
+ /** 创建图片内容块;imageUrl 可为 URL 或 data URL。 */
235
239
  function imageBlock(imageUrl) {
236
240
  return {
237
241
  type: "image",
238
242
  imageUrl
239
243
  };
240
244
  }
245
+ /** 创建 provider 专有 opaque 内容块。 */
241
246
  function opaqueBlock(payload) {
242
247
  return {
243
248
  type: "opaque",
@@ -283,6 +288,7 @@ function coalesceContentBlocks(blocks) {
283
288
  * 简单场景下 replay 与 output 一致。
284
289
  * 复杂场景(需要 opaque continuation)由 adapter 自行扩展。
285
290
  */
291
+ /** 从 output items 构建 replay;opaque continuation 可由 adapter 额外扩展。 */
286
292
  function replayFromOutput(output) {
287
293
  return output.map((item) => {
288
294
  switch (item.type) {
@@ -299,6 +305,7 @@ function replayFromOutput(output) {
299
305
  /**
300
306
  * 从 OutputItem 数组中提取所有 message 类型 item 的文本内容。
301
307
  */
308
+ /** 从 output 中提取所有消息 item 的文本内容。 */
302
309
  function extractText(output) {
303
310
  return output.filter((item) => item.type === "message").flatMap((m) => m.content).filter((b) => b.type === "text").map((b) => b.text).join("");
304
311
  }
@@ -637,6 +644,7 @@ function streamProtocolError(message) {
637
644
  }
638
645
  //#endregion
639
646
  //#region src/stream/collect-stream.ts
647
+ /** 消费完整个事件流并聚合为 AIResponse;适用于不需要逐事件处理的调用方。 */
640
648
  async function collectStream(stream) {
641
649
  const state = createAggregatorState();
642
650
  for await (const event of stream) aggregateEvent(state, event);
@@ -669,9 +677,11 @@ const STOP_REASON_MAP = {
669
677
  MISSING_THOUGHT_SIGNATURE: "error",
670
678
  error: "error"
671
679
  };
680
+ /** 将 provider stop/finish reason 映射为规范化 StopReason。 */
672
681
  function mapStopReason(providerReason) {
673
682
  return STOP_REASON_MAP[providerReason] ?? "unknown";
674
683
  }
684
+ /** 根据 thinking 和 redacted 标记推断 reasoning 可见性。 */
675
685
  function mapReasoningVisibility(hasThinking, hasRedacted) {
676
686
  if (hasRedacted) return "redacted";
677
687
  if (hasThinking) return "full";
@@ -679,6 +689,7 @@ function mapReasoningVisibility(hasThinking, hasRedacted) {
679
689
  }
680
690
  //#endregion
681
691
  //#region src/canonical/items.ts
692
+ /** 创建消息 item;可用 overrides 补充 id、role 和 citations。 */
682
693
  function messageItem(content, overrides) {
683
694
  return {
684
695
  type: "message",
@@ -687,6 +698,7 @@ function messageItem(content, overrides) {
687
698
  content
688
699
  };
689
700
  }
701
+ /** 创建 reasoning item。 */
690
702
  function reasoningItem(content, visibility = "full", id) {
691
703
  return {
692
704
  type: "reasoning",
@@ -695,6 +707,7 @@ function reasoningItem(content, visibility = "full", id) {
695
707
  content
696
708
  };
697
709
  }
710
+ /** 创建客户端工具调用 item。 */
698
711
  function toolCallItem(id, name, argumentsText) {
699
712
  return {
700
713
  type: "tool_call",
@@ -703,6 +716,7 @@ function toolCallItem(id, name, argumentsText) {
703
716
  argumentsText
704
717
  };
705
718
  }
719
+ /** 创建客户端工具结果 item。 */
706
720
  function toolResultItem(callId, toolName, outcome, content) {
707
721
  return {
708
722
  type: "tool_result",
@@ -712,6 +726,7 @@ function toolResultItem(callId, toolName, outcome, content) {
712
726
  content
713
727
  };
714
728
  }
729
+ /** 创建 opaque item,通常用于 provider replay。 */
715
730
  function opaqueItem(source, purpose, payload, id) {
716
731
  return {
717
732
  type: "opaque",
@@ -721,6 +736,7 @@ function opaqueItem(source, purpose, payload, id) {
721
736
  payload
722
737
  };
723
738
  }
739
+ /** 创建 provider 托管工具调用 item。 */
724
740
  function serverToolCallItem(id, tool, overrides) {
725
741
  return {
726
742
  type: "server_tool_call",
@@ -729,6 +745,7 @@ function serverToolCallItem(id, tool, overrides) {
729
745
  ...overrides
730
746
  };
731
747
  }
748
+ /** 创建 provider 托管工具结果 item。 */
732
749
  function serverToolResultItem(callId, tool, outcome, content, overrides) {
733
750
  return {
734
751
  type: "server_tool_result",
@@ -739,6 +756,7 @@ function serverToolResultItem(callId, tool, outcome, content, overrides) {
739
756
  ...overrides
740
757
  };
741
758
  }
759
+ /** 创建 MCP 等 provider 托管工具发现 item。 */
742
760
  function serverToolDiscoveryItem(id, serverLabel, tools, overrides) {
743
761
  return {
744
762
  type: "server_tool_discovery",
@@ -1585,7 +1603,7 @@ function createProviderJsonStreamSession(host) {
1585
1603
  /**
1586
1604
  * HTTP adapter 共享基类与构造选项
1587
1605
  *
1588
- * 收敛五家真实后端 adapter 的:
1606
+ * 收敛真实 HTTP adapter 的:
1589
1607
  * - apiKey / baseUrl / fetch / headers / extraBody 字段赋值
1590
1608
  * - 默认 baseUrl 解析
1591
1609
  * - beginJsonStream:open → batches → complete session
@@ -1923,6 +1941,7 @@ async function* finalizeStreamTurn(session, items, options = {}) {
1923
1941
  */
1924
1942
  const OPAQUE_SOURCE = {
1925
1943
  CHAT_COMPLETIONS: "chat.completions",
1944
+ DELTA_COMPLETIONS: "delta.completions",
1926
1945
  MESSAGES: "messages",
1927
1946
  OLLAMA: "ollama",
1928
1947
  GEMINI: "gemini",
@@ -2169,7 +2188,7 @@ function mapServerTools(serverTools) {
2169
2188
  *
2170
2189
  * 同时服务 stream(/responses)与 compress(/responses/compact)。
2171
2190
  */
2172
- const mapper$7 = new NormalizedRequestMapper("responses");
2191
+ const mapper$8 = new NormalizedRequestMapper("responses");
2173
2192
  /** compact replay opaque:整份 wire output window 保真回传 */
2174
2193
  const RESPONSES_COMPACTED_WINDOW_KIND = "compacted_window";
2175
2194
  function isReplayCanonicalInput(item) {
@@ -2189,13 +2208,13 @@ function readNonEmptyString(value, maxLen = 256) {
2189
2208
  * - user 纯 text/json:仍发 string,避免无谓 shape churn
2190
2209
  */
2191
2210
  function mapResponsesMessageContent(role, blocks, field) {
2192
- if (role !== "user") return mapper$7.textFromBlocks(blocks, field);
2193
- mapper$7.ensureBlocks(blocks, field, [
2211
+ if (role !== "user") return mapper$8.textFromBlocks(blocks, field);
2212
+ mapper$8.ensureBlocks(blocks, field, [
2194
2213
  "text",
2195
2214
  "json",
2196
2215
  "image"
2197
2216
  ], "only text/json/image blocks are supported");
2198
- if (!blocks.some((block) => block.type === "image")) return mapper$7.textFromBlocks(blocks, field);
2217
+ if (!blocks.some((block) => block.type === "image")) return mapper$8.textFromBlocks(blocks, field);
2199
2218
  return blocks.map((block) => {
2200
2219
  if (block.type === "text") return {
2201
2220
  type: "input_text",
@@ -2209,16 +2228,16 @@ function mapResponsesMessageContent(role, blocks, field) {
2209
2228
  type: "input_image",
2210
2229
  image_url: block.imageUrl
2211
2230
  };
2212
- throw new AIRequestError(`${mapper$7.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
2231
+ throw new AIRequestError(`${mapper$8.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
2213
2232
  });
2214
2233
  }
2215
2234
  function mapResponsesToolResultOutput(blocks, field) {
2216
- mapper$7.ensureBlocks(blocks, field, [
2235
+ mapper$8.ensureBlocks(blocks, field, [
2217
2236
  "text",
2218
2237
  "json",
2219
2238
  "image"
2220
2239
  ], "only text/json/image blocks are supported");
2221
- if (!blocks.some((block) => block.type === "image")) return mapper$7.textFromBlocks(blocks, field);
2240
+ if (!blocks.some((block) => block.type === "image")) return mapper$8.textFromBlocks(blocks, field);
2222
2241
  return blocks.map((block) => {
2223
2242
  if (block.type === "text") return {
2224
2243
  type: "input_text",
@@ -2232,11 +2251,11 @@ function mapResponsesToolResultOutput(blocks, field) {
2232
2251
  type: "input_image",
2233
2252
  image_url: block.imageUrl
2234
2253
  };
2235
- throw new AIRequestError(`${mapper$7.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
2254
+ throw new AIRequestError(`${mapper$8.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
2236
2255
  });
2237
2256
  }
2238
2257
  function mapReasoningInput(item, index) {
2239
- const text = mapper$7.textFromBlocks(mapper$7.ensureReasoningBlocks(item.content, "reasoning content"), "reasoning content");
2258
+ const text = mapper$8.textFromBlocks(mapper$8.ensureReasoningBlocks(item.content, "reasoning content"), "reasoning content");
2240
2259
  const id = item.id && item.id.length > 0 ? item.id : `reasoning_replay_${index}`;
2241
2260
  if (item.visibility === "full") return {
2242
2261
  type: "reasoning",
@@ -2346,7 +2365,7 @@ function mapResponsesCore(request, options) {
2346
2365
  usedCompactedWindow
2347
2366
  };
2348
2367
  if (previousResponseId && !usedCompactedWindow) mapped.previousResponseId = previousResponseId;
2349
- if (request.instructions) mapped.instructions = mapper$7.mapInstructions(request.instructions);
2368
+ if (request.instructions) mapped.instructions = mapper$8.mapInstructions(request.instructions);
2350
2369
  return mapped;
2351
2370
  }
2352
2371
  /** 构建 Responses 流式请求体;调用方再 `withExtraBody` 合并构造期扩展字段。 */
@@ -2359,7 +2378,7 @@ function buildResponsesRequest(request, options) {
2359
2378
  };
2360
2379
  if (core.previousResponseId) body.previous_response_id = core.previousResponseId;
2361
2380
  if (core.instructions) body.instructions = core.instructions;
2362
- const functionTools = mapper$7.mapToolsIfPresent(request.tools, (t) => ({
2381
+ const functionTools = mapper$8.mapToolsIfPresent(request.tools, (t) => ({
2363
2382
  type: "function",
2364
2383
  name: t.name,
2365
2384
  description: t.description,
@@ -2368,7 +2387,7 @@ function buildResponsesRequest(request, options) {
2368
2387
  const serverTools = mapServerTools(request.serverTools);
2369
2388
  const tools = [...functionTools, ...serverTools];
2370
2389
  if (tools.length > 0) body.tools = tools;
2371
- body.tool_choice = mapper$7.mapToolChoice(request.toolChoice, {
2390
+ body.tool_choice = mapper$8.mapToolChoice(request.toolChoice, {
2372
2391
  auto: "auto",
2373
2392
  none: "none",
2374
2393
  tool: (name) => ({
@@ -3135,6 +3154,7 @@ function createResponsesSseProcessor(factory) {
3135
3154
  * 编排层:buildRequest + runStream(open → SSE 处理 → complete)。
3136
3155
  * 可选能力:compress → POST /responses/compact(ContextCompressCapable)。
3137
3156
  */
3157
+ /** OpenAI Responses API 适配器;支持原生流式响应和上下文压缩。 */
3138
3158
  var ResponsesAdapter = class extends HttpAdapterBase {
3139
3159
  kind = "responses";
3140
3160
  isSyntheticStream = false;
@@ -3258,7 +3278,7 @@ function parseImageDataUrl(imageUrl) {
3258
3278
  /**
3259
3279
  * MessagesAdapter — request 映射
3260
3280
  */
3261
- const mapper$6 = new NormalizedRequestMapper("messages");
3281
+ const mapper$7 = new NormalizedRequestMapper("messages");
3262
3282
  function isMessagesReplayContentBlock(value) {
3263
3283
  if (!value || typeof value !== "object" || !("type" in value)) return false;
3264
3284
  const block = value;
@@ -3320,33 +3340,33 @@ function canonicalToMessagesBlock(b, field = "canonical mapping") {
3320
3340
  throw new AIRequestError(`messages does not support content block type "${b.type}" in ${field}`, "UNSUPPORTED_CONTENT_BLOCK");
3321
3341
  }
3322
3342
  function mapMessagesUserContent(blocks, field) {
3323
- mapper$6.ensureBlocks(blocks, field, [
3343
+ mapper$7.ensureBlocks(blocks, field, [
3324
3344
  "text",
3325
3345
  "json",
3326
3346
  "image"
3327
3347
  ], "only text/json/image blocks are supported");
3328
3348
  if (!blocks.some((block) => block.type === "image")) {
3329
- const supportedContent = mapper$6.ensureTextBlocks(blocks, field);
3349
+ const supportedContent = mapper$7.ensureTextBlocks(blocks, field);
3330
3350
  if (supportedContent.length === 1 && supportedContent[0]?.type === "text") return supportedContent[0].text;
3331
3351
  return supportedContent.map((block) => canonicalToMessagesBlock(block, field));
3332
3352
  }
3333
3353
  return blocks.map((block) => canonicalToMessagesBlock(block, field));
3334
3354
  }
3335
3355
  function mapMessagesToolResultContent(blocks, field) {
3336
- mapper$6.ensureBlocks(blocks, field, [
3356
+ mapper$7.ensureBlocks(blocks, field, [
3337
3357
  "text",
3338
3358
  "json",
3339
3359
  "image"
3340
3360
  ], "only text/json/image blocks are supported");
3341
- if (!blocks.some((block) => block.type === "image")) return mapper$6.textFromBlocks(blocks, field);
3361
+ if (!blocks.some((block) => block.type === "image")) return mapper$7.textFromBlocks(blocks, field);
3342
3362
  return blocks.map((block) => canonicalToMessagesBlock(block, field));
3343
3363
  }
3344
3364
  function buildMessagesRequest(request, options) {
3345
- mapper$6.assertNoServerTools(request.serverTools);
3365
+ mapper$7.assertNoServerTools(request.serverTools);
3346
3366
  const messages = [];
3347
3367
  let systemPrompt;
3348
3368
  let pendingToolResultMessage;
3349
- if (request.instructions) systemPrompt = mapper$6.mapInstructions(request.instructions);
3369
+ if (request.instructions) systemPrompt = mapper$7.mapInstructions(request.instructions);
3350
3370
  for (const item of request.input) {
3351
3371
  if (item.type !== "tool_result") pendingToolResultMessage = void 0;
3352
3372
  switch (item.type) {
@@ -3359,7 +3379,7 @@ function buildMessagesRequest(request, options) {
3359
3379
  });
3360
3380
  break;
3361
3381
  }
3362
- const supportedContent = mapper$6.ensureTextBlocks(item.content, field);
3382
+ const supportedContent = mapper$7.ensureTextBlocks(item.content, field);
3363
3383
  if (supportedContent.length === 1 && supportedContent[0]?.type === "text") messages.push({
3364
3384
  role: "assistant",
3365
3385
  content: supportedContent[0].text
@@ -3376,7 +3396,7 @@ function buildMessagesRequest(request, options) {
3376
3396
  type: "tool_use",
3377
3397
  id: item.id,
3378
3398
  name: item.name,
3379
- input: mapper$6.parseToolArguments(item)
3399
+ input: mapper$7.parseToolArguments(item)
3380
3400
  };
3381
3401
  if (lastMsg && lastMsg.role === "assistant" && typeof lastMsg.content !== "string") lastMsg.content.push(toolBlock);
3382
3402
  else messages.push({
@@ -3406,7 +3426,7 @@ function buildMessagesRequest(request, options) {
3406
3426
  case "reasoning": {
3407
3427
  const block = {
3408
3428
  type: "thinking",
3409
- thinking: contentBlocksToText(mapper$6.ensureReasoningBlocks(item.content, "reasoning content"))
3429
+ thinking: contentBlocksToText(mapper$7.ensureReasoningBlocks(item.content, "reasoning content"))
3410
3430
  };
3411
3431
  const lastMsg = messages[messages.length - 1];
3412
3432
  if (lastMsg && lastMsg.role === "assistant" && typeof lastMsg.content !== "string") lastMsg.content.push(block);
@@ -3421,7 +3441,7 @@ function buildMessagesRequest(request, options) {
3421
3441
  if (!payload) break;
3422
3442
  if (payload.role === "assistant" && "content" in payload) {
3423
3443
  assertMessagesReplayContent(payload.content);
3424
- mapper$6.rollbackTrailingAssistantMessages(messages);
3444
+ mapper$7.rollbackTrailingAssistantMessages(messages);
3425
3445
  messages.push({
3426
3446
  role: "assistant",
3427
3447
  content: payload.content
@@ -3438,12 +3458,12 @@ function buildMessagesRequest(request, options) {
3438
3458
  stream: true
3439
3459
  };
3440
3460
  if (systemPrompt) body.system = systemPrompt;
3441
- body.tools = mapper$6.mapToolsIfPresent(request.tools, (t) => ({
3461
+ body.tools = mapper$7.mapToolsIfPresent(request.tools, (t) => ({
3442
3462
  name: t.name,
3443
3463
  description: t.description,
3444
3464
  input_schema: t.inputSchema
3445
3465
  }));
3446
- body.tool_choice = mapper$6.mapToolChoice(request.toolChoice, {
3466
+ body.tool_choice = mapper$7.mapToolChoice(request.toolChoice, {
3447
3467
  auto: { type: "auto" },
3448
3468
  none: { type: "none" },
3449
3469
  tool: (name) => ({
@@ -3705,6 +3725,7 @@ async function* mapMessagesStream(host, providerRequest, factory, request) {
3705
3725
  *
3706
3726
  * 编排层:buildRequest + runStream(委托 map-request / map-stream)。
3707
3727
  */
3728
+ /** Anthropic Messages API 适配器。 */
3708
3729
  var MessagesAdapter = class extends HttpAdapterBase {
3709
3730
  kind = "messages";
3710
3731
  isSyntheticStream = false;
@@ -3747,7 +3768,7 @@ const REASONING_FIELDS = ["reasoning_content", "reasoning"];
3747
3768
  /**
3748
3769
  * ChatCompletionsAdapter — request 映射
3749
3770
  */
3750
- const mapper$4 = new NormalizedRequestMapper("chat-completions");
3771
+ const mapper$5 = new NormalizedRequestMapper("chat-completions");
3751
3772
  function extractReasoningText(value) {
3752
3773
  if (typeof value === "string") return value;
3753
3774
  if (Array.isArray(value)) return value.map(extractReasoningText).join("");
@@ -3838,7 +3859,7 @@ function buildAssistantReplayMessage(params) {
3838
3859
  return replayMessage;
3839
3860
  }
3840
3861
  function mapChatUserContent(blocks, field) {
3841
- mapper$4.ensureBlocks(blocks, field, [
3862
+ mapper$5.ensureBlocks(blocks, field, [
3842
3863
  "text",
3843
3864
  "json",
3844
3865
  "image"
@@ -3857,16 +3878,16 @@ function mapChatUserContent(blocks, field) {
3857
3878
  type: "image_url",
3858
3879
  image_url: { url: block.imageUrl }
3859
3880
  };
3860
- throw new AIRequestError(`${mapper$4.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
3881
+ throw new AIRequestError(`${mapper$5.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
3861
3882
  });
3862
3883
  }
3863
3884
  function mapChatToolResultContent(blocks, field) {
3864
- mapper$4.ensureBlocks(blocks, field, [
3885
+ mapper$5.ensureBlocks(blocks, field, [
3865
3886
  "text",
3866
3887
  "json",
3867
3888
  "image"
3868
3889
  ], "only text/json/image blocks are supported");
3869
- if (!blocks.some((block) => block.type === "image")) return mapper$4.textFromBlocks(blocks, field);
3890
+ if (!blocks.some((block) => block.type === "image")) return mapper$5.textFromBlocks(blocks, field);
3870
3891
  return blocks.map((block) => {
3871
3892
  if (block.type === "text") return {
3872
3893
  type: "text",
@@ -3880,19 +3901,19 @@ function mapChatToolResultContent(blocks, field) {
3880
3901
  type: "image_url",
3881
3902
  image_url: { url: block.imageUrl }
3882
3903
  };
3883
- throw new AIRequestError(`${mapper$4.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
3904
+ throw new AIRequestError(`${mapper$5.kind} does not support ${field} block of type "${block.type}"; only text/json/image blocks are supported`, "UNSUPPORTED_CONTENT_BLOCK");
3884
3905
  });
3885
3906
  }
3886
3907
  function mapChatMessageContent(role, blocks, field) {
3887
3908
  if (role === "user") return mapChatUserContent(blocks, field);
3888
- return mapper$4.textFromBlocks(blocks, field) || null;
3909
+ return mapper$5.textFromBlocks(blocks, field) || null;
3889
3910
  }
3890
3911
  function buildChatCompletionsRequest(request, options) {
3891
- mapper$4.assertNoServerTools(request.serverTools);
3912
+ mapper$5.assertNoServerTools(request.serverTools);
3892
3913
  const messages = [];
3893
3914
  if (request.instructions) messages.push({
3894
3915
  role: "system",
3895
- content: mapper$4.mapInstructions(request.instructions)
3916
+ content: mapper$5.mapInstructions(request.instructions)
3896
3917
  });
3897
3918
  for (const item of request.input) switch (item.type) {
3898
3919
  case "message": {
@@ -3932,7 +3953,7 @@ function buildChatCompletionsRequest(request, options) {
3932
3953
  case "reasoning":
3933
3954
  messages.push({
3934
3955
  role: "assistant",
3935
- content: mapper$4.textFromBlocks(item.content, "reasoning content")
3956
+ content: mapper$5.textFromBlocks(item.content, "reasoning content")
3936
3957
  });
3937
3958
  break;
3938
3959
  case "opaque": {
@@ -3940,7 +3961,7 @@ function buildChatCompletionsRequest(request, options) {
3940
3961
  if (!payload) break;
3941
3962
  if ("messages" in payload) {
3942
3963
  assertChatReplayMessages(payload.messages, "messages");
3943
- mapper$4.rollbackTrailingAssistantMessages(messages);
3964
+ mapper$5.rollbackTrailingAssistantMessages(messages);
3944
3965
  for (const m of payload.messages) messages.push(m);
3945
3966
  }
3946
3967
  break;
@@ -3952,8 +3973,8 @@ function buildChatCompletionsRequest(request, options) {
3952
3973
  stream: true,
3953
3974
  n: 1
3954
3975
  };
3955
- body.tools = mapper$4.mapToolsIfPresent(request.tools, mapOpenAiFunctionTool);
3956
- body.tool_choice = mapper$4.mapToolChoice(request.toolChoice, {
3976
+ body.tools = mapper$5.mapToolsIfPresent(request.tools, mapOpenAiFunctionTool);
3977
+ body.tool_choice = mapper$5.mapToolChoice(request.toolChoice, {
3957
3978
  auto: "auto",
3958
3979
  none: "none",
3959
3980
  tool: (name) => ({
@@ -4188,6 +4209,7 @@ async function* mapChatCompletionsStream(host, providerRequest, factory, request
4188
4209
  *
4189
4210
  * 编排层:buildRequest + runStream(委托 map-request / map-stream)。
4190
4211
  */
4212
+ /** OpenAI Chat Completions API 适配器。 */
4191
4213
  var ChatCompletionsAdapter = class extends HttpAdapterBase {
4192
4214
  kind = "chat-completions";
4193
4215
  isSyntheticStream = false;
@@ -4208,6 +4230,188 @@ var ChatCompletionsAdapter = class extends HttpAdapterBase {
4208
4230
  }
4209
4231
  };
4210
4232
  //#endregion
4233
+ //#region src/adapters/delta-completions/map-request.ts
4234
+ /**
4235
+ * DeltaCompletionsAdapter — request 映射
4236
+ */
4237
+ const mapper$4 = new NormalizedRequestMapper("delta-completions");
4238
+ function isDeltaReplayMessage(value) {
4239
+ if (!value || typeof value !== "object") return false;
4240
+ const msg = value;
4241
+ const role = msg.role;
4242
+ return (role === "system" || role === "user" || role === "assistant") && typeof msg.content === "string";
4243
+ }
4244
+ function assertDeltaReplayMessages(messages, field) {
4245
+ if (!Array.isArray(messages)) throw new AIRequestError(`Invalid opaque replay payload: ${field} must be an array`, "INVALID_OPAQUE_REPLAY");
4246
+ for (let i = 0; i < messages.length; i++) if (!isDeltaReplayMessage(messages[i])) throw new AIRequestError(`Invalid opaque replay payload: ${field}[${i}] is not a valid delta-completions text message`, "INVALID_OPAQUE_REPLAY");
4247
+ }
4248
+ function assertTextOnlyRequest(request) {
4249
+ mapper$4.assertNoServerTools(request.serverTools);
4250
+ if (request.tools && request.tools.length > 0) throw new AIRequestError("delta-completions does not support tools; use ChatCompletionsAdapter", "UNSUPPORTED_TOOL");
4251
+ if (request.toolChoice) throw new AIRequestError("delta-completions does not support toolChoice; use ChatCompletionsAdapter", "UNSUPPORTED_TOOL");
4252
+ }
4253
+ function buildDeltaCompletionsRequest(request, options) {
4254
+ assertTextOnlyRequest(request);
4255
+ const messages = [];
4256
+ if (request.instructions) messages.push({
4257
+ role: "system",
4258
+ content: mapper$4.mapInstructions(request.instructions)
4259
+ });
4260
+ for (const item of request.input) switch (item.type) {
4261
+ case "message":
4262
+ messages.push({
4263
+ role: item.role,
4264
+ content: mapper$4.textFromBlocks(item.content, `input message (${item.role}) content`)
4265
+ });
4266
+ break;
4267
+ case "reasoning":
4268
+ messages.push({
4269
+ role: "assistant",
4270
+ content: mapper$4.textFromBlocks(item.content, "reasoning content")
4271
+ });
4272
+ break;
4273
+ case "opaque": {
4274
+ const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.DELTA_COMPLETIONS, { maxBytes: options?.maxOpaquePayloadBytes });
4275
+ if (!payload) break;
4276
+ if ("messages" in payload) {
4277
+ assertDeltaReplayMessages(payload.messages, "messages");
4278
+ mapper$4.rollbackTrailingAssistantMessages(messages);
4279
+ for (const m of payload.messages) messages.push({
4280
+ role: m.role,
4281
+ content: m.content
4282
+ });
4283
+ }
4284
+ break;
4285
+ }
4286
+ case "tool_call":
4287
+ case "tool_result":
4288
+ case "server_tool_call":
4289
+ case "server_tool_result":
4290
+ case "server_tool_discovery": throw new AIRequestError(`delta-completions does not support input item type "${item.type}"; use ChatCompletionsAdapter`, "UNSUPPORTED_TOOL");
4291
+ }
4292
+ const body = {
4293
+ model: request.model,
4294
+ messages,
4295
+ stream: true
4296
+ };
4297
+ if (request.temperature !== void 0) body.temperature = request.temperature;
4298
+ if (request.maxOutputTokens !== void 0) body.max_tokens = request.maxOutputTokens;
4299
+ return body;
4300
+ }
4301
+ //#endregion
4302
+ //#region src/adapters/delta-completions/map-stream.ts
4303
+ /**
4304
+ * DeltaCompletionsAdapter — stream 映射
4305
+ *
4306
+ * 完成信号是 SSE 结束,不依赖 finish_reason / index / id。
4307
+ */
4308
+ async function* mapDeltaCompletionsStream(host, providerRequest, factory, request) {
4309
+ const session = host.beginJsonStream(factory, request);
4310
+ const { gate } = session;
4311
+ const items = createStreamingItemSession(factory);
4312
+ if (request.metadata) yield factory.responseWarning("Request metadata is not supported by the Delta Completions adapter", WarningCode.UNSUPPORTED_METADATA);
4313
+ if (request.reasoningLevel !== void 0) yield factory.responseWarning("Request reasoningLevel is not supported by the Delta Completions adapter", WarningCode.CAPABILITY_DOWNGRADE);
4314
+ const headers = { "Content-Type": "application/json" };
4315
+ if (host.apiKey) headers.Authorization = `Bearer ${host.apiKey}`;
4316
+ await session.open({
4317
+ url: `${host.baseUrl}/chat/completions`,
4318
+ headers: host.mergeHeaders(headers),
4319
+ body: providerRequest
4320
+ });
4321
+ const parser = createDataLineSseParser();
4322
+ let responseId;
4323
+ let accumulatedContent = "";
4324
+ let currentMessageId = "";
4325
+ let hasMessageStarted = false;
4326
+ let lastFinishReason;
4327
+ let warnedExtraChoices = false;
4328
+ let warnedIgnoredDelta = false;
4329
+ const ensureMessageStarted = function* (idSeed) {
4330
+ if (!currentMessageId) currentMessageId = `msg-${idSeed}`;
4331
+ const started = items.ensureMessageStarted(currentMessageId);
4332
+ if (started) {
4333
+ hasMessageStarted = true;
4334
+ yield started;
4335
+ }
4336
+ };
4337
+ for await (const batch of session.batches({
4338
+ parser,
4339
+ providerLabel: "Delta Completions",
4340
+ transportLabel: "SSE event(s)",
4341
+ incompleteMessage: "Stream ended with an incomplete Delta Completions SSE frame"
4342
+ })) {
4343
+ for (const warning of batch.warnings) yield warning;
4344
+ for (const chunk of batch.items) {
4345
+ if (chunk === null || typeof chunk !== "object") continue;
4346
+ if ("id" in chunk && typeof chunk.id === "string" && chunk.id) responseId = chunk.id;
4347
+ if (!("choices" in chunk) || !Array.isArray(chunk.choices) || chunk.choices.length === 0) continue;
4348
+ if (chunk.choices.length > 1 && !warnedExtraChoices) {
4349
+ yield factory.responseWarning("Delta Completions returned multiple choices; only the first array element is read. This adapter ignores choice.index.", WarningCode.MULTIPLE_CHOICES_IGNORED);
4350
+ warnedExtraChoices = true;
4351
+ }
4352
+ const choice = chunk.choices[0];
4353
+ if (choice === null || typeof choice !== "object") continue;
4354
+ if ("finish_reason" in choice && typeof choice.finish_reason === "string" && choice.finish_reason) lastFinishReason = choice.finish_reason;
4355
+ if (!("delta" in choice) || choice.delta === null || typeof choice.delta !== "object") continue;
4356
+ const delta = choice.delta;
4357
+ if (!warnedIgnoredDelta && ("tool_calls" in delta || "function_call" in delta || "reasoning" in delta || "reasoning_content" in delta)) {
4358
+ yield factory.responseWarning("Delta Completions ignored non-content delta fields (tool_calls / function_call / reasoning); use ChatCompletionsAdapter for those", WarningCode.CAPABILITY_DOWNGRADE);
4359
+ warnedIgnoredDelta = true;
4360
+ }
4361
+ if (!("content" in delta) || typeof delta.content !== "string" || !delta.content) continue;
4362
+ yield* ensureMessageStarted(responseId ?? request.requestId);
4363
+ accumulatedContent += delta.content;
4364
+ yield items.deltaMessage(currentMessageId, textBlock(delta.content));
4365
+ }
4366
+ }
4367
+ if (gate.completed) return;
4368
+ if (hasMessageStarted && items.isActive(currentMessageId)) yield items.completeMessage(currentMessageId);
4369
+ const stopReason = lastFinishReason ? mapStopReason(lastFinishReason) : "end_turn";
4370
+ const assistantReplayMessage = accumulatedContent.length > 0 ? {
4371
+ role: "assistant",
4372
+ content: accumulatedContent
4373
+ } : null;
4374
+ yield* finalizeStreamTurn(session, items, {
4375
+ stopReason,
4376
+ rawResponseId: responseId ?? request.requestId,
4377
+ factory,
4378
+ maxOpaquePayloadBytes: host.maxOpaquePayloadBytes,
4379
+ opaque: assistantReplayMessage ? opaqueItem(OPAQUE_SOURCE.DELTA_COMPLETIONS, "replay", {
4380
+ replaceCanonical: true,
4381
+ messages: [assistantReplayMessage]
4382
+ }) : null
4383
+ });
4384
+ }
4385
+ //#endregion
4386
+ //#region src/adapters/delta-completions/adapter.ts
4387
+ /**
4388
+ * DeltaCompletionsAdapter
4389
+ *
4390
+ * 残缺 OpenAI 兼容 SSE:只读 choices[0].delta.content,以流结束为完成。
4391
+ * 不要用本 adapter 对接完整 chat.completions。
4392
+ */
4393
+ /** 残缺 chat/completions SSE 适配器。 */
4394
+ var DeltaCompletionsAdapter = class extends HttpAdapterBase {
4395
+ kind = "delta-completions";
4396
+ isSyntheticStream = false;
4397
+ constructor(options) {
4398
+ if (!options.baseUrl) throw new AIRequestError("DeltaCompletionsAdapter requires baseUrl; it does not default to the OpenAI API", "ADAPTER_OPTIONS_INVALID");
4399
+ super(options, { baseUrl: options.baseUrl });
4400
+ }
4401
+ buildRequest(request) {
4402
+ return this.withExtraBody(buildDeltaCompletionsRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
4403
+ }
4404
+ async *runStream(providerRequest, factory, request) {
4405
+ yield* mapDeltaCompletionsStream({
4406
+ beginJsonStream: this.beginJsonStream.bind(this),
4407
+ baseUrl: this.baseUrl,
4408
+ apiKey: this.apiKey,
4409
+ mergeHeaders: this.mergeHeaders.bind(this),
4410
+ maxOpaquePayloadBytes: this.maxOpaquePayloadBytes
4411
+ }, providerRequest, factory, request);
4412
+ }
4413
+ };
4414
+ //#endregion
4211
4415
  //#region src/adapters/ollama/map-request.ts
4212
4416
  /**
4213
4417
  * OllamaAdapter — request 映射
@@ -4485,6 +4689,7 @@ async function* mapOllamaStream(host, providerRequest, factory, request) {
4485
4689
  *
4486
4690
  * 编排层:buildRequest + runStream(委托 map-request / map-stream)。
4487
4691
  */
4692
+ /** Ollama Chat API 适配器。 */
4488
4693
  var OllamaAdapter = class extends HttpAdapterBase {
4489
4694
  kind = "ollama";
4490
4695
  isSyntheticStream = false;
@@ -4826,6 +5031,7 @@ async function* mapGeminiStream(host, providerRequest, factory, request) {
4826
5031
  *
4827
5032
  * 编排层:buildRequest + runStream(委托 map-request / map-stream)。
4828
5033
  */
5034
+ /** Google Gemini generateContent API 适配器。 */
4829
5035
  var GeminiAdapter = class extends HttpAdapterBase {
4830
5036
  kind = "gemini";
4831
5037
  isSyntheticStream = false;
@@ -4850,6 +5056,7 @@ var GeminiAdapter = class extends HttpAdapterBase {
4850
5056
  /**
4851
5057
  * Mock 流式包装与事件发射
4852
5058
  */
5059
+ /** 为静态 Mock handler 添加可配置的文本/参数流式切分。 */
4853
5060
  function withMockStreaming(handler, options) {
4854
5061
  const defaults = resolveMockTextStreamOptions(options, "mock stream wrapper");
4855
5062
  if (!defaults) throw new AIRequestError("mock stream wrapper requires streaming options", "MOCK_STREAM_CONFIG_INVALID");
@@ -5045,6 +5252,7 @@ async function sleep(ms) {
5045
5252
  *
5046
5253
  * 这不是通用“假模型”,而是测试工具调用编排与错误路径的测试夹具。
5047
5254
  */
5255
+ /** 用于测试和本地场景的确定性模拟适配器。 */
5048
5256
  var MockAdapter = class extends AdapterBase {
5049
5257
  kind = "mock";
5050
5258
  isSyntheticStream = true;
@@ -5226,6 +5434,7 @@ function cloneItem(item) {
5226
5434
  /**
5227
5435
  * Mock 请求期望校验
5228
5436
  */
5437
+ /** 按 MockRequestExpectation 校验规范化请求;失败时抛出断言错误。 */
5229
5438
  function assertMockRequest(request, expectation, context) {
5230
5439
  const prefix = `MockAdapter turn ${context.turnIndex + 1} expectation failed`;
5231
5440
  if (expectation.minItems !== void 0 && request.input.length < expectation.minItems) throw new AIRequestError(`${prefix}: expected at least ${expectation.minItems} input item(s)`, "MOCK_EXPECTATION_FAILED");
@@ -5244,6 +5453,7 @@ function assertMockRequest(request, expectation, context) {
5244
5453
  if (expectation.items && expectation.items.length > 0) if (expectation.ordered) assertOrderedItems(request.input, expectation.items, prefix);
5245
5454
  else assertUnorderedItems(request.input, expectation.items, prefix);
5246
5455
  }
5456
+ /** 断言上一轮 replay 按顺序包含在当前输入中。 */
5247
5457
  function assertReplayIncluded(input, replay, prefix) {
5248
5458
  const fingerprints = input.map(fingerprintItem);
5249
5459
  let cursor = 0;
@@ -5254,6 +5464,7 @@ function assertReplayIncluded(input, replay, prefix) {
5254
5464
  cursor = foundIndex + 1;
5255
5465
  }
5256
5466
  }
5467
+ /** 按顺序匹配输入 item。 */
5257
5468
  function assertOrderedItems(input, expectations, prefix) {
5258
5469
  let cursor = 0;
5259
5470
  for (const expected of expectations) {
@@ -5270,9 +5481,11 @@ function assertOrderedItems(input, expectations, prefix) {
5270
5481
  if (!matched) throw new AIRequestError(`${prefix}: missing ordered input item ${describeExpectation(expected)}`, "MOCK_EXPECTATION_FAILED");
5271
5482
  }
5272
5483
  }
5484
+ /** 无序匹配输入 item。 */
5273
5485
  function assertUnorderedItems(input, expectations, prefix) {
5274
5486
  for (const expected of expectations) if (!input.some((item) => matchesItemExpectation(item, expected))) throw new AIRequestError(`${prefix}: missing input item ${describeExpectation(expected)}`, "MOCK_EXPECTATION_FAILED");
5275
5487
  }
5488
+ /** 判断输入 item 是否满足指定条件。 */
5276
5489
  function matchesItemExpectation(item, expected) {
5277
5490
  if (item.type !== expected.type) return false;
5278
5491
  if (expected.id !== void 0 && "id" in item && item.id !== expected.id) return false;
@@ -5287,6 +5500,7 @@ function matchesItemExpectation(item, expected) {
5287
5500
  case "opaque": return (expected.source === void 0 || item.source === expected.source) && (expected.purpose === void 0 || item.purpose === expected.purpose);
5288
5501
  }
5289
5502
  }
5503
+ /** 判断内容块文本是否包含指定片段。 */
5290
5504
  function matchesText(blocks, textIncludes) {
5291
5505
  if (textIncludes === void 0) return true;
5292
5506
  return blocks.some((block) => {
@@ -5295,9 +5509,11 @@ function matchesText(blocks, textIncludes) {
5295
5509
  return false;
5296
5510
  });
5297
5511
  }
5512
+ /** 为 item 生成稳定的 JSON 指纹。 */
5298
5513
  function fingerprintItem(item) {
5299
5514
  return JSON.stringify(item);
5300
5515
  }
5516
+ /** 将 Mock 断言条件格式化为可读文本。 */
5301
5517
  function describeExpectation(expectation) {
5302
5518
  const parts = [`type=${expectation.type}`];
5303
5519
  if (expectation.role) parts.push(`role=${expectation.role}`);
@@ -5308,6 +5524,6 @@ function describeExpectation(expectation) {
5308
5524
  return `{ ${parts.join(", ")} }`;
5309
5525
  }
5310
5526
  //#endregion
5311
- export { AIError, AIMappingError, AIProviderError, AIRecoverableError, AIRequestError, AIStreamError, ChatCompletionsAdapter, GeminiAdapter, KNOWN_ADAPTER_KINDS, MessagesAdapter, MockAdapter, OllamaAdapter, REASONING_LEVELS, REASONING_LEVEL_SET, ResponsesAdapter, WarningCode, assertMockRequest, blockToText, coalesceContentBlocks, collectStream, contentBlocksToText, createAIClient, extractText, imageBlock, jsonBlock, mapReasoningVisibility, mapStopReason, messageItem, opaqueBlock, opaqueItem, reasoningItem, replayFromOutput, serverToolCallItem, serverToolDiscoveryItem, serverToolResultItem, streamWarningKey, supportsContextCompress, textBlock, toolCallItem, toolResultItem, withMockStreaming };
5527
+ export { AIError, AIMappingError, AIProviderError, AIRecoverableError, AIRequestError, AIStreamError, ChatCompletionsAdapter, DeltaCompletionsAdapter, GeminiAdapter, KNOWN_ADAPTER_KINDS, MessagesAdapter, MockAdapter, OllamaAdapter, REASONING_LEVELS, REASONING_LEVEL_SET, ResponsesAdapter, WarningCode, assertMockRequest, blockToText, coalesceContentBlocks, collectStream, contentBlocksToText, createAIClient, extractText, imageBlock, jsonBlock, mapReasoningVisibility, mapStopReason, messageItem, opaqueBlock, opaqueItem, reasoningItem, replayFromOutput, serverToolCallItem, serverToolDiscoveryItem, serverToolResultItem, streamWarningKey, supportsContextCompress, textBlock, toolCallItem, toolResultItem, withMockStreaming };
5312
5528
 
5313
5529
  //# sourceMappingURL=index.mjs.map