@ai-sdk-tool/parser 2.1.1 → 2.1.3

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,9 +4,12 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
- // src/stream-handler.ts
7
+ // src/protocols/dummy-protocol.ts
8
8
  import { generateId } from "@ai-sdk/provider-utils";
9
9
 
10
+ // src/protocols/json-mix-protocol.ts
11
+ import { generateId as generateId2 } from "@ai-sdk/provider-utils";
12
+
10
13
  // src/utils/dynamic-tool-schema.ts
11
14
  function createDynamicIfThenElseSchema(tools) {
12
15
  let currentSchema = {};
@@ -81,6 +84,11 @@ function getPotentialStartIndex(text, searchedText) {
81
84
  return null;
82
85
  }
83
86
 
87
+ // src/utils/regex.ts
88
+ function escapeRegExp(literal) {
89
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
90
+ }
91
+
84
92
  // src/utils/relaxed-json.ts
85
93
  var relaxed_json_exports = {};
86
94
  __export(relaxed_json_exports, {
@@ -714,62 +722,6 @@ function stringify(obj) {
714
722
  return "null";
715
723
  }
716
724
 
717
- // src/utils/regex.ts
718
- function escapeRegExp(literal) {
719
- return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
720
- }
721
-
722
- // src/utils/type-guards.ts
723
- function isToolCallContent(content) {
724
- return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
725
- (typeof content.input === "string" || typeof content.input === "object");
726
- }
727
- function isToolResultPart(content) {
728
- const c = content;
729
- return !!c && c.type === "tool-result" && typeof c.toolName === "string" && typeof c.toolCallId === "string" && "output" in c;
730
- }
731
- function hasInputProperty(obj) {
732
- return typeof obj === "object" && obj !== null && "input" in obj;
733
- }
734
-
735
- // src/utils/tools.ts
736
- function isToolChoiceActive(params) {
737
- var _a, _b, _c;
738
- const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
739
- return !!(typeof params.providerOptions === "object" && params.providerOptions !== null && typeof ((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) === "object" && toolChoice && typeof toolChoice === "object" && (toolChoice.type === "tool" || toolChoice.type === "required"));
740
- }
741
- function getFunctionTools(params) {
742
- var _a, _b;
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) || [];
748
- const toStringArray = (val) => Array.isArray(val) ? val.filter(
749
- (item) => typeof item === "string"
750
- ) : [];
751
- const toolNames = toStringArray(rawToolNames);
752
- if (toolNames.length > 0) {
753
- return toolNames.map((name) => ({
754
- type: "function",
755
- name,
756
- description: "",
757
- inputSchema: { type: "object" }
758
- }));
759
- }
760
- return [];
761
- }
762
-
763
- // src/utils/on-error.ts
764
- function extractOnErrorOption(providerOptions) {
765
- var _a;
766
- if (providerOptions && typeof providerOptions === "object") {
767
- const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
768
- return onError ? { onError } : void 0;
769
- }
770
- return void 0;
771
- }
772
-
773
725
  // src/utils/coercion.ts
774
726
  function unwrapJsonSchema(schema) {
775
727
  if (!schema || typeof schema !== "object") return schema;
@@ -974,498 +926,1080 @@ function coerceToolCallInput(part, tools) {
974
926
  return fixToolCallWithSchema(part, tools);
975
927
  }
976
928
 
977
- // src/utils/protocol.ts
978
- function isProtocolFactory(protocol) {
979
- return typeof protocol === "function";
929
+ // src/utils/debug.ts
930
+ function normalizeBooleanString(value) {
931
+ const normalized = value.trim().toLowerCase();
932
+ if (normalized === "1" || normalized === "true" || normalized === "yes") {
933
+ return true;
934
+ }
935
+ if (normalized === "0" || normalized === "false" || normalized === "no") {
936
+ return false;
937
+ }
938
+ return void 0;
980
939
  }
981
-
982
- // src/stream-handler.ts
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
- });
940
+ function getDebugLevel() {
941
+ const envVal = typeof process !== "undefined" && process.env && process.env.DEBUG_PARSER_MW || "off";
942
+ const envLower = String(envVal).toLowerCase();
943
+ if (envLower === "stream" || envLower === "parse" || envLower === "off") {
944
+ return envLower;
995
945
  }
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
- };
946
+ const boolEnv = normalizeBooleanString(envLower);
947
+ if (boolEnv === true) return "stream";
948
+ if (envLower === "2") return "parse";
949
+ return "off";
1009
950
  }
1010
- async function toolChoiceStream({
1011
- doGenerate,
1012
- options
1013
- }) {
1014
- var _a, _b;
1015
- const result = await doGenerate();
1016
- let toolJson = {};
1017
- if ((result == null ? void 0 : result.content) && result.content.length > 0 && ((_a = result.content[0]) == null ? void 0 : _a.type) === "text") {
1018
- try {
1019
- toolJson = JSON.parse(result.content[0].text);
1020
- } catch (error) {
1021
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1022
- options,
1023
- "Failed to parse toolChoice JSON from streamed model output",
1024
- {
1025
- text: result.content[0].text,
1026
- error: error instanceof Error ? error.message : String(error)
1027
- }
1028
- );
1029
- toolJson = {};
1030
- }
951
+ function color(code) {
952
+ return (text) => `\x1B[${code}m${text}\x1B[0m`;
953
+ }
954
+ var cGray = color(90);
955
+ var cYellow = color(33);
956
+ var cCyan = color(36);
957
+ var cBgBlue = color(44);
958
+ var cBgGreen = color(42);
959
+ var cInverse = color(7);
960
+ var cUnderline = color(4);
961
+ var cBold = color(1);
962
+ function safeStringify(value) {
963
+ try {
964
+ return `
965
+ ${typeof value === "string" ? value : JSON.stringify(value, null, 2)}`;
966
+ } catch (e) {
967
+ return String(value);
1031
968
  }
1032
- const toolCallChunk = {
1033
- type: "tool-call",
1034
- toolCallId: generateId(),
1035
- toolName: toolJson.name || "unknown",
1036
- input: JSON.stringify(toolJson.arguments || {})
1037
- };
1038
- const finishChunk = {
1039
- type: "finish",
1040
- usage: (result == null ? void 0 : result.usage) || // TODO: If possible, try to return a certain amount of LLM usage.
1041
- {
1042
- inputTokens: 0,
1043
- outputTokens: 0,
1044
- totalTokens: 0
1045
- },
1046
- finishReason: "tool-calls"
1047
- };
1048
- const stream = new ReadableStream({
1049
- start(controller) {
1050
- controller.enqueue(toolCallChunk);
1051
- controller.enqueue(finishChunk);
1052
- controller.close();
1053
- }
1054
- });
1055
- return {
1056
- request: (result == null ? void 0 : result.request) || {},
1057
- response: (result == null ? void 0 : result.response) || {},
1058
- stream
1059
- };
1060
969
  }
1061
-
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
970
+ function logRawChunk(part) {
971
+ console.log(cGray("[debug:mw:raw]"), cYellow(safeStringify(part)));
972
+ }
973
+ function logParsedChunk(part) {
974
+ console.log(cGray("[debug:mw:out]"), cCyan(safeStringify(part)));
975
+ }
976
+ function logParsedSummary({
977
+ toolCalls,
978
+ originalText
1068
979
  }) {
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
- };
980
+ if (originalText) {
981
+ const style = (() => {
982
+ const envVal = typeof process !== "undefined" && process.env && process.env.DEBUG_PARSER_MW_STYLE || "bg";
983
+ const normalized = String(envVal).trim().toLowerCase();
984
+ if (normalized === "inverse" || normalized === "invert")
985
+ return "inverse";
986
+ if (normalized === "underline" || normalized === "ul")
987
+ return "underline";
988
+ if (normalized === "bold") return "bold";
989
+ if (normalized === "bg" || normalized === "background")
990
+ return "bg";
991
+ const asBool = normalizeBooleanString(normalized);
992
+ if (asBool === true) return "bg";
993
+ return "bg";
994
+ })();
995
+ const highlight = style === "inverse" ? cInverse : style === "underline" ? cUnderline : style === "bold" ? cBold : style === "bg" ? cBgGreen : cYellow;
996
+ const rendered = style === "bg" || style === "inverse" || style === "underline" || style === "bold" ? originalText.split(/\r?\n/).map((line) => line.length ? highlight(line) : line).join("\n") : highlight(originalText);
997
+ console.log(cGray("[debug:mw:origin]"), `
998
+ ${rendered}`);
1100
999
  }
1101
- const result = await doGenerate();
1102
- if (result.content.length === 0) {
1103
- return result;
1000
+ if (toolCalls.length > 0) {
1001
+ const styledSummary = safeStringify(toolCalls).split(/\r?\n/).map((line) => line.length ? cBgBlue(line) : line).join("\n");
1002
+ console.log(cGray("[debug:mw:summary]"), styledSummary);
1104
1003
  }
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
- };
1127
1004
  }
1128
1005
 
1129
- // src/transform-handler.ts
1130
- async function transformParams({
1131
- params,
1132
- protocol,
1133
- toolSystemPromptTemplate
1134
- }) {
1135
- var _a, _b, _c, _d, _e, _f, _g, _h;
1136
- const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
1006
+ // src/utils/on-error.ts
1007
+ function extractOnErrorOption(providerOptions) {
1008
+ var _a;
1009
+ if (providerOptions && typeof providerOptions === "object") {
1010
+ const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
1011
+ return onError ? { onError } : void 0;
1012
+ }
1013
+ return void 0;
1014
+ }
1015
+
1016
+ // src/utils/protocol.ts
1017
+ function isProtocolFactory(protocol) {
1018
+ return typeof protocol === "function";
1019
+ }
1020
+
1021
+ // src/utils/tools.ts
1022
+ function isToolChoiceActive(params) {
1023
+ var _a, _b, _c;
1024
+ const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
1025
+ return !!(typeof params.providerOptions === "object" && params.providerOptions !== null && typeof ((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) === "object" && toolChoice && typeof toolChoice === "object" && (toolChoice.type === "tool" || toolChoice.type === "required"));
1026
+ }
1027
+ function getFunctionTools(params) {
1028
+ var _a, _b;
1137
1029
  const functionTools = ((_a = params.tools) != null ? _a : []).filter(
1138
1030
  (t) => t.type === "function"
1139
1031
  );
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
1153
- },
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
- );
1032
+ if (functionTools.length > 0) return functionTools;
1033
+ const rawToolNames = params.providerOptions && typeof params.providerOptions === "object" && ((_b = params.providerOptions.toolCallMiddleware) == null ? void 0 : _b.toolNames) || [];
1034
+ const toStringArray = (val) => Array.isArray(val) ? val.filter(
1035
+ (item) => typeof item === "string"
1036
+ ) : [];
1037
+ const toolNames = toStringArray(rawToolNames);
1038
+ if (toolNames.length > 0) {
1039
+ return toolNames.map((name) => ({
1040
+ type: "function",
1041
+ name,
1042
+ description: "",
1043
+ inputSchema: { type: "object" }
1044
+ }));
1179
1045
  }
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
- );
1046
+ return [];
1047
+ }
1048
+
1049
+ // src/utils/type-guards.ts
1050
+ function isToolCallContent(content) {
1051
+ return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
1052
+ (typeof content.input === "string" || typeof content.input === "object");
1053
+ }
1054
+ function isToolResultPart(content) {
1055
+ const c = content;
1056
+ return !!c && c.type === "tool-result" && typeof c.toolName === "string" && typeof c.toolCallId === "string" && "output" in c;
1057
+ }
1058
+ function hasInputProperty(obj) {
1059
+ return typeof obj === "object" && obj !== null && "input" in obj;
1060
+ }
1061
+
1062
+ // src/protocols/json-mix-protocol.ts
1063
+ var jsonMixProtocol = ({
1064
+ toolCallStart = "<tool_call>",
1065
+ toolCallEnd = "</tool_call>",
1066
+ toolResponseStart = "<tool_response>",
1067
+ toolResponseEnd = "</tool_response>"
1068
+ } = {}) => ({
1069
+ formatTools({ tools, toolSystemPromptTemplate }) {
1070
+ const toolsForPrompt = (tools || []).filter((tool) => tool.type === "function").map((tool) => ({
1071
+ name: tool.name,
1072
+ description: tool.type === "function" && typeof tool.description === "string" ? tool.description : void 0,
1073
+ parameters: tool.inputSchema
1074
+ }));
1075
+ return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
1076
+ },
1077
+ formatToolCall(toolCall) {
1078
+ let args = {};
1079
+ try {
1080
+ args = JSON.parse(toolCall.input);
1081
+ } catch (e) {
1082
+ args = toolCall.input;
1191
1083
  }
1192
- const selectedTool = ((_g = params.tools) != null ? _g : []).find(
1193
- (t) => t.type === "function" && t.name === selectedToolName
1084
+ return `${toolCallStart}${JSON.stringify({
1085
+ name: toolCall.toolName,
1086
+ arguments: args
1087
+ })}${toolCallEnd}`;
1088
+ },
1089
+ formatToolResponse(toolResult) {
1090
+ return `${toolResponseStart}${JSON.stringify({
1091
+ toolName: toolResult.toolName,
1092
+ result: toolResult.output
1093
+ })}${toolResponseEnd}`;
1094
+ },
1095
+ parseGeneratedText({ text, options }) {
1096
+ var _a;
1097
+ const startEsc = escapeRegExp(toolCallStart);
1098
+ const endEsc = escapeRegExp(toolCallEnd);
1099
+ const toolCallRegex = new RegExp(
1100
+ `${startEsc}([\0-\uFFFF]*?)${endEsc}`,
1101
+ "gs"
1194
1102
  );
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
1103
+ const processedElements = [];
1104
+ let currentIndex = 0;
1105
+ let match;
1106
+ while ((match = toolCallRegex.exec(text)) !== null) {
1107
+ const startIndex = match.index;
1108
+ const toolCallJson = match[1];
1109
+ if (startIndex > currentIndex) {
1110
+ const textSegment = text.substring(currentIndex, startIndex);
1111
+ if (textSegment.trim()) {
1112
+ processedElements.push({ type: "text", text: textSegment });
1222
1113
  }
1223
1114
  }
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
- );
1115
+ if (toolCallJson) {
1116
+ try {
1117
+ const parsedToolCall = relaxed_json_exports.parse(toolCallJson);
1118
+ processedElements.push({
1119
+ type: "tool-call",
1120
+ toolCallId: generateId2(),
1121
+ toolName: parsedToolCall.name,
1122
+ input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1123
+ });
1124
+ } catch (error) {
1125
+ if (options == null ? void 0 : options.onError) {
1126
+ options.onError(
1127
+ "Could not process JSON tool call, keeping original text.",
1128
+ { toolCall: match[0], error }
1129
+ );
1130
+ }
1131
+ processedElements.push({ type: "text", text: match[0] });
1132
+ }
1133
+ }
1134
+ currentIndex = startIndex + match[0].length;
1231
1135
  }
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" }
1136
+ if (currentIndex < text.length) {
1137
+ const remainingText = text.substring(currentIndex);
1138
+ if (remainingText.trim()) {
1139
+ processedElements.push({ type: "text", text: remainingText });
1140
+ }
1141
+ }
1142
+ return processedElements;
1143
+ },
1144
+ createStreamParser({ tools: _tools, options } = { tools: [] }) {
1145
+ let isInsideToolCall = false;
1146
+ let buffer = "";
1147
+ let currentToolCallJson = "";
1148
+ let currentTextId = null;
1149
+ let hasEmittedTextStart = false;
1150
+ return new TransformStream({
1151
+ transform(chunk, controller) {
1152
+ var _a;
1153
+ if (chunk.type === "finish") {
1154
+ if (isInsideToolCall && buffer.length > 0) {
1155
+ if (!currentTextId) {
1156
+ currentTextId = generateId2();
1157
+ controller.enqueue({ type: "text-start", id: currentTextId });
1158
+ hasEmittedTextStart = true;
1159
+ }
1160
+ controller.enqueue({
1161
+ type: "text-delta",
1162
+ id: currentTextId,
1163
+ delta: `${toolCallStart}${buffer}`
1164
+ });
1165
+ buffer = "";
1166
+ } else if (!isInsideToolCall && buffer.length > 0) {
1167
+ if (!currentTextId) {
1168
+ currentTextId = generateId2();
1169
+ controller.enqueue({ type: "text-start", id: currentTextId });
1170
+ hasEmittedTextStart = true;
1171
+ }
1172
+ controller.enqueue({
1173
+ type: "text-delta",
1174
+ id: currentTextId,
1175
+ delta: buffer
1176
+ });
1177
+ buffer = "";
1178
+ }
1179
+ if (currentTextId && hasEmittedTextStart) {
1180
+ controller.enqueue({ type: "text-end", id: currentTextId });
1181
+ currentTextId = null;
1182
+ hasEmittedTextStart = false;
1183
+ }
1184
+ if (currentToolCallJson) {
1185
+ const errorId = generateId2();
1186
+ controller.enqueue({ type: "text-start", id: errorId });
1187
+ controller.enqueue({
1188
+ type: "text-delta",
1189
+ id: errorId,
1190
+ delta: `${toolCallStart}${currentToolCallJson}`
1191
+ });
1192
+ controller.enqueue({ type: "text-end", id: errorId });
1193
+ currentToolCallJson = "";
1194
+ }
1195
+ controller.enqueue(chunk);
1196
+ return;
1197
+ }
1198
+ if (chunk.type !== "text-delta") {
1199
+ controller.enqueue(chunk);
1200
+ return;
1201
+ }
1202
+ buffer += chunk.delta;
1203
+ const publish = (text) => {
1204
+ if (isInsideToolCall) {
1205
+ if (currentTextId && hasEmittedTextStart) {
1206
+ controller.enqueue({ type: "text-end", id: currentTextId });
1207
+ currentTextId = null;
1208
+ hasEmittedTextStart = false;
1209
+ }
1210
+ currentToolCallJson += text;
1211
+ } else if (text.length > 0) {
1212
+ if (!currentTextId) {
1213
+ currentTextId = generateId2();
1214
+ controller.enqueue({ type: "text-start", id: currentTextId });
1215
+ hasEmittedTextStart = true;
1216
+ }
1217
+ controller.enqueue({
1218
+ type: "text-delta",
1219
+ id: currentTextId,
1220
+ delta: text
1221
+ });
1222
+ }
1223
+ };
1224
+ let startIndex;
1225
+ while ((startIndex = getPotentialStartIndex(
1226
+ buffer,
1227
+ isInsideToolCall ? toolCallEnd : toolCallStart
1228
+ )) != null) {
1229
+ const tag = isInsideToolCall ? toolCallEnd : toolCallStart;
1230
+ if (startIndex + tag.length > buffer.length) {
1231
+ break;
1232
+ }
1233
+ publish(buffer.slice(0, startIndex));
1234
+ buffer = buffer.slice(startIndex + tag.length);
1235
+ if (!isInsideToolCall) {
1236
+ currentToolCallJson = "";
1237
+ isInsideToolCall = true;
1238
+ } else {
1239
+ try {
1240
+ const parsedToolCall = relaxed_json_exports.parse(currentToolCallJson);
1241
+ if (currentTextId && hasEmittedTextStart) {
1242
+ controller.enqueue({ type: "text-end", id: currentTextId });
1243
+ currentTextId = null;
1244
+ hasEmittedTextStart = false;
1245
+ }
1246
+ controller.enqueue({
1247
+ type: "tool-call",
1248
+ toolCallId: generateId2(),
1249
+ toolName: parsedToolCall.name,
1250
+ input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1251
+ });
1252
+ } catch (e) {
1253
+ const errorId = generateId2();
1254
+ controller.enqueue({ type: "text-start", id: errorId });
1255
+ controller.enqueue({
1256
+ type: "text-delta",
1257
+ id: errorId,
1258
+ delta: `${toolCallStart}${currentToolCallJson}${toolCallEnd}`
1259
+ });
1260
+ controller.enqueue({ type: "text-end", id: errorId });
1261
+ if (options == null ? void 0 : options.onError) {
1262
+ options.onError(
1263
+ "Could not process streaming JSON tool call; emitting original text.",
1264
+ {
1265
+ toolCall: `${toolCallStart}${currentToolCallJson}${toolCallEnd}`
1266
+ }
1267
+ );
1268
+ }
1269
+ }
1270
+ currentToolCallJson = "";
1271
+ isInsideToolCall = false;
1272
+ }
1273
+ }
1274
+ if (!isInsideToolCall) {
1275
+ const potentialIndex = getPotentialStartIndex(buffer, toolCallStart);
1276
+ if (potentialIndex != null && potentialIndex + toolCallStart.length > buffer.length) {
1277
+ publish(buffer.slice(0, potentialIndex));
1278
+ buffer = buffer.slice(potentialIndex);
1279
+ } else {
1280
+ publish(buffer);
1281
+ buffer = "";
1282
+ }
1243
1283
  }
1244
1284
  }
1245
- };
1285
+ });
1286
+ },
1287
+ extractToolCallSegments({ text }) {
1288
+ const startEsc = escapeRegExp(toolCallStart);
1289
+ const endEsc = escapeRegExp(toolCallEnd);
1290
+ const regex = new RegExp(`${startEsc}([\0-\uFFFF]*?)${endEsc}`, "gs");
1291
+ const segments = [];
1292
+ let m;
1293
+ while ((m = regex.exec(text)) != null) {
1294
+ segments.push(m[0]);
1295
+ }
1296
+ return segments;
1246
1297
  }
1247
- return baseReturnParams;
1298
+ });
1299
+
1300
+ // src/protocols/morph-xml-protocol.ts
1301
+ import { generateId as generateId3 } from "@ai-sdk/provider-utils";
1302
+ import { XMLBuilder, XMLParser } from "fast-xml-parser";
1303
+ var WARN_ON_DUPLICATE_STRING_TAGS = true;
1304
+ function getToolSchema(tools, originalSchemas, toolName) {
1305
+ var _a;
1306
+ const original = originalSchemas[toolName];
1307
+ if (original) return original;
1308
+ const fallback = (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
1309
+ return fallback;
1248
1310
  }
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
- });
1311
+ function getPropertySchema(toolSchema, key) {
1312
+ const unwrapped = unwrapJsonSchema(toolSchema);
1313
+ if (!unwrapped || typeof unwrapped !== "object") return void 0;
1314
+ const u = unwrapped;
1315
+ const props = u.properties;
1316
+ if (props && Object.prototype.hasOwnProperty.call(props, key)) {
1317
+ return props[key];
1318
+ }
1319
+ return void 0;
1320
+ }
1321
+ function extractRawInner(xmlContent, tagName) {
1322
+ const isNameStartChar = (ch) => /[A-Za-z_:]/.test(ch);
1323
+ const isNameChar = (ch) => /[A-Za-z0-9_.:-]/.test(ch);
1324
+ const len = xmlContent.length;
1325
+ const target = tagName;
1326
+ let bestStart = -1;
1327
+ let bestEnd = -1;
1328
+ let bestDepth = Number.POSITIVE_INFINITY;
1329
+ const skipQuoted = (s, i2) => {
1330
+ const quote = s[i2];
1331
+ i2++;
1332
+ while (i2 < s.length) {
1333
+ const ch = s[i2];
1334
+ if (ch === "\\") {
1335
+ i2 += 2;
1336
+ continue;
1337
+ }
1338
+ if (ch === quote) {
1339
+ return i2 + 1;
1340
+ }
1341
+ i2++;
1342
+ }
1343
+ return i2;
1344
+ };
1345
+ let i = 0;
1346
+ let depth = 0;
1347
+ while (i < len) {
1348
+ const lt = xmlContent.indexOf("<", i);
1349
+ if (lt === -1) return void 0;
1350
+ i = lt + 1;
1351
+ if (i >= len) return void 0;
1352
+ const ch = xmlContent[i];
1353
+ if (ch === "!") {
1354
+ if (xmlContent.startsWith("!--", i + 1)) {
1355
+ const close = xmlContent.indexOf("-->", i + 4);
1356
+ i = close === -1 ? len : close + 3;
1357
+ continue;
1358
+ }
1359
+ if (xmlContent.startsWith("![CDATA[", i + 1)) {
1360
+ const close = xmlContent.indexOf("]]>", i + 9);
1361
+ i = close === -1 ? len : close + 3;
1362
+ continue;
1363
+ }
1364
+ const gt = xmlContent.indexOf(">", i + 1);
1365
+ i = gt === -1 ? len : gt + 1;
1366
+ continue;
1367
+ } else if (ch === "?") {
1368
+ const close = xmlContent.indexOf("?>", i + 1);
1369
+ i = close === -1 ? len : close + 2;
1370
+ continue;
1371
+ } else if (ch === "/") {
1372
+ let j = i + 1;
1373
+ if (j < len && isNameStartChar(xmlContent[j])) {
1374
+ j++;
1375
+ while (j < len && isNameChar(xmlContent[j])) j++;
1376
+ }
1377
+ const gt = xmlContent.indexOf(">", j);
1378
+ i = gt === -1 ? len : gt + 1;
1379
+ depth = Math.max(0, depth - 1);
1380
+ continue;
1381
+ } else {
1382
+ let j = i;
1383
+ if (j < len && isNameStartChar(xmlContent[j])) {
1384
+ j++;
1385
+ while (j < len && isNameChar(xmlContent[j])) j++;
1386
+ }
1387
+ const name = xmlContent.slice(i, j);
1388
+ let k = j;
1389
+ let isSelfClosing = false;
1390
+ while (k < len) {
1391
+ const c = xmlContent[k];
1392
+ if (c === '"' || c === "'") {
1393
+ k = skipQuoted(xmlContent, k);
1394
+ continue;
1395
+ }
1396
+ if (c === ">") {
1397
+ break;
1275
1398
  }
1399
+ if (c === "/" && xmlContent[k + 1] === ">") {
1400
+ isSelfClosing = true;
1401
+ k++;
1402
+ break;
1403
+ }
1404
+ k++;
1276
1405
  }
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")
1406
+ const tagEnd = k;
1407
+ if (name === target) {
1408
+ const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1409
+ if (isSelfClosing) {
1410
+ if (depth < bestDepth) {
1411
+ bestStart = contentStart;
1412
+ bestEnd = contentStart;
1413
+ bestDepth = depth;
1414
+ if (bestDepth === 0) {
1415
+ }
1416
+ }
1417
+ } else {
1418
+ let pos = contentStart;
1419
+ let sameDepth = 1;
1420
+ while (pos < len) {
1421
+ const nextLt = xmlContent.indexOf("<", pos);
1422
+ if (nextLt === -1) break;
1423
+ const nx = nextLt + 1;
1424
+ if (nx >= len) break;
1425
+ const h = xmlContent[nx];
1426
+ if (h === "!") {
1427
+ if (xmlContent.startsWith("!--", nx + 1)) {
1428
+ const close = xmlContent.indexOf("-->", nx + 4);
1429
+ pos = close === -1 ? len : close + 3;
1430
+ continue;
1431
+ }
1432
+ if (xmlContent.startsWith("![CDATA[", nx + 1)) {
1433
+ const close = xmlContent.indexOf("]]>", nx + 9);
1434
+ pos = close === -1 ? len : close + 3;
1435
+ continue;
1436
+ }
1437
+ const gt2 = xmlContent.indexOf(">", nx + 1);
1438
+ pos = gt2 === -1 ? len : gt2 + 1;
1439
+ continue;
1440
+ } else if (h === "?") {
1441
+ const close = xmlContent.indexOf("?>", nx + 1);
1442
+ pos = close === -1 ? len : close + 2;
1443
+ continue;
1444
+ } else if (h === "/") {
1445
+ let t = nx + 1;
1446
+ if (t < len && isNameStartChar(xmlContent[t])) {
1447
+ t++;
1448
+ while (t < len && isNameChar(xmlContent[t])) t++;
1449
+ }
1450
+ const endName = xmlContent.slice(nx + 1, t);
1451
+ const gt2 = xmlContent.indexOf(">", t);
1452
+ if (endName === target) {
1453
+ sameDepth--;
1454
+ if (sameDepth === 0) {
1455
+ if (depth < bestDepth) {
1456
+ bestStart = contentStart;
1457
+ bestEnd = nextLt;
1458
+ bestDepth = depth;
1459
+ if (bestDepth === 0) {
1460
+ }
1461
+ }
1462
+ break;
1463
+ }
1464
+ }
1465
+ pos = gt2 === -1 ? len : gt2 + 1;
1466
+ continue;
1467
+ } else {
1468
+ let t = nx;
1469
+ if (t < len && isNameStartChar(xmlContent[t])) {
1470
+ t++;
1471
+ while (t < len && isNameChar(xmlContent[t])) t++;
1472
+ }
1473
+ const startName = xmlContent.slice(nx, t);
1474
+ let u = t;
1475
+ let selfClose = false;
1476
+ while (u < len) {
1477
+ const cu = xmlContent[u];
1478
+ if (cu === '"' || cu === "'") {
1479
+ u = skipQuoted(xmlContent, u);
1480
+ continue;
1481
+ }
1482
+ if (cu === ">") break;
1483
+ if (cu === "/" && xmlContent[u + 1] === ">") {
1484
+ selfClose = true;
1485
+ u++;
1486
+ break;
1487
+ }
1488
+ u++;
1489
+ }
1490
+ if (startName === target && !selfClose) {
1491
+ sameDepth++;
1492
+ }
1493
+ pos = xmlContent[u] === ">" ? u + 1 : u + 1;
1494
+ continue;
1495
+ }
1496
+ }
1282
1497
  }
1283
- ] : newContent;
1284
- return { role: "assistant", content: condensedAssistant };
1498
+ }
1499
+ i = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1500
+ depth += isSelfClosing ? 0 : 1;
1501
+ continue;
1285
1502
  }
1286
- if (message.role === "tool") {
1287
- return {
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
- ]
1300
- };
1503
+ }
1504
+ if (bestStart !== -1) {
1505
+ return xmlContent.slice(bestStart, bestEnd);
1506
+ }
1507
+ return void 0;
1508
+ }
1509
+ function findFirstTopLevelRange(xmlContent, tagName) {
1510
+ const isNameStartChar = (ch) => /[A-Za-z_:]/.test(ch);
1511
+ const isNameChar = (ch) => /[A-Za-z0-9_.:-]/.test(ch);
1512
+ const len = xmlContent.length;
1513
+ const target = tagName;
1514
+ const skipQuoted = (s, i2) => {
1515
+ const quote = s[i2];
1516
+ i2++;
1517
+ while (i2 < s.length) {
1518
+ const ch = s[i2];
1519
+ if (ch === "\\") {
1520
+ i2 += 2;
1521
+ continue;
1522
+ }
1523
+ if (ch === quote) return i2 + 1;
1524
+ i2++;
1301
1525
  }
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
1526
+ return i2;
1527
+ };
1528
+ let i = 0;
1529
+ let depth = 0;
1530
+ while (i < len) {
1531
+ const lt = xmlContent.indexOf("<", i);
1532
+ if (lt === -1) return void 0;
1533
+ i = lt + 1;
1534
+ if (i >= len) return void 0;
1535
+ const ch = xmlContent[i];
1536
+ if (ch === "!") {
1537
+ if (xmlContent.startsWith("!--", i + 1)) {
1538
+ const close = xmlContent.indexOf("-->", i + 4);
1539
+ i = close === -1 ? len : close + 3;
1540
+ continue;
1541
+ }
1542
+ if (xmlContent.startsWith("![CDATA[", i + 1)) {
1543
+ const close = xmlContent.indexOf("]]>", i + 9);
1544
+ i = close === -1 ? len : close + 3;
1545
+ continue;
1546
+ }
1547
+ const gt = xmlContent.indexOf(">", i + 1);
1548
+ i = gt === -1 ? len : gt + 1;
1549
+ continue;
1550
+ } else if (ch === "?") {
1551
+ const close = xmlContent.indexOf("?>", i + 1);
1552
+ i = close === -1 ? len : close + 2;
1553
+ continue;
1554
+ } else if (ch === "/") {
1555
+ const gt = xmlContent.indexOf(">", i + 1);
1556
+ i = gt === -1 ? len : gt + 1;
1557
+ depth = Math.max(0, depth - 1);
1558
+ continue;
1559
+ } else {
1560
+ let j = i;
1561
+ if (j < len && isNameStartChar(xmlContent[j])) {
1562
+ j++;
1563
+ while (j < len && isNameChar(xmlContent[j])) j++;
1564
+ }
1565
+ const name = xmlContent.slice(i, j);
1566
+ let k = j;
1567
+ let isSelfClosing = false;
1568
+ while (k < len) {
1569
+ const c = xmlContent[k];
1570
+ if (c === '"' || c === "'") {
1571
+ k = skipQuoted(xmlContent, k);
1572
+ continue;
1573
+ }
1574
+ if (c === ">") break;
1575
+ if (c === "/" && xmlContent[k + 1] === ">") {
1576
+ isSelfClosing = true;
1577
+ k++;
1578
+ break;
1579
+ }
1580
+ k++;
1581
+ }
1582
+ const tagEnd = k;
1583
+ if (depth === 0 && name === target) {
1584
+ const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1585
+ if (isSelfClosing) return { start: contentStart, end: contentStart };
1586
+ let pos = contentStart;
1587
+ let sameDepth = 1;
1588
+ while (pos < len) {
1589
+ const nextLt = xmlContent.indexOf("<", pos);
1590
+ if (nextLt === -1) break;
1591
+ const nx = nextLt + 1;
1592
+ if (nx >= len) break;
1593
+ const h = xmlContent[nx];
1594
+ if (h === "!") {
1595
+ if (xmlContent.startsWith("!--", nx + 1)) {
1596
+ const close = xmlContent.indexOf("-->", nx + 4);
1597
+ pos = close === -1 ? len : close + 3;
1598
+ continue;
1599
+ }
1600
+ if (xmlContent.startsWith("![CDATA[", nx + 1)) {
1601
+ const close = xmlContent.indexOf("]]>", nx + 9);
1602
+ pos = close === -1 ? len : close + 3;
1603
+ continue;
1604
+ }
1605
+ const gt2 = xmlContent.indexOf(">", nx + 1);
1606
+ pos = gt2 === -1 ? len : gt2 + 1;
1607
+ continue;
1608
+ } else if (h === "?") {
1609
+ const close = xmlContent.indexOf("?>", nx + 1);
1610
+ pos = close === -1 ? len : close + 2;
1611
+ continue;
1612
+ } else if (h === "/") {
1613
+ let t = nx + 1;
1614
+ if (t < len && isNameStartChar(xmlContent[t])) {
1615
+ t++;
1616
+ while (t < len && isNameChar(xmlContent[t])) t++;
1617
+ }
1618
+ const endName = xmlContent.slice(nx + 1, t);
1619
+ const gt2 = xmlContent.indexOf(">", t);
1620
+ if (endName === target) {
1621
+ sameDepth--;
1622
+ if (sameDepth === 0) {
1623
+ return { start: contentStart, end: nextLt };
1324
1624
  }
1325
- ]
1326
- };
1327
- } else {
1328
- processedPrompt[i] = {
1329
- role: "user",
1330
- content: [
1331
- {
1332
- type: "text",
1333
- text: joinedText
1625
+ }
1626
+ pos = gt2 === -1 ? len : gt2 + 1;
1627
+ continue;
1628
+ } else {
1629
+ let t = nx;
1630
+ if (t < len && isNameStartChar(xmlContent[t])) {
1631
+ t++;
1632
+ while (t < len && isNameChar(xmlContent[t])) t++;
1633
+ }
1634
+ let u = t;
1635
+ let selfClose = false;
1636
+ while (u < len) {
1637
+ const cu = xmlContent[u];
1638
+ if (cu === '"' || cu === "'") {
1639
+ u = skipQuoted(xmlContent, u);
1640
+ continue;
1334
1641
  }
1335
- ]
1336
- };
1642
+ if (cu === ">") break;
1643
+ if (cu === "/" && xmlContent[u + 1] === ">") {
1644
+ selfClose = true;
1645
+ u++;
1646
+ break;
1647
+ }
1648
+ u++;
1649
+ }
1650
+ if (!selfClose) {
1651
+ }
1652
+ pos = xmlContent[u] === ">" ? u + 1 : u + 1;
1653
+ continue;
1654
+ }
1655
+ }
1656
+ return void 0;
1657
+ }
1658
+ i = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1659
+ depth += isSelfClosing ? 0 : 1;
1660
+ continue;
1661
+ }
1662
+ }
1663
+ return void 0;
1664
+ }
1665
+ function countTagOccurrences(xmlContent, tagName, excludeRanges, skipFirst = true) {
1666
+ const isNameStartChar = (ch) => /[A-Za-z_:]/.test(ch);
1667
+ const isNameChar = (ch) => /[A-Za-z0-9_.:-]/.test(ch);
1668
+ const len = xmlContent.length;
1669
+ const target = tagName;
1670
+ const skipQuoted = (s, i2) => {
1671
+ const quote = s[i2];
1672
+ i2++;
1673
+ while (i2 < s.length) {
1674
+ const ch = s[i2];
1675
+ if (ch === "\\") {
1676
+ i2 += 2;
1677
+ continue;
1678
+ }
1679
+ if (ch === quote) return i2 + 1;
1680
+ i2++;
1681
+ }
1682
+ return i2;
1683
+ };
1684
+ let i = 0;
1685
+ let count = 0;
1686
+ const isExcluded = (pos) => {
1687
+ if (!excludeRanges || excludeRanges.length === 0) return false;
1688
+ for (const r of excludeRanges) {
1689
+ if (pos >= r.start && pos < r.end) return true;
1690
+ }
1691
+ return false;
1692
+ };
1693
+ while (i < len) {
1694
+ const lt = xmlContent.indexOf("<", i);
1695
+ if (lt === -1) break;
1696
+ i = lt + 1;
1697
+ if (i >= len) break;
1698
+ const ch = xmlContent[i];
1699
+ if (ch === "!") {
1700
+ if (xmlContent.startsWith("!--", i + 1)) {
1701
+ const close = xmlContent.indexOf("-->", i + 4);
1702
+ i = close === -1 ? len : close + 3;
1703
+ continue;
1704
+ }
1705
+ if (xmlContent.startsWith("![CDATA[", i + 1)) {
1706
+ const close = xmlContent.indexOf("]]>", i + 9);
1707
+ i = close === -1 ? len : close + 3;
1708
+ continue;
1709
+ }
1710
+ const gt = xmlContent.indexOf(">", i + 1);
1711
+ i = gt === -1 ? len : gt + 1;
1712
+ continue;
1713
+ } else if (ch === "?") {
1714
+ const close = xmlContent.indexOf("?>", i + 1);
1715
+ i = close === -1 ? len : close + 2;
1716
+ continue;
1717
+ } else if (ch === "/") {
1718
+ const gt = xmlContent.indexOf(">", i + 1);
1719
+ i = gt === -1 ? len : gt + 1;
1720
+ continue;
1721
+ } else {
1722
+ let j = i;
1723
+ if (j < len && isNameStartChar(xmlContent[j])) {
1724
+ j++;
1725
+ while (j < len && isNameChar(xmlContent[j])) j++;
1726
+ }
1727
+ const name = xmlContent.slice(i, j);
1728
+ let k = j;
1729
+ while (k < len) {
1730
+ const c = xmlContent[k];
1731
+ if (c === '"' || c === "'") {
1732
+ k = skipQuoted(xmlContent, k);
1733
+ continue;
1337
1734
  }
1735
+ if (c === ">") break;
1736
+ if (c === "/" && xmlContent[k + 1] === ">") {
1737
+ k++;
1738
+ break;
1739
+ }
1740
+ k++;
1338
1741
  }
1742
+ if (name === target && !isExcluded(lt)) {
1743
+ if (skipFirst) {
1744
+ skipFirst = false;
1745
+ } else {
1746
+ count++;
1747
+ }
1748
+ }
1749
+ i = k + 1;
1750
+ continue;
1339
1751
  }
1340
1752
  }
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;
1753
+ return count;
1355
1754
  }
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)
1755
+ function processParsedArgs(parsedArgs, toolSchema, toolContent, toolName, options) {
1756
+ var _a, _b, _c;
1757
+ const args = {};
1758
+ let cancelToolCall = false;
1759
+ const stringTypedProps = (() => {
1760
+ const set = /* @__PURE__ */ new Set();
1761
+ const unwrapped = unwrapJsonSchema(toolSchema);
1762
+ if (unwrapped && typeof unwrapped === "object") {
1763
+ const u = unwrapped;
1764
+ const props = u.properties;
1765
+ if (props && typeof props === "object") {
1766
+ for (const key of Object.keys(props)) {
1767
+ const t = getSchemaType(props[key]);
1768
+ if (t === "string") set.add(key);
1769
+ }
1770
+ }
1771
+ }
1772
+ return set;
1773
+ })();
1774
+ for (const k of Object.keys(parsedArgs || {})) {
1775
+ const v = parsedArgs[k];
1776
+ let val = v;
1777
+ const propSchema = getPropertySchema(toolSchema, k);
1778
+ const propType = getSchemaType(propSchema);
1779
+ if (propType === "string" && !Array.isArray(v)) {
1780
+ const excludeRanges = [];
1781
+ for (const other of stringTypedProps) {
1782
+ if (other === k) continue;
1783
+ const range = findFirstTopLevelRange(toolContent, other);
1784
+ if (range) excludeRanges.push(range);
1785
+ }
1786
+ const occurrences = countTagOccurrences(
1787
+ toolContent,
1788
+ k,
1789
+ excludeRanges,
1790
+ true
1791
+ );
1792
+ if (occurrences > 0) {
1793
+ if (WARN_ON_DUPLICATE_STRING_TAGS) {
1794
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1795
+ options,
1796
+ `Duplicate string tags for <${k}> detected; cancelling tool call`,
1797
+ {
1798
+ toolName,
1799
+ toolCall: `<${toolName}>${toolContent}</${toolName}>`
1800
+ }
1801
+ );
1802
+ }
1803
+ cancelToolCall = true;
1804
+ break;
1805
+ }
1806
+ const raw = extractRawInner(toolContent, k);
1807
+ if (typeof raw === "string") {
1808
+ args[k] = raw;
1809
+ continue;
1810
+ }
1811
+ }
1812
+ if (v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, "#text")) {
1813
+ val = v == null ? void 0 : v["#text"];
1814
+ }
1815
+ if (Array.isArray(v)) {
1816
+ if (propType === "string") {
1817
+ const mapped = v.map((item) => {
1818
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1819
+ const textVal = item == null ? void 0 : item["#text"];
1820
+ return typeof textVal === "string" ? textVal : String(textVal);
1821
+ }
1822
+ return typeof item === "string" ? item : String(item);
1370
1823
  });
1824
+ if (mapped.length > 1 && WARN_ON_DUPLICATE_STRING_TAGS) {
1825
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1826
+ options,
1827
+ `Duplicate string tags for <${k}> detected; cancelling tool call`,
1828
+ {
1829
+ toolName,
1830
+ toolCall: `<${toolName}>${toolContent}</${toolName}>`
1831
+ }
1832
+ );
1833
+ }
1834
+ if (mapped.length > 1) {
1835
+ cancelToolCall = true;
1836
+ break;
1837
+ } else {
1838
+ args[k] = (_c = mapped[0]) != null ? _c : "";
1839
+ continue;
1840
+ }
1371
1841
  } else {
1372
- return wrapStream({
1373
- protocol: resolvedProtocol,
1374
- doStream,
1375
- doGenerate,
1376
- params
1842
+ val = v.map((item) => {
1843
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1844
+ const textVal = item == null ? void 0 : item["#text"];
1845
+ return typeof textVal === "string" ? textVal.trim() : textVal;
1846
+ }
1847
+ return typeof item === "string" ? item.trim() : item;
1377
1848
  });
1378
1849
  }
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
- });
1850
+ } else if (v && typeof v === "object" && !Object.prototype.hasOwnProperty.call(v, "#text")) {
1851
+ const obj = v;
1852
+ const keys = Object.keys(obj);
1853
+ if (keys.length === 1 && keys[0] === "item") {
1854
+ const itemValue = obj.item;
1855
+ if (Array.isArray(itemValue)) {
1856
+ val = itemValue.map((item) => {
1857
+ let currentVal = item;
1858
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1859
+ currentVal = item == null ? void 0 : item["#text"];
1860
+ }
1861
+ const trimmed = typeof currentVal === "string" ? currentVal.trim() : currentVal;
1862
+ if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1863
+ const num = Number(trimmed);
1864
+ if (Number.isFinite(num)) return num;
1865
+ }
1866
+ return trimmed;
1867
+ });
1868
+ } else {
1869
+ const trimmed = typeof itemValue === "string" ? itemValue.trim() : itemValue;
1870
+ if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1871
+ const num = Number(trimmed);
1872
+ val = Number.isFinite(num) ? num : trimmed;
1873
+ } else {
1874
+ val = trimmed;
1875
+ }
1876
+ }
1877
+ } else {
1878
+ let isIndexedTuple = false;
1879
+ if (keys.length > 0 && keys.every((key) => /^\d+$/.test(key))) {
1880
+ const indices = keys.map((k2) => parseInt(k2, 10)).sort((a, b) => a - b);
1881
+ isIndexedTuple = indices[0] === 0 && indices.every((val2, idx) => val2 === idx);
1882
+ }
1883
+ if (isIndexedTuple) {
1884
+ const sortedKeys = keys.sort(
1885
+ (a, b) => parseInt(a, 10) - parseInt(b, 10)
1886
+ );
1887
+ val = sortedKeys.map((key) => {
1888
+ const item = obj[key];
1889
+ if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, "#text")) {
1890
+ const textVal = item == null ? void 0 : item["#text"];
1891
+ return typeof textVal === "string" ? textVal.trim() : textVal;
1892
+ }
1893
+ return typeof item === "string" ? item.trim() : item;
1894
+ });
1895
+ } else {
1896
+ val = v;
1897
+ }
1898
+ }
1393
1899
  }
1394
- };
1900
+ args[k] = typeof val === "string" ? val.trim() : val;
1901
+ }
1902
+ return { args, cancelToolCall };
1395
1903
  }
1396
-
1397
- // src/protocols/json-mix-protocol.ts
1398
- import { generateId as generateId3 } from "@ai-sdk/provider-utils";
1399
- var jsonMixProtocol = ({
1400
- toolCallStart = "<tool_call>",
1401
- toolCallEnd = "</tool_call>",
1402
- toolResponseStart = "<tool_response>",
1403
- toolResponseEnd = "</tool_response>"
1404
- } = {}) => ({
1904
+ var morphXmlProtocol = () => ({
1405
1905
  formatTools({ tools, toolSystemPromptTemplate }) {
1406
- const toolsForPrompt = (tools || []).filter((tool) => tool.type === "function").map((tool) => ({
1906
+ const toolsForPrompt = (tools || []).map((tool) => ({
1407
1907
  name: tool.name,
1408
- description: tool.type === "function" && typeof tool.description === "string" ? tool.description : void 0,
1409
- parameters: tool.inputSchema
1908
+ description: tool.description,
1909
+ parameters: unwrapJsonSchema(tool.inputSchema)
1410
1910
  }));
1411
1911
  return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
1412
1912
  },
1413
1913
  formatToolCall(toolCall) {
1914
+ const builder = new XMLBuilder({ format: true, suppressEmptyNode: true });
1414
1915
  let args = {};
1415
- try {
1416
- args = JSON.parse(toolCall.input);
1417
- } catch (e) {
1418
- args = toolCall.input;
1916
+ const inputValue = hasInputProperty(toolCall) ? toolCall.input : void 0;
1917
+ if (typeof inputValue === "string") {
1918
+ try {
1919
+ args = JSON.parse(inputValue);
1920
+ } catch (e) {
1921
+ args = inputValue;
1922
+ }
1923
+ } else {
1924
+ args = inputValue;
1419
1925
  }
1420
- return `${toolCallStart}${JSON.stringify({
1421
- name: toolCall.toolName,
1422
- arguments: args
1423
- })}${toolCallEnd}`;
1926
+ const xmlContent = builder.build({
1927
+ [toolCall.toolName]: args
1928
+ });
1929
+ return xmlContent;
1424
1930
  },
1425
1931
  formatToolResponse(toolResult) {
1426
- return `${toolResponseStart}${JSON.stringify({
1427
- toolName: toolResult.toolName,
1428
- result: toolResult.output
1429
- })}${toolResponseEnd}`;
1932
+ const builder = new XMLBuilder({ format: true });
1933
+ const xmlContent = builder.build({
1934
+ tool_response: {
1935
+ tool_name: toolResult.toolName,
1936
+ result: toolResult.output
1937
+ }
1938
+ });
1939
+ return xmlContent;
1430
1940
  },
1431
- parseGeneratedText({ text, options }) {
1432
- var _a;
1433
- const startEsc = escapeRegExp(toolCallStart);
1434
- const endEsc = escapeRegExp(toolCallEnd);
1941
+ parseGeneratedText({ text, tools, options }) {
1942
+ var _a, _b, _c;
1943
+ const originalSchemas = (options == null ? void 0 : options.originalToolSchemas) || {};
1944
+ const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1945
+ if (toolNames.length === 0) {
1946
+ return [{ type: "text", text }];
1947
+ }
1948
+ const toolNamesPattern = toolNames.map((n) => escapeRegExp(n)).join("|");
1435
1949
  const toolCallRegex = new RegExp(
1436
- `${startEsc}([\0-\uFFFF]*?)${endEsc}`,
1437
- "gs"
1950
+ String.raw`<(${toolNamesPattern})>([\s\S]*?)<\/\1>`,
1951
+ "g"
1438
1952
  );
1439
1953
  const processedElements = [];
1440
1954
  let currentIndex = 0;
1441
1955
  let match;
1442
1956
  while ((match = toolCallRegex.exec(text)) !== null) {
1443
1957
  const startIndex = match.index;
1444
- const toolCallJson = match[1];
1958
+ const toolName = match[1];
1959
+ const toolContent = match[2].trim();
1445
1960
  if (startIndex > currentIndex) {
1446
1961
  const textSegment = text.substring(currentIndex, startIndex);
1447
1962
  if (textSegment.trim()) {
1448
1963
  processedElements.push({ type: "text", text: textSegment });
1449
1964
  }
1450
1965
  }
1451
- if (toolCallJson) {
1452
- try {
1453
- const parsedToolCall = relaxed_json_exports.parse(toolCallJson);
1966
+ try {
1967
+ const parser = new XMLParser({
1968
+ ignoreAttributes: false,
1969
+ parseTagValue: false,
1970
+ ignoreDeclaration: true,
1971
+ textNodeName: "#text"
1972
+ });
1973
+ const parsedArgs = ((_a = parser.parse(`<root>${toolContent}</root>`)) == null ? void 0 : _a.root) || {};
1974
+ const toolSchema = getToolSchema(tools, originalSchemas, toolName);
1975
+ const { args, cancelToolCall } = processParsedArgs(
1976
+ parsedArgs,
1977
+ toolSchema,
1978
+ toolContent,
1979
+ toolName,
1980
+ options
1981
+ );
1982
+ if (cancelToolCall) {
1983
+ const originalCallText = match[0];
1984
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1985
+ options,
1986
+ `Duplicate string tags detected; cancelling tool call`,
1987
+ { toolCall: originalCallText, toolName }
1988
+ );
1989
+ processedElements.push({ type: "text", text: originalCallText });
1990
+ } else {
1991
+ const coercedArgs = coerceBySchema(args, toolSchema);
1454
1992
  processedElements.push({
1455
1993
  type: "tool-call",
1456
1994
  toolCallId: generateId3(),
1457
- toolName: parsedToolCall.name,
1458
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1995
+ toolName,
1996
+ input: JSON.stringify(coercedArgs)
1459
1997
  });
1460
- } catch (error) {
1461
- if (options == null ? void 0 : options.onError) {
1462
- options.onError(
1463
- "Could not process JSON tool call, keeping original text.",
1464
- { toolCall: match[0], error }
1465
- );
1466
- }
1467
- processedElements.push({ type: "text", text: match[0] });
1468
1998
  }
1999
+ } catch (error) {
2000
+ const message = `Could not process XML tool call, keeping original text: ${match[0]}`;
2001
+ (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, message, { toolCall: match[0], toolName, error });
2002
+ processedElements.push({ type: "text", text: match[0] });
1469
2003
  }
1470
2004
  currentIndex = startIndex + match[0].length;
1471
2005
  }
@@ -1477,524 +2011,722 @@ var jsonMixProtocol = ({
1477
2011
  }
1478
2012
  return processedElements;
1479
2013
  },
1480
- createStreamParser({ tools: _tools, options } = { tools: [] }) {
1481
- let isInsideToolCall = false;
2014
+ createStreamParser({ tools, options }) {
2015
+ const originalSchemas = (options == null ? void 0 : options.originalToolSchemas) || {};
2016
+ const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1482
2017
  let buffer = "";
1483
- let currentToolCallJson = "";
2018
+ let currentToolCall = null;
1484
2019
  let currentTextId = null;
1485
- let hasEmittedTextStart = false;
1486
- return new TransformStream({
1487
- transform(chunk, controller) {
1488
- var _a;
1489
- if (chunk.type === "finish") {
1490
- if (isInsideToolCall && buffer.length > 0) {
1491
- if (!currentTextId) {
1492
- currentTextId = generateId3();
1493
- controller.enqueue({ type: "text-start", id: currentTextId });
1494
- hasEmittedTextStart = true;
1495
- }
1496
- controller.enqueue({
1497
- type: "text-delta",
1498
- id: currentTextId,
1499
- delta: `${toolCallStart}${buffer}`
1500
- });
1501
- buffer = "";
1502
- } else if (!isInsideToolCall && buffer.length > 0) {
1503
- if (!currentTextId) {
1504
- currentTextId = generateId3();
1505
- controller.enqueue({ type: "text-start", id: currentTextId });
1506
- hasEmittedTextStart = true;
1507
- }
1508
- controller.enqueue({
1509
- type: "text-delta",
1510
- id: currentTextId,
1511
- delta: buffer
1512
- });
1513
- buffer = "";
1514
- }
1515
- if (currentTextId && hasEmittedTextStart) {
1516
- controller.enqueue({ type: "text-end", id: currentTextId });
1517
- currentTextId = null;
1518
- hasEmittedTextStart = false;
1519
- }
1520
- if (currentToolCallJson) {
1521
- const errorId = generateId3();
1522
- controller.enqueue({ type: "text-start", id: errorId });
1523
- controller.enqueue({
1524
- type: "text-delta",
1525
- id: errorId,
1526
- delta: `${toolCallStart}${currentToolCallJson}`
1527
- });
1528
- controller.enqueue({ type: "text-end", id: errorId });
1529
- currentToolCallJson = "";
1530
- }
1531
- controller.enqueue(chunk);
1532
- return;
2020
+ const flushText = (controller, text) => {
2021
+ const content = text != null ? text : buffer;
2022
+ if (content) {
2023
+ if (!currentTextId) {
2024
+ currentTextId = generateId3();
2025
+ controller.enqueue({ type: "text-start", id: currentTextId });
2026
+ }
2027
+ controller.enqueue({
2028
+ type: "text-delta",
2029
+ id: currentTextId,
2030
+ delta: content
2031
+ });
2032
+ if (text === void 0) {
2033
+ buffer = "";
1533
2034
  }
2035
+ }
2036
+ if (currentTextId && !text) {
2037
+ controller.enqueue({ type: "text-end", id: currentTextId });
2038
+ currentTextId = null;
2039
+ }
2040
+ };
2041
+ return new TransformStream({
2042
+ transform(chunk, controller) {
2043
+ var _a;
1534
2044
  if (chunk.type !== "text-delta") {
2045
+ if (buffer) flushText(controller);
1535
2046
  controller.enqueue(chunk);
1536
2047
  return;
1537
2048
  }
1538
2049
  buffer += chunk.delta;
1539
- const publish = (text) => {
1540
- if (isInsideToolCall) {
1541
- if (currentTextId && hasEmittedTextStart) {
1542
- controller.enqueue({ type: "text-end", id: currentTextId });
1543
- currentTextId = null;
1544
- hasEmittedTextStart = false;
1545
- }
1546
- currentToolCallJson += text;
1547
- } else if (text.length > 0) {
1548
- if (!currentTextId) {
1549
- currentTextId = generateId3();
1550
- controller.enqueue({ type: "text-start", id: currentTextId });
1551
- hasEmittedTextStart = true;
2050
+ while (true) {
2051
+ if (currentToolCall) {
2052
+ const endTag = `</${currentToolCall.name}>`;
2053
+ const endTagIndex = buffer.indexOf(endTag);
2054
+ if (endTagIndex !== -1) {
2055
+ const toolContent = buffer.substring(0, endTagIndex);
2056
+ buffer = buffer.substring(endTagIndex + endTag.length);
2057
+ try {
2058
+ const parser = new XMLParser({
2059
+ ignoreAttributes: false,
2060
+ parseTagValue: false,
2061
+ ignoreDeclaration: true,
2062
+ textNodeName: "#text"
2063
+ });
2064
+ const parsedArgs = ((_a = parser.parse(`<root>${toolContent}</root>`)) == null ? void 0 : _a.root) || {};
2065
+ const toolSchema = getToolSchema(
2066
+ tools,
2067
+ originalSchemas,
2068
+ currentToolCall.name
2069
+ );
2070
+ const { args, cancelToolCall } = processParsedArgs(
2071
+ parsedArgs,
2072
+ toolSchema,
2073
+ toolContent,
2074
+ currentToolCall.name,
2075
+ options
2076
+ );
2077
+ if (cancelToolCall) {
2078
+ const originalCallText = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
2079
+ if (options == null ? void 0 : options.onError) {
2080
+ options.onError(
2081
+ "Duplicate string tags detected; cancelling tool call",
2082
+ {
2083
+ toolCall: originalCallText,
2084
+ toolName: currentToolCall.name
2085
+ }
2086
+ );
2087
+ }
2088
+ flushText(controller, originalCallText);
2089
+ } else {
2090
+ const coercedArgs = coerceBySchema(
2091
+ args,
2092
+ toolSchema
2093
+ );
2094
+ flushText(controller);
2095
+ controller.enqueue({
2096
+ type: "tool-call",
2097
+ toolCallId: generateId3(),
2098
+ toolName: currentToolCall.name,
2099
+ input: JSON.stringify(coercedArgs)
2100
+ });
2101
+ }
2102
+ } catch (e) {
2103
+ const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
2104
+ if (options == null ? void 0 : options.onError) {
2105
+ options.onError(
2106
+ "Could not process streaming XML tool call; emitting original text.",
2107
+ {
2108
+ toolCall: originalCallText,
2109
+ toolName: currentToolCall.name
2110
+ }
2111
+ );
2112
+ }
2113
+ flushText(controller, originalCallText);
2114
+ }
2115
+ currentToolCall = null;
2116
+ } else {
2117
+ break;
1552
2118
  }
1553
- controller.enqueue({
1554
- type: "text-delta",
1555
- id: currentTextId,
1556
- delta: text
1557
- });
1558
- }
1559
- };
1560
- let startIndex;
1561
- while ((startIndex = getPotentialStartIndex(
1562
- buffer,
1563
- isInsideToolCall ? toolCallEnd : toolCallStart
1564
- )) != null) {
1565
- const tag = isInsideToolCall ? toolCallEnd : toolCallStart;
1566
- if (startIndex + tag.length > buffer.length) {
1567
- break;
1568
- }
1569
- publish(buffer.slice(0, startIndex));
1570
- buffer = buffer.slice(startIndex + tag.length);
1571
- if (!isInsideToolCall) {
1572
- currentToolCallJson = "";
1573
- isInsideToolCall = true;
1574
2119
  } else {
1575
- try {
1576
- const parsedToolCall = relaxed_json_exports.parse(currentToolCallJson);
1577
- if (currentTextId && hasEmittedTextStart) {
1578
- controller.enqueue({ type: "text-end", id: currentTextId });
1579
- currentTextId = null;
1580
- hasEmittedTextStart = false;
1581
- }
1582
- controller.enqueue({
1583
- type: "tool-call",
1584
- toolCallId: generateId3(),
1585
- toolName: parsedToolCall.name,
1586
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1587
- });
1588
- } catch (e) {
1589
- const errorId = generateId3();
1590
- controller.enqueue({ type: "text-start", id: errorId });
1591
- controller.enqueue({
1592
- type: "text-delta",
1593
- id: errorId,
1594
- delta: `${toolCallStart}${currentToolCallJson}${toolCallEnd}`
1595
- });
1596
- controller.enqueue({ type: "text-end", id: errorId });
1597
- if (options == null ? void 0 : options.onError) {
1598
- options.onError(
1599
- "Could not process streaming JSON tool call; emitting original text.",
1600
- {
1601
- toolCall: `${toolCallStart}${currentToolCallJson}${toolCallEnd}`
1602
- }
1603
- );
2120
+ let earliestStartTagIndex = -1;
2121
+ let earliestToolName = "";
2122
+ if (toolNames.length > 0) {
2123
+ for (const name of toolNames) {
2124
+ const startTag = `<${name}>`;
2125
+ const index = buffer.indexOf(startTag);
2126
+ if (index !== -1 && (earliestStartTagIndex === -1 || index < earliestStartTagIndex)) {
2127
+ earliestStartTagIndex = index;
2128
+ earliestToolName = name;
2129
+ }
1604
2130
  }
1605
2131
  }
1606
- currentToolCallJson = "";
1607
- isInsideToolCall = false;
2132
+ if (earliestStartTagIndex !== -1) {
2133
+ const textBeforeTag = buffer.substring(0, earliestStartTagIndex);
2134
+ flushText(controller, textBeforeTag);
2135
+ const startTag = `<${earliestToolName}>`;
2136
+ buffer = buffer.substring(
2137
+ earliestStartTagIndex + startTag.length
2138
+ );
2139
+ currentToolCall = { name: earliestToolName, content: "" };
2140
+ } else {
2141
+ break;
2142
+ }
1608
2143
  }
1609
2144
  }
1610
- if (!isInsideToolCall) {
1611
- const potentialIndex = getPotentialStartIndex(buffer, toolCallStart);
1612
- if (potentialIndex != null && potentialIndex + toolCallStart.length > buffer.length) {
1613
- publish(buffer.slice(0, potentialIndex));
1614
- buffer = buffer.slice(potentialIndex);
1615
- } else {
1616
- publish(buffer);
1617
- buffer = "";
1618
- }
2145
+ },
2146
+ flush(controller) {
2147
+ if (currentToolCall) {
2148
+ const unfinishedCall = `<${currentToolCall.name}>${buffer}`;
2149
+ flushText(controller, unfinishedCall);
2150
+ } else if (buffer) {
2151
+ flushText(controller);
2152
+ }
2153
+ if (currentTextId) {
2154
+ controller.enqueue({ type: "text-end", id: currentTextId });
1619
2155
  }
1620
2156
  }
1621
2157
  });
2158
+ },
2159
+ extractToolCallSegments({ text, tools }) {
2160
+ const toolNames = tools.map((t) => t.name).filter(Boolean);
2161
+ if (toolNames.length === 0) return [];
2162
+ const names = toolNames.map((n) => escapeRegExp(String(n))).join("|");
2163
+ if (!names) return [];
2164
+ const regex = new RegExp(`<(${names})>[\\s\\S]*?<\\/\\1>`, "g");
2165
+ const segments = [];
2166
+ let m;
2167
+ while ((m = regex.exec(text)) != null) {
2168
+ segments.push(m[0]);
2169
+ }
2170
+ return segments;
1622
2171
  }
1623
2172
  });
1624
2173
 
1625
- // src/protocols/xml-protocol.ts
2174
+ // src/generate-handler.ts
1626
2175
  import { generateId as generateId4 } from "@ai-sdk/provider-utils";
1627
- import { XMLParser, XMLBuilder } from "fast-xml-parser";
1628
- var xmlProtocol = () => ({
1629
- formatTools({ tools, toolSystemPromptTemplate }) {
1630
- const toolsForPrompt = (tools || []).map((tool) => ({
1631
- name: tool.name,
1632
- description: tool.description,
1633
- parameters: unwrapJsonSchema(tool.inputSchema)
1634
- }));
1635
- return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
1636
- },
1637
- formatToolCall(toolCall) {
1638
- const builder = new XMLBuilder({ format: true, suppressEmptyNode: true });
1639
- let args = {};
1640
- const inputValue = hasInputProperty(toolCall) ? toolCall.input : void 0;
1641
- if (typeof inputValue === "string") {
2176
+ async function wrapGenerate({
2177
+ protocol,
2178
+ doGenerate,
2179
+ params
2180
+ }) {
2181
+ var _a, _b;
2182
+ if (isToolChoiceActive(params)) {
2183
+ const result2 = await doGenerate();
2184
+ let parsed2 = {};
2185
+ const first = (_a = result2.content) == null ? void 0 : _a[0];
2186
+ if (first && first.type === "text") {
2187
+ const debugLevel2 = getDebugLevel();
2188
+ if (debugLevel2 === "parse") {
2189
+ logRawChunk(first.text);
2190
+ }
1642
2191
  try {
1643
- args = JSON.parse(inputValue);
1644
- } catch (e) {
1645
- args = inputValue;
2192
+ parsed2 = JSON.parse(first.text);
2193
+ } catch (error) {
2194
+ const options = extractOnErrorOption(params.providerOptions);
2195
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2196
+ options,
2197
+ "Failed to parse toolChoice JSON from generated model output",
2198
+ {
2199
+ text: first.text,
2200
+ error: error instanceof Error ? error.message : String(error)
2201
+ }
2202
+ );
2203
+ parsed2 = {};
1646
2204
  }
1647
- } else {
1648
- args = inputValue;
1649
2205
  }
1650
- const xmlContent = builder.build({
1651
- [toolCall.toolName]: args
1652
- });
1653
- return xmlContent;
1654
- },
1655
- formatToolResponse(toolResult) {
1656
- const builder = new XMLBuilder({ format: true });
1657
- const xmlContent = builder.build({
1658
- tool_response: {
1659
- tool_name: toolResult.toolName,
1660
- result: toolResult.output
2206
+ const toolCall = {
2207
+ type: "tool-call",
2208
+ toolCallId: generateId4(),
2209
+ toolName: parsed2.name || "unknown",
2210
+ input: JSON.stringify(parsed2.arguments || {})
2211
+ };
2212
+ const debugLevelToolChoice = getDebugLevel();
2213
+ const originText = first && first.type === "text" ? first.text : "";
2214
+ if (debugLevelToolChoice === "parse") {
2215
+ logParsedSummary({ toolCalls: [toolCall], originalText: originText });
2216
+ }
2217
+ return {
2218
+ ...result2,
2219
+ content: [toolCall]
2220
+ };
2221
+ }
2222
+ const result = await doGenerate();
2223
+ if (result.content.length === 0) {
2224
+ return result;
2225
+ }
2226
+ const parsed = result.content.flatMap((contentItem) => {
2227
+ var _a2;
2228
+ if (contentItem.type !== "text") {
2229
+ return [contentItem];
2230
+ }
2231
+ const debugLevel2 = getDebugLevel();
2232
+ if (debugLevel2 === "stream") {
2233
+ logRawChunk(contentItem.text);
2234
+ }
2235
+ return protocol.parseGeneratedText({
2236
+ text: contentItem.text,
2237
+ tools: getFunctionTools(params),
2238
+ options: {
2239
+ ...extractOnErrorOption(params.providerOptions),
2240
+ ...(_a2 = params.providerOptions) == null ? void 0 : _a2.toolCallMiddleware
1661
2241
  }
1662
2242
  });
1663
- return xmlContent;
1664
- },
1665
- parseGeneratedText({ text, tools, options }) {
1666
- var _a, _b, _c;
1667
- const originalSchemas = (options == null ? void 0 : options.originalToolSchemas) || {};
1668
- const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1669
- if (toolNames.length === 0) {
1670
- return [{ type: "text", text }];
1671
- }
1672
- const toolNamesPattern = toolNames.map((n) => escapeRegExp(n)).join("|");
1673
- const toolCallRegex = new RegExp(
1674
- String.raw`<(${toolNamesPattern})>([\s\S]*?)<\/\1>`,
1675
- "g"
2243
+ });
2244
+ const tools = getFunctionTools(params);
2245
+ const newContent = parsed.map(
2246
+ (part) => coerceToolCallInput(part, tools)
2247
+ );
2248
+ const debugLevel = getDebugLevel();
2249
+ if (debugLevel === "stream") {
2250
+ newContent.forEach((part) => logParsedChunk(part));
2251
+ }
2252
+ if (debugLevel === "parse") {
2253
+ const allText = result.content.filter(
2254
+ (c) => c.type === "text"
2255
+ ).map((c) => c.text).join("\n\n");
2256
+ const segments = protocol.extractToolCallSegments ? protocol.extractToolCallSegments({ text: allText, tools }) : [];
2257
+ const originalText = segments.join("\n\n");
2258
+ const toolCalls = newContent.filter(
2259
+ (p) => p.type === "tool-call"
2260
+ );
2261
+ logParsedSummary({ toolCalls, originalText });
2262
+ }
2263
+ return {
2264
+ ...result,
2265
+ content: newContent
2266
+ };
2267
+ }
2268
+
2269
+ // src/stream-handler.ts
2270
+ import { generateId as generateId5 } from "@ai-sdk/provider-utils";
2271
+ async function wrapStream({
2272
+ protocol,
2273
+ doStream,
2274
+ doGenerate,
2275
+ params
2276
+ }) {
2277
+ var _a;
2278
+ if (isToolChoiceActive(params)) {
2279
+ return toolChoiceStream({
2280
+ doGenerate,
2281
+ options: extractOnErrorOption(params.providerOptions)
2282
+ });
2283
+ }
2284
+ const { stream, ...rest } = await doStream();
2285
+ const debugLevel = getDebugLevel();
2286
+ const tools = getFunctionTools(params);
2287
+ const options = {
2288
+ ...extractOnErrorOption(params.providerOptions),
2289
+ ...(_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware
2290
+ };
2291
+ if (debugLevel === "off") {
2292
+ return {
2293
+ stream: stream.pipeThrough(
2294
+ protocol.createStreamParser({
2295
+ tools,
2296
+ options
2297
+ })
2298
+ ),
2299
+ ...rest
2300
+ };
2301
+ }
2302
+ if (debugLevel === "stream") {
2303
+ const withRawTap2 = stream.pipeThrough(
2304
+ new TransformStream(
2305
+ {
2306
+ transform(part, controller) {
2307
+ logRawChunk(part);
2308
+ controller.enqueue(part);
2309
+ }
2310
+ }
2311
+ )
1676
2312
  );
1677
- const processedElements = [];
1678
- let currentIndex = 0;
1679
- let match;
1680
- while ((match = toolCallRegex.exec(text)) !== null) {
1681
- const startIndex = match.index;
1682
- const toolName = match[1];
1683
- const toolContent = match[2].trim();
1684
- if (startIndex > currentIndex) {
1685
- const textSegment = text.substring(currentIndex, startIndex);
1686
- if (textSegment.trim()) {
1687
- processedElements.push({ type: "text", text: textSegment });
2313
+ const parsed2 = withRawTap2.pipeThrough(
2314
+ protocol.createStreamParser({
2315
+ tools,
2316
+ options
2317
+ })
2318
+ );
2319
+ const withParsedTap = parsed2.pipeThrough(
2320
+ new TransformStream(
2321
+ {
2322
+ transform(part, controller) {
2323
+ logParsedChunk(part);
2324
+ controller.enqueue(part);
2325
+ }
2326
+ }
2327
+ )
2328
+ );
2329
+ return {
2330
+ stream: withParsedTap,
2331
+ ...rest
2332
+ };
2333
+ }
2334
+ let fullRawText = "";
2335
+ const withRawTap = stream.pipeThrough(
2336
+ new TransformStream({
2337
+ transform(part, controller) {
2338
+ if (part.type === "text-delta") {
2339
+ const delta = part.delta;
2340
+ if (typeof delta === "string" && delta.length > 0) {
2341
+ fullRawText += delta;
2342
+ }
1688
2343
  }
2344
+ controller.enqueue(part);
1689
2345
  }
1690
- try {
1691
- const parser = new XMLParser({
1692
- ignoreAttributes: false,
1693
- parseTagValue: false,
1694
- ignoreDeclaration: true,
1695
- textNodeName: "#text"
1696
- });
1697
- const parsedArgs = ((_a = parser.parse(`<root>${toolContent}</root>`)) == null ? void 0 : _a.root) || {};
1698
- const args = {};
1699
- for (const k of Object.keys(parsedArgs || {})) {
1700
- const v = parsedArgs[k];
1701
- let val = v;
1702
- if (v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, "#text")) {
1703
- val = v == null ? void 0 : v["#text"];
2346
+ })
2347
+ );
2348
+ const parsed = withRawTap.pipeThrough(
2349
+ protocol.createStreamParser({
2350
+ tools,
2351
+ options
2352
+ })
2353
+ );
2354
+ const withSummary = parsed.pipeThrough(
2355
+ new TransformStream({
2356
+ transform: /* @__PURE__ */ (() => {
2357
+ const parsedToolCalls = [];
2358
+ return (part, controller) => {
2359
+ if (part.type === "tool-call") {
2360
+ parsedToolCalls.push(part);
1704
2361
  }
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
- }
2362
+ if (part.type === "finish") {
2363
+ try {
2364
+ const segments = protocol.extractToolCallSegments ? protocol.extractToolCallSegments({
2365
+ text: fullRawText,
2366
+ tools
2367
+ }) : [];
2368
+ const origin = segments.join("\n\n");
2369
+ logParsedSummary({
2370
+ toolCalls: parsedToolCalls,
2371
+ originalText: origin
2372
+ });
2373
+ } catch (e) {
1769
2374
  }
1770
2375
  }
1771
- args[k] = typeof val === "string" ? val.trim() : val;
2376
+ controller.enqueue(part);
2377
+ };
2378
+ })()
2379
+ })
2380
+ );
2381
+ return {
2382
+ stream: withSummary,
2383
+ ...rest
2384
+ };
2385
+ }
2386
+ async function toolChoiceStream({
2387
+ doGenerate,
2388
+ options
2389
+ }) {
2390
+ var _a, _b;
2391
+ const result = await doGenerate();
2392
+ let toolJson = {};
2393
+ if ((result == null ? void 0 : result.content) && result.content.length > 0 && ((_a = result.content[0]) == null ? void 0 : _a.type) === "text") {
2394
+ try {
2395
+ toolJson = JSON.parse(result.content[0].text);
2396
+ } catch (error) {
2397
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2398
+ options,
2399
+ "Failed to parse toolChoice JSON from streamed model output",
2400
+ {
2401
+ text: result.content[0].text,
2402
+ error: error instanceof Error ? error.message : String(error)
1772
2403
  }
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);
1777
- processedElements.push({
1778
- type: "tool-call",
1779
- toolCallId: generateId4(),
1780
- toolName,
1781
- input: JSON.stringify(coercedArgs)
1782
- });
1783
- } catch (error) {
1784
- const message = `Could not process XML tool call, keeping original text: ${match[0]}`;
1785
- (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, message, { toolCall: match[0], toolName, error });
1786
- processedElements.push({ type: "text", text: match[0] });
1787
- }
1788
- currentIndex = startIndex + match[0].length;
2404
+ );
2405
+ toolJson = {};
1789
2406
  }
1790
- if (currentIndex < text.length) {
1791
- const remainingText = text.substring(currentIndex);
1792
- if (remainingText.trim()) {
1793
- processedElements.push({ type: "text", text: remainingText });
1794
- }
2407
+ }
2408
+ const toolCallChunk = {
2409
+ type: "tool-call",
2410
+ toolCallId: generateId5(),
2411
+ toolName: toolJson.name || "unknown",
2412
+ input: JSON.stringify(toolJson.arguments || {})
2413
+ };
2414
+ const finishChunk = {
2415
+ type: "finish",
2416
+ usage: (result == null ? void 0 : result.usage) || // TODO: If possible, try to return a certain amount of LLM usage.
2417
+ {
2418
+ inputTokens: 0,
2419
+ outputTokens: 0,
2420
+ totalTokens: 0
2421
+ },
2422
+ finishReason: "tool-calls"
2423
+ };
2424
+ const stream = new ReadableStream({
2425
+ start(controller) {
2426
+ controller.enqueue(toolCallChunk);
2427
+ controller.enqueue(finishChunk);
2428
+ controller.close();
1795
2429
  }
1796
- return processedElements;
1797
- },
1798
- createStreamParser({ tools, options }) {
1799
- const originalSchemas = (options == null ? void 0 : options.originalToolSchemas) || {};
1800
- const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1801
- let buffer = "";
1802
- let currentToolCall = null;
1803
- let currentTextId = null;
1804
- const flushText = (controller, text) => {
1805
- const content = text != null ? text : buffer;
1806
- if (content) {
1807
- if (!currentTextId) {
1808
- currentTextId = generateId4();
1809
- controller.enqueue({ type: "text-start", id: currentTextId });
2430
+ });
2431
+ const debugLevel = getDebugLevel();
2432
+ const firstText = (result == null ? void 0 : result.content) && result.content[0] && result.content[0].type === "text" && result.content[0].text || "";
2433
+ const streamWithSummary = debugLevel === "parse" ? stream.pipeThrough(
2434
+ new TransformStream({
2435
+ transform(part, controller) {
2436
+ if (part.type === "finish") {
2437
+ try {
2438
+ logParsedSummary({
2439
+ toolCalls: [toolCallChunk],
2440
+ originalText: typeof firstText === "string" ? firstText : ""
2441
+ });
2442
+ } catch (e) {
2443
+ }
1810
2444
  }
1811
- controller.enqueue({
1812
- type: "text-delta",
1813
- id: currentTextId,
1814
- delta: content
1815
- });
1816
- if (text === void 0) {
1817
- buffer = "";
2445
+ controller.enqueue(part);
2446
+ }
2447
+ })
2448
+ ) : stream;
2449
+ return {
2450
+ request: (result == null ? void 0 : result.request) || {},
2451
+ response: (result == null ? void 0 : result.response) || {},
2452
+ stream: streamWithSummary
2453
+ };
2454
+ }
2455
+
2456
+ // src/transform-handler.ts
2457
+ async function transformParams({
2458
+ params,
2459
+ protocol,
2460
+ toolSystemPromptTemplate
2461
+ }) {
2462
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2463
+ const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
2464
+ const functionTools = ((_a = params.tools) != null ? _a : []).filter(
2465
+ (t) => t.type === "function"
2466
+ );
2467
+ const systemPrompt = resolvedProtocol.formatTools({
2468
+ tools: functionTools,
2469
+ toolSystemPromptTemplate
2470
+ });
2471
+ const processedPrompt = convertToolPrompt(
2472
+ (_b = params.prompt) != null ? _b : [],
2473
+ resolvedProtocol,
2474
+ extractOnErrorOption(params.providerOptions)
2475
+ );
2476
+ const finalPrompt = ((_c = processedPrompt[0]) == null ? void 0 : _c.role) === "system" ? [
2477
+ {
2478
+ role: "system",
2479
+ content: systemPrompt + "\n\n" + processedPrompt[0].content
2480
+ },
2481
+ ...processedPrompt.slice(1)
2482
+ ] : [
2483
+ {
2484
+ role: "system",
2485
+ content: systemPrompt
2486
+ },
2487
+ ...processedPrompt
2488
+ ];
2489
+ const baseReturnParams = {
2490
+ ...params,
2491
+ prompt: finalPrompt,
2492
+ tools: [],
2493
+ toolChoice: void 0,
2494
+ providerOptions: {
2495
+ ...params.providerOptions || {},
2496
+ toolCallMiddleware: {
2497
+ ...params.providerOptions && typeof params.providerOptions === "object" && params.providerOptions.toolCallMiddleware || {},
2498
+ // INTERNAL: used by the middleware to propagate the names of custom
2499
+ // function tools into downstream handlers (stream/generate) when
2500
+ // providers strip or ignore `params.tools`. Not a stable public API.
2501
+ toolNames: functionTools.map((t) => t.name)
2502
+ }
2503
+ }
2504
+ };
2505
+ if (((_d = params.toolChoice) == null ? void 0 : _d.type) === "none") {
2506
+ throw new Error(
2507
+ "The 'none' toolChoice type is not supported by this middleware. Please use 'auto', 'required', or specify a tool name."
2508
+ );
2509
+ }
2510
+ if (((_e = params.toolChoice) == null ? void 0 : _e.type) === "tool") {
2511
+ const selectedToolName = params.toolChoice.toolName;
2512
+ const providerDefinedMatch = ((_f = params.tools) != null ? _f : []).find((t) => {
2513
+ if (t.type === "function") return false;
2514
+ const anyTool = t;
2515
+ return anyTool.id === selectedToolName || anyTool.name === selectedToolName;
2516
+ });
2517
+ if (providerDefinedMatch) {
2518
+ throw new Error(
2519
+ "Provider-defined tools are not supported by this middleware. Please use custom tools."
2520
+ );
2521
+ }
2522
+ const selectedTool = ((_g = params.tools) != null ? _g : []).find(
2523
+ (t) => t.type === "function" && t.name === selectedToolName
2524
+ );
2525
+ if (!selectedTool) {
2526
+ throw new Error(
2527
+ `Tool with name '${selectedToolName}' not found in params.tools.`
2528
+ );
2529
+ }
2530
+ return {
2531
+ ...baseReturnParams,
2532
+ responseFormat: {
2533
+ type: "json",
2534
+ schema: {
2535
+ type: "object",
2536
+ properties: {
2537
+ name: {
2538
+ const: selectedTool.name
2539
+ },
2540
+ arguments: selectedTool.inputSchema
2541
+ },
2542
+ required: ["name", "arguments"]
2543
+ },
2544
+ name: selectedTool.name,
2545
+ description: typeof selectedTool.description === "string" ? selectedTool.description : void 0
2546
+ },
2547
+ providerOptions: {
2548
+ ...baseReturnParams.providerOptions || {},
2549
+ toolCallMiddleware: {
2550
+ ...baseReturnParams.providerOptions && typeof baseReturnParams.providerOptions === "object" && baseReturnParams.providerOptions.toolCallMiddleware || {},
2551
+ // INTERNAL: used by the middleware to activate the tool-choice
2552
+ // fast-path in handlers. Not a stable public API.
2553
+ toolChoice: params.toolChoice
1818
2554
  }
1819
2555
  }
1820
- if (currentTextId && !text) {
1821
- controller.enqueue({ type: "text-end", id: currentTextId });
1822
- currentTextId = null;
2556
+ };
2557
+ }
2558
+ if (((_h = params.toolChoice) == null ? void 0 : _h.type) === "required") {
2559
+ if (!params.tools || params.tools.length === 0) {
2560
+ throw new Error(
2561
+ "Tool choice type 'required' is set, but no tools are provided in params.tools."
2562
+ );
2563
+ }
2564
+ return {
2565
+ ...baseReturnParams,
2566
+ responseFormat: {
2567
+ type: "json",
2568
+ schema: createDynamicIfThenElseSchema(functionTools)
2569
+ },
2570
+ providerOptions: {
2571
+ ...baseReturnParams.providerOptions || {},
2572
+ toolCallMiddleware: {
2573
+ ...baseReturnParams.providerOptions && typeof baseReturnParams.providerOptions === "object" && baseReturnParams.providerOptions.toolCallMiddleware || {},
2574
+ // INTERNAL: used by the middleware to activate the tool-choice
2575
+ // fast-path in handlers. Not a stable public API.
2576
+ toolChoice: { type: "required" }
2577
+ }
1823
2578
  }
1824
2579
  };
1825
- return new TransformStream({
1826
- transform(chunk, controller) {
1827
- var _a, _b;
1828
- if (chunk.type !== "text-delta") {
1829
- if (buffer) flushText(controller);
1830
- controller.enqueue(chunk);
1831
- return;
2580
+ }
2581
+ return baseReturnParams;
2582
+ }
2583
+ function convertToolPrompt(prompt, resolvedProtocol, providerOptions) {
2584
+ const processedPrompt = prompt.map((message) => {
2585
+ var _a;
2586
+ if (message.role === "assistant") {
2587
+ const newContent = [];
2588
+ for (const content of message.content) {
2589
+ if (isToolCallContent(content)) {
2590
+ newContent.push({
2591
+ type: "text",
2592
+ text: resolvedProtocol.formatToolCall(content)
2593
+ });
2594
+ } else if (content.type === "text") {
2595
+ newContent.push(content);
2596
+ } else if (content.type === "reasoning") {
2597
+ newContent.push(content);
2598
+ } else {
2599
+ const options = extractOnErrorOption(providerOptions);
2600
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
2601
+ options,
2602
+ "tool-call-middleware: unknown assistant content; stringifying for provider compatibility",
2603
+ { content }
2604
+ );
2605
+ newContent.push({
2606
+ type: "text",
2607
+ text: JSON.stringify(content)
2608
+ });
1832
2609
  }
1833
- buffer += chunk.delta;
1834
- while (true) {
1835
- if (currentToolCall) {
1836
- const endTag = `</${currentToolCall.name}>`;
1837
- const endTagIndex = buffer.indexOf(endTag);
1838
- if (endTagIndex !== -1) {
1839
- const toolContent = buffer.substring(0, endTagIndex);
1840
- buffer = buffer.substring(endTagIndex + endTag.length);
1841
- try {
1842
- const parser = new XMLParser({
1843
- ignoreAttributes: false,
1844
- parseTagValue: false,
1845
- ignoreDeclaration: true,
1846
- textNodeName: "#text"
1847
- });
1848
- const parsedArgs = ((_a = parser.parse(`<root>${toolContent}</root>`)) == null ? void 0 : _a.root) || {};
1849
- const args = {};
1850
- for (const k of Object.keys(parsedArgs || {})) {
1851
- const v = parsedArgs[k];
1852
- let val = v;
1853
- if (v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, "#text")) {
1854
- val = v == null ? void 0 : v["#text"];
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
- }
1922
- args[k] = typeof val === "string" ? val.trim() : val;
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);
1930
- flushText(controller);
1931
- controller.enqueue({
1932
- type: "tool-call",
1933
- toolCallId: generateId4(),
1934
- toolName: currentToolCall.name,
1935
- input: JSON.stringify(coercedArgs)
1936
- });
1937
- } catch (e) {
1938
- const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1939
- if (options == null ? void 0 : options.onError) {
1940
- options.onError(
1941
- "Could not process streaming XML tool call; emitting original text.",
1942
- {
1943
- toolCall: originalCallText,
1944
- toolName: currentToolCall.name
1945
- }
1946
- );
1947
- }
1948
- flushText(controller, originalCallText);
2610
+ }
2611
+ const onlyText = newContent.every((c) => c.type === "text");
2612
+ const condensedAssistant = onlyText ? [
2613
+ {
2614
+ type: "text",
2615
+ text: newContent.map((c) => c.text).join("\n")
2616
+ }
2617
+ ] : newContent;
2618
+ return { role: "assistant", content: condensedAssistant };
2619
+ }
2620
+ if (message.role === "tool") {
2621
+ return {
2622
+ role: "user",
2623
+ // Map tool results to text response blocks, then condense into a single text block
2624
+ content: [
2625
+ {
2626
+ type: "text",
2627
+ text: message.content.map(
2628
+ (toolResult) => isToolResultPart(toolResult) ? resolvedProtocol.formatToolResponse(toolResult) : resolvedProtocol.formatToolResponse(
2629
+ toolResult
2630
+ )
2631
+ ).join("\n")
2632
+ }
2633
+ ]
2634
+ };
2635
+ }
2636
+ return message;
2637
+ });
2638
+ for (let i = 0; i < processedPrompt.length; i++) {
2639
+ const msg = processedPrompt[i];
2640
+ if (Array.isArray(msg.content)) {
2641
+ const allText = msg.content.every(
2642
+ (c) => (c == null ? void 0 : c.type) === "text"
2643
+ );
2644
+ if (allText && msg.content.length > 1) {
2645
+ const joinedText = msg.content.map((c) => c.text).join("\n");
2646
+ if (msg.role === "system") {
2647
+ processedPrompt[i] = {
2648
+ role: "system",
2649
+ content: joinedText
2650
+ };
2651
+ } else if (msg.role === "assistant") {
2652
+ processedPrompt[i] = {
2653
+ role: "assistant",
2654
+ content: [
2655
+ {
2656
+ type: "text",
2657
+ text: joinedText
1949
2658
  }
1950
- currentToolCall = null;
1951
- } else {
1952
- break;
1953
- }
1954
- } else {
1955
- let earliestStartTagIndex = -1;
1956
- let earliestToolName = "";
1957
- if (toolNames.length > 0) {
1958
- for (const name of toolNames) {
1959
- const startTag = `<${name}>`;
1960
- const index = buffer.indexOf(startTag);
1961
- if (index !== -1 && (earliestStartTagIndex === -1 || index < earliestStartTagIndex)) {
1962
- earliestStartTagIndex = index;
1963
- earliestToolName = name;
1964
- }
2659
+ ]
2660
+ };
2661
+ } else {
2662
+ processedPrompt[i] = {
2663
+ role: "user",
2664
+ content: [
2665
+ {
2666
+ type: "text",
2667
+ text: joinedText
1965
2668
  }
1966
- }
1967
- if (earliestStartTagIndex !== -1) {
1968
- const textBeforeTag = buffer.substring(0, earliestStartTagIndex);
1969
- flushText(controller, textBeforeTag);
1970
- const startTag = `<${earliestToolName}>`;
1971
- buffer = buffer.substring(
1972
- earliestStartTagIndex + startTag.length
1973
- );
1974
- currentToolCall = { name: earliestToolName, content: "" };
1975
- } else {
1976
- break;
1977
- }
1978
- }
1979
- }
1980
- },
1981
- flush(controller) {
1982
- if (currentToolCall) {
1983
- const unfinishedCall = `<${currentToolCall.name}>${buffer}`;
1984
- flushText(controller, unfinishedCall);
1985
- } else if (buffer) {
1986
- flushText(controller);
1987
- }
1988
- if (currentTextId) {
1989
- controller.enqueue({ type: "text-end", id: currentTextId });
2669
+ ]
2670
+ };
1990
2671
  }
1991
2672
  }
1992
- });
2673
+ }
1993
2674
  }
1994
- });
2675
+ for (let i = processedPrompt.length - 1; i > 0; i--) {
2676
+ const current = processedPrompt[i];
2677
+ const prev = processedPrompt[i - 1];
2678
+ if (current.role === "user" && prev.role === "user") {
2679
+ const prevContent = prev.content.map((c) => c.type === "text" ? c.text : "").join("\n");
2680
+ const currentContent = current.content.map((c) => c.type === "text" ? c.text : "").join("\n");
2681
+ processedPrompt[i - 1] = {
2682
+ role: "user",
2683
+ content: [{ type: "text", text: prevContent + "\n" + currentContent }]
2684
+ };
2685
+ processedPrompt.splice(i, 1);
2686
+ }
2687
+ }
2688
+ return processedPrompt;
2689
+ }
1995
2690
 
1996
- // src/protocols/dummy-protocol.ts
1997
- import { generateId as generateId5 } from "@ai-sdk/provider-utils";
2691
+ // src/tool-call-middleware.ts
2692
+ function createToolMiddleware({
2693
+ protocol,
2694
+ toolSystemPromptTemplate
2695
+ }) {
2696
+ const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
2697
+ return {
2698
+ middlewareVersion: "v2",
2699
+ wrapStream: async ({ doStream, doGenerate, params }) => {
2700
+ if (isToolChoiceActive(params)) {
2701
+ return toolChoiceStream({
2702
+ doGenerate,
2703
+ options: extractOnErrorOption(params.providerOptions)
2704
+ });
2705
+ } else {
2706
+ return wrapStream({
2707
+ protocol: resolvedProtocol,
2708
+ doStream,
2709
+ doGenerate,
2710
+ params
2711
+ });
2712
+ }
2713
+ },
2714
+ wrapGenerate: async ({ doGenerate, params }) => wrapGenerate({
2715
+ protocol: resolvedProtocol,
2716
+ doGenerate,
2717
+ params
2718
+ }),
2719
+ transformParams: async ({
2720
+ params
2721
+ }) => {
2722
+ return transformParams({
2723
+ protocol: resolvedProtocol,
2724
+ toolSystemPromptTemplate,
2725
+ params
2726
+ });
2727
+ }
2728
+ };
2729
+ }
1998
2730
 
1999
2731
  // src/index.ts
2000
2732
  var gemmaToolMiddleware = createToolMiddleware({
@@ -2002,8 +2734,9 @@ var gemmaToolMiddleware = createToolMiddleware({
2002
2734
  // Customize the tool call delimiters to use markdown code fences
2003
2735
  {
2004
2736
  toolCallStart: "```tool_call\n",
2005
- toolCallEnd: "\n``",
2006
- // two backticks are more common in gemma output @
2737
+ // TODO: Support specifying multiple possible tags,
2738
+ // e.g., for gemma, it would be nice to be able to set both `` and ``` at the same time.
2739
+ toolCallEnd: "\n```",
2007
2740
  toolResponseStart: "```tool_response\n",
2008
2741
  toolResponseEnd: "\n```"
2009
2742
  }
@@ -2034,7 +2767,7 @@ For each function call return a json object with function name and arguments wit
2034
2767
  }
2035
2768
  });
2036
2769
  var xmlToolMiddleware = createToolMiddleware({
2037
- protocol: xmlProtocol,
2770
+ protocol: morphXmlProtocol,
2038
2771
  toolSystemPromptTemplate(tools) {
2039
2772
  return `You are a function calling AI model.
2040
2773
  You are provided with function signatures within <tools></tools> XML tags.
@@ -2052,15 +2785,11 @@ San Fransisco
2052
2785
  }
2053
2786
  });
2054
2787
  export {
2055
- coerceBySchema,
2056
2788
  createToolMiddleware,
2057
- fixToolCallWithSchema,
2058
2789
  gemmaToolMiddleware,
2059
- getSchemaType,
2060
2790
  hermesToolMiddleware,
2061
2791
  jsonMixProtocol,
2062
- unwrapJsonSchema,
2063
- xmlProtocol,
2792
+ morphXmlProtocol,
2064
2793
  xmlToolMiddleware
2065
2794
  };
2066
2795
  //# sourceMappingURL=index.js.map