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