ai-protocol-adapters 1.0.0-alpha.4 → 1.0.0-alpha.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.d.mts CHANGED
@@ -61,9 +61,17 @@ declare class StreamingProtocolAdapter {
61
61
  */
62
62
  private processStreamChunk;
63
63
  /**
64
- * 处理工具调用
64
+ * 处理工具调用 - 支持OpenAI流式分块累积
65
+ * OpenAI流式API会将tool_calls分多个chunk发送:
66
+ * - Chunk 1: {index:0, id:"call_xxx", type:"function", function:{name:"web_search"}}
67
+ * - Chunk 2: {index:0, function:{arguments:"{\"query\":\"xxx\"}"}}
68
+ * - Chunk N: 继续累积arguments
65
69
  */
66
70
  private processToolCalls;
71
+ /**
72
+ * 在流结束时关闭所有未关闭的工具调用块
73
+ */
74
+ private closeAllToolCallBlocks;
67
75
  /**
68
76
  * 添加最终事件 - 支持thinking+content双模式
69
77
  */
package/dist/index.d.ts CHANGED
@@ -61,9 +61,17 @@ declare class StreamingProtocolAdapter {
61
61
  */
62
62
  private processStreamChunk;
63
63
  /**
64
- * 处理工具调用
64
+ * 处理工具调用 - 支持OpenAI流式分块累积
65
+ * OpenAI流式API会将tool_calls分多个chunk发送:
66
+ * - Chunk 1: {index:0, id:"call_xxx", type:"function", function:{name:"web_search"}}
67
+ * - Chunk 2: {index:0, function:{arguments:"{\"query\":\"xxx\"}"}}
68
+ * - Chunk N: 继续累积arguments
65
69
  */
66
70
  private processToolCalls;
71
+ /**
72
+ * 在流结束时关闭所有未关闭的工具调用块
73
+ */
74
+ private closeAllToolCallBlocks;
67
75
  /**
68
76
  * 添加最终事件 - 支持thinking+content双模式
69
77
  */
package/dist/index.js CHANGED
@@ -626,6 +626,14 @@ var StreamingProtocolAdapter = class {
626
626
  */
627
627
  processStreamChunk(chunk, state, sseLines) {
628
628
  const choice = chunk.choices?.[0];
629
+ if (choice) {
630
+ const hasToolCalls = choice.delta?.tool_calls;
631
+ const hasFinishReason = choice.finish_reason;
632
+ const isNonText = !choice.delta?.content;
633
+ if (hasToolCalls || hasFinishReason || isNonText && choice.delta) {
634
+ console.log("\u{1F50D}\u{1F50D}\u{1F50D} [StreamingProtocolAdapter] CHUNK:", JSON.stringify(chunk, null, 2));
635
+ }
636
+ }
629
637
  if (!choice) return;
630
638
  const delta = choice.delta;
631
639
  if (delta.reasoning_content) {
@@ -683,37 +691,82 @@ var StreamingProtocolAdapter = class {
683
691
  }
684
692
  }
685
693
  /**
686
- * 处理工具调用
694
+ * 处理工具调用 - 支持OpenAI流式分块累积
695
+ * OpenAI流式API会将tool_calls分多个chunk发送:
696
+ * - Chunk 1: {index:0, id:"call_xxx", type:"function", function:{name:"web_search"}}
697
+ * - Chunk 2: {index:0, function:{arguments:"{\"query\":\"xxx\"}"}}
698
+ * - Chunk N: 继续累积arguments
687
699
  */
688
700
  processToolCalls(toolCalls, state, sseLines) {
701
+ console.log("\u{1F50D} [StreamingProtocolAdapter] processToolCalls called:", JSON.stringify(toolCalls, null, 2));
689
702
  for (const toolCall of toolCalls) {
690
- if (toolCall.id && toolCall.function?.name) {
691
- const toolArgs = toolCall.function.arguments || "";
692
- const toolData = {
693
- id: toolCall.id,
694
- name: toolCall.function.name,
695
- input: toolArgs
703
+ const index = toolCall.index ?? 0;
704
+ const toolId = toolCall.id;
705
+ const toolName = toolCall.function?.name;
706
+ const toolArgs = toolCall.function?.arguments;
707
+ console.log(`\u{1F50D} [StreamingProtocolAdapter] Processing chunk for index ${index}:`, {
708
+ hasId: !!toolId,
709
+ hasName: !!toolName,
710
+ hasArgs: !!toolArgs,
711
+ argsLength: toolArgs?.length
712
+ });
713
+ const stateKey = `tool_${index}`;
714
+ let toolData = state.toolCallsMap.get(stateKey);
715
+ if (!toolData) {
716
+ toolData = {
717
+ id: toolId || "",
718
+ name: toolName || "",
719
+ input: "",
720
+ blockStartSent: false,
721
+ blockStopSent: false
696
722
  };
697
- state.toolCallsMap.set(toolCall.id, toolData);
723
+ state.toolCallsMap.set(stateKey, toolData);
724
+ } else {
725
+ if (toolId) toolData.id = toolId;
726
+ if (toolName) toolData.name = toolName;
727
+ }
728
+ if (toolArgs) {
729
+ toolData.input += toolArgs;
730
+ console.log(`\u{1F50D} [StreamingProtocolAdapter] Accumulated arguments for index ${index}:`, {
731
+ currentLength: toolData.input.length,
732
+ totalArgs: toolData.input
733
+ });
734
+ }
735
+ if (toolData.id && toolData.name && !toolData.blockStartSent) {
698
736
  const blockIndex = state.completedToolCalls.length + 1;
737
+ toolData.blockIndex = blockIndex;
699
738
  sseLines.push(
700
739
  "event: content_block_start",
701
- `data: {"type":"content_block_start","index":${blockIndex},"content_block":{"type":"tool_use","id":"${toolCall.id}","name":"${toolCall.function.name}","input":{}}}`,
740
+ `data: {"type":"content_block_start","index":${blockIndex},"content_block":{"type":"tool_use","id":"${toolData.id}","name":"${toolData.name}","input":{}}}`,
702
741
  ""
703
742
  );
704
- if (toolArgs) {
705
- sseLines.push(
706
- "event: content_block_delta",
707
- `data: {"type":"content_block_delta","index":${blockIndex},"delta":{"type":"input_json_delta","partial_json":${JSON.stringify(toolArgs)}}}`,
708
- ""
709
- );
710
- }
743
+ toolData.blockStartSent = true;
744
+ console.log(`\u2705 [StreamingProtocolAdapter] Sent content_block_start for ${toolData.name} at index ${blockIndex}`);
745
+ }
746
+ if (toolArgs && toolData.blockStartSent && toolData.blockIndex !== void 0) {
747
+ sseLines.push(
748
+ "event: content_block_delta",
749
+ `data: {"type":"content_block_delta","index":${toolData.blockIndex},"delta":{"type":"input_json_delta","partial_json":${JSON.stringify(toolArgs)}}}`,
750
+ ""
751
+ );
752
+ console.log(`\u2705 [StreamingProtocolAdapter] Sent input_json_delta for index ${toolData.blockIndex}`);
753
+ }
754
+ }
755
+ }
756
+ /**
757
+ * 在流结束时关闭所有未关闭的工具调用块
758
+ */
759
+ closeAllToolCallBlocks(state, sseLines) {
760
+ for (const [key, toolData] of state.toolCallsMap.entries()) {
761
+ if (toolData.blockStartSent && !toolData.blockStopSent && toolData.blockIndex !== void 0) {
711
762
  sseLines.push(
712
763
  "event: content_block_stop",
713
- `data: {"type":"content_block_stop","index":${blockIndex}}`,
764
+ `data: {"type":"content_block_stop","index":${toolData.blockIndex}}`,
714
765
  ""
715
766
  );
716
- state.completedToolCalls.push(toolCall.id);
767
+ toolData.blockStopSent = true;
768
+ state.completedToolCalls.push(toolData.id);
769
+ console.log(`\u2705 [StreamingProtocolAdapter] Sent content_block_stop for ${toolData.name} at index ${toolData.blockIndex}`);
717
770
  }
718
771
  }
719
772
  }
@@ -721,6 +774,7 @@ var StreamingProtocolAdapter = class {
721
774
  * 添加最终事件 - 支持thinking+content双模式
722
775
  */
723
776
  addFinalEvents(state, sseLines) {
777
+ this.closeAllToolCallBlocks(state, sseLines);
724
778
  if (state.contentBlockStarted) {
725
779
  const blockIndex = state.thinkingBlockStarted ? 1 : 0;
726
780
  sseLines.push(
package/dist/index.mjs CHANGED
@@ -519,6 +519,14 @@ var StreamingProtocolAdapter = class {
519
519
  */
520
520
  processStreamChunk(chunk, state, sseLines) {
521
521
  const choice = chunk.choices?.[0];
522
+ if (choice) {
523
+ const hasToolCalls = choice.delta?.tool_calls;
524
+ const hasFinishReason = choice.finish_reason;
525
+ const isNonText = !choice.delta?.content;
526
+ if (hasToolCalls || hasFinishReason || isNonText && choice.delta) {
527
+ console.log("\u{1F50D}\u{1F50D}\u{1F50D} [StreamingProtocolAdapter] CHUNK:", JSON.stringify(chunk, null, 2));
528
+ }
529
+ }
522
530
  if (!choice) return;
523
531
  const delta = choice.delta;
524
532
  if (delta.reasoning_content) {
@@ -576,37 +584,82 @@ var StreamingProtocolAdapter = class {
576
584
  }
577
585
  }
578
586
  /**
579
- * 处理工具调用
587
+ * 处理工具调用 - 支持OpenAI流式分块累积
588
+ * OpenAI流式API会将tool_calls分多个chunk发送:
589
+ * - Chunk 1: {index:0, id:"call_xxx", type:"function", function:{name:"web_search"}}
590
+ * - Chunk 2: {index:0, function:{arguments:"{\"query\":\"xxx\"}"}}
591
+ * - Chunk N: 继续累积arguments
580
592
  */
581
593
  processToolCalls(toolCalls, state, sseLines) {
594
+ console.log("\u{1F50D} [StreamingProtocolAdapter] processToolCalls called:", JSON.stringify(toolCalls, null, 2));
582
595
  for (const toolCall of toolCalls) {
583
- if (toolCall.id && toolCall.function?.name) {
584
- const toolArgs = toolCall.function.arguments || "";
585
- const toolData = {
586
- id: toolCall.id,
587
- name: toolCall.function.name,
588
- input: toolArgs
596
+ const index = toolCall.index ?? 0;
597
+ const toolId = toolCall.id;
598
+ const toolName = toolCall.function?.name;
599
+ const toolArgs = toolCall.function?.arguments;
600
+ console.log(`\u{1F50D} [StreamingProtocolAdapter] Processing chunk for index ${index}:`, {
601
+ hasId: !!toolId,
602
+ hasName: !!toolName,
603
+ hasArgs: !!toolArgs,
604
+ argsLength: toolArgs?.length
605
+ });
606
+ const stateKey = `tool_${index}`;
607
+ let toolData = state.toolCallsMap.get(stateKey);
608
+ if (!toolData) {
609
+ toolData = {
610
+ id: toolId || "",
611
+ name: toolName || "",
612
+ input: "",
613
+ blockStartSent: false,
614
+ blockStopSent: false
589
615
  };
590
- state.toolCallsMap.set(toolCall.id, toolData);
616
+ state.toolCallsMap.set(stateKey, toolData);
617
+ } else {
618
+ if (toolId) toolData.id = toolId;
619
+ if (toolName) toolData.name = toolName;
620
+ }
621
+ if (toolArgs) {
622
+ toolData.input += toolArgs;
623
+ console.log(`\u{1F50D} [StreamingProtocolAdapter] Accumulated arguments for index ${index}:`, {
624
+ currentLength: toolData.input.length,
625
+ totalArgs: toolData.input
626
+ });
627
+ }
628
+ if (toolData.id && toolData.name && !toolData.blockStartSent) {
591
629
  const blockIndex = state.completedToolCalls.length + 1;
630
+ toolData.blockIndex = blockIndex;
592
631
  sseLines.push(
593
632
  "event: content_block_start",
594
- `data: {"type":"content_block_start","index":${blockIndex},"content_block":{"type":"tool_use","id":"${toolCall.id}","name":"${toolCall.function.name}","input":{}}}`,
633
+ `data: {"type":"content_block_start","index":${blockIndex},"content_block":{"type":"tool_use","id":"${toolData.id}","name":"${toolData.name}","input":{}}}`,
595
634
  ""
596
635
  );
597
- if (toolArgs) {
598
- sseLines.push(
599
- "event: content_block_delta",
600
- `data: {"type":"content_block_delta","index":${blockIndex},"delta":{"type":"input_json_delta","partial_json":${JSON.stringify(toolArgs)}}}`,
601
- ""
602
- );
603
- }
636
+ toolData.blockStartSent = true;
637
+ console.log(`\u2705 [StreamingProtocolAdapter] Sent content_block_start for ${toolData.name} at index ${blockIndex}`);
638
+ }
639
+ if (toolArgs && toolData.blockStartSent && toolData.blockIndex !== void 0) {
640
+ sseLines.push(
641
+ "event: content_block_delta",
642
+ `data: {"type":"content_block_delta","index":${toolData.blockIndex},"delta":{"type":"input_json_delta","partial_json":${JSON.stringify(toolArgs)}}}`,
643
+ ""
644
+ );
645
+ console.log(`\u2705 [StreamingProtocolAdapter] Sent input_json_delta for index ${toolData.blockIndex}`);
646
+ }
647
+ }
648
+ }
649
+ /**
650
+ * 在流结束时关闭所有未关闭的工具调用块
651
+ */
652
+ closeAllToolCallBlocks(state, sseLines) {
653
+ for (const [key, toolData] of state.toolCallsMap.entries()) {
654
+ if (toolData.blockStartSent && !toolData.blockStopSent && toolData.blockIndex !== void 0) {
604
655
  sseLines.push(
605
656
  "event: content_block_stop",
606
- `data: {"type":"content_block_stop","index":${blockIndex}}`,
657
+ `data: {"type":"content_block_stop","index":${toolData.blockIndex}}`,
607
658
  ""
608
659
  );
609
- state.completedToolCalls.push(toolCall.id);
660
+ toolData.blockStopSent = true;
661
+ state.completedToolCalls.push(toolData.id);
662
+ console.log(`\u2705 [StreamingProtocolAdapter] Sent content_block_stop for ${toolData.name} at index ${toolData.blockIndex}`);
610
663
  }
611
664
  }
612
665
  }
@@ -614,6 +667,7 @@ var StreamingProtocolAdapter = class {
614
667
  * 添加最终事件 - 支持thinking+content双模式
615
668
  */
616
669
  addFinalEvents(state, sseLines) {
670
+ this.closeAllToolCallBlocks(state, sseLines);
617
671
  if (state.contentBlockStarted) {
618
672
  const blockIndex = state.thinkingBlockStarted ? 1 : 0;
619
673
  sseLines.push(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-protocol-adapters",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.5",
4
4
  "description": "Universal AI Protocol Converter - OpenAI ⇄ Anthropic with full TypeScript support",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",