@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.mjs CHANGED
@@ -1,5 +1,149 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
1
7
  // src/tool-call-middleware.ts
2
- 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
+ }
3
147
 
4
148
  // src/utils/get-potential-start-index.ts
5
149
  function getPotentialStartIndex(text, searchedText) {
@@ -20,6 +164,12 @@ function getPotentialStartIndex(text, searchedText) {
20
164
  }
21
165
 
22
166
  // src/utils/relaxed-json.ts
167
+ var relaxed_json_exports = {};
168
+ __export(relaxed_json_exports, {
169
+ parse: () => parse,
170
+ stringify: () => stringify,
171
+ transform: () => transform
172
+ });
23
173
  function some(array, f) {
24
174
  let acc = false;
25
175
  for (let i = 0; i < array.length; i++) {
@@ -241,6 +391,13 @@ function stripTrailingComma(tokens) {
241
391
  });
242
392
  return res;
243
393
  }
394
+ function transform(text) {
395
+ let tokens = lexer(text);
396
+ tokens = stripTrailingComma(tokens);
397
+ return tokens.reduce((str, token) => {
398
+ return str + token.match;
399
+ }, "");
400
+ }
244
401
  function popToken(tokens, state) {
245
402
  const token = tokens[state.pos];
246
403
  state.pos += 1;
@@ -608,8 +765,161 @@ function parse(text, optsOrReviver) {
608
765
  }
609
766
  }
610
767
  }
768
+ function stringifyPair(obj, key) {
769
+ return JSON.stringify(key) + ":" + stringify(obj[key]);
770
+ }
771
+ function stringify(obj) {
772
+ const type = typeof obj;
773
+ if (type === "string" || type === "number" || type === "boolean" || obj === null) {
774
+ return JSON.stringify(obj);
775
+ }
776
+ if (type === "undefined") {
777
+ return "null";
778
+ }
779
+ if (Array.isArray(obj)) {
780
+ const elements = obj.map(stringify).join(",");
781
+ return "[" + elements + "]";
782
+ }
783
+ if (type === "object") {
784
+ const keys = Object.keys(obj);
785
+ keys.sort();
786
+ const pairs = keys.map((key) => stringifyPair(obj, key)).join(",");
787
+ return "{" + pairs + "}";
788
+ }
789
+ return "null";
790
+ }
791
+
792
+ // src/stream-handler.ts
793
+ import { generateId } from "@ai-sdk/provider-utils";
794
+ async function normalToolStream({
795
+ doStream,
796
+ toolCallTag,
797
+ toolCallEndTag
798
+ }) {
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
+ }
828
+ });
829
+ }
830
+ controller.enqueue(chunk);
831
+ return;
832
+ } else if (chunk.type !== "text") {
833
+ controller.enqueue(chunk);
834
+ return;
835
+ }
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({
847
+ type: "text",
848
+ text: prefix + text
849
+ });
850
+ }
851
+ afterSwitch = false;
852
+ if (isToolCall) {
853
+ isFirstToolCall = false;
854
+ } else {
855
+ isFirstText = false;
856
+ }
857
+ }
858
+ }
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);
879
+ }
880
+ });
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
+ };
915
+ }
611
916
 
612
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
+ }
613
923
  function createToolMiddleware({
614
924
  toolCallTag,
615
925
  toolCallEndTag,
@@ -619,99 +929,39 @@ function createToolMiddleware({
619
929
  }) {
620
930
  return {
621
931
  middlewareVersion: "v2",
622
- wrapStream: async ({ doStream }) => {
623
- const { stream, ...rest } = await doStream();
624
- let isFirstToolCall = true;
625
- let isFirstText = true;
626
- let afterSwitch = false;
627
- let isToolCall = false;
628
- let buffer = "";
629
- let toolCallIndex = -1;
630
- let toolCallBuffer = [];
631
- const transformStream = new TransformStream({
632
- transform(chunk, controller) {
633
- if (chunk.type === "finish") {
634
- if (toolCallBuffer.length > 0) {
635
- toolCallBuffer.forEach((toolCall) => {
636
- try {
637
- const parsedToolCall = parse(toolCall);
638
- controller.enqueue({
639
- type: "tool-call",
640
- toolCallType: "function",
641
- toolCallId: generateId(),
642
- toolName: parsedToolCall.name,
643
- args: JSON.stringify(parsedToolCall.arguments)
644
- });
645
- } catch (e) {
646
- console.error(`Error parsing tool call: ${toolCall}`, e);
647
- controller.enqueue({
648
- type: "text",
649
- text: `Failed to parse tool call: ${e}`
650
- });
651
- }
652
- });
653
- }
654
- controller.enqueue(chunk);
655
- return;
656
- } else if (chunk.type !== "text") {
657
- controller.enqueue(chunk);
658
- return;
659
- }
660
- buffer += chunk.text;
661
- function publish(text) {
662
- if (text.length > 0) {
663
- const prefix = afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText) ? "\n" : "";
664
- if (isToolCall) {
665
- if (!toolCallBuffer[toolCallIndex]) {
666
- toolCallBuffer[toolCallIndex] = "";
667
- }
668
- toolCallBuffer[toolCallIndex] += text;
669
- } else {
670
- controller.enqueue({
671
- type: "text",
672
- text: prefix + text
673
- });
674
- }
675
- afterSwitch = false;
676
- if (isToolCall) {
677
- isFirstToolCall = false;
678
- } else {
679
- isFirstText = false;
680
- }
681
- }
682
- }
683
- do {
684
- const nextTag = isToolCall ? toolCallEndTag : toolCallTag;
685
- const startIndex = getPotentialStartIndex(buffer, nextTag);
686
- if (startIndex == null) {
687
- publish(buffer);
688
- buffer = "";
689
- break;
690
- }
691
- publish(buffer.slice(0, startIndex));
692
- const foundFullMatch = startIndex + nextTag.length <= buffer.length;
693
- if (foundFullMatch) {
694
- buffer = buffer.slice(startIndex + nextTag.length);
695
- toolCallIndex++;
696
- isToolCall = !isToolCall;
697
- afterSwitch = true;
698
- } else {
699
- buffer = buffer.slice(startIndex);
700
- break;
701
- }
702
- } while (true);
703
- }
704
- });
705
- return {
706
- stream: stream.pipeThrough(transformStream),
707
- ...rest
708
- };
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
+ }
709
944
  },
710
- wrapGenerate: async ({ doGenerate }) => {
945
+ wrapGenerate: async ({ doGenerate, params }) => {
711
946
  const result = await doGenerate();
712
947
  if (result.content.length === 0) {
713
948
  return result;
714
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
+ }
715
965
  const toolCallRegex = new RegExp(
716
966
  `${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,
717
967
  "gs"
@@ -727,7 +977,7 @@ function createToolMiddleware({
727
977
  let match;
728
978
  const parseAndCreateToolCall = (toolCallJson) => {
729
979
  try {
730
- const parsedToolCall = parse(toolCallJson);
980
+ const parsedToolCall = relaxed_json_exports.parse(toolCallJson);
731
981
  if (!parsedToolCall || typeof parsedToolCall.name !== "string" || typeof parsedToolCall.arguments === "undefined") {
732
982
  console.error(
733
983
  "Failed to parse tool call: Invalid structure",
@@ -738,7 +988,7 @@ function createToolMiddleware({
738
988
  return {
739
989
  type: "tool-call",
740
990
  toolCallType: "function",
741
- toolCallId: generateId(),
991
+ toolCallId: generateId2(),
742
992
  toolName: parsedToolCall.name,
743
993
  // Ensure args is always a JSON string
744
994
  args: typeof parsedToolCall.arguments === "string" ? parsedToolCall.arguments : JSON.stringify(parsedToolCall.arguments)
@@ -791,64 +1041,87 @@ function createToolMiddleware({
791
1041
  };
792
1042
  },
793
1043
  transformParams: async ({ params }) => {
794
- const processedPrompt = params.prompt.map((message) => {
795
- if (message.role === "assistant") {
796
- return {
797
- role: "assistant",
798
- content: message.content.map((content) => {
799
- if (content.type === "tool-call") {
800
- return {
801
- type: "text",
802
- text: `${toolCallTag}${JSON.stringify({
803
- arguments: content.args,
804
- name: content.toolName
805
- })}${toolCallEndTag}`
806
- };
807
- }
808
- return content;
809
- })
810
- };
811
- } else if (message.role === "tool") {
812
- return {
813
- role: "user",
814
- content: [
815
- {
816
- type: "text",
817
- text: message.content.map(
818
- (content) => `${toolResponseTag}${JSON.stringify({
819
- toolName: content.toolName,
820
- result: content.result
821
- })}${toolResponseEndTag}`
822
- ).join("\n")
823
- }
824
- ]
825
- };
826
- }
827
- return message;
1044
+ var _a, _b, _c, _d;
1045
+ const toolSystemPrompt = convertToolPrompt({
1046
+ paramsPrompt: params.prompt,
1047
+ paramsTools: params.tools,
1048
+ toolSystemPromptTemplate,
1049
+ toolCallTag,
1050
+ toolCallEndTag,
1051
+ toolResponseTag,
1052
+ toolResponseEndTag
828
1053
  });
829
- const HermesPrompt = toolSystemPromptTemplate(
830
- JSON.stringify(Object.entries(params.tools || {}))
831
- );
832
- const toolSystemPrompt = processedPrompt[0].role === "system" ? [
833
- {
834
- role: "system",
835
- content: HermesPrompt + "\n\n" + processedPrompt[0].content
836
- },
837
- ...processedPrompt.slice(1)
838
- ] : [
839
- {
840
- role: "system",
841
- content: HermesPrompt
842
- },
843
- ...processedPrompt
844
- ];
845
- return {
1054
+ const baseReturnParams = {
846
1055
  ...params,
847
1056
  prompt: toolSystemPrompt,
848
- // set the mode back to regular and remove the default tools.
1057
+ // Reset tools and toolChoice to default after prompt transformation
849
1058
  tools: [],
850
1059
  toolChoice: void 0
851
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;
852
1125
  }
853
1126
  };
854
1127
  }