@ai-sdk-tool/parser 2.0.5 → 2.0.7

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,7 +5,145 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/tool-call-middleware.ts
8
- import { generateId } from "@ai-sdk/provider-utils";
8
+ import { generateId as generateId2 } from "@ai-sdk/provider-utils";
9
+
10
+ // src/utils/conv-tool-prompt.ts
11
+ function convertToolPrompt({
12
+ paramsPrompt,
13
+ paramsTools,
14
+ toolSystemPromptTemplate,
15
+ toolCallTag,
16
+ toolCallEndTag,
17
+ toolResponseTag,
18
+ toolResponseEndTag
19
+ }) {
20
+ const processedPrompt = paramsPrompt.map((message) => {
21
+ if (message.role === "assistant") {
22
+ const mergedContents = [];
23
+ for (const content of message.content) {
24
+ if (content.type === "tool-call") {
25
+ mergedContents.push({
26
+ type: "text",
27
+ text: `${toolCallTag}${JSON.stringify({
28
+ arguments: content.args,
29
+ name: content.toolName
30
+ })}${toolCallEndTag}`
31
+ });
32
+ } else {
33
+ mergedContents.push(content);
34
+ }
35
+ }
36
+ const finalContents = [];
37
+ for (const item of mergedContents) {
38
+ if (finalContents.length > 0 && item.type === "text" && finalContents[finalContents.length - 1].type === "text") {
39
+ const last = finalContents[finalContents.length - 1];
40
+ if (last.type === "text" && item.type === "text") {
41
+ finalContents[finalContents.length - 1] = {
42
+ type: "text",
43
+ text: last.text + "\n" + item.text
44
+ };
45
+ }
46
+ } else {
47
+ finalContents.push(item);
48
+ }
49
+ }
50
+ return {
51
+ role: "assistant",
52
+ content: finalContents
53
+ };
54
+ } else if (message.role === "tool") {
55
+ return {
56
+ role: "user",
57
+ content: [
58
+ {
59
+ type: "text",
60
+ text: message.content.map(
61
+ (content) => `${toolResponseTag}${JSON.stringify({
62
+ toolName: content.toolName,
63
+ result: content.result
64
+ })}${toolResponseEndTag}`
65
+ ).join("\n")
66
+ }
67
+ ]
68
+ };
69
+ }
70
+ return message;
71
+ });
72
+ const HermesPrompt = toolSystemPromptTemplate(
73
+ JSON.stringify(Object.entries(paramsTools || {}))
74
+ );
75
+ const toolSystemPrompt = processedPrompt[0].role === "system" ? [
76
+ {
77
+ role: "system",
78
+ content: HermesPrompt + "\n\n" + processedPrompt[0].content
79
+ },
80
+ ...processedPrompt.slice(1)
81
+ ] : [
82
+ {
83
+ role: "system",
84
+ content: HermesPrompt
85
+ },
86
+ ...processedPrompt
87
+ ];
88
+ return toolSystemPrompt;
89
+ }
90
+
91
+ // src/utils/dynamic-tool-schema.ts
92
+ function createDynamicIfThenElseSchema(tools) {
93
+ let currentSchema = {};
94
+ const toolNames = [];
95
+ for (let i = tools.length - 1; i >= 0; i--) {
96
+ const tool = tools[i];
97
+ if (tool.type === "provider-defined") {
98
+ throw new Error(
99
+ "Provider-defined tools are not supported by this middleware. Please use custom tools."
100
+ );
101
+ }
102
+ toolNames.unshift(tool.name);
103
+ const toolCondition = {
104
+ if: {
105
+ properties: {
106
+ name: {
107
+ const: tool.name
108
+ }
109
+ },
110
+ required: ["name"]
111
+ },
112
+ then: {
113
+ properties: {
114
+ name: {
115
+ const: tool.name
116
+ },
117
+ // Cast tool.parameters to JSONSchema7 here.
118
+ arguments: tool.parameters
119
+ },
120
+ required: ["name", "arguments"]
121
+ }
122
+ };
123
+ if (Object.keys(currentSchema).length > 0) {
124
+ toolCondition.else = currentSchema;
125
+ }
126
+ currentSchema = toolCondition;
127
+ }
128
+ return {
129
+ type: "object",
130
+ // Explicitly specify type as "object"
131
+ properties: {
132
+ name: {
133
+ type: "string",
134
+ description: "Name of the tool to call",
135
+ enum: toolNames
136
+ },
137
+ arguments: {
138
+ type: "object",
139
+ // By default, arguments is also specified as object type
140
+ description: "Argument object to be passed to the tool"
141
+ }
142
+ },
143
+ required: ["name", "arguments"],
144
+ ...currentSchema
145
+ };
146
+ }
9
147
 
10
148
  // src/utils/get-potential-start-index.ts
11
149
  function getPotentialStartIndex(text, searchedText) {
@@ -651,88 +789,137 @@ function stringify(obj) {
651
789
  return "null";
652
790
  }
653
791
 
654
- // src/utils/conv-tool-prompt.ts
655
- function convertToolPrompt({
656
- paramsPrompt,
657
- paramsTools,
658
- toolSystemPromptTemplate,
792
+ // src/stream-handler.ts
793
+ import { generateId } from "@ai-sdk/provider-utils";
794
+ async function normalToolStream({
795
+ doStream,
659
796
  toolCallTag,
660
- toolCallEndTag,
661
- toolResponseTag,
662
- toolResponseEndTag
797
+ toolCallEndTag
663
798
  }) {
664
- const processedPrompt = paramsPrompt.map((message) => {
665
- if (message.role === "assistant") {
666
- const mergedContents = [];
667
- for (const content of message.content) {
668
- if (content.type === "tool-call") {
669
- mergedContents.push({
670
- type: "text",
671
- text: `${toolCallTag}${JSON.stringify({
672
- arguments: content.args,
673
- name: content.toolName
674
- })}${toolCallEndTag}`
799
+ const { stream, ...rest } = await doStream();
800
+ let isFirstToolCall = true;
801
+ let isFirstText = true;
802
+ let afterSwitch = false;
803
+ let isToolCall = false;
804
+ let buffer = "";
805
+ let toolCallIndex = -1;
806
+ let toolCallBuffer = [];
807
+ const transformStream = new TransformStream({
808
+ transform(chunk, controller) {
809
+ if (chunk.type === "finish") {
810
+ if (toolCallBuffer.length > 0) {
811
+ toolCallBuffer.forEach((toolCall) => {
812
+ try {
813
+ const parsedToolCall = relaxed_json_exports.parse(toolCall);
814
+ controller.enqueue({
815
+ type: "tool-call",
816
+ toolCallType: "function",
817
+ toolCallId: generateId(),
818
+ toolName: parsedToolCall.name,
819
+ args: JSON.stringify(parsedToolCall.arguments)
820
+ });
821
+ } catch (e) {
822
+ console.error(`Error parsing tool call: ${toolCall}`, e);
823
+ controller.enqueue({
824
+ type: "text",
825
+ text: `Failed to parse tool call: ${e}`
826
+ });
827
+ }
675
828
  });
676
- } else {
677
- mergedContents.push(content);
678
829
  }
830
+ controller.enqueue(chunk);
831
+ return;
832
+ } else if (chunk.type !== "text") {
833
+ controller.enqueue(chunk);
834
+ return;
679
835
  }
680
- const finalContents = [];
681
- for (const item of mergedContents) {
682
- if (finalContents.length > 0 && item.type === "text" && finalContents[finalContents.length - 1].type === "text") {
683
- const last = finalContents[finalContents.length - 1];
684
- if (last.type === "text" && item.type === "text") {
685
- finalContents[finalContents.length - 1] = {
836
+ buffer += chunk.text;
837
+ function publish(text) {
838
+ if (text.length > 0) {
839
+ const prefix = afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText) ? "\n" : "";
840
+ if (isToolCall) {
841
+ if (!toolCallBuffer[toolCallIndex]) {
842
+ toolCallBuffer[toolCallIndex] = "";
843
+ }
844
+ toolCallBuffer[toolCallIndex] += text;
845
+ } else {
846
+ controller.enqueue({
686
847
  type: "text",
687
- text: last.text + "\n" + item.text
688
- };
848
+ text: prefix + text
849
+ });
850
+ }
851
+ afterSwitch = false;
852
+ if (isToolCall) {
853
+ isFirstToolCall = false;
854
+ } else {
855
+ isFirstText = false;
689
856
  }
690
- } else {
691
- finalContents.push(item);
692
857
  }
693
858
  }
694
- return {
695
- role: "assistant",
696
- content: finalContents
697
- };
698
- } else if (message.role === "tool") {
699
- return {
700
- role: "user",
701
- content: [
702
- {
703
- type: "text",
704
- text: message.content.map(
705
- (content) => `${toolResponseTag}${JSON.stringify({
706
- toolName: content.toolName,
707
- result: content.result
708
- })}${toolResponseEndTag}`
709
- ).join("\n")
710
- }
711
- ]
712
- };
859
+ do {
860
+ const nextTag = isToolCall ? toolCallEndTag : toolCallTag;
861
+ const startIndex = getPotentialStartIndex(buffer, nextTag);
862
+ if (startIndex == null) {
863
+ publish(buffer);
864
+ buffer = "";
865
+ break;
866
+ }
867
+ publish(buffer.slice(0, startIndex));
868
+ const foundFullMatch = startIndex + nextTag.length <= buffer.length;
869
+ if (foundFullMatch) {
870
+ buffer = buffer.slice(startIndex + nextTag.length);
871
+ toolCallIndex++;
872
+ isToolCall = !isToolCall;
873
+ afterSwitch = true;
874
+ } else {
875
+ buffer = buffer.slice(startIndex);
876
+ break;
877
+ }
878
+ } while (true);
713
879
  }
714
- return message;
715
880
  });
716
- const HermesPrompt = toolSystemPromptTemplate(
717
- JSON.stringify(Object.entries(paramsTools || {}))
718
- );
719
- const toolSystemPrompt = processedPrompt[0].role === "system" ? [
720
- {
721
- role: "system",
722
- content: HermesPrompt + "\n\n" + processedPrompt[0].content
723
- },
724
- ...processedPrompt.slice(1)
725
- ] : [
726
- {
727
- role: "system",
728
- content: HermesPrompt
729
- },
730
- ...processedPrompt
731
- ];
732
- return toolSystemPrompt;
881
+ return {
882
+ stream: stream.pipeThrough(transformStream),
883
+ ...rest
884
+ };
885
+ }
886
+ async function toolChoiceStream({
887
+ doGenerate
888
+ }) {
889
+ const result = await doGenerate();
890
+ const toolJson = result.content[0].type === "text" ? JSON.parse(result.content[0].text) : {};
891
+ const toolCallChunk = {
892
+ type: "tool-call",
893
+ toolCallType: "function",
894
+ toolCallId: generateId(),
895
+ toolName: toolJson.name,
896
+ args: JSON.stringify(toolJson.arguments || {})
897
+ };
898
+ const finishChunk = {
899
+ type: "finish",
900
+ usage: result.usage,
901
+ finishReason: "tool-calls"
902
+ };
903
+ const stream = new ReadableStream({
904
+ start(controller) {
905
+ controller.enqueue(toolCallChunk);
906
+ controller.enqueue(finishChunk);
907
+ controller.close();
908
+ }
909
+ });
910
+ return {
911
+ request: result.request,
912
+ response: result.response,
913
+ stream
914
+ };
733
915
  }
734
916
 
735
917
  // src/tool-call-middleware.ts
918
+ function isToolChoiceActive(params) {
919
+ var _a, _b;
920
+ const toolChoice = (_b = (_a = params == null ? void 0 : params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
921
+ return typeof params.providerOptions === "object" && params.providerOptions !== null && typeof params.providerOptions.toolCallMiddleware === "object" && toolChoice && typeof toolChoice === "object" && (toolChoice.type === "tool" || toolChoice.type === "required");
922
+ }
736
923
  function createToolMiddleware({
737
924
  toolCallTag,
738
925
  toolCallEndTag,
@@ -742,99 +929,39 @@ function createToolMiddleware({
742
929
  }) {
743
930
  return {
744
931
  middlewareVersion: "v2",
745
- wrapStream: async ({ doStream }) => {
746
- const { stream, ...rest } = await doStream();
747
- let isFirstToolCall = true;
748
- let isFirstText = true;
749
- let afterSwitch = false;
750
- let isToolCall = false;
751
- let buffer = "";
752
- let toolCallIndex = -1;
753
- let toolCallBuffer = [];
754
- const transformStream = new TransformStream({
755
- transform(chunk, controller) {
756
- if (chunk.type === "finish") {
757
- if (toolCallBuffer.length > 0) {
758
- toolCallBuffer.forEach((toolCall) => {
759
- try {
760
- const parsedToolCall = relaxed_json_exports.parse(toolCall);
761
- controller.enqueue({
762
- type: "tool-call",
763
- toolCallType: "function",
764
- toolCallId: generateId(),
765
- toolName: parsedToolCall.name,
766
- args: JSON.stringify(parsedToolCall.arguments)
767
- });
768
- } catch (e) {
769
- console.error(`Error parsing tool call: ${toolCall}`, e);
770
- controller.enqueue({
771
- type: "text",
772
- text: `Failed to parse tool call: ${e}`
773
- });
774
- }
775
- });
776
- }
777
- controller.enqueue(chunk);
778
- return;
779
- } else if (chunk.type !== "text") {
780
- controller.enqueue(chunk);
781
- return;
782
- }
783
- buffer += chunk.text;
784
- function publish(text) {
785
- if (text.length > 0) {
786
- const prefix = afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText) ? "\n" : "";
787
- if (isToolCall) {
788
- if (!toolCallBuffer[toolCallIndex]) {
789
- toolCallBuffer[toolCallIndex] = "";
790
- }
791
- toolCallBuffer[toolCallIndex] += text;
792
- } else {
793
- controller.enqueue({
794
- type: "text",
795
- text: prefix + text
796
- });
797
- }
798
- afterSwitch = false;
799
- if (isToolCall) {
800
- isFirstToolCall = false;
801
- } else {
802
- isFirstText = false;
803
- }
804
- }
805
- }
806
- do {
807
- const nextTag = isToolCall ? toolCallEndTag : toolCallTag;
808
- const startIndex = getPotentialStartIndex(buffer, nextTag);
809
- if (startIndex == null) {
810
- publish(buffer);
811
- buffer = "";
812
- break;
813
- }
814
- publish(buffer.slice(0, startIndex));
815
- const foundFullMatch = startIndex + nextTag.length <= buffer.length;
816
- if (foundFullMatch) {
817
- buffer = buffer.slice(startIndex + nextTag.length);
818
- toolCallIndex++;
819
- isToolCall = !isToolCall;
820
- afterSwitch = true;
821
- } else {
822
- buffer = buffer.slice(startIndex);
823
- break;
824
- }
825
- } while (true);
826
- }
827
- });
828
- return {
829
- stream: stream.pipeThrough(transformStream),
830
- ...rest
831
- };
932
+ wrapStream: async ({ doStream, doGenerate, params }) => {
933
+ if (isToolChoiceActive(params)) {
934
+ return toolChoiceStream({
935
+ doGenerate
936
+ });
937
+ } else {
938
+ return normalToolStream({
939
+ doStream,
940
+ toolCallTag,
941
+ toolCallEndTag
942
+ });
943
+ }
832
944
  },
833
- wrapGenerate: async ({ doGenerate }) => {
945
+ wrapGenerate: async ({ doGenerate, params }) => {
834
946
  const result = await doGenerate();
835
947
  if (result.content.length === 0) {
836
948
  return result;
837
949
  }
950
+ if (isToolChoiceActive(params)) {
951
+ const toolJson = result.content[0].type === "text" ? JSON.parse(result.content[0].text) : {};
952
+ return {
953
+ ...result,
954
+ content: [
955
+ {
956
+ type: "tool-call",
957
+ toolCallType: "function",
958
+ toolCallId: generateId2(),
959
+ toolName: toolJson.name,
960
+ args: JSON.stringify(toolJson.arguments || {})
961
+ }
962
+ ]
963
+ };
964
+ }
838
965
  const toolCallRegex = new RegExp(
839
966
  `${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,
840
967
  "gs"
@@ -861,7 +988,7 @@ function createToolMiddleware({
861
988
  return {
862
989
  type: "tool-call",
863
990
  toolCallType: "function",
864
- toolCallId: generateId(),
991
+ toolCallId: generateId2(),
865
992
  toolName: parsedToolCall.name,
866
993
  // Ensure args is always a JSON string
867
994
  args: typeof parsedToolCall.arguments === "string" ? parsedToolCall.arguments : JSON.stringify(parsedToolCall.arguments)
@@ -914,6 +1041,7 @@ function createToolMiddleware({
914
1041
  };
915
1042
  },
916
1043
  transformParams: async ({ params }) => {
1044
+ var _a, _b, _c, _d;
917
1045
  const toolSystemPrompt = convertToolPrompt({
918
1046
  paramsPrompt: params.prompt,
919
1047
  paramsTools: params.tools,
@@ -923,13 +1051,77 @@ function createToolMiddleware({
923
1051
  toolResponseTag,
924
1052
  toolResponseEndTag
925
1053
  });
926
- return {
1054
+ const baseReturnParams = {
927
1055
  ...params,
928
1056
  prompt: toolSystemPrompt,
929
- // set the mode back to regular and remove the default tools.
1057
+ // Reset tools and toolChoice to default after prompt transformation
930
1058
  tools: [],
931
1059
  toolChoice: void 0
932
1060
  };
1061
+ if (((_a = params.toolChoice) == null ? void 0 : _a.type) === "none") {
1062
+ throw new Error(
1063
+ "The 'none' toolChoice type is not supported by this middleware. Please use 'auto', 'required', or specify a tool name."
1064
+ );
1065
+ }
1066
+ if (((_b = params.toolChoice) == null ? void 0 : _b.type) === "tool") {
1067
+ const selectedToolName = params.toolChoice.toolName;
1068
+ const selectedTool = (_c = params.tools) == null ? void 0 : _c.find(
1069
+ (tool) => tool.name === selectedToolName
1070
+ );
1071
+ if (!selectedTool) {
1072
+ throw new Error(
1073
+ `Tool with name '${selectedToolName}' not found in params.tools.`
1074
+ );
1075
+ }
1076
+ if (selectedTool.type === "provider-defined") {
1077
+ throw new Error(
1078
+ "Provider-defined tools are not supported by this middleware. Please use custom tools."
1079
+ );
1080
+ }
1081
+ return {
1082
+ ...baseReturnParams,
1083
+ responseFormat: {
1084
+ type: "json",
1085
+ schema: {
1086
+ type: "object",
1087
+ properties: {
1088
+ name: {
1089
+ const: selectedTool.name
1090
+ },
1091
+ arguments: selectedTool.parameters
1092
+ },
1093
+ required: ["name", "arguments"]
1094
+ },
1095
+ name: selectedTool.name,
1096
+ description: selectedTool.description
1097
+ },
1098
+ providerOptions: {
1099
+ toolCallMiddleware: {
1100
+ toolChoice: params.toolChoice
1101
+ }
1102
+ }
1103
+ };
1104
+ }
1105
+ if (((_d = params.toolChoice) == null ? void 0 : _d.type) === "required") {
1106
+ if (!params.tools || params.tools.length === 0) {
1107
+ throw new Error(
1108
+ "Tool choice type 'required' is set, but no tools are provided in params.tools."
1109
+ );
1110
+ }
1111
+ return {
1112
+ ...baseReturnParams,
1113
+ responseFormat: {
1114
+ type: "json",
1115
+ schema: createDynamicIfThenElseSchema(params.tools)
1116
+ },
1117
+ providerOptions: {
1118
+ toolCallMiddleware: {
1119
+ toolChoice: { type: "required" }
1120
+ }
1121
+ }
1122
+ };
1123
+ }
1124
+ return baseReturnParams;
933
1125
  }
934
1126
  };
935
1127
  }