@ai-sdk-tool/parser 2.1.0 → 2.1.1

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
@@ -4,8 +4,8 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
- // src/tool-call-middleware.ts
8
- import { generateId as generateId2 } from "@ai-sdk/provider-utils";
7
+ // src/stream-handler.ts
8
+ import { generateId } from "@ai-sdk/provider-utils";
9
9
 
10
10
  // src/utils/dynamic-tool-schema.ts
11
11
  function createDynamicIfThenElseSchema(tools) {
@@ -415,7 +415,6 @@ function appendPair(state, obj, key, value) {
415
415
  }
416
416
  function parsePair(tokens, state, obj) {
417
417
  let token = skipPunctuation(tokens, state, [":", "string", "number", "atom"]);
418
- let key;
419
418
  let value;
420
419
  if (token.type !== "string") {
421
420
  raiseUnexpected(state, token, "string key");
@@ -465,7 +464,7 @@ function parsePair(tokens, state, obj) {
465
464
  }
466
465
  }
467
466
  checkDuplicates(state, obj, token);
468
- key = String(token.value);
467
+ const key = String(token.value);
469
468
  skipColon(tokens, state);
470
469
  value = parseAny(tokens, state);
471
470
  appendPair(state, obj, key, value);
@@ -636,7 +635,10 @@ function parse(text, optsOrReviver) {
636
635
  if (!options.relaxed && !options.warnings && !options.tolerant) {
637
636
  if (!options.duplicate) {
638
637
  } else {
639
- return JSON.parse(text, options.reviver);
638
+ return JSON.parse(
639
+ text,
640
+ options.reviver
641
+ );
640
642
  }
641
643
  }
642
644
  const lexerToUse = options.relaxed ? lexer : strictLexer;
@@ -656,7 +658,7 @@ function parse(text, optsOrReviver) {
656
658
  };
657
659
  return parseAny(tokens, state, true);
658
660
  } else {
659
- const newtext = tokens.reduce((str, token) => {
661
+ tokens.reduce((str, token) => {
660
662
  return str + token.match;
661
663
  }, "");
662
664
  if (!options.relaxed && !options.warnings && !options.tolerant && options.duplicate) {
@@ -680,8 +682,11 @@ function parse(text, optsOrReviver) {
680
682
  } else {
681
683
  tokens = lexer(text);
682
684
  tokens = stripTrailingComma(tokens);
683
- const newtext2 = tokens.reduce((str, token) => str + token.match, "");
684
- return JSON.parse(newtext2, options.reviver);
685
+ const newtext = tokens.reduce((str, token) => str + token.match, "");
686
+ return JSON.parse(
687
+ newtext,
688
+ options.reviver
689
+ );
685
690
  }
686
691
  }
687
692
  }
@@ -735,7 +740,11 @@ function isToolChoiceActive(params) {
735
740
  }
736
741
  function getFunctionTools(params) {
737
742
  var _a, _b;
738
- const rawToolNames = params.providerOptions && typeof params.providerOptions === "object" && ((_a = params.providerOptions.toolCallMiddleware) == null ? void 0 : _a.toolNames) || [];
743
+ const functionTools = ((_a = params.tools) != null ? _a : []).filter(
744
+ (t) => t.type === "function"
745
+ );
746
+ if (functionTools.length > 0) return functionTools;
747
+ const rawToolNames = params.providerOptions && typeof params.providerOptions === "object" && ((_b = params.providerOptions.toolCallMiddleware) == null ? void 0 : _b.toolNames) || [];
739
748
  const toStringArray = (val) => Array.isArray(val) ? val.filter(
740
749
  (item) => typeof item === "string"
741
750
  ) : [];
@@ -748,9 +757,7 @@ function getFunctionTools(params) {
748
757
  inputSchema: { type: "object" }
749
758
  }));
750
759
  }
751
- return ((_b = params.tools) != null ? _b : []).filter(
752
- (t) => t.type === "function"
753
- );
760
+ return [];
754
761
  }
755
762
 
756
763
  // src/utils/on-error.ts
@@ -763,8 +770,243 @@ function extractOnErrorOption(providerOptions) {
763
770
  return void 0;
764
771
  }
765
772
 
773
+ // src/utils/coercion.ts
774
+ function unwrapJsonSchema(schema) {
775
+ if (!schema || typeof schema !== "object") return schema;
776
+ const s = schema;
777
+ if (s.jsonSchema && typeof s.jsonSchema === "object") {
778
+ return unwrapJsonSchema(s.jsonSchema);
779
+ }
780
+ return schema;
781
+ }
782
+ function getSchemaType(schema) {
783
+ const unwrapped = unwrapJsonSchema(schema);
784
+ if (!unwrapped || typeof unwrapped !== "object") return void 0;
785
+ const t = unwrapped.type;
786
+ if (typeof t === "string") return t;
787
+ if (Array.isArray(t)) {
788
+ const preferred = [
789
+ "object",
790
+ "array",
791
+ "boolean",
792
+ "number",
793
+ "integer",
794
+ "string"
795
+ ];
796
+ for (const p of preferred) if (t.includes(p)) return p;
797
+ }
798
+ const s = unwrapped;
799
+ if (s && typeof s === "object" && (s.properties || s.additionalProperties)) {
800
+ return "object";
801
+ }
802
+ if (s && typeof s === "object" && (s.items || s.prefixItems)) {
803
+ return "array";
804
+ }
805
+ return void 0;
806
+ }
807
+ function coerceBySchema(value, schema) {
808
+ const unwrapped = unwrapJsonSchema(schema);
809
+ if (!unwrapped || typeof unwrapped !== "object") {
810
+ if (typeof value === "string") {
811
+ const s = value.trim();
812
+ const lower = s.toLowerCase();
813
+ if (lower === "true") return true;
814
+ if (lower === "false") return false;
815
+ if (/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(s)) {
816
+ const num = Number(s);
817
+ if (Number.isFinite(num)) return num;
818
+ }
819
+ if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
820
+ try {
821
+ const parsed = JSON.parse(s);
822
+ return coerceBySchema(parsed, void 0);
823
+ } catch (e) {
824
+ }
825
+ }
826
+ }
827
+ return value;
828
+ }
829
+ const schemaType = getSchemaType(unwrapped);
830
+ if (typeof value === "string") {
831
+ const s = value.trim();
832
+ if (schemaType === "object") {
833
+ try {
834
+ let normalized = s.replace(/'/g, '"');
835
+ normalized = normalized.replace(/^\{\s*\}$/s, "{}");
836
+ const obj = JSON.parse(normalized);
837
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
838
+ const props = unwrapped.properties;
839
+ const out = {};
840
+ for (const [k, v] of Object.entries(obj)) {
841
+ const propSchema = props ? props[k] : void 0;
842
+ out[k] = typeof propSchema === "boolean" ? v : coerceBySchema(v, propSchema);
843
+ }
844
+ return out;
845
+ }
846
+ } catch (e) {
847
+ }
848
+ }
849
+ if (schemaType === "array") {
850
+ try {
851
+ const normalized = s.replace(/'/g, '"');
852
+ const arr = JSON.parse(normalized);
853
+ if (Array.isArray(arr)) {
854
+ const u = unwrapped;
855
+ const prefixItems = Array.isArray(
856
+ u.prefixItems
857
+ ) ? u.prefixItems : void 0;
858
+ const itemsSchema = u.items;
859
+ if (prefixItems && arr.length === prefixItems.length) {
860
+ return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
861
+ }
862
+ return arr.map((v) => coerceBySchema(v, itemsSchema));
863
+ }
864
+ } catch (e) {
865
+ const csv = s.includes("\n") ? s.split(/\n+/) : s.split(/,\s*/);
866
+ const trimmed = csv.map((x) => x.trim()).filter((x) => x.length > 0);
867
+ const u = unwrapped;
868
+ const prefixItems = Array.isArray(
869
+ u.prefixItems
870
+ ) ? u.prefixItems : void 0;
871
+ const itemsSchema = u.items;
872
+ if (prefixItems && trimmed.length === prefixItems.length) {
873
+ return trimmed.map((x, i) => coerceBySchema(x, prefixItems[i]));
874
+ }
875
+ return trimmed.map((x) => coerceBySchema(x, itemsSchema));
876
+ }
877
+ }
878
+ }
879
+ if (schemaType === "object" && value && typeof value === "object" && !Array.isArray(value)) {
880
+ const out = {};
881
+ const props = unwrapped.properties;
882
+ for (const [k, v] of Object.entries(value)) {
883
+ const propSchema = props ? props[k] : void 0;
884
+ out[k] = typeof propSchema === "boolean" ? v : coerceBySchema(v, propSchema);
885
+ }
886
+ return out;
887
+ }
888
+ if (schemaType === "array") {
889
+ const u = unwrapped;
890
+ const itemsSchema = u.items;
891
+ const prefixItems = Array.isArray(
892
+ u.prefixItems
893
+ ) ? u.prefixItems : void 0;
894
+ if (Array.isArray(value)) {
895
+ if (prefixItems && value.length === prefixItems.length) {
896
+ return value.map((v, i) => coerceBySchema(v, prefixItems[i]));
897
+ }
898
+ return value.map((v) => coerceBySchema(v, itemsSchema));
899
+ }
900
+ if (value && typeof value === "object") {
901
+ const maybe = value;
902
+ if (Object.prototype.hasOwnProperty.call(maybe, "item")) {
903
+ const items = maybe.item;
904
+ const arr = Array.isArray(items) ? items : [items];
905
+ if (prefixItems && arr.length === prefixItems.length) {
906
+ return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
907
+ }
908
+ return arr.map((v) => coerceBySchema(v, itemsSchema));
909
+ }
910
+ const keys = Object.keys(maybe);
911
+ if (keys.length === 1) {
912
+ const singleKey = keys[0];
913
+ const singleValue = maybe[singleKey];
914
+ if (Array.isArray(singleValue)) {
915
+ const coercedArray = singleValue.map(
916
+ (v) => coerceBySchema(v, itemsSchema)
917
+ );
918
+ return coercedArray;
919
+ }
920
+ }
921
+ if (keys.length > 0 && keys.every((k) => /^\d+$/.test(k))) {
922
+ const arr = keys.sort((a, b) => Number(a) - Number(b)).map((k) => maybe[k]);
923
+ if (prefixItems && arr.length === prefixItems.length) {
924
+ return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
925
+ }
926
+ return arr.map((v) => coerceBySchema(v, itemsSchema));
927
+ }
928
+ }
929
+ if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
930
+ if (prefixItems && prefixItems.length > 0) {
931
+ return [coerceBySchema(value, prefixItems[0])];
932
+ }
933
+ return [coerceBySchema(value, itemsSchema)];
934
+ }
935
+ }
936
+ if (typeof value === "string") {
937
+ const s = value.trim();
938
+ if (schemaType === "boolean") {
939
+ const lower = s.toLowerCase();
940
+ if (lower === "true") return true;
941
+ if (lower === "false") return false;
942
+ }
943
+ if (schemaType === "number" || schemaType === "integer") {
944
+ if (/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(s)) {
945
+ const num = Number(s);
946
+ if (Number.isFinite(num)) return num;
947
+ }
948
+ }
949
+ }
950
+ return value;
951
+ }
952
+ function fixToolCallWithSchema(part, tools) {
953
+ var _a;
954
+ if (part.type !== "tool-call") return part;
955
+ const tc = part;
956
+ let args = {};
957
+ if (typeof tc.input === "string") {
958
+ try {
959
+ args = JSON.parse(tc.input);
960
+ } catch (e) {
961
+ return part;
962
+ }
963
+ } else if (tc.input && typeof tc.input === "object") {
964
+ args = tc.input;
965
+ }
966
+ const schema = (_a = tools.find((t) => t.name === tc.toolName)) == null ? void 0 : _a.inputSchema;
967
+ const coerced = coerceBySchema(args, schema);
968
+ return {
969
+ ...part,
970
+ input: JSON.stringify(coerced != null ? coerced : {})
971
+ };
972
+ }
973
+ function coerceToolCallInput(part, tools) {
974
+ return fixToolCallWithSchema(part, tools);
975
+ }
976
+
977
+ // src/utils/protocol.ts
978
+ function isProtocolFactory(protocol) {
979
+ return typeof protocol === "function";
980
+ }
981
+
766
982
  // src/stream-handler.ts
767
- import { generateId } from "@ai-sdk/provider-utils";
983
+ async function wrapStream({
984
+ protocol,
985
+ doStream,
986
+ doGenerate,
987
+ params
988
+ }) {
989
+ var _a;
990
+ if (isToolChoiceActive(params)) {
991
+ return toolChoiceStream({
992
+ doGenerate,
993
+ options: extractOnErrorOption(params.providerOptions)
994
+ });
995
+ }
996
+ const { stream, ...rest } = await doStream();
997
+ return {
998
+ stream: stream.pipeThrough(
999
+ protocol.createStreamParser({
1000
+ tools: getFunctionTools(params),
1001
+ options: {
1002
+ ...extractOnErrorOption(params.providerOptions),
1003
+ ...(_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware
1004
+ }
1005
+ })
1006
+ ),
1007
+ ...rest
1008
+ };
1009
+ }
768
1010
  async function toolChoiceStream({
769
1011
  doGenerate,
770
1012
  options
@@ -817,283 +1059,337 @@ async function toolChoiceStream({
817
1059
  };
818
1060
  }
819
1061
 
820
- // src/tool-call-middleware.ts
821
- function isProtocolFactory(protocol) {
822
- return typeof protocol === "function";
1062
+ // src/generate-handler.ts
1063
+ import { generateId as generateId2 } from "@ai-sdk/provider-utils";
1064
+ async function wrapGenerate({
1065
+ protocol,
1066
+ doGenerate,
1067
+ params
1068
+ }) {
1069
+ var _a, _b;
1070
+ if (isToolChoiceActive(params)) {
1071
+ const result2 = await doGenerate();
1072
+ let parsed2 = {};
1073
+ const first = (_a = result2.content) == null ? void 0 : _a[0];
1074
+ if (first && first.type === "text") {
1075
+ try {
1076
+ parsed2 = JSON.parse(first.text);
1077
+ } catch (error) {
1078
+ const options = extractOnErrorOption(params.providerOptions);
1079
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1080
+ options,
1081
+ "Failed to parse toolChoice JSON from generated model output",
1082
+ {
1083
+ text: first.text,
1084
+ error: error instanceof Error ? error.message : String(error)
1085
+ }
1086
+ );
1087
+ parsed2 = {};
1088
+ }
1089
+ }
1090
+ const toolCall = {
1091
+ type: "tool-call",
1092
+ toolCallId: generateId2(),
1093
+ toolName: parsed2.name || "unknown",
1094
+ input: JSON.stringify(parsed2.arguments || {})
1095
+ };
1096
+ return {
1097
+ ...result2,
1098
+ content: [toolCall]
1099
+ };
1100
+ }
1101
+ const result = await doGenerate();
1102
+ if (result.content.length === 0) {
1103
+ return result;
1104
+ }
1105
+ const parsed = result.content.flatMap((contentItem) => {
1106
+ var _a2;
1107
+ if (contentItem.type !== "text") {
1108
+ return [contentItem];
1109
+ }
1110
+ return protocol.parseGeneratedText({
1111
+ text: contentItem.text,
1112
+ tools: getFunctionTools(params),
1113
+ options: {
1114
+ ...extractOnErrorOption(params.providerOptions),
1115
+ ...(_a2 = params.providerOptions) == null ? void 0 : _a2.toolCallMiddleware
1116
+ }
1117
+ });
1118
+ });
1119
+ const tools = getFunctionTools(params);
1120
+ const newContent = parsed.map(
1121
+ (part) => coerceToolCallInput(part, tools)
1122
+ );
1123
+ return {
1124
+ ...result,
1125
+ content: newContent
1126
+ };
823
1127
  }
824
- function createToolMiddleware({
1128
+
1129
+ // src/transform-handler.ts
1130
+ async function transformParams({
1131
+ params,
825
1132
  protocol,
826
1133
  toolSystemPromptTemplate
827
1134
  }) {
1135
+ var _a, _b, _c, _d, _e, _f, _g, _h;
828
1136
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
829
- return {
830
- middlewareVersion: "v2",
831
- wrapStream: async ({ doStream, doGenerate, params }) => {
832
- if (isToolChoiceActive(params)) {
833
- return toolChoiceStream({
834
- doGenerate,
835
- options: extractOnErrorOption(params.providerOptions)
836
- });
837
- }
838
- const { stream, ...rest } = await doStream();
839
- return {
840
- stream: stream.pipeThrough(
841
- resolvedProtocol.createStreamParser({
842
- tools: getFunctionTools(params),
843
- options: extractOnErrorOption(params.providerOptions)
844
- })
845
- ),
846
- ...rest
847
- };
1137
+ const functionTools = ((_a = params.tools) != null ? _a : []).filter(
1138
+ (t) => t.type === "function"
1139
+ );
1140
+ const systemPrompt = resolvedProtocol.formatTools({
1141
+ tools: functionTools,
1142
+ toolSystemPromptTemplate
1143
+ });
1144
+ const processedPrompt = convertToolPrompt(
1145
+ (_b = params.prompt) != null ? _b : [],
1146
+ resolvedProtocol,
1147
+ extractOnErrorOption(params.providerOptions)
1148
+ );
1149
+ const finalPrompt = ((_c = processedPrompt[0]) == null ? void 0 : _c.role) === "system" ? [
1150
+ {
1151
+ role: "system",
1152
+ content: systemPrompt + "\n\n" + processedPrompt[0].content
848
1153
  },
849
- wrapGenerate: async ({ doGenerate, params }) => {
850
- var _a, _b;
851
- if (isToolChoiceActive(params)) {
852
- const result2 = await doGenerate();
853
- let parsed = {};
854
- const first = (_a = result2.content) == null ? void 0 : _a[0];
855
- if (first && first.type === "text") {
856
- try {
857
- parsed = JSON.parse(first.text);
858
- } catch (error) {
859
- const options = extractOnErrorOption(params.providerOptions);
860
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
861
- options,
862
- "Failed to parse toolChoice JSON from generated model output",
863
- {
864
- text: first.text,
865
- error: error instanceof Error ? error.message : String(error)
866
- }
867
- );
868
- parsed = {};
869
- }
1154
+ ...processedPrompt.slice(1)
1155
+ ] : [
1156
+ {
1157
+ role: "system",
1158
+ content: systemPrompt
1159
+ },
1160
+ ...processedPrompt
1161
+ ];
1162
+ const baseReturnParams = {
1163
+ ...params,
1164
+ prompt: finalPrompt,
1165
+ tools: [],
1166
+ toolChoice: void 0,
1167
+ providerOptions: {
1168
+ ...params.providerOptions || {},
1169
+ toolCallMiddleware: {
1170
+ ...params.providerOptions && typeof params.providerOptions === "object" && params.providerOptions.toolCallMiddleware || {},
1171
+ toolNames: functionTools.map((t) => t.name)
1172
+ }
1173
+ }
1174
+ };
1175
+ if (((_d = params.toolChoice) == null ? void 0 : _d.type) === "none") {
1176
+ throw new Error(
1177
+ "The 'none' toolChoice type is not supported by this middleware. Please use 'auto', 'required', or specify a tool name."
1178
+ );
1179
+ }
1180
+ if (((_e = params.toolChoice) == null ? void 0 : _e.type) === "tool") {
1181
+ const selectedToolName = params.toolChoice.toolName;
1182
+ const providerDefinedMatch = ((_f = params.tools) != null ? _f : []).find((t) => {
1183
+ if (t.type === "function") return false;
1184
+ const anyTool = t;
1185
+ return anyTool.id === selectedToolName || anyTool.name === selectedToolName;
1186
+ });
1187
+ if (providerDefinedMatch) {
1188
+ throw new Error(
1189
+ "Provider-defined tools are not supported by this middleware. Please use custom tools."
1190
+ );
1191
+ }
1192
+ const selectedTool = ((_g = params.tools) != null ? _g : []).find(
1193
+ (t) => t.type === "function" && t.name === selectedToolName
1194
+ );
1195
+ if (!selectedTool) {
1196
+ throw new Error(
1197
+ `Tool with name '${selectedToolName}' not found in params.tools.`
1198
+ );
1199
+ }
1200
+ return {
1201
+ ...baseReturnParams,
1202
+ responseFormat: {
1203
+ type: "json",
1204
+ schema: {
1205
+ type: "object",
1206
+ properties: {
1207
+ name: {
1208
+ const: selectedTool.name
1209
+ },
1210
+ arguments: selectedTool.inputSchema
1211
+ },
1212
+ required: ["name", "arguments"]
1213
+ },
1214
+ name: selectedTool.name,
1215
+ description: typeof selectedTool.description === "string" ? selectedTool.description : void 0
1216
+ },
1217
+ providerOptions: {
1218
+ ...baseReturnParams.providerOptions || {},
1219
+ toolCallMiddleware: {
1220
+ ...baseReturnParams.providerOptions && typeof baseReturnParams.providerOptions === "object" && baseReturnParams.providerOptions.toolCallMiddleware || {},
1221
+ toolChoice: params.toolChoice
870
1222
  }
871
- return {
872
- ...result2,
873
- content: [
874
- {
875
- type: "tool-call",
876
- toolCallId: generateId2(),
877
- toolName: parsed.name || "unknown",
878
- input: JSON.stringify(parsed.arguments || {})
879
- }
880
- ]
881
- };
882
1223
  }
883
- const result = await doGenerate();
884
- if (result.content.length === 0) {
885
- return result;
1224
+ };
1225
+ }
1226
+ if (((_h = params.toolChoice) == null ? void 0 : _h.type) === "required") {
1227
+ if (!params.tools || params.tools.length === 0) {
1228
+ throw new Error(
1229
+ "Tool choice type 'required' is set, but no tools are provided in params.tools."
1230
+ );
1231
+ }
1232
+ return {
1233
+ ...baseReturnParams,
1234
+ responseFormat: {
1235
+ type: "json",
1236
+ schema: createDynamicIfThenElseSchema(functionTools)
1237
+ },
1238
+ providerOptions: {
1239
+ ...baseReturnParams.providerOptions || {},
1240
+ toolCallMiddleware: {
1241
+ ...baseReturnParams.providerOptions && typeof baseReturnParams.providerOptions === "object" && baseReturnParams.providerOptions.toolCallMiddleware || {},
1242
+ toolChoice: { type: "required" }
1243
+ }
886
1244
  }
887
- const newContent = result.content.flatMap((contentItem) => {
888
- if (contentItem.type !== "text") {
889
- return [contentItem];
1245
+ };
1246
+ }
1247
+ return baseReturnParams;
1248
+ }
1249
+ function convertToolPrompt(prompt, resolvedProtocol, providerOptions) {
1250
+ const processedPrompt = prompt.map((message) => {
1251
+ var _a;
1252
+ if (message.role === "assistant") {
1253
+ const newContent = [];
1254
+ for (const content of message.content) {
1255
+ if (isToolCallContent(content)) {
1256
+ newContent.push({
1257
+ type: "text",
1258
+ text: resolvedProtocol.formatToolCall(content)
1259
+ });
1260
+ } else if (content.type === "text") {
1261
+ newContent.push(content);
1262
+ } else if (content.type === "reasoning") {
1263
+ newContent.push(content);
1264
+ } else {
1265
+ const options = extractOnErrorOption(providerOptions);
1266
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1267
+ options,
1268
+ "tool-call-middleware: unknown assistant content; stringifying for provider compatibility",
1269
+ { content }
1270
+ );
1271
+ newContent.push({
1272
+ type: "text",
1273
+ text: JSON.stringify(content)
1274
+ });
890
1275
  }
891
- return resolvedProtocol.parseGeneratedText({
892
- text: contentItem.text,
893
- tools: getFunctionTools(params),
894
- options: extractOnErrorOption(params.providerOptions)
895
- });
896
- });
1276
+ }
1277
+ const onlyText = newContent.every((c) => c.type === "text");
1278
+ const condensedAssistant = onlyText ? [
1279
+ {
1280
+ type: "text",
1281
+ text: newContent.map((c) => c.text).join("\n")
1282
+ }
1283
+ ] : newContent;
1284
+ return { role: "assistant", content: condensedAssistant };
1285
+ }
1286
+ if (message.role === "tool") {
897
1287
  return {
898
- ...result,
899
- content: newContent
1288
+ role: "user",
1289
+ // Map tool results to text response blocks, then condense into a single text block
1290
+ content: [
1291
+ {
1292
+ type: "text",
1293
+ text: message.content.map(
1294
+ (toolResult) => isToolResultPart(toolResult) ? resolvedProtocol.formatToolResponse(toolResult) : resolvedProtocol.formatToolResponse(
1295
+ toolResult
1296
+ )
1297
+ ).join("\n")
1298
+ }
1299
+ ]
900
1300
  };
901
- },
902
- transformParams: async ({ params }) => {
903
- var _a, _b, _c, _d, _e, _f;
904
- const convertToolPrompt = (prompt) => {
905
- const processedPrompt2 = prompt.map((message) => {
906
- var _a2;
907
- if (message.role === "assistant") {
908
- const newContent = [];
909
- for (const content of message.content) {
910
- if (isToolCallContent(content)) {
911
- newContent.push({
912
- type: "text",
913
- text: resolvedProtocol.formatToolCall(content)
914
- });
915
- } else if (content.type === "text") {
916
- newContent.push(content);
917
- } else if (content.type === "reasoning") {
918
- newContent.push(content);
919
- } else {
920
- const options = extractOnErrorOption(params.providerOptions);
921
- (_a2 = options == null ? void 0 : options.onError) == null ? void 0 : _a2.call(
922
- options,
923
- "tool-call-middleware: unknown assistant content; stringifying for provider compatibility",
924
- { content }
925
- );
926
- newContent.push({
927
- type: "text",
928
- text: JSON.stringify(content)
929
- });
1301
+ }
1302
+ return message;
1303
+ });
1304
+ for (let i = 0; i < processedPrompt.length; i++) {
1305
+ const msg = processedPrompt[i];
1306
+ if (Array.isArray(msg.content)) {
1307
+ const allText = msg.content.every(
1308
+ (c) => (c == null ? void 0 : c.type) === "text"
1309
+ );
1310
+ if (allText && msg.content.length > 1) {
1311
+ const joinedText = msg.content.map((c) => c.text).join("\n");
1312
+ if (msg.role === "system") {
1313
+ processedPrompt[i] = {
1314
+ role: "system",
1315
+ content: joinedText
1316
+ };
1317
+ } else if (msg.role === "assistant") {
1318
+ processedPrompt[i] = {
1319
+ role: "assistant",
1320
+ content: [
1321
+ {
1322
+ type: "text",
1323
+ text: joinedText
930
1324
  }
931
- }
932
- const onlyText = newContent.every((c) => c.type === "text");
933
- const condensedAssistant = onlyText ? [
1325
+ ]
1326
+ };
1327
+ } else {
1328
+ processedPrompt[i] = {
1329
+ role: "user",
1330
+ content: [
934
1331
  {
935
1332
  type: "text",
936
- text: newContent.map((c) => c.text).join("\n")
1333
+ text: joinedText
937
1334
  }
938
- ] : newContent;
939
- return { role: "assistant", content: condensedAssistant };
940
- }
941
- if (message.role === "tool") {
942
- return {
943
- role: "user",
944
- // Map tool results to text response blocks, then condense into a single text block
945
- content: [
946
- {
947
- type: "text",
948
- text: message.content.map(
949
- (toolResult) => isToolResultPart(toolResult) ? resolvedProtocol.formatToolResponse(toolResult) : resolvedProtocol.formatToolResponse(
950
- toolResult
951
- )
952
- ).join("\n")
953
- }
954
- ]
955
- };
956
- }
957
- return message;
958
- });
959
- for (let i = 0; i < processedPrompt2.length; i++) {
960
- const msg = processedPrompt2[i];
961
- if (Array.isArray(msg.content)) {
962
- const allText = msg.content.every((c) => (c == null ? void 0 : c.type) === "text");
963
- if (allText && msg.content.length > 1) {
964
- processedPrompt2[i] = {
965
- role: msg.role,
966
- content: [
967
- {
968
- type: "text",
969
- text: msg.content.map((c) => c.text).join("\n")
970
- }
971
- ]
972
- };
973
- }
974
- }
975
- }
976
- for (let i = processedPrompt2.length - 1; i > 0; i--) {
977
- const current = processedPrompt2[i];
978
- const prev = processedPrompt2[i - 1];
979
- if (current.role === "user" && prev.role === "user") {
980
- const prevContent = prev.content.map((c) => c.type === "text" ? c.text : "").join("\n");
981
- const currentContent = current.content.map((c) => c.type === "text" ? c.text : "").join("\n");
982
- processedPrompt2[i - 1] = {
983
- role: "user",
984
- content: [
985
- { type: "text", text: prevContent + "\n" + currentContent }
986
- ]
987
- };
988
- processedPrompt2.splice(i, 1);
989
- }
990
- }
991
- return processedPrompt2;
992
- };
993
- const functionTools = ((_a = params.tools) != null ? _a : []).filter(
994
- (t) => t.type === "function"
995
- );
996
- const systemPrompt = resolvedProtocol.formatTools({
997
- tools: functionTools,
998
- toolSystemPromptTemplate
999
- });
1000
- const processedPrompt = convertToolPrompt(params.prompt);
1001
- const finalPrompt = ((_b = processedPrompt[0]) == null ? void 0 : _b.role) === "system" ? [
1002
- {
1003
- role: "system",
1004
- content: systemPrompt + "\n\n" + processedPrompt[0].content
1005
- },
1006
- ...processedPrompt.slice(1)
1007
- ] : [
1008
- {
1009
- role: "system",
1010
- content: systemPrompt
1011
- },
1012
- ...processedPrompt
1013
- ];
1014
- const baseReturnParams = {
1015
- ...params,
1016
- prompt: finalPrompt,
1017
- tools: [],
1018
- toolChoice: void 0,
1019
- providerOptions: {
1020
- ...params.providerOptions || {},
1021
- toolCallMiddleware: {
1022
- ...params.providerOptions && typeof params.providerOptions === "object" && params.providerOptions.toolCallMiddleware || {},
1023
- toolNames: functionTools.map((t) => t.name)
1024
- }
1025
- }
1026
- };
1027
- if (((_c = params.toolChoice) == null ? void 0 : _c.type) === "none") {
1028
- throw new Error(
1029
- "The 'none' toolChoice type is not supported by this middleware. Please use 'auto', 'required', or specify a tool name."
1030
- );
1031
- }
1032
- if (((_d = params.toolChoice) == null ? void 0 : _d.type) === "tool") {
1033
- const selectedToolName = params.toolChoice.toolName;
1034
- const selectedTool = (_e = params.tools) == null ? void 0 : _e.find(
1035
- (tool) => tool.type === "function" ? tool.name === selectedToolName : tool.id === selectedToolName
1036
- );
1037
- if (!selectedTool) {
1038
- throw new Error(
1039
- `Tool with name '${selectedToolName}' not found in params.tools.`
1040
- );
1041
- }
1042
- if (selectedTool.type === "provider-defined") {
1043
- throw new Error(
1044
- "Provider-defined tools are not supported by this middleware. Please use custom tools."
1045
- );
1335
+ ]
1336
+ };
1046
1337
  }
1047
- return {
1048
- ...baseReturnParams,
1049
- responseFormat: {
1050
- type: "json",
1051
- schema: {
1052
- type: "object",
1053
- properties: {
1054
- name: {
1055
- const: selectedTool.name
1056
- },
1057
- arguments: selectedTool.inputSchema
1058
- },
1059
- required: ["name", "arguments"]
1060
- },
1061
- name: selectedTool.name,
1062
- description: selectedTool.type === "function" && typeof selectedTool.description === "string" ? selectedTool.description : void 0
1063
- },
1064
- providerOptions: {
1065
- ...baseReturnParams.providerOptions || {},
1066
- toolCallMiddleware: {
1067
- ...baseReturnParams.providerOptions && typeof baseReturnParams.providerOptions === "object" && baseReturnParams.providerOptions.toolCallMiddleware || {},
1068
- toolChoice: params.toolChoice
1069
- }
1070
- }
1071
- };
1072
1338
  }
1073
- if (((_f = params.toolChoice) == null ? void 0 : _f.type) === "required") {
1074
- if (!params.tools || params.tools.length === 0) {
1075
- throw new Error(
1076
- "Tool choice type 'required' is set, but no tools are provided in params.tools."
1077
- );
1078
- }
1079
- return {
1080
- ...baseReturnParams,
1081
- responseFormat: {
1082
- type: "json",
1083
- schema: createDynamicIfThenElseSchema(
1084
- params.tools.filter((t) => t.type === "function")
1085
- )
1086
- },
1087
- providerOptions: {
1088
- ...baseReturnParams.providerOptions || {},
1089
- toolCallMiddleware: {
1090
- ...baseReturnParams.providerOptions && typeof baseReturnParams.providerOptions === "object" && baseReturnParams.providerOptions.toolCallMiddleware || {},
1091
- toolChoice: { type: "required" }
1092
- }
1093
- }
1094
- };
1339
+ }
1340
+ }
1341
+ for (let i = processedPrompt.length - 1; i > 0; i--) {
1342
+ const current = processedPrompt[i];
1343
+ const prev = processedPrompt[i - 1];
1344
+ if (current.role === "user" && prev.role === "user") {
1345
+ const prevContent = prev.content.map((c) => c.type === "text" ? c.text : "").join("\n");
1346
+ const currentContent = current.content.map((c) => c.type === "text" ? c.text : "").join("\n");
1347
+ processedPrompt[i - 1] = {
1348
+ role: "user",
1349
+ content: [{ type: "text", text: prevContent + "\n" + currentContent }]
1350
+ };
1351
+ processedPrompt.splice(i, 1);
1352
+ }
1353
+ }
1354
+ return processedPrompt;
1355
+ }
1356
+
1357
+ // src/tool-call-middleware.ts
1358
+ function createToolMiddleware({
1359
+ protocol,
1360
+ toolSystemPromptTemplate
1361
+ }) {
1362
+ const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
1363
+ return {
1364
+ middlewareVersion: "v2",
1365
+ wrapStream: async ({ doStream, doGenerate, params }) => {
1366
+ if (isToolChoiceActive(params)) {
1367
+ return toolChoiceStream({
1368
+ doGenerate,
1369
+ options: extractOnErrorOption(params.providerOptions)
1370
+ });
1371
+ } else {
1372
+ return wrapStream({
1373
+ protocol: resolvedProtocol,
1374
+ doStream,
1375
+ doGenerate,
1376
+ params
1377
+ });
1095
1378
  }
1096
- return baseReturnParams;
1379
+ },
1380
+ wrapGenerate: async ({ doGenerate, params }) => wrapGenerate({
1381
+ protocol: resolvedProtocol,
1382
+ doGenerate,
1383
+ params
1384
+ }),
1385
+ transformParams: async ({
1386
+ params
1387
+ }) => {
1388
+ return transformParams({
1389
+ protocol: resolvedProtocol,
1390
+ toolSystemPromptTemplate,
1391
+ params
1392
+ });
1097
1393
  }
1098
1394
  };
1099
1395
  }
@@ -1334,7 +1630,7 @@ var xmlProtocol = () => ({
1334
1630
  const toolsForPrompt = (tools || []).map((tool) => ({
1335
1631
  name: tool.name,
1336
1632
  description: tool.description,
1337
- parameters: tool.inputSchema
1633
+ parameters: unwrapJsonSchema(tool.inputSchema)
1338
1634
  }));
1339
1635
  return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
1340
1636
  },
@@ -1367,7 +1663,8 @@ var xmlProtocol = () => ({
1367
1663
  return xmlContent;
1368
1664
  },
1369
1665
  parseGeneratedText({ text, tools, options }) {
1370
- var _a, _b;
1666
+ var _a, _b, _c;
1667
+ const originalSchemas = (options == null ? void 0 : options.originalToolSchemas) || {};
1371
1668
  const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1372
1669
  if (toolNames.length === 0) {
1373
1670
  return [{ type: "text", text }];
@@ -1405,17 +1702,87 @@ var xmlProtocol = () => ({
1405
1702
  if (v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, "#text")) {
1406
1703
  val = v == null ? void 0 : v["#text"];
1407
1704
  }
1705
+ if (Array.isArray(v)) {
1706
+ val = v.map((item) => {
1707
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1708
+ const textVal = item == null ? void 0 : item["#text"];
1709
+ return typeof textVal === "string" ? textVal.trim() : textVal;
1710
+ }
1711
+ return typeof item === "string" ? item.trim() : item;
1712
+ });
1713
+ } else if (v && typeof v === "object" && !Object.prototype.hasOwnProperty.call(v, "#text")) {
1714
+ const obj = v;
1715
+ const keys = Object.keys(obj);
1716
+ if (keys.length === 1 && keys[0] === "item") {
1717
+ const itemValue = obj.item;
1718
+ if (Array.isArray(itemValue)) {
1719
+ val = itemValue.map((item) => {
1720
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1721
+ const textVal = item == null ? void 0 : item["#text"];
1722
+ const trimmed2 = typeof textVal === "string" ? textVal.trim() : textVal;
1723
+ if (typeof trimmed2 === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed2)) {
1724
+ const num = Number(trimmed2);
1725
+ if (Number.isFinite(num)) return num;
1726
+ }
1727
+ return trimmed2;
1728
+ }
1729
+ const trimmed = typeof item === "string" ? item.trim() : item;
1730
+ if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1731
+ const num = Number(trimmed);
1732
+ if (Number.isFinite(num)) return num;
1733
+ }
1734
+ return trimmed;
1735
+ });
1736
+ } else {
1737
+ const trimmed = typeof itemValue === "string" ? itemValue.trim() : itemValue;
1738
+ if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1739
+ const num = Number(trimmed);
1740
+ if (Number.isFinite(num)) {
1741
+ val = num;
1742
+ } else {
1743
+ val = trimmed;
1744
+ }
1745
+ } else {
1746
+ val = trimmed;
1747
+ }
1748
+ }
1749
+ } else {
1750
+ const isIndexedTuple = keys.length > 0 && keys.every((key) => /^\d+$/.test(key)) && (() => {
1751
+ const indices = keys.map((k2) => parseInt(k2)).sort((a, b) => a - b);
1752
+ return indices[0] === 0 && indices.every((val2, idx) => val2 === idx);
1753
+ })();
1754
+ if (isIndexedTuple) {
1755
+ const sortedKeys = keys.sort(
1756
+ (a, b) => parseInt(a) - parseInt(b)
1757
+ );
1758
+ val = sortedKeys.map((key) => {
1759
+ const item = obj[key];
1760
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1761
+ const textVal = item == null ? void 0 : item["#text"];
1762
+ return typeof textVal === "string" ? textVal.trim() : textVal;
1763
+ }
1764
+ return typeof item === "string" ? item.trim() : item;
1765
+ });
1766
+ } else {
1767
+ val = v;
1768
+ }
1769
+ }
1770
+ }
1408
1771
  args[k] = typeof val === "string" ? val.trim() : val;
1409
1772
  }
1773
+ const originalSchema = originalSchemas[toolName];
1774
+ const fallbackSchema = (_b = tools.find((t) => t.name === toolName)) == null ? void 0 : _b.inputSchema;
1775
+ const schema = originalSchema || fallbackSchema;
1776
+ const coercedArgs = coerceBySchema(args, schema);
1410
1777
  processedElements.push({
1411
1778
  type: "tool-call",
1412
1779
  toolCallId: generateId4(),
1413
1780
  toolName,
1414
- input: JSON.stringify(args)
1781
+ input: JSON.stringify(coercedArgs)
1415
1782
  });
1416
1783
  } catch (error) {
1417
1784
  const message = `Could not process XML tool call, keeping original text: ${match[0]}`;
1418
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, message, { toolCall: match[0], toolName, error });
1785
+ (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, message, { toolCall: match[0], toolName, error });
1419
1786
  processedElements.push({ type: "text", text: match[0] });
1420
1787
  }
1421
1788
  currentIndex = startIndex + match[0].length;
@@ -1429,6 +1796,7 @@ var xmlProtocol = () => ({
1429
1796
  return processedElements;
1430
1797
  },
1431
1798
  createStreamParser({ tools, options }) {
1799
+ const originalSchemas = (options == null ? void 0 : options.originalToolSchemas) || {};
1432
1800
  const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1433
1801
  let buffer = "";
1434
1802
  let currentToolCall = null;
@@ -1456,7 +1824,7 @@ var xmlProtocol = () => ({
1456
1824
  };
1457
1825
  return new TransformStream({
1458
1826
  transform(chunk, controller) {
1459
- var _a;
1827
+ var _a, _b;
1460
1828
  if (chunk.type !== "text-delta") {
1461
1829
  if (buffer) flushText(controller);
1462
1830
  controller.enqueue(chunk);
@@ -1485,14 +1853,86 @@ var xmlProtocol = () => ({
1485
1853
  if (v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, "#text")) {
1486
1854
  val = v == null ? void 0 : v["#text"];
1487
1855
  }
1856
+ if (Array.isArray(v)) {
1857
+ val = v.map((item) => {
1858
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1859
+ const textVal = item == null ? void 0 : item["#text"];
1860
+ return typeof textVal === "string" ? textVal.trim() : textVal;
1861
+ }
1862
+ return typeof item === "string" ? item.trim() : item;
1863
+ });
1864
+ } else if (v && typeof v === "object" && !Object.prototype.hasOwnProperty.call(v, "#text")) {
1865
+ const obj = v;
1866
+ const keys = Object.keys(obj);
1867
+ if (keys.length === 1 && keys[0] === "item") {
1868
+ const itemValue = obj.item;
1869
+ if (Array.isArray(itemValue)) {
1870
+ val = itemValue.map((item) => {
1871
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1872
+ const textVal = item == null ? void 0 : item["#text"];
1873
+ const trimmed2 = typeof textVal === "string" ? textVal.trim() : textVal;
1874
+ if (typeof trimmed2 === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed2)) {
1875
+ const num = Number(trimmed2);
1876
+ if (Number.isFinite(num)) return num;
1877
+ }
1878
+ return trimmed2;
1879
+ }
1880
+ const trimmed = typeof item === "string" ? item.trim() : item;
1881
+ if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1882
+ const num = Number(trimmed);
1883
+ if (Number.isFinite(num)) return num;
1884
+ }
1885
+ return trimmed;
1886
+ });
1887
+ } else {
1888
+ const trimmed = typeof itemValue === "string" ? itemValue.trim() : itemValue;
1889
+ if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1890
+ const num = Number(trimmed);
1891
+ if (Number.isFinite(num)) {
1892
+ val = num;
1893
+ } else {
1894
+ val = trimmed;
1895
+ }
1896
+ } else {
1897
+ val = trimmed;
1898
+ }
1899
+ }
1900
+ } else {
1901
+ const isIndexedTuple = keys.length > 0 && keys.every((key) => /^\d+$/.test(key)) && (() => {
1902
+ const indices = keys.map((k2) => parseInt(k2)).sort((a, b) => a - b);
1903
+ return indices[0] === 0 && indices.every((val2, idx) => val2 === idx);
1904
+ })();
1905
+ if (isIndexedTuple) {
1906
+ const sortedKeys = keys.sort(
1907
+ (a, b) => parseInt(a) - parseInt(b)
1908
+ );
1909
+ val = sortedKeys.map((key) => {
1910
+ const item = obj[key];
1911
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1912
+ const textVal = item == null ? void 0 : item["#text"];
1913
+ return typeof textVal === "string" ? textVal.trim() : textVal;
1914
+ }
1915
+ return typeof item === "string" ? item.trim() : item;
1916
+ });
1917
+ } else {
1918
+ val = v;
1919
+ }
1920
+ }
1921
+ }
1488
1922
  args[k] = typeof val === "string" ? val.trim() : val;
1489
1923
  }
1924
+ const originalSchema = originalSchemas[currentToolCall.name];
1925
+ const fallbackSchema = (_b = tools.find(
1926
+ (t) => t.name === currentToolCall.name
1927
+ )) == null ? void 0 : _b.inputSchema;
1928
+ const toolSchema = originalSchema || fallbackSchema;
1929
+ const coercedArgs = coerceBySchema(args, toolSchema);
1490
1930
  flushText(controller);
1491
1931
  controller.enqueue({
1492
1932
  type: "tool-call",
1493
1933
  toolCallId: generateId4(),
1494
1934
  toolName: currentToolCall.name,
1495
- input: JSON.stringify(args)
1935
+ input: JSON.stringify(coercedArgs)
1496
1936
  });
1497
1937
  } catch (e) {
1498
1938
  const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
@@ -1596,13 +2036,14 @@ For each function call return a json object with function name and arguments wit
1596
2036
  var xmlToolMiddleware = createToolMiddleware({
1597
2037
  protocol: xmlProtocol,
1598
2038
  toolSystemPromptTemplate(tools) {
1599
- return `You are KorinAI, a function-calling AI model.
2039
+ return `You are a function calling AI model.
1600
2040
  You are provided with function signatures within <tools></tools> XML tags.
1601
2041
  You may call one or more functions to assist with the user query.
1602
2042
  Don't make assumptions about what values to plug into functions.
1603
2043
  Here are the available tools: <tools>${tools}</tools>
1604
- For each function call return a tool call in an XML tag that matches the tool's name, and nothing else.
1605
- Example KorinAI-style call (text form):
2044
+ For a function call, return exactly one XML element whose tag name matches the tool's name, and nothing else.
2045
+ When an argument is an array, write each item inside a single element on one line separated by commas (or provide a JSON-like list). When an argument is an object, provide a JSON-like value.
2046
+ Examples:
1606
2047
  <get_weather>
1607
2048
  <location>
1608
2049
  San Fransisco
@@ -1611,10 +2052,14 @@ San Fransisco
1611
2052
  }
1612
2053
  });
1613
2054
  export {
2055
+ coerceBySchema,
1614
2056
  createToolMiddleware,
2057
+ fixToolCallWithSchema,
1615
2058
  gemmaToolMiddleware,
2059
+ getSchemaType,
1616
2060
  hermesToolMiddleware,
1617
2061
  jsonMixProtocol,
2062
+ unwrapJsonSchema,
1618
2063
  xmlProtocol,
1619
2064
  xmlToolMiddleware
1620
2065
  };