@ai-sdk-tool/parser 2.0.4 → 2.0.6

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) {
@@ -48,6 +186,12 @@ function getPotentialStartIndex(text, searchedText) {
48
186
  }
49
187
 
50
188
  // src/utils/relaxed-json.ts
189
+ var relaxed_json_exports = {};
190
+ __export(relaxed_json_exports, {
191
+ parse: () => parse,
192
+ stringify: () => stringify,
193
+ transform: () => transform
194
+ });
51
195
  function some(array, f) {
52
196
  let acc = false;
53
197
  for (let i = 0; i < array.length; i++) {
@@ -269,6 +413,13 @@ function stripTrailingComma(tokens) {
269
413
  });
270
414
  return res;
271
415
  }
416
+ function transform(text) {
417
+ let tokens = lexer(text);
418
+ tokens = stripTrailingComma(tokens);
419
+ return tokens.reduce((str, token) => {
420
+ return str + token.match;
421
+ }, "");
422
+ }
272
423
  function popToken(tokens, state) {
273
424
  const token = tokens[state.pos];
274
425
  state.pos += 1;
@@ -636,8 +787,161 @@ function parse(text, optsOrReviver) {
636
787
  }
637
788
  }
638
789
  }
790
+ function stringifyPair(obj, key) {
791
+ return JSON.stringify(key) + ":" + stringify(obj[key]);
792
+ }
793
+ function stringify(obj) {
794
+ const type = typeof obj;
795
+ if (type === "string" || type === "number" || type === "boolean" || obj === null) {
796
+ return JSON.stringify(obj);
797
+ }
798
+ if (type === "undefined") {
799
+ return "null";
800
+ }
801
+ if (Array.isArray(obj)) {
802
+ const elements = obj.map(stringify).join(",");
803
+ return "[" + elements + "]";
804
+ }
805
+ if (type === "object") {
806
+ const keys = Object.keys(obj);
807
+ keys.sort();
808
+ const pairs = keys.map((key) => stringifyPair(obj, key)).join(",");
809
+ return "{" + pairs + "}";
810
+ }
811
+ return "null";
812
+ }
813
+
814
+ // src/stream-handler.ts
815
+ var import_provider_utils = require("@ai-sdk/provider-utils");
816
+ async function normalToolStream({
817
+ doStream,
818
+ toolCallTag,
819
+ toolCallEndTag
820
+ }) {
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
+ }
850
+ });
851
+ }
852
+ controller.enqueue(chunk);
853
+ return;
854
+ } else if (chunk.type !== "text") {
855
+ controller.enqueue(chunk);
856
+ return;
857
+ }
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({
869
+ type: "text",
870
+ text: prefix + text
871
+ });
872
+ }
873
+ afterSwitch = false;
874
+ if (isToolCall) {
875
+ isFirstToolCall = false;
876
+ } else {
877
+ isFirstText = false;
878
+ }
879
+ }
880
+ }
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);
901
+ }
902
+ });
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
+ };
937
+ }
639
938
 
640
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
+ }
641
945
  function createToolMiddleware({
642
946
  toolCallTag,
643
947
  toolCallEndTag,
@@ -647,99 +951,39 @@ function createToolMiddleware({
647
951
  }) {
648
952
  return {
649
953
  middlewareVersion: "v2",
650
- wrapStream: async ({ doStream }) => {
651
- const { stream, ...rest } = await doStream();
652
- let isFirstToolCall = true;
653
- let isFirstText = true;
654
- let afterSwitch = false;
655
- let isToolCall = false;
656
- let buffer = "";
657
- let toolCallIndex = -1;
658
- let toolCallBuffer = [];
659
- const transformStream = new TransformStream({
660
- transform(chunk, controller) {
661
- if (chunk.type === "finish") {
662
- if (toolCallBuffer.length > 0) {
663
- toolCallBuffer.forEach((toolCall) => {
664
- try {
665
- const parsedToolCall = parse(toolCall);
666
- controller.enqueue({
667
- type: "tool-call",
668
- toolCallType: "function",
669
- toolCallId: (0, import_provider_utils.generateId)(),
670
- toolName: parsedToolCall.name,
671
- args: JSON.stringify(parsedToolCall.arguments)
672
- });
673
- } catch (e) {
674
- console.error(`Error parsing tool call: ${toolCall}`, e);
675
- controller.enqueue({
676
- type: "text",
677
- text: `Failed to parse tool call: ${e}`
678
- });
679
- }
680
- });
681
- }
682
- controller.enqueue(chunk);
683
- return;
684
- } else if (chunk.type !== "text") {
685
- controller.enqueue(chunk);
686
- return;
687
- }
688
- buffer += chunk.text;
689
- function publish(text) {
690
- if (text.length > 0) {
691
- const prefix = afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText) ? "\n" : "";
692
- if (isToolCall) {
693
- if (!toolCallBuffer[toolCallIndex]) {
694
- toolCallBuffer[toolCallIndex] = "";
695
- }
696
- toolCallBuffer[toolCallIndex] += text;
697
- } else {
698
- controller.enqueue({
699
- type: "text",
700
- text: prefix + text
701
- });
702
- }
703
- afterSwitch = false;
704
- if (isToolCall) {
705
- isFirstToolCall = false;
706
- } else {
707
- isFirstText = false;
708
- }
709
- }
710
- }
711
- do {
712
- const nextTag = isToolCall ? toolCallEndTag : toolCallTag;
713
- const startIndex = getPotentialStartIndex(buffer, nextTag);
714
- if (startIndex == null) {
715
- publish(buffer);
716
- buffer = "";
717
- break;
718
- }
719
- publish(buffer.slice(0, startIndex));
720
- const foundFullMatch = startIndex + nextTag.length <= buffer.length;
721
- if (foundFullMatch) {
722
- buffer = buffer.slice(startIndex + nextTag.length);
723
- toolCallIndex++;
724
- isToolCall = !isToolCall;
725
- afterSwitch = true;
726
- } else {
727
- buffer = buffer.slice(startIndex);
728
- break;
729
- }
730
- } while (true);
731
- }
732
- });
733
- return {
734
- stream: stream.pipeThrough(transformStream),
735
- ...rest
736
- };
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
+ }
737
966
  },
738
- wrapGenerate: async ({ doGenerate }) => {
967
+ wrapGenerate: async ({ doGenerate, params }) => {
739
968
  const result = await doGenerate();
740
969
  if (result.content.length === 0) {
741
970
  return result;
742
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
+ }
743
987
  const toolCallRegex = new RegExp(
744
988
  `${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,
745
989
  "gs"
@@ -755,7 +999,7 @@ function createToolMiddleware({
755
999
  let match;
756
1000
  const parseAndCreateToolCall = (toolCallJson) => {
757
1001
  try {
758
- const parsedToolCall = parse(toolCallJson);
1002
+ const parsedToolCall = relaxed_json_exports.parse(toolCallJson);
759
1003
  if (!parsedToolCall || typeof parsedToolCall.name !== "string" || typeof parsedToolCall.arguments === "undefined") {
760
1004
  console.error(
761
1005
  "Failed to parse tool call: Invalid structure",
@@ -766,7 +1010,7 @@ function createToolMiddleware({
766
1010
  return {
767
1011
  type: "tool-call",
768
1012
  toolCallType: "function",
769
- toolCallId: (0, import_provider_utils.generateId)(),
1013
+ toolCallId: (0, import_provider_utils2.generateId)(),
770
1014
  toolName: parsedToolCall.name,
771
1015
  // Ensure args is always a JSON string
772
1016
  args: typeof parsedToolCall.arguments === "string" ? parsedToolCall.arguments : JSON.stringify(parsedToolCall.arguments)
@@ -819,64 +1063,87 @@ function createToolMiddleware({
819
1063
  };
820
1064
  },
821
1065
  transformParams: async ({ params }) => {
822
- const processedPrompt = params.prompt.map((message) => {
823
- if (message.role === "assistant") {
824
- return {
825
- role: "assistant",
826
- content: message.content.map((content) => {
827
- if (content.type === "tool-call") {
828
- return {
829
- type: "text",
830
- text: `${toolCallTag}${JSON.stringify({
831
- arguments: content.args,
832
- name: content.toolName
833
- })}${toolCallEndTag}`
834
- };
835
- }
836
- return content;
837
- })
838
- };
839
- } else if (message.role === "tool") {
840
- return {
841
- role: "user",
842
- content: [
843
- {
844
- type: "text",
845
- text: message.content.map(
846
- (content) => `${toolResponseTag}${JSON.stringify({
847
- toolName: content.toolName,
848
- result: content.result
849
- })}${toolResponseEndTag}`
850
- ).join("\n")
851
- }
852
- ]
853
- };
854
- }
855
- return message;
1066
+ var _a, _b, _c, _d;
1067
+ const toolSystemPrompt = convertToolPrompt({
1068
+ paramsPrompt: params.prompt,
1069
+ paramsTools: params.tools,
1070
+ toolSystemPromptTemplate,
1071
+ toolCallTag,
1072
+ toolCallEndTag,
1073
+ toolResponseTag,
1074
+ toolResponseEndTag
856
1075
  });
857
- const HermesPrompt = toolSystemPromptTemplate(
858
- JSON.stringify(Object.entries(params.tools || {}))
859
- );
860
- const toolSystemPrompt = processedPrompt[0].role === "system" ? [
861
- {
862
- role: "system",
863
- content: HermesPrompt + "\n\n" + processedPrompt[0].content
864
- },
865
- ...processedPrompt.slice(1)
866
- ] : [
867
- {
868
- role: "system",
869
- content: HermesPrompt
870
- },
871
- ...processedPrompt
872
- ];
873
- return {
1076
+ const baseReturnParams = {
874
1077
  ...params,
875
1078
  prompt: toolSystemPrompt,
876
- // set the mode back to regular and remove the default tools.
1079
+ // Reset tools and toolChoice to default after prompt transformation
877
1080
  tools: [],
878
1081
  toolChoice: void 0
879
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;
880
1147
  }
881
1148
  };
882
1149
  }