@codehz/ai 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -107,6 +107,10 @@ console.log(response.replay); // 续接材料
107
107
  | `warnings` | `string[]` | 非致命警告 |
108
108
  | `backend` | `BackendTrace` | 调用链路元数据 |
109
109
 
110
+ 流式 `message.delta` / `reasoning.delta` 保持后端分片粒度;完成态 `output` 中的
111
+ `message` / `reasoning` 会合并相邻 `text` content blocks(直接拼接且不添加分隔符),
112
+ 非文本 block 仍保留原有边界。
113
+
110
114
  ## 后端 Adapter
111
115
 
112
116
  | Adapter | 类 | 说明 |
package/dist/index.mjs CHANGED
@@ -483,7 +483,9 @@ function createAggregatorState() {
483
483
  toolCalls: [],
484
484
  started: false,
485
485
  completed: false,
486
- activeItems: /* @__PURE__ */ new Map()
486
+ activeItems: /* @__PURE__ */ new Map(),
487
+ itemOrder: [],
488
+ completedItems: /* @__PURE__ */ new Map()
487
489
  };
488
490
  }
489
491
  function getActiveItem(state, itemId, expectedType) {
@@ -492,12 +494,21 @@ function getActiveItem(state, itemId, expectedType) {
492
494
  if (item.type !== expectedType) throw streamProtocolError(`Item ${itemId} started as ${item.type} but received ${expectedType} event`);
493
495
  return item;
494
496
  }
497
+ function coalesceContentBlocks(blocks) {
498
+ const result = [];
499
+ for (const block of blocks) {
500
+ const previous = result[result.length - 1];
501
+ if (block.type === "text" && previous?.type === "text") previous.text += block.text;
502
+ else result.push({ ...block });
503
+ }
504
+ return result;
505
+ }
495
506
  function finalizeMessage(active) {
496
507
  return {
497
508
  type: "message",
498
509
  id: active.id,
499
510
  role: active.role,
500
- content: active.content
511
+ content: coalesceContentBlocks(active.content)
501
512
  };
502
513
  }
503
514
  function finalizeReasoning(active) {
@@ -505,7 +516,7 @@ function finalizeReasoning(active) {
505
516
  type: "reasoning",
506
517
  id: active.id,
507
518
  visibility: active.visibility,
508
- content: active.content
519
+ content: coalesceContentBlocks(active.content)
509
520
  };
510
521
  }
511
522
  function finalizeToolCall(active) {
@@ -546,6 +557,7 @@ function handleMessageStarted(state, event) {
546
557
  role: event.item.role,
547
558
  content: []
548
559
  });
560
+ state.itemOrder.push(id);
549
561
  }
550
562
  function handleMessageDelta(state, event) {
551
563
  getActiveItem(state, event.itemId, "message").content.push(event.delta);
@@ -554,7 +566,7 @@ function handleMessageCompleted(state, event) {
554
566
  const active = getActiveItem(state, event.itemId, "message");
555
567
  state.activeItems.delete(event.itemId);
556
568
  const item = finalizeMessage(active);
557
- state.output.push(item);
569
+ state.completedItems.set(event.itemId, item);
558
570
  pushMessageText(state, item);
559
571
  }
560
572
  function handleReasoningStarted(state, event) {
@@ -566,6 +578,7 @@ function handleReasoningStarted(state, event) {
566
578
  visibility: event.item.visibility,
567
579
  content: []
568
580
  });
581
+ state.itemOrder.push(id);
569
582
  }
570
583
  function handleReasoningDelta(state, event) {
571
584
  getActiveItem(state, event.itemId, "reasoning").content.push(event.delta);
@@ -573,7 +586,7 @@ function handleReasoningDelta(state, event) {
573
586
  function handleReasoningCompleted(state, event) {
574
587
  const active = getActiveItem(state, event.itemId, "reasoning");
575
588
  state.activeItems.delete(event.itemId);
576
- state.output.push(finalizeReasoning(active));
589
+ state.completedItems.set(event.itemId, finalizeReasoning(active));
577
590
  }
578
591
  function handleToolCallStarted(state, event) {
579
592
  const id = event.item.id;
@@ -584,6 +597,7 @@ function handleToolCallStarted(state, event) {
584
597
  name: event.item.name,
585
598
  argumentsText: ""
586
599
  });
600
+ state.itemOrder.push(id);
587
601
  }
588
602
  function handleToolCallDelta(state, event) {
589
603
  const active = getActiveItem(state, event.itemId, "tool_call");
@@ -593,7 +607,7 @@ function handleToolCallCompleted(state, event) {
593
607
  const active = getActiveItem(state, event.itemId, "tool_call");
594
608
  state.activeItems.delete(event.itemId);
595
609
  const item = finalizeToolCall(active);
596
- state.output.push(item);
610
+ state.completedItems.set(event.itemId, item);
597
611
  state.toolCalls.push(item);
598
612
  }
599
613
  function handleResponseCompleted(state, event) {
@@ -624,9 +638,14 @@ function buildResponse(state) {
624
638
  metadataSources: backendFromCompleted?.metadataSources,
625
639
  warnings: backendFromCompleted?.warnings
626
640
  };
641
+ const orderedOutput = state.itemOrder.map((id) => {
642
+ const item = state.completedItems.get(id);
643
+ if (!item) throw streamProtocolError(`Item ${id} was started but not completed`);
644
+ return item;
645
+ });
627
646
  return {
628
647
  id: state.responseId,
629
- output: state.output,
648
+ output: [...orderedOutput, ...state.output],
630
649
  replay: state.replayFromAdapter ?? [],
631
650
  text: state.textParts.join(""),
632
651
  toolCalls: state.toolCalls,
@@ -2675,7 +2694,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
2675
2694
  if (hasMessageStarted) {
2676
2695
  const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
2677
2696
  events.push(factory.messageCompleted(currentMessageId));
2678
- if (accumulatedContent) output.push(message);
2697
+ output.push(message);
2679
2698
  }
2680
2699
  for (const pending of finalizedToolCalls) {
2681
2700
  const toolCall = toolCallItem(pending.id, pending.name, pending.args);
@@ -2770,10 +2789,6 @@ var ChatCompletionsAdapter = class extends AdapterBase {
2770
2789
  hasMessageStarted = true;
2771
2790
  accumulatedContent = "";
2772
2791
  };
2773
- if (delta.role === "assistant" && !hasMessageStarted) {
2774
- ensureMessageStarted();
2775
- yield factory.messageStarted(currentMessageId);
2776
- }
2777
2792
  if (reasoningDeltas.length > 0) {
2778
2793
  if (!hasReasoningStarted) {
2779
2794
  currentReasoningId = `reason-${chunk.id}`;