@ai-sdk-tool/parser 3.3.2 → 3.3.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.
@@ -2,13 +2,15 @@ import {
2
2
  escapeRegExp,
3
3
  parse as parse2,
4
4
  stringify
5
- } from "./chunk-ZDBNJWLY.js";
5
+ } from "./chunk-CXWS24JX.js";
6
6
  import {
7
7
  parse
8
8
  } from "./chunk-IX4FJELL.js";
9
9
  import {
10
- coerceBySchema
11
- } from "./chunk-OUGMLYAW.js";
10
+ coerceBySchema,
11
+ getSchemaType,
12
+ unwrapJsonSchema
13
+ } from "./chunk-2KK5BDZF.js";
12
14
 
13
15
  // src/core/utils/debug.ts
14
16
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -518,6 +520,30 @@ function isTCMProtocolFactory(protocol) {
518
520
  var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
519
521
  var WHITESPACE_REGEX = /\s/;
520
522
 
523
+ // src/core/utils/xml-root-repair.ts
524
+ var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
525
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
526
+ const trimmed = rawText.trim();
527
+ if (trimmed.length === 0) {
528
+ return null;
529
+ }
530
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
531
+ if (!match) {
532
+ return null;
533
+ }
534
+ const rootTag = match[1];
535
+ if (!toolNames.includes(rootTag)) {
536
+ return null;
537
+ }
538
+ const body = match[2].trimEnd();
539
+ if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
540
+ return null;
541
+ }
542
+ return `<${rootTag}>
543
+ ${body}
544
+ </${rootTag}>`;
545
+ }
546
+
521
547
  // src/core/protocols/xml-protocol.ts
522
548
  function getToolSchema(tools, toolName) {
523
549
  var _a;
@@ -799,6 +825,102 @@ function findToolCalls(text, toolNames) {
799
825
  }
800
826
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
801
827
  }
828
+ function handleSpecialToken(depth) {
829
+ return { depth, lastCompleteEnd: -1, shouldBreak: false };
830
+ }
831
+ function handleOpenToken(token, depth, lastCompleteEnd) {
832
+ if (token.selfClosing) {
833
+ return {
834
+ depth,
835
+ lastCompleteEnd: depth === 0 ? token.nextPos : lastCompleteEnd,
836
+ shouldBreak: false
837
+ };
838
+ }
839
+ return { depth: depth + 1, lastCompleteEnd, shouldBreak: false };
840
+ }
841
+ function handleCloseToken(token, depth) {
842
+ if (depth <= 0) {
843
+ return { depth, lastCompleteEnd: -1, shouldBreak: true };
844
+ }
845
+ const newDepth = depth - 1;
846
+ return {
847
+ depth: newDepth,
848
+ lastCompleteEnd: newDepth === 0 ? token.nextPos : -1,
849
+ shouldBreak: false
850
+ };
851
+ }
852
+ function findLinePrefixedXmlBodyEnd(text, bodyStartIndex) {
853
+ let cursor = bodyStartIndex;
854
+ let depth = 0;
855
+ let lastCompleteEnd = -1;
856
+ while (cursor < text.length) {
857
+ if (depth === 0) {
858
+ cursor = consumeWhitespace(text, cursor);
859
+ if (cursor >= text.length || text.charAt(cursor) !== "<") {
860
+ break;
861
+ }
862
+ }
863
+ const token = nextTagToken(text, cursor);
864
+ if (token.kind === "eof") {
865
+ break;
866
+ }
867
+ let result;
868
+ if (token.kind === "special") {
869
+ result = handleSpecialToken(depth);
870
+ } else if (token.kind === "open") {
871
+ result = handleOpenToken(token, depth, lastCompleteEnd);
872
+ } else {
873
+ result = handleCloseToken(token, depth);
874
+ }
875
+ depth = result.depth;
876
+ if (result.lastCompleteEnd !== -1) {
877
+ lastCompleteEnd = result.lastCompleteEnd;
878
+ }
879
+ if (result.shouldBreak) {
880
+ break;
881
+ }
882
+ cursor = token.nextPos;
883
+ }
884
+ return lastCompleteEnd;
885
+ }
886
+ function findLinePrefixedToolCall(text, toolNames) {
887
+ var _a;
888
+ let best = null;
889
+ for (const toolName of toolNames) {
890
+ const linePattern = new RegExp(
891
+ `(^|\\n)[\\t ]*${escapeRegExp(toolName)}[\\t ]*:?[\\t ]*(?:\\r?\\n|$)`,
892
+ "g"
893
+ );
894
+ let match = linePattern.exec(text);
895
+ while (match !== null) {
896
+ const prefix = (_a = match[1]) != null ? _a : "";
897
+ const startIndex = match.index + prefix.length;
898
+ const contentStart = consumeWhitespace(text, linePattern.lastIndex);
899
+ if (contentStart >= text.length || text.charAt(contentStart) !== "<") {
900
+ match = linePattern.exec(text);
901
+ continue;
902
+ }
903
+ const contentEnd = findLinePrefixedXmlBodyEnd(text, contentStart);
904
+ if (contentEnd === -1 || contentEnd <= contentStart) {
905
+ match = linePattern.exec(text);
906
+ continue;
907
+ }
908
+ const content = text.slice(contentStart, contentEnd);
909
+ const candidate = {
910
+ toolName,
911
+ startIndex,
912
+ endIndex: contentEnd,
913
+ content,
914
+ segment: text.slice(startIndex, contentEnd)
915
+ };
916
+ if (best === null || candidate.startIndex < best.startIndex) {
917
+ best = candidate;
918
+ }
919
+ break;
920
+ }
921
+ }
922
+ return best;
923
+ }
802
924
  function findEarliestToolTag(buffer, toolNames) {
803
925
  var _a, _b;
804
926
  let bestIndex = -1;
@@ -1088,6 +1210,27 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1088
1210
  }
1089
1211
  };
1090
1212
  }
1213
+ function findToolCallsWithFallbacks(text, toolNames) {
1214
+ let parseText = text;
1215
+ let toolCalls = findToolCalls(parseText, toolNames);
1216
+ if (toolCalls.length === 0) {
1217
+ const fallbackToolCall = findLinePrefixedToolCall(parseText, toolNames);
1218
+ if (fallbackToolCall !== null) {
1219
+ toolCalls.push(fallbackToolCall);
1220
+ }
1221
+ }
1222
+ if (toolCalls.length === 0) {
1223
+ const repaired = tryRepairXmlSelfClosingRootWithBody(parseText, toolNames);
1224
+ if (repaired) {
1225
+ const repairedCalls = findToolCalls(repaired, toolNames);
1226
+ if (repairedCalls.length > 0) {
1227
+ parseText = repaired;
1228
+ toolCalls = repairedCalls;
1229
+ }
1230
+ }
1231
+ }
1232
+ return { parseText, toolCalls };
1233
+ }
1091
1234
  var xmlProtocol = (protocolOptions) => {
1092
1235
  var _a;
1093
1236
  const parseOptions = {
@@ -1121,28 +1264,31 @@ var xmlProtocol = (protocolOptions) => {
1121
1264
  }
1122
1265
  const processedElements = [];
1123
1266
  let currentIndex = 0;
1124
- const toolCalls = findToolCalls(text, toolNames);
1267
+ const { parseText, toolCalls } = findToolCallsWithFallbacks(
1268
+ text,
1269
+ toolNames
1270
+ );
1125
1271
  for (const tc of toolCalls) {
1126
1272
  if (tc.startIndex > currentIndex) {
1127
1273
  processedElements.push({
1128
1274
  type: "text",
1129
- text: text.substring(currentIndex, tc.startIndex)
1275
+ text: parseText.substring(currentIndex, tc.startIndex)
1130
1276
  });
1131
1277
  }
1132
1278
  processToolCall({
1133
1279
  toolCall: tc,
1134
1280
  tools,
1135
1281
  options,
1136
- text,
1282
+ text: parseText,
1137
1283
  processedElements,
1138
1284
  parseOptions
1139
1285
  });
1140
1286
  currentIndex = tc.endIndex;
1141
1287
  }
1142
- if (currentIndex < text.length) {
1288
+ if (currentIndex < parseText.length) {
1143
1289
  processedElements.push({
1144
1290
  type: "text",
1145
- text: text.substring(currentIndex)
1291
+ text: parseText.substring(currentIndex)
1146
1292
  });
1147
1293
  }
1148
1294
  return processedElements;
@@ -1181,6 +1327,20 @@ var xmlProtocol = (protocolOptions) => {
1181
1327
  return new TransformStream({
1182
1328
  transform(chunk, controller) {
1183
1329
  var _a2;
1330
+ if (chunk.type === "finish") {
1331
+ if (currentToolCall) {
1332
+ const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
1333
+ flushText(controller, unfinishedContent);
1334
+ buffer = "";
1335
+ currentToolCall = null;
1336
+ } else if (buffer) {
1337
+ flushText(controller, buffer);
1338
+ buffer = "";
1339
+ }
1340
+ flushText(controller);
1341
+ controller.enqueue(chunk);
1342
+ return;
1343
+ }
1184
1344
  if (chunk.type !== "text-delta") {
1185
1345
  if (buffer) {
1186
1346
  flushText(controller, buffer);
@@ -1507,18 +1667,32 @@ ${yamlContent}</${toolCall.toolName}>`;
1507
1667
  }
1508
1668
  const processedElements = [];
1509
1669
  let currentIndex = 0;
1510
- const toolCalls = findToolCalls2(text, toolNames);
1670
+ let parseText = text;
1671
+ let toolCalls = findToolCalls2(parseText, toolNames);
1672
+ if (toolCalls.length === 0) {
1673
+ const repaired = tryRepairXmlSelfClosingRootWithBody(
1674
+ parseText,
1675
+ toolNames
1676
+ );
1677
+ if (repaired) {
1678
+ const repairedCalls = findToolCalls2(repaired, toolNames);
1679
+ if (repairedCalls.length > 0) {
1680
+ parseText = repaired;
1681
+ toolCalls = repairedCalls;
1682
+ }
1683
+ }
1684
+ }
1511
1685
  for (const tc of toolCalls) {
1512
1686
  currentIndex = processToolCallMatch(
1513
- text,
1687
+ parseText,
1514
1688
  tc,
1515
1689
  currentIndex,
1516
1690
  processedElements,
1517
1691
  options
1518
1692
  );
1519
1693
  }
1520
- if (currentIndex < text.length) {
1521
- addTextSegment(text.substring(currentIndex), processedElements);
1694
+ if (currentIndex < parseText.length) {
1695
+ addTextSegment(parseText.substring(currentIndex), processedElements);
1522
1696
  }
1523
1697
  return processedElements;
1524
1698
  },
@@ -1614,6 +1788,20 @@ ${yamlContent}</${toolCall.toolName}>`;
1614
1788
  return new TransformStream({
1615
1789
  transform(chunk, controller) {
1616
1790
  var _a;
1791
+ if (chunk.type === "finish") {
1792
+ if (currentToolCall) {
1793
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
1794
+ flushText(controller, unfinishedContent);
1795
+ buffer = "";
1796
+ currentToolCall = null;
1797
+ } else if (buffer) {
1798
+ flushText(controller, buffer);
1799
+ buffer = "";
1800
+ }
1801
+ flushText(controller);
1802
+ controller.enqueue(chunk);
1803
+ return;
1804
+ }
1617
1805
  if (chunk.type !== "text-delta") {
1618
1806
  if (buffer) {
1619
1807
  flushText(controller, buffer);
@@ -1737,17 +1925,56 @@ function encodeOriginalTools(tools) {
1737
1925
  inputSchema: JSON.stringify(t.inputSchema)
1738
1926
  }))) || [];
1739
1927
  }
1740
- function decodeOriginalTools(originalTools) {
1928
+ function decodeOriginalTools(originalTools, options) {
1929
+ var _a, _b, _c;
1741
1930
  if (!originalTools) {
1742
1931
  return [];
1743
1932
  }
1744
- return originalTools.map(
1745
- (t) => ({
1746
- type: "function",
1747
- name: t.name,
1748
- inputSchema: JSON.parse(t.inputSchema)
1749
- })
1750
- );
1933
+ const decodedTools = [];
1934
+ for (const [index, tool] of originalTools.entries()) {
1935
+ if (!tool || typeof tool.name !== "string") {
1936
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Invalid originalTools entry: missing tool name", {
1937
+ index,
1938
+ tool
1939
+ });
1940
+ continue;
1941
+ }
1942
+ if (typeof tool.inputSchema !== "string") {
1943
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1944
+ options,
1945
+ "Invalid originalTools entry: inputSchema must be a string",
1946
+ {
1947
+ index,
1948
+ toolName: tool.name
1949
+ }
1950
+ );
1951
+ continue;
1952
+ }
1953
+ try {
1954
+ decodedTools.push({
1955
+ type: "function",
1956
+ name: tool.name,
1957
+ inputSchema: JSON.parse(tool.inputSchema)
1958
+ });
1959
+ } catch (error) {
1960
+ (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(
1961
+ options,
1962
+ "Failed to decode originalTools input schema, using permissive fallback schema",
1963
+ {
1964
+ index,
1965
+ toolName: tool.name,
1966
+ inputSchema: tool.inputSchema,
1967
+ error: error instanceof Error ? error.message : String(error)
1968
+ }
1969
+ );
1970
+ decodedTools.push({
1971
+ type: "function",
1972
+ name: tool.name,
1973
+ inputSchema: { type: "object" }
1974
+ });
1975
+ }
1976
+ }
1977
+ return decodedTools;
1751
1978
  }
1752
1979
  function extractToolNamesFromOriginalTools(originalTools) {
1753
1980
  return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
@@ -1772,23 +1999,337 @@ function hasInputProperty(obj) {
1772
1999
 
1773
2000
  // src/generate-handler.ts
1774
2001
  import { generateId as generateId2 } from "@ai-sdk/provider-utils";
1775
- function parseToolChoiceJson(text, providerOptions) {
2002
+
2003
+ // src/core/utils/generated-text-json-recovery.ts
2004
+ function isRecord(value) {
2005
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2006
+ }
2007
+ function safeStringify2(value) {
2008
+ try {
2009
+ return JSON.stringify(value != null ? value : {});
2010
+ } catch (e) {
2011
+ return "{}";
2012
+ }
2013
+ }
2014
+ function parseJsonCandidate(candidateText) {
2015
+ try {
2016
+ return parse(candidateText);
2017
+ } catch (e) {
2018
+ return void 0;
2019
+ }
2020
+ }
2021
+ function extractCodeBlockCandidates(text) {
2022
+ var _a, _b;
2023
+ const codeBlockRegex = /```(?:json|yaml|xml)?\s*([\s\S]*?)```/gi;
2024
+ const candidates = [];
2025
+ let match;
2026
+ while (true) {
2027
+ match = codeBlockRegex.exec(text);
2028
+ if (!match) {
2029
+ break;
2030
+ }
2031
+ const body = (_a = match[1]) == null ? void 0 : _a.trim();
2032
+ if (body) {
2033
+ const startIndex = (_b = match.index) != null ? _b : 0;
2034
+ const endIndex = startIndex + match[0].length;
2035
+ candidates.push({
2036
+ text: body,
2037
+ startIndex,
2038
+ endIndex
2039
+ });
2040
+ }
2041
+ }
2042
+ return candidates;
2043
+ }
2044
+ function scanJsonChar(state, char) {
2045
+ if (state.inString) {
2046
+ if (state.escaping) {
2047
+ return { ...state, escaping: false };
2048
+ }
2049
+ if (char === "\\") {
2050
+ return { ...state, escaping: true };
2051
+ }
2052
+ if (char === '"') {
2053
+ return { ...state, inString: false };
2054
+ }
2055
+ return state;
2056
+ }
2057
+ if (char === '"') {
2058
+ return { ...state, inString: true };
2059
+ }
2060
+ if (char === "{") {
2061
+ return { ...state, depth: state.depth + 1 };
2062
+ }
2063
+ if (char === "}") {
2064
+ return { ...state, depth: Math.max(0, state.depth - 1) };
2065
+ }
2066
+ return state;
2067
+ }
2068
+ function extractBalancedJsonObjects(text) {
2069
+ const maxCandidateLength = 1e4;
2070
+ const candidates = [];
2071
+ let state = { depth: 0, inString: false, escaping: false };
2072
+ let currentStart = null;
2073
+ let ignoreCurrent = false;
2074
+ for (let index = 0; index < text.length; index += 1) {
2075
+ const char = text[index];
2076
+ if (!state.inString && char === "{" && state.depth === 0) {
2077
+ currentStart = index;
2078
+ ignoreCurrent = false;
2079
+ }
2080
+ state = scanJsonChar(state, char);
2081
+ if (currentStart !== null && !ignoreCurrent && index - currentStart + 1 > maxCandidateLength) {
2082
+ ignoreCurrent = true;
2083
+ }
2084
+ if (!state.inString && char === "}" && state.depth === 0) {
2085
+ if (currentStart !== null && !ignoreCurrent) {
2086
+ const endIndex = index + 1;
2087
+ const candidate = text.slice(currentStart, endIndex);
2088
+ if (candidate.length > 1) {
2089
+ candidates.push({
2090
+ text: candidate,
2091
+ startIndex: currentStart,
2092
+ endIndex
2093
+ });
2094
+ }
2095
+ }
2096
+ currentStart = null;
2097
+ ignoreCurrent = false;
2098
+ }
2099
+ }
2100
+ return candidates;
2101
+ }
2102
+ function extractTaggedToolCallCandidates(rawText) {
2103
+ var _a, _b;
2104
+ const toolCallRegex = /<tool_call>([\s\S]*?)<\/tool_call>/gi;
2105
+ const candidates = [];
2106
+ let match;
2107
+ while (true) {
2108
+ match = toolCallRegex.exec(rawText);
2109
+ if (!match) {
2110
+ break;
2111
+ }
2112
+ const body = (_a = match[1]) == null ? void 0 : _a.trim();
2113
+ if (!body) {
2114
+ continue;
2115
+ }
2116
+ const startIndex = (_b = match.index) != null ? _b : 0;
2117
+ const endIndex = startIndex + match[0].length;
2118
+ candidates.push({
2119
+ text: body,
2120
+ startIndex,
2121
+ endIndex
2122
+ });
2123
+ }
2124
+ return candidates;
2125
+ }
2126
+ function extractJsonLikeCandidates(rawText) {
2127
+ return mergeJsonCandidatesByStart(
2128
+ extractTaggedToolCallCandidates(rawText),
2129
+ extractCodeBlockCandidates(rawText),
2130
+ extractBalancedJsonObjects(rawText)
2131
+ );
2132
+ }
2133
+ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
2134
+ return [...tagged, ...codeBlocks, ...balanced].sort(
2135
+ (a, b) => a.startIndex !== b.startIndex ? a.startIndex - b.startIndex : b.endIndex - a.endIndex
2136
+ );
2137
+ }
2138
+ function toToolCallPart(candidate) {
2139
+ return {
2140
+ type: "tool-call",
2141
+ toolCallId: generateId(),
2142
+ toolName: candidate.toolName,
2143
+ input: candidate.input
2144
+ };
2145
+ }
2146
+ function toRecoveredParts(text, candidate, toolCallPart) {
2147
+ const out = [];
2148
+ const prefix = text.slice(0, candidate.startIndex);
2149
+ if (prefix.length > 0) {
2150
+ out.push({ type: "text", text: prefix });
2151
+ }
2152
+ out.push(toolCallPart);
2153
+ const suffix = text.slice(candidate.endIndex);
2154
+ if (suffix.length > 0) {
2155
+ out.push({ type: "text", text: suffix });
2156
+ }
2157
+ return out;
2158
+ }
2159
+ function parseAsToolPayload(payload, tools) {
2160
+ if (!isRecord(payload)) {
2161
+ return null;
2162
+ }
2163
+ const toolName = typeof payload.name === "string" && payload.name.trim().length > 0 ? payload.name.trim() : null;
2164
+ if (!toolName) {
2165
+ return null;
2166
+ }
2167
+ if (!tools.some((tool) => tool.name === toolName)) {
2168
+ return null;
2169
+ }
2170
+ const rawArgs = Object.hasOwn(payload, "arguments") ? payload.arguments : {};
2171
+ if (!isRecord(rawArgs)) {
2172
+ return null;
2173
+ }
2174
+ return {
2175
+ toolName,
2176
+ input: safeStringify2(rawArgs)
2177
+ };
2178
+ }
2179
+ function isLikelyArgumentsShapeForTool(args, tool) {
2180
+ const unwrapped = unwrapJsonSchema(tool.inputSchema);
2181
+ if (!isRecord(unwrapped)) {
2182
+ return false;
2183
+ }
2184
+ if (getSchemaType(unwrapped) !== "object") {
2185
+ return false;
2186
+ }
2187
+ const properties = unwrapped.properties;
2188
+ if (!isRecord(properties)) {
2189
+ return false;
2190
+ }
2191
+ const keys = Object.keys(args);
2192
+ if (keys.length === 0) {
2193
+ return false;
2194
+ }
2195
+ const knownKeys = keys.filter((key) => Object.hasOwn(properties, key));
2196
+ if (knownKeys.length === 0) {
2197
+ return false;
2198
+ }
2199
+ if (unwrapped.additionalProperties === false && knownKeys.length !== keys.length) {
2200
+ return false;
2201
+ }
2202
+ return true;
2203
+ }
2204
+ function parseAsArgumentsOnly(payload, tools) {
2205
+ if (tools.length !== 1) {
2206
+ return null;
2207
+ }
2208
+ if (!isRecord(payload)) {
2209
+ return null;
2210
+ }
2211
+ const hasNameEnvelope = Object.hasOwn(payload, "name") && typeof payload.name === "string" && payload.name.length > 0;
2212
+ const hasArgumentsEnvelope = Object.hasOwn(payload, "arguments") && (typeof payload.arguments === "string" || isRecord(payload.arguments));
2213
+ if (hasNameEnvelope || hasArgumentsEnvelope) {
2214
+ return null;
2215
+ }
2216
+ const tool = tools[0];
2217
+ if (!isLikelyArgumentsShapeForTool(payload, tool)) {
2218
+ return null;
2219
+ }
2220
+ return {
2221
+ toolName: tool.name,
2222
+ input: safeStringify2(payload)
2223
+ };
2224
+ }
2225
+ function recoverToolCallFromJsonCandidates(text, tools) {
2226
+ if (tools.length === 0) {
2227
+ return null;
2228
+ }
2229
+ const jsonCandidates = extractJsonLikeCandidates(text);
2230
+ for (const jsonCandidate of jsonCandidates) {
2231
+ const parsed = parseJsonCandidate(jsonCandidate.text);
2232
+ if (parsed === void 0) {
2233
+ continue;
2234
+ }
2235
+ const toolPayload = parseAsToolPayload(parsed, tools);
2236
+ if (toolPayload) {
2237
+ return toRecoveredParts(text, jsonCandidate, toToolCallPart(toolPayload));
2238
+ }
2239
+ const argsPayload = parseAsArgumentsOnly(parsed, tools);
2240
+ if (argsPayload) {
2241
+ return toRecoveredParts(text, jsonCandidate, toToolCallPart(argsPayload));
2242
+ }
2243
+ }
2244
+ return null;
2245
+ }
2246
+
2247
+ // src/core/utils/tool-call-coercion.ts
2248
+ function coerceToolCallInput(toolName, input, tools) {
1776
2249
  var _a;
2250
+ let args = {};
2251
+ if (typeof input === "string") {
2252
+ try {
2253
+ args = JSON.parse(input);
2254
+ } catch (e) {
2255
+ return;
2256
+ }
2257
+ } else if (input && typeof input === "object") {
2258
+ args = input;
2259
+ } else {
2260
+ return;
2261
+ }
2262
+ const schema = (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
2263
+ const coerced = coerceBySchema(args, schema);
2264
+ return JSON.stringify(coerced != null ? coerced : {});
2265
+ }
2266
+ function coerceToolCallPart(part, tools) {
2267
+ const coercedInput = coerceToolCallInput(part.toolName, part.input, tools);
2268
+ if (coercedInput === void 0) {
2269
+ return part;
2270
+ }
2271
+ return {
2272
+ ...part,
2273
+ input: coercedInput
2274
+ };
2275
+ }
2276
+
2277
+ // src/core/utils/tool-choice.ts
2278
+ function ensureNonEmptyToolName(name) {
2279
+ if (typeof name !== "string") {
2280
+ return "unknown";
2281
+ }
2282
+ const trimmed = name.trim();
2283
+ return trimmed.length > 0 ? trimmed : "unknown";
2284
+ }
2285
+ function safeStringify3(value) {
2286
+ try {
2287
+ return JSON.stringify(value != null ? value : {});
2288
+ } catch (e) {
2289
+ return "{}";
2290
+ }
2291
+ }
2292
+ function parseToolChoicePayload({
2293
+ text,
2294
+ tools,
2295
+ onError,
2296
+ errorMessage
2297
+ }) {
2298
+ let parsed;
1777
2299
  try {
1778
- return JSON.parse(text);
2300
+ parsed = JSON.parse(text);
1779
2301
  } catch (error) {
1780
- const options = extractOnErrorOption(providerOptions);
1781
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1782
- options,
1783
- "Failed to parse toolChoice JSON from generated model output",
1784
- {
1785
- text,
1786
- error: error instanceof Error ? error.message : String(error)
1787
- }
1788
- );
1789
- return {};
2302
+ onError == null ? void 0 : onError(errorMessage, {
2303
+ text,
2304
+ error: error instanceof Error ? error.message : String(error)
2305
+ });
2306
+ return { toolName: "unknown", input: "{}" };
2307
+ }
2308
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
2309
+ onError == null ? void 0 : onError("toolChoice JSON payload must be an object", {
2310
+ parsedType: typeof parsed,
2311
+ parsed
2312
+ });
2313
+ return { toolName: "unknown", input: "{}" };
2314
+ }
2315
+ const payload = parsed;
2316
+ const toolName = ensureNonEmptyToolName(payload.name);
2317
+ const rawArgs = Object.hasOwn(payload, "arguments") ? payload.arguments : {};
2318
+ if (rawArgs == null || typeof rawArgs !== "object" || Array.isArray(rawArgs)) {
2319
+ onError == null ? void 0 : onError("toolChoice arguments must be a JSON object", {
2320
+ toolName,
2321
+ arguments: rawArgs
2322
+ });
2323
+ return { toolName, input: "{}" };
1790
2324
  }
2325
+ const coercedInput = coerceToolCallInput(toolName, rawArgs, tools);
2326
+ return {
2327
+ toolName,
2328
+ input: coercedInput != null ? coercedInput : safeStringify3(rawArgs)
2329
+ };
1791
2330
  }
2331
+
2332
+ // src/generate-handler.ts
1792
2333
  function logDebugSummary(debugSummary, toolCall, originText) {
1793
2334
  if (debugSummary) {
1794
2335
  debugSummary.originalText = originText;
@@ -1802,25 +2343,34 @@ function logDebugSummary(debugSummary, toolCall, originText) {
1802
2343
  logParsedSummary({ toolCalls: [toolCall], originalText: originText });
1803
2344
  }
1804
2345
  }
1805
- async function handleToolChoice(doGenerate, params) {
1806
- var _a, _b, _c;
2346
+ async function handleToolChoice(doGenerate, params, tools) {
2347
+ var _a, _b, _c, _d;
1807
2348
  const result = await doGenerate();
1808
2349
  const first = (_a = result.content) == null ? void 0 : _a[0];
1809
- let parsed = {};
2350
+ const onError = (_b = extractOnErrorOption(params.providerOptions)) == null ? void 0 : _b.onError;
2351
+ let toolName = "unknown";
2352
+ let input = "{}";
1810
2353
  if (first && first.type === "text") {
1811
2354
  if (getDebugLevel() === "parse") {
1812
2355
  logRawChunk(first.text);
1813
2356
  }
1814
- parsed = parseToolChoiceJson(first.text, params.providerOptions);
2357
+ const parsed = parseToolChoicePayload({
2358
+ text: first.text,
2359
+ tools,
2360
+ onError,
2361
+ errorMessage: "Failed to parse toolChoice JSON from generated model output"
2362
+ });
2363
+ toolName = parsed.toolName;
2364
+ input = parsed.input;
1815
2365
  }
1816
2366
  const toolCall = {
1817
2367
  type: "tool-call",
1818
2368
  toolCallId: generateId2(),
1819
- toolName: parsed.name || "unknown",
1820
- input: JSON.stringify(parsed.arguments || {})
2369
+ toolName,
2370
+ input
1821
2371
  };
1822
2372
  const originText = first && first.type === "text" ? first.text : "";
1823
- const debugSummary = (_c = (_b = params.providerOptions) == null ? void 0 : _b.toolCallMiddleware) == null ? void 0 : _c.debugSummary;
2373
+ const debugSummary = (_d = (_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) == null ? void 0 : _d.debugSummary;
1824
2374
  logDebugSummary(debugSummary, toolCall, originText);
1825
2375
  return {
1826
2376
  ...result,
@@ -1835,7 +2385,7 @@ function parseContent(content, protocol, tools, providerOptions) {
1835
2385
  if (getDebugLevel() === "stream") {
1836
2386
  logRawChunk(contentItem.text);
1837
2387
  }
1838
- return protocol.parseGeneratedText({
2388
+ const parsedByProtocol = protocol.parseGeneratedText({
1839
2389
  text: contentItem.text,
1840
2390
  tools,
1841
2391
  options: {
@@ -1843,9 +2393,20 @@ function parseContent(content, protocol, tools, providerOptions) {
1843
2393
  ...providerOptions == null ? void 0 : providerOptions.toolCallMiddleware
1844
2394
  }
1845
2395
  });
2396
+ const hasToolCall = parsedByProtocol.some(
2397
+ (part) => part.type === "tool-call"
2398
+ );
2399
+ if (hasToolCall) {
2400
+ return parsedByProtocol;
2401
+ }
2402
+ const recoveredFromJson = recoverToolCallFromJsonCandidates(
2403
+ contentItem.text,
2404
+ tools
2405
+ );
2406
+ return recoveredFromJson != null ? recoveredFromJson : parsedByProtocol;
1846
2407
  });
1847
2408
  return parsed.map(
1848
- (part) => fixToolCallWithSchema(part, tools)
2409
+ (part) => part.type === "tool-call" ? coerceToolCallPart(part, tools) : part
1849
2410
  );
1850
2411
  }
1851
2412
  function logParsedContent(content) {
@@ -1888,12 +2449,14 @@ async function wrapGenerate({
1888
2449
  params
1889
2450
  }) {
1890
2451
  var _a, _b;
1891
- if (isToolChoiceActive(params)) {
1892
- return handleToolChoice(doGenerate, params);
1893
- }
2452
+ const onError = extractOnErrorOption(params.providerOptions);
1894
2453
  const tools = originalToolsSchema.decode(
1895
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
2454
+ (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools,
2455
+ onError
1896
2456
  );
2457
+ if (isToolChoiceActive(params)) {
2458
+ return handleToolChoice(doGenerate, params, tools);
2459
+ }
1897
2460
  const result = await doGenerate();
1898
2461
  if (result.content.length === 0) {
1899
2462
  return result;
@@ -1917,28 +2480,6 @@ async function wrapGenerate({
1917
2480
  content: newContent
1918
2481
  };
1919
2482
  }
1920
- function fixToolCallWithSchema(part, tools) {
1921
- var _a;
1922
- if (part.type !== "tool-call") {
1923
- return part;
1924
- }
1925
- let args = {};
1926
- if (typeof part.input === "string") {
1927
- try {
1928
- args = JSON.parse(part.input);
1929
- } catch (e) {
1930
- return part;
1931
- }
1932
- } else if (part.input && typeof part.input === "object") {
1933
- args = part.input;
1934
- }
1935
- const schema = (_a = tools.find((t) => t.name === part.toolName)) == null ? void 0 : _a.inputSchema;
1936
- const coerced = coerceBySchema(args, schema);
1937
- return {
1938
- ...part,
1939
- input: JSON.stringify(coerced != null ? coerced : {})
1940
- };
1941
- }
1942
2483
 
1943
2484
  // src/core/prompts/hermes-system-prompt.ts
1944
2485
  function hermesSystemPromptTemplate(tools) {
@@ -2342,19 +2883,22 @@ async function wrapStream({
2342
2883
  params
2343
2884
  }) {
2344
2885
  var _a, _b, _c;
2886
+ const onErrorOptions = extractOnErrorOption(params.providerOptions);
2887
+ const tools = originalToolsSchema.decode(
2888
+ (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools,
2889
+ onErrorOptions
2890
+ );
2345
2891
  if (isToolChoiceActive(params)) {
2346
2892
  return toolChoiceStream({
2347
2893
  doGenerate,
2348
- options: extractOnErrorOption(params.providerOptions)
2894
+ tools,
2895
+ options: onErrorOptions
2349
2896
  });
2350
2897
  }
2351
2898
  const { stream, ...rest } = await doStream();
2352
2899
  const debugLevel = getDebugLevel();
2353
- const tools = originalToolsSchema.decode(
2354
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
2355
- );
2356
2900
  const options = {
2357
- ...extractOnErrorOption(params.providerOptions),
2901
+ ...onErrorOptions,
2358
2902
  ...((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) || {}
2359
2903
  };
2360
2904
  const coreStream = stream.pipeThrough(
@@ -2372,10 +2916,11 @@ async function wrapStream({
2372
2916
  const v3Stream = coreStream.pipeThrough(
2373
2917
  new TransformStream({
2374
2918
  transform(part, controller) {
2919
+ const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
2375
2920
  if (debugLevel === "stream") {
2376
- logParsedChunk(part);
2921
+ logParsedChunk(normalizedPart);
2377
2922
  }
2378
- controller.enqueue(part);
2923
+ controller.enqueue(normalizedPart);
2379
2924
  }
2380
2925
  })
2381
2926
  );
@@ -2386,41 +2931,36 @@ async function wrapStream({
2386
2931
  }
2387
2932
  async function toolChoiceStream({
2388
2933
  doGenerate,
2934
+ tools,
2389
2935
  options
2390
2936
  }) {
2391
- var _a, _b;
2937
+ var _a;
2938
+ const normalizedTools = Array.isArray(tools) ? tools : [];
2392
2939
  const result = await doGenerate();
2393
- let toolJson = {};
2940
+ let toolName = "unknown";
2941
+ let input = "{}";
2394
2942
  if ((result == null ? void 0 : result.content) && result.content.length > 0 && ((_a = result.content[0]) == null ? void 0 : _a.type) === "text") {
2395
- try {
2396
- toolJson = JSON.parse(result.content[0].text);
2397
- } catch (error) {
2398
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2399
- options,
2400
- "Failed to parse toolChoice JSON from streamed model output",
2401
- {
2402
- text: result.content[0].text,
2403
- error: error instanceof Error ? error.message : String(error)
2404
- }
2405
- );
2406
- toolJson = {};
2407
- }
2943
+ const parsed = parseToolChoicePayload({
2944
+ text: result.content[0].text,
2945
+ tools: normalizedTools,
2946
+ onError: options == null ? void 0 : options.onError,
2947
+ errorMessage: "Failed to parse toolChoice JSON from streamed model output"
2948
+ });
2949
+ toolName = parsed.toolName;
2950
+ input = parsed.input;
2408
2951
  }
2409
2952
  const stream = new ReadableStream({
2410
2953
  start(controller) {
2411
2954
  controller.enqueue({
2412
2955
  type: "tool-call",
2413
2956
  toolCallId: generateId3(),
2414
- toolName: toolJson.name || "unknown",
2415
- input: JSON.stringify(toolJson.arguments || {})
2957
+ toolName,
2958
+ input
2416
2959
  });
2417
2960
  controller.enqueue({
2418
2961
  type: "finish",
2419
- usage: (result == null ? void 0 : result.usage) || {
2420
- inputTokens: 0,
2421
- outputTokens: 0
2422
- },
2423
- finishReason: "tool-calls"
2962
+ usage: normalizeUsage(result == null ? void 0 : result.usage),
2963
+ finishReason: normalizeToolCallsFinishReason(result == null ? void 0 : result.finishReason)
2424
2964
  });
2425
2965
  controller.close();
2426
2966
  }
@@ -2431,6 +2971,60 @@ async function toolChoiceStream({
2431
2971
  stream
2432
2972
  };
2433
2973
  }
2974
+ var ZERO_USAGE = {
2975
+ inputTokens: {
2976
+ total: 0,
2977
+ noCache: void 0,
2978
+ cacheRead: void 0,
2979
+ cacheWrite: void 0
2980
+ },
2981
+ outputTokens: {
2982
+ total: 0,
2983
+ text: void 0,
2984
+ reasoning: void 0
2985
+ }
2986
+ };
2987
+ function normalizeToolCallsFinishReason(finishReason) {
2988
+ let raw = "tool-calls";
2989
+ if (typeof finishReason === "string") {
2990
+ raw = finishReason;
2991
+ } else if (finishReason && typeof finishReason === "object" && "raw" in finishReason && typeof finishReason.raw === "string") {
2992
+ raw = finishReason.raw;
2993
+ } else if (finishReason && typeof finishReason === "object" && "unified" in finishReason && typeof finishReason.unified === "string") {
2994
+ raw = finishReason.unified;
2995
+ }
2996
+ return {
2997
+ unified: "tool-calls",
2998
+ raw
2999
+ };
3000
+ }
3001
+ function normalizeUsage(usage) {
3002
+ if (!usage || typeof usage !== "object") {
3003
+ return ZERO_USAGE;
3004
+ }
3005
+ const usageRecord = usage;
3006
+ const input = usageRecord.inputTokens;
3007
+ const output = usageRecord.outputTokens;
3008
+ if (input && typeof input === "object" && output && typeof output === "object") {
3009
+ return usage;
3010
+ }
3011
+ if (typeof input === "number" && typeof output === "number") {
3012
+ return {
3013
+ inputTokens: {
3014
+ total: input,
3015
+ noCache: void 0,
3016
+ cacheRead: void 0,
3017
+ cacheWrite: void 0
3018
+ },
3019
+ outputTokens: {
3020
+ total: output,
3021
+ text: void 0,
3022
+ reasoning: void 0
3023
+ }
3024
+ };
3025
+ }
3026
+ return ZERO_USAGE;
3027
+ }
2434
3028
 
2435
3029
  // src/transform-handler.ts
2436
3030
  function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
@@ -2556,6 +3150,11 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2556
3150
  "Tool choice type 'required' is set, but no tools are provided in params.tools."
2557
3151
  );
2558
3152
  }
3153
+ if (functionTools.length === 0) {
3154
+ throw new Error(
3155
+ "Tool choice type 'required' is set, but no function tools are provided. Provider-defined tools are not supported by this middleware."
3156
+ );
3157
+ }
2559
3158
  return {
2560
3159
  ...baseReturnParams,
2561
3160
  responseFormat: {
@@ -2866,4 +3465,4 @@ export {
2866
3465
  xmlToolMiddleware,
2867
3466
  yamlToolMiddleware
2868
3467
  };
2869
- //# sourceMappingURL=chunk-5WKXBBCU.js.map
3468
+ //# sourceMappingURL=chunk-NAQSTPDQ.js.map