@codehz/ai 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -63,12 +63,7 @@ const WarningCode = {
63
63
  };
64
64
  //#endregion
65
65
  //#region src/core/validation.ts
66
- const MESSAGE_ROLES = /* @__PURE__ */ new Set([
67
- "user",
68
- "assistant",
69
- "system",
70
- "developer"
71
- ]);
66
+ const MESSAGE_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
72
67
  const REASONING_VISIBILITIES = /* @__PURE__ */ new Set([
73
68
  "full",
74
69
  "summary",
@@ -122,6 +117,19 @@ function validateContentArray(content, field, issues, code) {
122
117
  }
123
118
  for (let i = 0; i < content.length; i++) validateContentBlock(content[i], `${field}[${i}]`, issues);
124
119
  }
120
+ function validateInstructionArray(content, field, issues) {
121
+ if (!Array.isArray(content)) {
122
+ pushIssue(issues, field, "INSTRUCTIONS_INVALID", `${field} must be an InstructionBlock[]`);
123
+ return;
124
+ }
125
+ for (let i = 0; i < content.length; i++) {
126
+ const block = content[i];
127
+ const blockField = `${field}[${i}]`;
128
+ validateContentBlock(block, blockField, issues);
129
+ if (!isRecord(block) || typeof block.type !== "string") continue;
130
+ if (block.type !== "text" && block.type !== "json") pushIssue(issues, blockField, "INSTRUCTIONS_INVALID", `${blockField} only supports text/json blocks`);
131
+ }
132
+ }
125
133
  function validateInputItem(item, field, issues) {
126
134
  if (!isRecord(item)) {
127
135
  pushIssue(issues, field, "INPUT_INVALID_ITEM", `${field} must be a valid InputItem`);
@@ -192,8 +200,8 @@ function validateToolChoice(toolChoice, issues) {
192
200
  */
193
201
  function validateRequest(request) {
194
202
  const issues = [];
195
- if (request.instructions !== void 0) if (typeof request.instructions === "string") {} else if (Array.isArray(request.instructions)) validateContentArray(request.instructions, "instructions", issues, "INSTRUCTIONS_INVALID");
196
- else pushIssue(issues, "instructions", "INSTRUCTIONS_INVALID", "instructions must be a string or ContentBlock[]");
203
+ if (request.instructions !== void 0) if (typeof request.instructions === "string") {} else if (Array.isArray(request.instructions)) validateInstructionArray(request.instructions, "instructions", issues);
204
+ else pushIssue(issues, "instructions", "INSTRUCTIONS_INVALID", "instructions must be a string or InstructionBlock[]");
197
205
  if (!Array.isArray(request.input) || request.input.length === 0) pushIssue(issues, "input", "INPUT_EMPTY", "input must be a non-empty array");
198
206
  if (Array.isArray(request.input)) for (let i = 0; i < request.input.length; i++) validateInputItem(request.input[i], `input[${i}]`, issues);
199
207
  if (request.temperature !== void 0) {
@@ -713,7 +721,7 @@ function contentBlocksToText(blocks) {
713
721
  return blocks.map(blockToText).join("\n");
714
722
  }
715
723
  /**
716
- * 将 instructions(string | ContentBlock[])归一化为纯文本。
724
+ * 将 instructions(string | InstructionBlock[])归一化为纯文本。
717
725
  */
718
726
  function instructionsToText(instructions) {
719
727
  return typeof instructions === "string" ? instructions : contentBlocksToText(instructions);
@@ -1380,6 +1388,10 @@ function parseMessagesSSE(chunk) {
1380
1388
  function rollbackTrailingAssistantMessages$2(messages) {
1381
1389
  while (messages.length > 0 && messages[messages.length - 1]?.role === "assistant") messages.pop();
1382
1390
  }
1391
+ /** 用 response 级别的命名空间合成 content block 的 item ID,避免多轮工具循环 ID 碰撞 */
1392
+ function synthesizeItemId(kind, blockIndex, responseId) {
1393
+ return `${kind}-${blockIndex}-${responseId}`;
1394
+ }
1383
1395
  function parseToolUseInput(input) {
1384
1396
  try {
1385
1397
  const parsed = JSON.parse(input);
@@ -1447,11 +1459,6 @@ var MessagesAdapter = class extends AdapterBase {
1447
1459
  if (request.instructions) systemPrompt = instructionsToMessagesText(request.instructions);
1448
1460
  for (const item of request.input) switch (item.type) {
1449
1461
  case "message": {
1450
- if (item.role === "system" || item.role === "developer") {
1451
- const text = contentBlocksToText(ensureMessagesTextBlocks(item.content, `input message (${item.role}) content`));
1452
- systemPrompt = systemPrompt ? `${systemPrompt}\n${text}` : text;
1453
- break;
1454
- }
1455
1462
  const role = item.role === "user" ? "user" : "assistant";
1456
1463
  const supportedContent = ensureMessagesTextBlocks(item.content, `input message (${item.role}) content`);
1457
1464
  if (supportedContent.length === 1 && supportedContent[0]?.type === "text") messages.push({
@@ -1581,7 +1588,7 @@ var MessagesAdapter = class extends AdapterBase {
1581
1588
  let argsBuffer = "";
1582
1589
  let stopReason;
1583
1590
  let stopSequence;
1584
- let rawResponseId;
1591
+ let rawResponseId = "";
1585
1592
  if (request.include?.providerMetadata !== "off") {
1586
1593
  const headerMetadata = pickProviderHeaders(response.headers);
1587
1594
  auxiliary.recordProviderMetadata("header", Object.keys(headerMetadata).length > 0 ? { headers: headerMetadata } : void 0);
@@ -1618,14 +1625,14 @@ var MessagesAdapter = class extends AdapterBase {
1618
1625
  switch (block.type) {
1619
1626
  case "text":
1620
1627
  currentItemType = "message";
1621
- currentItemId = `msg-${block.type}-${currentContentBlockIndex}`;
1628
+ currentItemId = synthesizeItemId("msg", currentContentBlockIndex, rawResponseId);
1622
1629
  textBuffer = "";
1623
1630
  yield factory.messageStarted(currentItemId);
1624
1631
  break;
1625
1632
  case "thinking":
1626
1633
  hasStreamedReasoning = true;
1627
1634
  currentItemType = "reasoning";
1628
- currentItemId = `reason-${currentContentBlockIndex}`;
1635
+ currentItemId = synthesizeItemId("reason", currentContentBlockIndex, rawResponseId);
1629
1636
  currentThinkingVisibility = "full";
1630
1637
  thinkingBuffer = "";
1631
1638
  yield factory.reasoningStarted(currentItemId, "full");
@@ -1633,7 +1640,7 @@ var MessagesAdapter = class extends AdapterBase {
1633
1640
  case "redacted_thinking": {
1634
1641
  hasStreamedReasoning = true;
1635
1642
  currentItemType = "reasoning";
1636
- currentItemId = `reason-redacted-${currentContentBlockIndex}`;
1643
+ currentItemId = synthesizeItemId("reason-redacted", currentContentBlockIndex, rawResponseId);
1637
1644
  currentThinkingVisibility = "redacted";
1638
1645
  const data = block.data;
1639
1646
  yield factory.reasoningStarted(currentItemId, "redacted");
@@ -1899,7 +1906,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
1899
1906
  }
1900
1907
  for (const item of request.input) switch (item.type) {
1901
1908
  case "message": {
1902
- const role = item.role === "developer" ? "system" : item.role === "system" ? "system" : item.role === "user" ? "user" : "assistant";
1909
+ const role = item.role;
1903
1910
  const text = contentBlocksToChatText(item.content, `input message (${item.role}) content`);
1904
1911
  messages.push({
1905
1912
  role,
@@ -2289,7 +2296,7 @@ var OllamaAdapter = class extends AdapterBase {
2289
2296
  });
2290
2297
  for (const item of request.input) switch (item.type) {
2291
2298
  case "message": {
2292
- const role = item.role === "developer" ? "system" : item.role === "system" ? "system" : item.role === "user" ? "user" : "assistant";
2299
+ const role = item.role;
2293
2300
  messages.push({
2294
2301
  role,
2295
2302
  content: contentBlocksToText(ensureOllamaTextBlocks(item.content, `input message (${item.role}) content`))
@@ -2511,20 +2518,35 @@ var OllamaAdapter = class extends AdapterBase {
2511
2518
  /**
2512
2519
  * Mock Adapter
2513
2520
  *
2514
- * 面向测试的脚本化 adapter:
2515
- * - turn 顺序消费请求,验证调用方是否正确续接 replay / tool_result
2521
+ * 面向测试的回调驱动 adapter:
2522
+ * - 每次请求执行用户提供的 handler
2523
+ * - 验证调用方是否正确续接 replay / tool_result
2516
2524
  * - 发出可控的 message / reasoning / tool_call 流
2517
2525
  * - 注入 warning / auxiliary / content_filter / 中断 / provider error
2518
2526
  *
2519
2527
  * 这不是通用“假模型”,而是测试工具调用编排与错误路径的测试夹具。
2520
2528
  */
2529
+ function assertMockRequest(request, expectation, context) {
2530
+ const prefix = `MockAdapter turn ${context.turnIndex + 1} expectation failed`;
2531
+ 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");
2532
+ if (expectation.maxItems !== void 0 && request.input.length > expectation.maxItems) throw new AIRequestError(`${prefix}: expected at most ${expectation.maxItems} input item(s)`, "MOCK_EXPECTATION_FAILED");
2533
+ if (expectation.tools === "present" && (!request.tools || request.tools.length === 0)) throw new AIRequestError(`${prefix}: expected tools to be present`, "MOCK_EXPECTATION_FAILED");
2534
+ if (expectation.tools === "absent" && request.tools && request.tools.length > 0) throw new AIRequestError(`${prefix}: expected tools to be absent`, "MOCK_EXPECTATION_FAILED");
2535
+ if (expectation.toolChoice === "present" && request.toolChoice === void 0) throw new AIRequestError(`${prefix}: expected toolChoice to be present`, "MOCK_EXPECTATION_FAILED");
2536
+ if (expectation.toolChoice === "absent" && request.toolChoice !== void 0) throw new AIRequestError(`${prefix}: expected toolChoice to be absent`, "MOCK_EXPECTATION_FAILED");
2537
+ if (expectation.requireReplayFromPreviousTurn && context.previousReplay.length > 0) assertReplayIncluded(request.input, context.previousReplay, prefix);
2538
+ if (expectation.requireToolResultsForPendingCalls && context.pendingToolCalls.length > 0) {
2539
+ const toolResultIds = new Set(request.input.filter((item) => item.type === "tool_result").map((item) => item.callId));
2540
+ for (const call of context.pendingToolCalls) if (!toolResultIds.has(call.id)) throw new AIRequestError(`${prefix}: expected tool_result for pending tool call "${call.id}"`, "MOCK_EXPECTATION_FAILED");
2541
+ }
2542
+ if (expectation.items && expectation.items.length > 0) if (expectation.ordered) assertOrderedItems(request.input, expectation.items, prefix);
2543
+ else assertUnorderedItems(request.input, expectation.items, prefix);
2544
+ }
2521
2545
  var MockAdapter = class extends AdapterBase {
2522
2546
  kind = "mock";
2523
2547
  nativeStreaming = false;
2524
- turns;
2525
- onExhausted;
2548
+ handler;
2526
2549
  providerMetadata;
2527
- defaultStream;
2528
2550
  cursor = 0;
2529
2551
  previousReplay = [];
2530
2552
  pendingToolCalls = [];
@@ -2532,25 +2554,19 @@ var MockAdapter = class extends AdapterBase {
2532
2554
  activeStream = false;
2533
2555
  constructor(options) {
2534
2556
  super();
2535
- this.turns = options.turns;
2536
- this.onExhausted = options.onExhausted ?? "throw";
2557
+ this.handler = options.handler;
2537
2558
  this.providerMetadata = options.providerMetadata;
2538
- this.defaultStream = resolveMockTextStreamOptions(options.stream, "adapter stream");
2539
2559
  }
2540
2560
  async buildRequest(request) {
2541
2561
  const turnIndex = this.cursor;
2542
- const turn = this.resolveTurn(turnIndex);
2543
- const turnName = turn.name;
2544
- const context = this.buildTurnContext(turnIndex);
2545
- if (turn.expect) if (typeof turn.expect === "function") await turn.expect(request, context);
2546
- else assertRequestMatchesExpectation(request, turn.expect, context);
2562
+ const context = this.buildHandlerContext(turnIndex);
2547
2563
  const remainingPendingToolCalls = consumePendingToolCalls(this.pendingToolCalls, request.input);
2564
+ const handlerResult = this.handler(request, context);
2548
2565
  this.cursor += 1;
2549
2566
  return {
2550
2567
  request,
2551
- turn,
2568
+ handlerResult,
2552
2569
  turnIndex,
2553
- turnName,
2554
2570
  remainingPendingToolCalls
2555
2571
  };
2556
2572
  }
@@ -2560,76 +2576,79 @@ var MockAdapter = class extends AdapterBase {
2560
2576
  try {
2561
2577
  const mockRequest = providerRequest;
2562
2578
  const output = [];
2563
- for (const [stepIndex, step] of mockRequest.turn.steps.entries()) switch (step.type) {
2564
- case "warning":
2565
- yield factory.responseWarning(step.message, step.code);
2566
- break;
2567
- case "auxiliary":
2568
- yield factory.responseAuxiliary({
2569
- usage: step.usage,
2570
- billing: step.billing,
2571
- auxiliary: step.auxiliary
2572
- });
2573
- break;
2574
- case "message": {
2575
- const item = createMessageFromStep(step, request, mockRequest.turnIndex, stepIndex);
2576
- yield* emitMessage(factory, item, resolveStepStreamOptions(this.defaultStream, step.stream, "message"));
2577
- output.push(item);
2578
- break;
2579
- }
2580
- case "reasoning": {
2581
- const item = createReasoningFromStep(step, request, mockRequest.turnIndex, stepIndex);
2582
- yield* emitReasoning(factory, item, resolveStepStreamOptions(this.defaultStream, step.stream, "reasoning"));
2583
- output.push(item);
2584
- break;
2585
- }
2586
- case "tool_call": {
2587
- const item = createToolCallFromStep(step);
2588
- yield* emitToolCall(factory, item, step.streamArguments ?? true, resolveStepStreamOptions(this.defaultStream, step.stream, "tool_call"));
2589
- output.push(item);
2590
- break;
2591
- }
2592
- case "output": {
2593
- assertSupportedOutputItem(step.item);
2594
- const item = attachSyntheticId(step.item, request, mockRequest.turnIndex, stepIndex);
2595
- yield* emitOutputItem(factory, item, resolveStepStreamOptions(this.defaultStream, step.stream, "output"));
2596
- output.push(item);
2597
- break;
2598
- }
2599
- case "complete": {
2600
- const response = this.finalizeTurn(request, factory, mockRequest, output, step);
2601
- yield factory.responseCompleted(response);
2602
- return;
2603
- }
2604
- case "error": {
2605
- yield factory.responseWarning(step.message, step.code);
2606
- const response = this.finalizeTurn(request, factory, mockRequest, output, {
2607
- type: "complete",
2608
- stopReason: step.stopReason ?? "error",
2609
- providerMetadata: step.providerMetadata
2610
- });
2611
- yield factory.responseCompleted(response);
2612
- return;
2579
+ let stepCount = 0;
2580
+ for await (const step of mockRequest.handlerResult) {
2581
+ stepCount += 1;
2582
+ switch (step.type) {
2583
+ case "warning":
2584
+ yield factory.responseWarning(step.message, step.code);
2585
+ break;
2586
+ case "auxiliary":
2587
+ yield factory.responseAuxiliary({
2588
+ usage: step.usage,
2589
+ billing: step.billing,
2590
+ auxiliary: step.auxiliary
2591
+ });
2592
+ break;
2593
+ case "message": {
2594
+ const item = createMessageFromStep(step, request, mockRequest.turnIndex, stepCount - 1);
2595
+ yield* emitMessage(factory, item, resolveStepStreamOptions(void 0, step.stream, "message"));
2596
+ output.push(item);
2597
+ break;
2598
+ }
2599
+ case "reasoning": {
2600
+ const item = createReasoningFromStep(step, request, mockRequest.turnIndex, stepCount - 1);
2601
+ yield* emitReasoning(factory, item, resolveStepStreamOptions(void 0, step.stream, "reasoning"));
2602
+ output.push(item);
2603
+ break;
2604
+ }
2605
+ case "tool_call": {
2606
+ const item = createToolCallFromStep(step);
2607
+ yield* emitToolCall(factory, item, step.streamArguments ?? true, resolveStepStreamOptions(void 0, step.stream, "tool_call"));
2608
+ output.push(item);
2609
+ break;
2610
+ }
2611
+ case "output": {
2612
+ assertSupportedOutputItem(step.item);
2613
+ const item = attachSyntheticId(step.item, request, mockRequest.turnIndex, stepCount - 1);
2614
+ yield* emitOutputItem(factory, item, resolveStepStreamOptions(void 0, step.stream, "output"));
2615
+ output.push(item);
2616
+ break;
2617
+ }
2618
+ case "complete": {
2619
+ const response = this.finalizeTurn(request, factory, mockRequest, output, step, stepCount);
2620
+ yield factory.responseCompleted(response);
2621
+ return;
2622
+ }
2623
+ case "error": {
2624
+ yield factory.responseWarning(step.message, step.code);
2625
+ const response = this.finalizeTurn(request, factory, mockRequest, output, {
2626
+ type: "complete",
2627
+ stopReason: step.stopReason ?? "error",
2628
+ providerMetadata: step.providerMetadata
2629
+ }, stepCount);
2630
+ yield factory.responseCompleted(response);
2631
+ return;
2632
+ }
2633
+ case "interrupt":
2634
+ this.pendingToolCalls = mockRequest.remainingPendingToolCalls;
2635
+ return;
2636
+ case "throw": throw typeof step.error === "string" ? new Error(step.error) : step.error;
2613
2637
  }
2614
- case "interrupt":
2615
- this.pendingToolCalls = mockRequest.remainingPendingToolCalls;
2616
- return;
2617
- case "throw": throw typeof step.error === "string" ? new Error(step.error) : step.error;
2618
2638
  }
2619
- const response = this.finalizeTurn(request, factory, mockRequest, output, { type: "complete" });
2639
+ const response = this.finalizeTurn(request, factory, mockRequest, output, { type: "complete" }, stepCount);
2620
2640
  yield factory.responseCompleted(response);
2621
2641
  } finally {
2622
2642
  this.activeStream = false;
2623
2643
  }
2624
2644
  }
2625
- finalizeTurn(request, factory, mockRequest, output, completion) {
2645
+ finalizeTurn(request, factory, mockRequest, output, completion, stepCount) {
2626
2646
  const replay = completion.replay ?? replayFromOutput(output);
2627
2647
  const toolCalls = output.filter((item) => item.type === "tool_call");
2628
2648
  this.previousReplay = replay;
2629
2649
  this.pendingToolCalls = [...mockRequest.remainingPendingToolCalls, ...toolCalls];
2630
2650
  this.history.push({
2631
2651
  turnIndex: mockRequest.turnIndex,
2632
- turnName: mockRequest.turnName,
2633
2652
  requestId: request.requestId,
2634
2653
  replay,
2635
2654
  toolCalls
@@ -2643,8 +2662,7 @@ var MockAdapter = class extends AdapterBase {
2643
2662
  auxiliary: completion.auxiliary,
2644
2663
  providerMetadata: {
2645
2664
  turnIndex: mockRequest.turnIndex,
2646
- turnName: mockRequest.turnName,
2647
- scriptedSteps: mockRequest.turn.steps.length,
2665
+ stepCount,
2648
2666
  pendingToolCallIds: this.pendingToolCalls.map((item) => item.id),
2649
2667
  historyLength: this.history.length,
2650
2668
  ...this.providerMetadata,
@@ -2655,18 +2673,7 @@ var MockAdapter = class extends AdapterBase {
2655
2673
  rawResponseId: completion.rawResponseId
2656
2674
  }, factory);
2657
2675
  }
2658
- resolveTurn(turnIndex) {
2659
- const turn = this.turns[turnIndex];
2660
- if (turn !== void 0) return turn;
2661
- const lastTurn = this.turns.at(-1);
2662
- if (this.onExhausted === "repeat-last" && lastTurn !== void 0) return lastTurn;
2663
- if (this.onExhausted === "complete-empty") return {
2664
- name: "exhausted",
2665
- steps: []
2666
- };
2667
- throw new AIRequestError(`MockAdapter turn ${turnIndex + 1} requested, but only ${this.turns.length} turn(s) were scripted`, "MOCK_TURN_EXHAUSTED");
2668
- }
2669
- buildTurnContext(turnIndex) {
2676
+ buildHandlerContext(turnIndex) {
2670
2677
  return {
2671
2678
  turnIndex,
2672
2679
  previousReplay: this.previousReplay.map(cloneItem),
@@ -2679,6 +2686,32 @@ var MockAdapter = class extends AdapterBase {
2679
2686
  };
2680
2687
  }
2681
2688
  };
2689
+ function withMockStreaming(handler, options) {
2690
+ const defaults = resolveMockTextStreamOptions(options, "mock stream wrapper");
2691
+ if (!defaults) throw new AIRequestError("mock stream wrapper requires streaming options", "MOCK_STREAM_CONFIG_INVALID");
2692
+ return async function* streamWrappedHandler(request, context) {
2693
+ const source = await handler(request, context);
2694
+ for await (const step of source) yield applyDefaultStreaming(step, defaults);
2695
+ };
2696
+ }
2697
+ function applyDefaultStreaming(step, defaults) {
2698
+ switch (step.type) {
2699
+ case "message":
2700
+ case "reasoning":
2701
+ case "tool_call":
2702
+ case "output":
2703
+ if (step.stream !== void 0) return step;
2704
+ return {
2705
+ ...step,
2706
+ stream: {
2707
+ charsPerSecond: defaults.charsPerSecond,
2708
+ chunkSize: defaults.chunkSize,
2709
+ initialDelayMs: defaults.initialDelayMs
2710
+ }
2711
+ };
2712
+ default: return step;
2713
+ }
2714
+ }
2682
2715
  function createMessageFromStep(step, request, turnIndex, stepIndex) {
2683
2716
  return {
2684
2717
  ...messageItem(normalizeBlocks(step.content), { id: step.id ?? `mock-msg-${request.requestId}-${turnIndex}-${stepIndex}` }),
@@ -2811,22 +2844,6 @@ function consumePendingToolCalls(pending, input) {
2811
2844
  const fulfilledIds = new Set(input.filter((item) => item.type === "tool_result").map((item) => item.callId));
2812
2845
  return pending.filter((item) => !fulfilledIds.has(item.id)).map(cloneItem);
2813
2846
  }
2814
- function assertRequestMatchesExpectation(request, expectation, context) {
2815
- const prefix = `MockAdapter turn ${context.turnIndex + 1} expectation failed`;
2816
- 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");
2817
- if (expectation.maxItems !== void 0 && request.input.length > expectation.maxItems) throw new AIRequestError(`${prefix}: expected at most ${expectation.maxItems} input item(s)`, "MOCK_EXPECTATION_FAILED");
2818
- if (expectation.tools === "present" && (!request.tools || request.tools.length === 0)) throw new AIRequestError(`${prefix}: expected tools to be present`, "MOCK_EXPECTATION_FAILED");
2819
- if (expectation.tools === "absent" && request.tools && request.tools.length > 0) throw new AIRequestError(`${prefix}: expected tools to be absent`, "MOCK_EXPECTATION_FAILED");
2820
- if (expectation.toolChoice === "present" && request.toolChoice === void 0) throw new AIRequestError(`${prefix}: expected toolChoice to be present`, "MOCK_EXPECTATION_FAILED");
2821
- if (expectation.toolChoice === "absent" && request.toolChoice !== void 0) throw new AIRequestError(`${prefix}: expected toolChoice to be absent`, "MOCK_EXPECTATION_FAILED");
2822
- if (expectation.requireReplayFromPreviousTurn && context.previousReplay.length > 0) assertReplayIncluded(request.input, context.previousReplay, prefix);
2823
- if (expectation.requireToolResultsForPendingCalls && context.pendingToolCalls.length > 0) {
2824
- const toolResultIds = new Set(request.input.filter((item) => item.type === "tool_result").map((item) => item.callId));
2825
- for (const call of context.pendingToolCalls) if (!toolResultIds.has(call.id)) throw new AIRequestError(`${prefix}: expected tool_result for pending tool call "${call.id}"`, "MOCK_EXPECTATION_FAILED");
2826
- }
2827
- if (expectation.items && expectation.items.length > 0) if (expectation.ordered) assertOrderedItems(request.input, expectation.items, prefix);
2828
- else assertUnorderedItems(request.input, expectation.items, prefix);
2829
- }
2830
2847
  function assertReplayIncluded(input, replay, prefix) {
2831
2848
  const fingerprints = input.map(fingerprintItem);
2832
2849
  let cursor = 0;
@@ -2993,6 +3010,6 @@ function* emitToolCallEvents(item, factory) {
2993
3010
  yield factory.toolCallCompleted(item);
2994
3011
  }
2995
3012
  //#endregion
2996
- export { AIError, AIMappingError, AIProviderError, AIRequestError, AIStreamError, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, ChatCompletionsAdapter, MessagesAdapter, MockAdapter, OllamaAdapter, ResponsesAdapter, WarningCode, aggregateEvents, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createEventFactory, emitMalformedStreamWarning, extractText, imageBlock, instructionsToText, jsonBlock, mapReasoningVisibility, mapStopReason, messageItem, metadataSourceList, normalizeRequest, opaqueBlock, opaqueItem, parseSSEEvents, reasoningItem, replayFromOutput, syntheticStream, textBlock, toolCallItem, toolResultItem, validateRequest };
3013
+ export { AIError, AIMappingError, AIProviderError, AIRequestError, AIStreamError, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, ChatCompletionsAdapter, MessagesAdapter, MockAdapter, OllamaAdapter, ResponsesAdapter, WarningCode, aggregateEvents, assertMockRequest, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createEventFactory, emitMalformedStreamWarning, extractText, imageBlock, instructionsToText, jsonBlock, mapReasoningVisibility, mapStopReason, messageItem, metadataSourceList, normalizeRequest, opaqueBlock, opaqueItem, parseSSEEvents, reasoningItem, replayFromOutput, syntheticStream, textBlock, toolCallItem, toolResultItem, validateRequest, withMockStreaming };
2997
3014
 
2998
3015
  //# sourceMappingURL=index.mjs.map