@ai-sdk-tool/parser 3.3.1 → 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.
@@ -1,13 +1,16 @@
1
1
  import {
2
+ escapeRegExp,
2
3
  parse as parse2,
3
4
  stringify
4
- } from "./chunk-EW3A6Y7O.js";
5
+ } from "./chunk-CXWS24JX.js";
5
6
  import {
6
7
  parse
7
8
  } from "./chunk-IX4FJELL.js";
8
9
  import {
9
- coerceBySchema
10
- } from "./chunk-OUGMLYAW.js";
10
+ coerceBySchema,
11
+ getSchemaType,
12
+ unwrapJsonSchema
13
+ } from "./chunk-2KK5BDZF.js";
11
14
 
12
15
  // src/core/utils/debug.ts
13
16
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -187,9 +190,11 @@ function generateId() {
187
190
  return Math.random().toString(36).substring(2, 15);
188
191
  }
189
192
 
190
- // src/core/utils/regex.ts
191
- function escapeRegExp(literal) {
192
- return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
193
+ // src/core/utils/protocol-utils.ts
194
+ function addTextSegment(text, processedElements) {
195
+ if (text.trim()) {
196
+ processedElements.push({ type: "text", text });
197
+ }
193
198
  }
194
199
 
195
200
  // src/core/protocols/json-protocol.ts
@@ -218,11 +223,6 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
218
223
  processedElements.push({ type: "text", text: fullMatch });
219
224
  }
220
225
  }
221
- function addTextSegment(text, processedElements) {
222
- if (text.trim()) {
223
- processedElements.push({ type: "text", text });
224
- }
225
- }
226
226
  function processMatchedToolCall(context) {
227
227
  const { match, text, currentIndex, processedElements, options } = context;
228
228
  const startIndex = match.index;
@@ -516,9 +516,35 @@ function isTCMProtocolFactory(protocol) {
516
516
  return typeof protocol === "function";
517
517
  }
518
518
 
519
- // src/core/protocols/xml-protocol.ts
519
+ // src/core/utils/regex-constants.ts
520
520
  var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
521
521
  var WHITESPACE_REGEX = /\s/;
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
+
547
+ // src/core/protocols/xml-protocol.ts
522
548
  function getToolSchema(tools, toolName) {
523
549
  var _a;
524
550
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
@@ -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);
@@ -1226,8 +1386,6 @@ var xmlProtocol = (protocolOptions) => {
1226
1386
 
1227
1387
  // src/core/protocols/yaml-protocol.ts
1228
1388
  import YAML from "yaml";
1229
- var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
1230
- var WHITESPACE_REGEX2 = /\s/;
1231
1389
  var LEADING_WHITESPACE_RE = /^(\s*)/;
1232
1390
  function findClosingTagEnd(text, contentStart, toolName) {
1233
1391
  let pos = contentStart;
@@ -1244,11 +1402,11 @@ function findClosingTagEnd(text, contentStart, toolName) {
1244
1402
  break;
1245
1403
  }
1246
1404
  let p = ltIdx + 2;
1247
- while (p < gtIdx && WHITESPACE_REGEX2.test(text[p])) {
1405
+ while (p < gtIdx && WHITESPACE_REGEX.test(text[p])) {
1248
1406
  p++;
1249
1407
  }
1250
1408
  const nameStart = p;
1251
- while (p < gtIdx && NAME_CHAR_RE2.test(text.charAt(p))) {
1409
+ while (p < gtIdx && NAME_CHAR_RE.test(text.charAt(p))) {
1252
1410
  p++;
1253
1411
  }
1254
1412
  const name = text.slice(nameStart, p);
@@ -1264,11 +1422,11 @@ function findClosingTagEnd(text, contentStart, toolName) {
1264
1422
  pos = gtIdx === -1 ? text.length : gtIdx + 1;
1265
1423
  } else {
1266
1424
  let p = ltIdx + 1;
1267
- while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
1425
+ while (p < text.length && WHITESPACE_REGEX.test(text[p])) {
1268
1426
  p++;
1269
1427
  }
1270
1428
  const nameStart = p;
1271
- while (p < text.length && NAME_CHAR_RE2.test(text.charAt(p))) {
1429
+ while (p < text.length && NAME_CHAR_RE.test(text.charAt(p))) {
1272
1430
  p++;
1273
1431
  }
1274
1432
  const name = text.slice(nameStart, p);
@@ -1277,7 +1435,7 @@ function findClosingTagEnd(text, contentStart, toolName) {
1277
1435
  break;
1278
1436
  }
1279
1437
  let r = gtIdx - 1;
1280
- while (r >= nameStart && WHITESPACE_REGEX2.test(text[r])) {
1438
+ while (r >= nameStart && WHITESPACE_REGEX.test(text[r])) {
1281
1439
  r--;
1282
1440
  }
1283
1441
  const selfClosing = text[r] === "/";
@@ -1395,22 +1553,14 @@ function parseYamlContent(yamlContent, options) {
1395
1553
  return null;
1396
1554
  }
1397
1555
  }
1398
- function appendTextPart(processedElements, textPart) {
1399
- if (textPart.trim()) {
1400
- processedElements.push({
1401
- type: "text",
1402
- text: textPart
1403
- });
1404
- }
1405
- }
1406
1556
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
1407
1557
  var _a;
1408
1558
  if (tc.startIndex < currentIndex) {
1409
1559
  return currentIndex;
1410
1560
  }
1411
- appendTextPart(
1412
- processedElements,
1413
- text.substring(currentIndex, tc.startIndex)
1561
+ addTextSegment(
1562
+ text.substring(currentIndex, tc.startIndex),
1563
+ processedElements
1414
1564
  );
1415
1565
  const parsedArgs = parseYamlContent(tc.content, options);
1416
1566
  if (parsedArgs !== null) {
@@ -1517,18 +1667,32 @@ ${yamlContent}</${toolCall.toolName}>`;
1517
1667
  }
1518
1668
  const processedElements = [];
1519
1669
  let currentIndex = 0;
1520
- 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
+ }
1521
1685
  for (const tc of toolCalls) {
1522
1686
  currentIndex = processToolCallMatch(
1523
- text,
1687
+ parseText,
1524
1688
  tc,
1525
1689
  currentIndex,
1526
1690
  processedElements,
1527
1691
  options
1528
1692
  );
1529
1693
  }
1530
- if (currentIndex < text.length) {
1531
- appendTextPart(processedElements, text.substring(currentIndex));
1694
+ if (currentIndex < parseText.length) {
1695
+ addTextSegment(parseText.substring(currentIndex), processedElements);
1532
1696
  }
1533
1697
  return processedElements;
1534
1698
  },
@@ -1624,6 +1788,20 @@ ${yamlContent}</${toolCall.toolName}>`;
1624
1788
  return new TransformStream({
1625
1789
  transform(chunk, controller) {
1626
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
+ }
1627
1805
  if (chunk.type !== "text-delta") {
1628
1806
  if (buffer) {
1629
1807
  flushText(controller, buffer);
@@ -1747,17 +1925,56 @@ function encodeOriginalTools(tools) {
1747
1925
  inputSchema: JSON.stringify(t.inputSchema)
1748
1926
  }))) || [];
1749
1927
  }
1750
- function decodeOriginalTools(originalTools) {
1928
+ function decodeOriginalTools(originalTools, options) {
1929
+ var _a, _b, _c;
1751
1930
  if (!originalTools) {
1752
1931
  return [];
1753
1932
  }
1754
- return originalTools.map(
1755
- (t) => ({
1756
- type: "function",
1757
- name: t.name,
1758
- inputSchema: JSON.parse(t.inputSchema)
1759
- })
1760
- );
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;
1761
1978
  }
1762
1979
  function extractToolNamesFromOriginalTools(originalTools) {
1763
1980
  return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
@@ -1782,23 +1999,337 @@ function hasInputProperty(obj) {
1782
1999
 
1783
2000
  // src/generate-handler.ts
1784
2001
  import { generateId as generateId2 } from "@ai-sdk/provider-utils";
1785
- 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) {
1786
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) {
1787
2286
  try {
1788
- return JSON.parse(text);
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;
2299
+ try {
2300
+ parsed = JSON.parse(text);
1789
2301
  } catch (error) {
1790
- const options = extractOnErrorOption(providerOptions);
1791
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1792
- options,
1793
- "Failed to parse toolChoice JSON from generated model output",
1794
- {
1795
- text,
1796
- error: error instanceof Error ? error.message : String(error)
1797
- }
1798
- );
1799
- 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: "{}" };
1800
2324
  }
2325
+ const coercedInput = coerceToolCallInput(toolName, rawArgs, tools);
2326
+ return {
2327
+ toolName,
2328
+ input: coercedInput != null ? coercedInput : safeStringify3(rawArgs)
2329
+ };
1801
2330
  }
2331
+
2332
+ // src/generate-handler.ts
1802
2333
  function logDebugSummary(debugSummary, toolCall, originText) {
1803
2334
  if (debugSummary) {
1804
2335
  debugSummary.originalText = originText;
@@ -1812,25 +2343,34 @@ function logDebugSummary(debugSummary, toolCall, originText) {
1812
2343
  logParsedSummary({ toolCalls: [toolCall], originalText: originText });
1813
2344
  }
1814
2345
  }
1815
- async function handleToolChoice(doGenerate, params) {
1816
- var _a, _b, _c;
2346
+ async function handleToolChoice(doGenerate, params, tools) {
2347
+ var _a, _b, _c, _d;
1817
2348
  const result = await doGenerate();
1818
2349
  const first = (_a = result.content) == null ? void 0 : _a[0];
1819
- let parsed = {};
2350
+ const onError = (_b = extractOnErrorOption(params.providerOptions)) == null ? void 0 : _b.onError;
2351
+ let toolName = "unknown";
2352
+ let input = "{}";
1820
2353
  if (first && first.type === "text") {
1821
2354
  if (getDebugLevel() === "parse") {
1822
2355
  logRawChunk(first.text);
1823
2356
  }
1824
- 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;
1825
2365
  }
1826
2366
  const toolCall = {
1827
2367
  type: "tool-call",
1828
2368
  toolCallId: generateId2(),
1829
- toolName: parsed.name || "unknown",
1830
- input: JSON.stringify(parsed.arguments || {})
2369
+ toolName,
2370
+ input
1831
2371
  };
1832
2372
  const originText = first && first.type === "text" ? first.text : "";
1833
- 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;
1834
2374
  logDebugSummary(debugSummary, toolCall, originText);
1835
2375
  return {
1836
2376
  ...result,
@@ -1845,7 +2385,7 @@ function parseContent(content, protocol, tools, providerOptions) {
1845
2385
  if (getDebugLevel() === "stream") {
1846
2386
  logRawChunk(contentItem.text);
1847
2387
  }
1848
- return protocol.parseGeneratedText({
2388
+ const parsedByProtocol = protocol.parseGeneratedText({
1849
2389
  text: contentItem.text,
1850
2390
  tools,
1851
2391
  options: {
@@ -1853,9 +2393,20 @@ function parseContent(content, protocol, tools, providerOptions) {
1853
2393
  ...providerOptions == null ? void 0 : providerOptions.toolCallMiddleware
1854
2394
  }
1855
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;
1856
2407
  });
1857
2408
  return parsed.map(
1858
- (part) => fixToolCallWithSchema(part, tools)
2409
+ (part) => part.type === "tool-call" ? coerceToolCallPart(part, tools) : part
1859
2410
  );
1860
2411
  }
1861
2412
  function logParsedContent(content) {
@@ -1898,12 +2449,14 @@ async function wrapGenerate({
1898
2449
  params
1899
2450
  }) {
1900
2451
  var _a, _b;
1901
- if (isToolChoiceActive(params)) {
1902
- return handleToolChoice(doGenerate, params);
1903
- }
2452
+ const onError = extractOnErrorOption(params.providerOptions);
1904
2453
  const tools = originalToolsSchema.decode(
1905
- (_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
1906
2456
  );
2457
+ if (isToolChoiceActive(params)) {
2458
+ return handleToolChoice(doGenerate, params, tools);
2459
+ }
1907
2460
  const result = await doGenerate();
1908
2461
  if (result.content.length === 0) {
1909
2462
  return result;
@@ -1927,28 +2480,6 @@ async function wrapGenerate({
1927
2480
  content: newContent
1928
2481
  };
1929
2482
  }
1930
- function fixToolCallWithSchema(part, tools) {
1931
- var _a;
1932
- if (part.type !== "tool-call") {
1933
- return part;
1934
- }
1935
- let args = {};
1936
- if (typeof part.input === "string") {
1937
- try {
1938
- args = JSON.parse(part.input);
1939
- } catch (e) {
1940
- return part;
1941
- }
1942
- } else if (part.input && typeof part.input === "object") {
1943
- args = part.input;
1944
- }
1945
- const schema = (_a = tools.find((t) => t.name === part.toolName)) == null ? void 0 : _a.inputSchema;
1946
- const coerced = coerceBySchema(args, schema);
1947
- return {
1948
- ...part,
1949
- input: JSON.stringify(coerced != null ? coerced : {})
1950
- };
1951
- }
1952
2483
 
1953
2484
  // src/core/prompts/hermes-system-prompt.ts
1954
2485
  function hermesSystemPromptTemplate(tools) {
@@ -2352,19 +2883,22 @@ async function wrapStream({
2352
2883
  params
2353
2884
  }) {
2354
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
+ );
2355
2891
  if (isToolChoiceActive(params)) {
2356
2892
  return toolChoiceStream({
2357
2893
  doGenerate,
2358
- options: extractOnErrorOption(params.providerOptions)
2894
+ tools,
2895
+ options: onErrorOptions
2359
2896
  });
2360
2897
  }
2361
2898
  const { stream, ...rest } = await doStream();
2362
2899
  const debugLevel = getDebugLevel();
2363
- const tools = originalToolsSchema.decode(
2364
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
2365
- );
2366
2900
  const options = {
2367
- ...extractOnErrorOption(params.providerOptions),
2901
+ ...onErrorOptions,
2368
2902
  ...((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) || {}
2369
2903
  };
2370
2904
  const coreStream = stream.pipeThrough(
@@ -2382,10 +2916,11 @@ async function wrapStream({
2382
2916
  const v3Stream = coreStream.pipeThrough(
2383
2917
  new TransformStream({
2384
2918
  transform(part, controller) {
2919
+ const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
2385
2920
  if (debugLevel === "stream") {
2386
- logParsedChunk(part);
2921
+ logParsedChunk(normalizedPart);
2387
2922
  }
2388
- controller.enqueue(part);
2923
+ controller.enqueue(normalizedPart);
2389
2924
  }
2390
2925
  })
2391
2926
  );
@@ -2396,41 +2931,36 @@ async function wrapStream({
2396
2931
  }
2397
2932
  async function toolChoiceStream({
2398
2933
  doGenerate,
2934
+ tools,
2399
2935
  options
2400
2936
  }) {
2401
- var _a, _b;
2937
+ var _a;
2938
+ const normalizedTools = Array.isArray(tools) ? tools : [];
2402
2939
  const result = await doGenerate();
2403
- let toolJson = {};
2940
+ let toolName = "unknown";
2941
+ let input = "{}";
2404
2942
  if ((result == null ? void 0 : result.content) && result.content.length > 0 && ((_a = result.content[0]) == null ? void 0 : _a.type) === "text") {
2405
- try {
2406
- toolJson = JSON.parse(result.content[0].text);
2407
- } catch (error) {
2408
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2409
- options,
2410
- "Failed to parse toolChoice JSON from streamed model output",
2411
- {
2412
- text: result.content[0].text,
2413
- error: error instanceof Error ? error.message : String(error)
2414
- }
2415
- );
2416
- toolJson = {};
2417
- }
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;
2418
2951
  }
2419
2952
  const stream = new ReadableStream({
2420
2953
  start(controller) {
2421
2954
  controller.enqueue({
2422
2955
  type: "tool-call",
2423
2956
  toolCallId: generateId3(),
2424
- toolName: toolJson.name || "unknown",
2425
- input: JSON.stringify(toolJson.arguments || {})
2957
+ toolName,
2958
+ input
2426
2959
  });
2427
2960
  controller.enqueue({
2428
2961
  type: "finish",
2429
- usage: (result == null ? void 0 : result.usage) || {
2430
- inputTokens: 0,
2431
- outputTokens: 0
2432
- },
2433
- finishReason: "tool-calls"
2962
+ usage: normalizeUsage(result == null ? void 0 : result.usage),
2963
+ finishReason: normalizeToolCallsFinishReason(result == null ? void 0 : result.finishReason)
2434
2964
  });
2435
2965
  controller.close();
2436
2966
  }
@@ -2441,6 +2971,60 @@ async function toolChoiceStream({
2441
2971
  stream
2442
2972
  };
2443
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
+ }
2444
3028
 
2445
3029
  // src/transform-handler.ts
2446
3030
  function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
@@ -2566,6 +3150,11 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2566
3150
  "Tool choice type 'required' is set, but no tools are provided in params.tools."
2567
3151
  );
2568
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
+ }
2569
3158
  return {
2570
3159
  ...baseReturnParams,
2571
3160
  responseFormat: {
@@ -2853,7 +3442,6 @@ export {
2853
3442
  logParsedChunk,
2854
3443
  logParsedSummary,
2855
3444
  getPotentialStartIndex,
2856
- escapeRegExp,
2857
3445
  jsonProtocol,
2858
3446
  isProtocolFactory,
2859
3447
  isTCMProtocolFactory,
@@ -2877,4 +3465,4 @@ export {
2877
3465
  xmlToolMiddleware,
2878
3466
  yamlToolMiddleware
2879
3467
  };
2880
- //# sourceMappingURL=chunk-7E6UFDFQ.js.map
3468
+ //# sourceMappingURL=chunk-NAQSTPDQ.js.map