extrait 0.5.6 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1463,7 +1463,15 @@ function normalizeBaseURL(baseURL) {
1463
1463
  return baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
1464
1464
  }
1465
1465
  function buildURL(baseURL, path) {
1466
- return new URL(path, normalizeBaseURL(baseURL)).toString();
1466
+ try {
1467
+ return new URL(path).toString();
1468
+ } catch {}
1469
+ const base = new URL(normalizeBaseURL(baseURL));
1470
+ const resolvedPath = new URL(path, "http://provider-path.local");
1471
+ base.pathname = mergePathnames(base.pathname, resolvedPath.pathname);
1472
+ base.search = resolvedPath.search;
1473
+ base.hash = resolvedPath.hash;
1474
+ return base.toString();
1467
1475
  }
1468
1476
  function safeJSONParse(input) {
1469
1477
  try {
@@ -1538,6 +1546,36 @@ function addOptional(a, b) {
1538
1546
  }
1539
1547
  return (a ?? 0) + (b ?? 0);
1540
1548
  }
1549
+ function mergePathnames(basePathname, pathPathname) {
1550
+ const baseSegments = splitPathSegments(basePathname);
1551
+ const pathSegments = splitPathSegments(pathPathname);
1552
+ const overlap = findPathOverlap(baseSegments, pathSegments);
1553
+ const mergedSegments = [...baseSegments, ...pathSegments.slice(overlap)];
1554
+ if (mergedSegments.length === 0) {
1555
+ return "/";
1556
+ }
1557
+ const mergedPathname = `/${mergedSegments.join("/")}`;
1558
+ return pathPathname.endsWith("/") && pathPathname !== "/" ? `${mergedPathname}/` : mergedPathname;
1559
+ }
1560
+ function splitPathSegments(pathname) {
1561
+ return pathname.split("/").filter((segment) => segment.length > 0);
1562
+ }
1563
+ function findPathOverlap(baseSegments, pathSegments) {
1564
+ const maxOverlap = Math.min(baseSegments.length, pathSegments.length);
1565
+ for (let size = maxOverlap;size > 0; size -= 1) {
1566
+ let matches = true;
1567
+ for (let index = 0;index < size; index += 1) {
1568
+ if (baseSegments[baseSegments.length - size + index] !== pathSegments[index]) {
1569
+ matches = false;
1570
+ break;
1571
+ }
1572
+ }
1573
+ if (matches) {
1574
+ return size;
1575
+ }
1576
+ }
1577
+ return 0;
1578
+ }
1541
1579
 
1542
1580
  // src/providers/openai-compatible.ts
1543
1581
  function createOpenAICompatibleAdapter(options) {
@@ -1572,6 +1610,7 @@ function createOpenAICompatibleAdapter(options) {
1572
1610
  model: options.model,
1573
1611
  messages: buildMessages(request),
1574
1612
  temperature: request.temperature,
1613
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1575
1614
  max_tokens: request.maxTokens,
1576
1615
  stream: true
1577
1616
  })),
@@ -1583,6 +1622,7 @@ function createOpenAICompatibleAdapter(options) {
1583
1622
  }
1584
1623
  callbacks.onStart?.();
1585
1624
  let text = "";
1625
+ let reasoning = "";
1586
1626
  let usage;
1587
1627
  let finishReason;
1588
1628
  await consumeSSE(response, (data) => {
@@ -1594,6 +1634,7 @@ function createOpenAICompatibleAdapter(options) {
1594
1634
  return;
1595
1635
  }
1596
1636
  const delta = pickAssistantDelta(json);
1637
+ const reasoningDelta = pickAssistantReasoningDelta(json);
1597
1638
  const chunkUsage = pickUsage(json);
1598
1639
  const chunkFinishReason = pickFinishReason(json);
1599
1640
  usage = preferLatestUsage(usage, chunkUsage);
@@ -1604,9 +1645,13 @@ function createOpenAICompatibleAdapter(options) {
1604
1645
  text += delta;
1605
1646
  callbacks.onToken?.(delta);
1606
1647
  }
1607
- if (delta || chunkUsage || chunkFinishReason) {
1648
+ if (reasoningDelta) {
1649
+ reasoning += reasoningDelta;
1650
+ }
1651
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
1608
1652
  const chunk = {
1609
1653
  textDelta: delta,
1654
+ reasoningDelta: reasoningDelta || undefined,
1610
1655
  raw: json,
1611
1656
  usage: chunkUsage,
1612
1657
  finishReason: chunkFinishReason
@@ -1614,7 +1659,12 @@ function createOpenAICompatibleAdapter(options) {
1614
1659
  callbacks.onChunk?.(chunk);
1615
1660
  }
1616
1661
  });
1617
- const out = { text, usage, finishReason };
1662
+ const out = {
1663
+ text,
1664
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1665
+ usage,
1666
+ finishReason
1667
+ };
1618
1668
  callbacks.onComplete?.(out);
1619
1669
  return out;
1620
1670
  },
@@ -1674,6 +1724,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1674
1724
  model: options.model,
1675
1725
  messages: buildMessages(request),
1676
1726
  temperature: request.temperature,
1727
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1677
1728
  max_tokens: request.maxTokens,
1678
1729
  stream: false
1679
1730
  })),
@@ -1683,20 +1734,41 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1683
1734
  const message = await response.text();
1684
1735
  throw new Error(`HTTP ${response.status}: ${message}`);
1685
1736
  }
1686
- const payload = await response.json();
1737
+ const payload = await parseOpenAICompatibleJSONResponse(response, "Failed to parse OpenAI-compatible chat completion response");
1687
1738
  const assistantMessage = pickAssistantMessage(payload);
1688
1739
  if (!assistantMessage) {
1689
1740
  throw new Error("No assistant message in OpenAI-compatible response.");
1690
1741
  }
1691
1742
  const toolCalls = pickChatToolCalls(payload);
1743
+ const reasoning = pickAssistantReasoning(payload);
1692
1744
  return {
1693
1745
  text: pickAssistantText(payload),
1746
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1694
1747
  raw: payload,
1695
1748
  usage: pickUsage(payload),
1696
1749
  finishReason: pickFinishReason(payload),
1697
1750
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined
1698
1751
  };
1699
1752
  }
1753
+ async function parseOpenAICompatibleJSONResponse(response, context) {
1754
+ const rawBody = await response.text();
1755
+ try {
1756
+ return JSON.parse(rawBody);
1757
+ } catch (error) {
1758
+ const message = error instanceof Error ? error.message : String(error);
1759
+ throw new Error(`${context} (HTTP ${response.status}): ${message}. Raw body: ${formatResponseBodyForError(rawBody)}`);
1760
+ }
1761
+ }
1762
+ function formatResponseBodyForError(rawBody, maxLength = 2000) {
1763
+ const normalized = rawBody.trim();
1764
+ if (normalized.length === 0) {
1765
+ return "[empty body]";
1766
+ }
1767
+ if (normalized.length <= maxLength) {
1768
+ return normalized;
1769
+ }
1770
+ return `${normalized.slice(0, maxLength)}...[truncated ${normalized.length - maxLength} chars]`;
1771
+ }
1700
1772
  async function completeWithChatCompletionsWithMCP(options, fetcher, path, request) {
1701
1773
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
1702
1774
  let messages = buildMessages(request);
@@ -1717,6 +1789,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1717
1789
  model: options.model,
1718
1790
  messages,
1719
1791
  temperature: request.temperature,
1792
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1720
1793
  max_tokens: request.maxTokens,
1721
1794
  tools: transportTools,
1722
1795
  tool_choice: request.toolChoice,
@@ -1738,8 +1811,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1738
1811
  throw new Error("No assistant message in OpenAI-compatible response.");
1739
1812
  }
1740
1813
  if (calledTools.length === 0) {
1814
+ const reasoning = pickAssistantReasoning(payload);
1741
1815
  return {
1742
1816
  text: pickAssistantText(payload),
1817
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1743
1818
  raw: payload,
1744
1819
  usage: aggregatedUsage,
1745
1820
  finishReason,
@@ -1767,6 +1842,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1767
1842
  }
1768
1843
  return {
1769
1844
  text: pickAssistantText(lastPayload ?? {}),
1845
+ reasoning: (() => {
1846
+ const value = pickAssistantReasoning(lastPayload ?? {});
1847
+ return value.length > 0 ? value : undefined;
1848
+ })(),
1770
1849
  raw: lastPayload,
1771
1850
  usage: aggregatedUsage,
1772
1851
  finishReason,
@@ -1786,6 +1865,7 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
1786
1865
  input: buildResponsesInput(request),
1787
1866
  previous_response_id: pickString(body?.previous_response_id),
1788
1867
  temperature: request.temperature,
1868
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1789
1869
  max_output_tokens: request.maxTokens
1790
1870
  })),
1791
1871
  signal: request.signal
@@ -1826,6 +1906,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1826
1906
  input,
1827
1907
  previous_response_id: previousResponseId,
1828
1908
  temperature: request.temperature,
1909
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1829
1910
  max_output_tokens: request.maxTokens,
1830
1911
  tools: transportTools,
1831
1912
  tool_choice: request.toolChoice,
@@ -1890,6 +1971,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1890
1971
  const executedToolCalls = [];
1891
1972
  const toolExecutions = [];
1892
1973
  callbacks.onStart?.();
1974
+ let lastRoundText = "";
1975
+ let lastRoundReasoning = "";
1893
1976
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1894
1977
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1895
1978
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -1902,6 +1985,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1902
1985
  model: options.model,
1903
1986
  messages,
1904
1987
  temperature: request.temperature,
1988
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1905
1989
  max_tokens: request.maxTokens,
1906
1990
  tools: transportTools,
1907
1991
  tool_choice: request.toolChoice,
@@ -1915,9 +1999,11 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1915
1999
  throw new Error(`HTTP ${response.status}: ${message}`);
1916
2000
  }
1917
2001
  let roundText = "";
2002
+ let roundReasoning = "";
1918
2003
  let roundUsage;
1919
2004
  let roundFinishReason;
1920
2005
  const streamedToolCalls = new Map;
2006
+ let reasoningFieldName;
1921
2007
  await consumeSSE(response, (data) => {
1922
2008
  if (data === "[DONE]") {
1923
2009
  return;
@@ -1928,6 +2014,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1928
2014
  }
1929
2015
  lastPayload = json;
1930
2016
  const delta = pickAssistantDelta(json);
2017
+ const reasoningDelta = pickAssistantReasoningDelta(json);
1931
2018
  const chunkUsage = pickUsage(json);
1932
2019
  const chunkFinishReason = pickFinishReason(json);
1933
2020
  collectOpenAIStreamToolCalls(json, streamedToolCalls);
@@ -1939,9 +2026,14 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1939
2026
  roundText += delta;
1940
2027
  callbacks.onToken?.(delta);
1941
2028
  }
1942
- if (delta || chunkUsage || chunkFinishReason) {
2029
+ if (reasoningDelta) {
2030
+ roundReasoning += reasoningDelta;
2031
+ reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
2032
+ }
2033
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
1943
2034
  const chunk = {
1944
2035
  textDelta: delta,
2036
+ reasoningDelta: reasoningDelta || undefined,
1945
2037
  raw: json,
1946
2038
  usage: chunkUsage,
1947
2039
  finishReason: chunkFinishReason
@@ -1957,6 +2049,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1957
2049
  if (calledTools.length === 0) {
1958
2050
  const out2 = {
1959
2051
  text: roundText,
2052
+ reasoning: roundReasoning.length > 0 ? roundReasoning : undefined,
1960
2053
  raw: lastPayload,
1961
2054
  usage: aggregatedUsage,
1962
2055
  finishReason,
@@ -1977,7 +2070,12 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1977
2070
  });
1978
2071
  executedToolCalls.push(...outputs.map((entry) => entry.call));
1979
2072
  toolExecutions.push(...outputs.map((entry) => entry.execution));
1980
- const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools);
2073
+ lastRoundText = roundText;
2074
+ lastRoundReasoning = roundReasoning;
2075
+ const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
2076
+ reasoning: roundReasoning,
2077
+ reasoningFieldName
2078
+ });
1981
2079
  const toolMessages = outputs.map((entry) => ({
1982
2080
  role: "tool",
1983
2081
  tool_call_id: entry.call.id,
@@ -1986,7 +2084,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1986
2084
  messages = [...messages, assistantMessage, ...toolMessages];
1987
2085
  }
1988
2086
  const out = {
1989
- text: "",
2087
+ text: lastRoundText,
2088
+ reasoning: lastRoundReasoning.length > 0 ? lastRoundReasoning : undefined,
1990
2089
  raw: lastPayload,
1991
2090
  usage: aggregatedUsage,
1992
2091
  finishReason,
@@ -2008,6 +2107,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
2008
2107
  input: buildResponsesInput(request),
2009
2108
  previous_response_id: pickString(body?.previous_response_id),
2010
2109
  temperature: request.temperature,
2110
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2011
2111
  max_output_tokens: request.maxTokens,
2012
2112
  stream: true
2013
2113
  })),
@@ -2088,6 +2188,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2088
2188
  input,
2089
2189
  previous_response_id: previousResponseId,
2090
2190
  temperature: request.temperature,
2191
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2091
2192
  max_output_tokens: request.maxTokens,
2092
2193
  tools: transportTools,
2093
2194
  tool_choice: request.toolChoice,
@@ -2254,6 +2355,12 @@ function toResponsesTools(tools) {
2254
2355
  return { ...tool };
2255
2356
  });
2256
2357
  }
2358
+ function toOpenAIReasoningEffort(value) {
2359
+ if (!value) {
2360
+ return;
2361
+ }
2362
+ return value === "max" ? "xhigh" : value;
2363
+ }
2257
2364
  function pickChatToolCalls(payload) {
2258
2365
  const message = pickAssistantMessage(payload);
2259
2366
  if (!message) {
@@ -2335,20 +2442,50 @@ function pickAssistantDelta(payload) {
2335
2442
  if (!isRecord2(delta)) {
2336
2443
  return "";
2337
2444
  }
2338
- const content = delta.content;
2339
- if (typeof content === "string") {
2340
- return content;
2445
+ return pickTextFromOpenAIContent(delta.content);
2446
+ }
2447
+ function pickAssistantReasoning(payload) {
2448
+ const message = pickAssistantMessage(payload);
2449
+ if (!message) {
2450
+ return "";
2341
2451
  }
2342
- if (Array.isArray(content)) {
2343
- return content.map((part) => {
2344
- if (!isRecord2(part)) {
2345
- return "";
2346
- }
2347
- const text = part.text;
2348
- return typeof text === "string" ? text : "";
2349
- }).join("");
2452
+ return pickReasoningText(message);
2453
+ }
2454
+ function pickAssistantReasoningDelta(payload) {
2455
+ const choices = payload.choices;
2456
+ if (!Array.isArray(choices) || choices.length === 0) {
2457
+ return "";
2350
2458
  }
2351
- return "";
2459
+ const first = choices[0];
2460
+ if (!isRecord2(first)) {
2461
+ return "";
2462
+ }
2463
+ const delta = first.delta;
2464
+ if (!isRecord2(delta)) {
2465
+ return "";
2466
+ }
2467
+ return pickReasoningText(delta);
2468
+ }
2469
+ function pickAssistantReasoningDeltaFieldName(payload) {
2470
+ const choices = payload.choices;
2471
+ if (!Array.isArray(choices) || choices.length === 0) {
2472
+ return;
2473
+ }
2474
+ const first = choices[0];
2475
+ if (!isRecord2(first)) {
2476
+ return;
2477
+ }
2478
+ const delta = first.delta;
2479
+ if (!isRecord2(delta)) {
2480
+ return;
2481
+ }
2482
+ if (hasTextLikeValue(delta.reasoning)) {
2483
+ return "reasoning";
2484
+ }
2485
+ if (hasTextLikeValue(delta.reasoning_content)) {
2486
+ return "reasoning_content";
2487
+ }
2488
+ return;
2352
2489
  }
2353
2490
  function collectOpenAIStreamToolCalls(payload, state) {
2354
2491
  const choices = payload.choices;
@@ -2397,8 +2534,8 @@ function buildOpenAIStreamToolCalls(state) {
2397
2534
  arguments: entry.argumentsText.length > 0 ? entry.argumentsText : {}
2398
2535
  }));
2399
2536
  }
2400
- function buildOpenAIAssistantToolMessage(text, toolCalls) {
2401
- return {
2537
+ function buildOpenAIAssistantToolMessage(text, toolCalls, reasoning) {
2538
+ const message = {
2402
2539
  role: "assistant",
2403
2540
  content: text,
2404
2541
  tool_calls: toolCalls.map((call) => ({
@@ -2410,6 +2547,10 @@ function buildOpenAIAssistantToolMessage(text, toolCalls) {
2410
2547
  }
2411
2548
  }))
2412
2549
  };
2550
+ if (reasoning?.reasoning && reasoning.reasoning.length > 0) {
2551
+ message[reasoning.reasoningFieldName ?? "reasoning"] = reasoning.reasoning;
2552
+ }
2553
+ return message;
2413
2554
  }
2414
2555
  function pickResponsesStreamPayload(payload) {
2415
2556
  if (isRecord2(payload.response)) {
@@ -2571,21 +2712,9 @@ function pickResponsesText(payload) {
2571
2712
  function pickAssistantText(payload) {
2572
2713
  const message = pickAssistantMessage(payload);
2573
2714
  if (message) {
2574
- const content = message.content;
2575
- if (typeof content === "string") {
2576
- return content;
2577
- }
2578
- if (Array.isArray(content)) {
2579
- return content.map((part) => {
2580
- if (typeof part === "string") {
2581
- return part;
2582
- }
2583
- if (!isRecord2(part)) {
2584
- return "";
2585
- }
2586
- const text = part.text;
2587
- return typeof text === "string" ? text : "";
2588
- }).join("");
2715
+ const text = pickTextFromOpenAIContent(message.content);
2716
+ if (text.length > 0) {
2717
+ return text;
2589
2718
  }
2590
2719
  }
2591
2720
  const choices = payload.choices;
@@ -2597,6 +2726,36 @@ function pickAssistantText(payload) {
2597
2726
  }
2598
2727
  return "";
2599
2728
  }
2729
+ function pickReasoningText(value) {
2730
+ return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
2731
+ }
2732
+ function pickTextFromOpenAIContent(value) {
2733
+ return pickTextLike(value);
2734
+ }
2735
+ function pickTextLike(value) {
2736
+ if (typeof value === "string") {
2737
+ return value;
2738
+ }
2739
+ if (Array.isArray(value)) {
2740
+ return value.map((part) => pickTextLikePart(part)).join("");
2741
+ }
2742
+ if (!isRecord2(value)) {
2743
+ return "";
2744
+ }
2745
+ return pickTextLikePart(value);
2746
+ }
2747
+ function pickTextLikePart(value) {
2748
+ if (typeof value === "string") {
2749
+ return value;
2750
+ }
2751
+ if (!isRecord2(value)) {
2752
+ return "";
2753
+ }
2754
+ return pickString(value.text) ?? pickString(value.output_text) ?? pickString(value.reasoning) ?? pickString(value.reasoning_content) ?? (Array.isArray(value.content) ? value.content.map((part) => pickTextLikePart(part)).join("") : "");
2755
+ }
2756
+ function hasTextLikeValue(value) {
2757
+ return pickTextLike(value).length > 0;
2758
+ }
2600
2759
  function pickUsage(payload) {
2601
2760
  const usage = payload.usage;
2602
2761
  if (!isRecord2(usage)) {
@@ -2650,14 +2809,13 @@ function createAnthropicCompatibleAdapter(options) {
2650
2809
  const response = await fetcher(buildURL(options.baseURL, path), {
2651
2810
  method: "POST",
2652
2811
  headers: buildHeaders2(options),
2653
- body: JSON.stringify(cleanUndefined({
2812
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2654
2813
  ...options.defaultBody,
2655
2814
  ...request.body,
2656
2815
  model: options.model,
2657
2816
  system: input.systemPrompt,
2658
2817
  messages: input.messages,
2659
2818
  temperature: request.temperature,
2660
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2661
2819
  stream: true
2662
2820
  })),
2663
2821
  signal: request.signal
@@ -2713,14 +2871,13 @@ async function completePassThrough(options, fetcher, path, request) {
2713
2871
  const response = await fetcher(buildURL(options.baseURL, path), {
2714
2872
  method: "POST",
2715
2873
  headers: buildHeaders2(options),
2716
- body: JSON.stringify(cleanUndefined({
2874
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2717
2875
  ...options.defaultBody,
2718
2876
  ...request.body,
2719
2877
  model: options.model,
2720
2878
  system: input.systemPrompt,
2721
2879
  messages: input.messages,
2722
2880
  temperature: request.temperature,
2723
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2724
2881
  stream: false
2725
2882
  })),
2726
2883
  signal: request.signal
@@ -2758,14 +2915,13 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2758
2915
  const response = await fetcher(buildURL(options.baseURL, path), {
2759
2916
  method: "POST",
2760
2917
  headers: buildHeaders2(options),
2761
- body: JSON.stringify(cleanUndefined({
2918
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2762
2919
  ...options.defaultBody,
2763
2920
  ...request.body,
2764
2921
  model: options.model,
2765
2922
  system: input.systemPrompt,
2766
2923
  messages,
2767
2924
  temperature: request.temperature,
2768
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2769
2925
  tools,
2770
2926
  tool_choice: toAnthropicToolChoice(request.toolChoice),
2771
2927
  stream: false
@@ -2843,14 +2999,13 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2843
2999
  const response = await fetcher(buildURL(options.baseURL, path), {
2844
3000
  method: "POST",
2845
3001
  headers: buildHeaders2(options),
2846
- body: JSON.stringify(cleanUndefined({
3002
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2847
3003
  ...options.defaultBody,
2848
3004
  ...request.body,
2849
3005
  model: options.model,
2850
3006
  system: input.systemPrompt,
2851
3007
  messages,
2852
3008
  temperature: request.temperature,
2853
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2854
3009
  tools,
2855
3010
  tool_choice: toAnthropicToolChoice(request.toolChoice),
2856
3011
  stream: true
@@ -2958,6 +3113,21 @@ function buildHeaders2(options) {
2958
3113
  ...options.headers
2959
3114
  };
2960
3115
  }
3116
+ function buildAnthropicRequestBody(options, request, body) {
3117
+ const bodyOutputConfig = isRecord2(body.output_config) ? body.output_config : undefined;
3118
+ const bodyThinking = body.thinking;
3119
+ const hasExplicitThinking = Object.prototype.hasOwnProperty.call(body, "thinking");
3120
+ const reasoningEffort = request.reasoningEffort;
3121
+ return cleanUndefined({
3122
+ ...body,
3123
+ max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
3124
+ output_config: reasoningEffort ? cleanUndefined({
3125
+ ...bodyOutputConfig,
3126
+ effort: reasoningEffort
3127
+ }) : bodyOutputConfig,
3128
+ thinking: reasoningEffort ? hasExplicitThinking ? bodyThinking : { type: "adaptive" } : bodyThinking
3129
+ });
3130
+ }
2961
3131
  function resolveAnthropicInput(request) {
2962
3132
  if (Array.isArray(request.messages) && request.messages.length > 0) {
2963
3133
  return toAnthropicInput(request.messages);
@@ -3312,8 +3482,34 @@ function buildProviderOptions(config) {
3312
3482
  };
3313
3483
  }
3314
3484
 
3315
- // src/structured.ts
3316
- import { jsonrepair as jsonrepair3 } from "jsonrepair";
3485
+ // src/utils/debug-colors.ts
3486
+ var ANSI = {
3487
+ reset: "\x1B[0m",
3488
+ bold: "\x1B[1m",
3489
+ cyan: "\x1B[36m",
3490
+ yellow: "\x1B[33m",
3491
+ green: "\x1B[32m",
3492
+ red: "\x1B[31m",
3493
+ dim: "\x1B[2m"
3494
+ };
3495
+ function color(config, text, tone) {
3496
+ if (!config.colors) {
3497
+ return text;
3498
+ }
3499
+ return `${ANSI[tone]}${text}${ANSI.reset}`;
3500
+ }
3501
+ function dim(config, text) {
3502
+ if (!config.colors) {
3503
+ return text;
3504
+ }
3505
+ return `${ANSI.dim}${text}${ANSI.reset}`;
3506
+ }
3507
+ function title(config, text) {
3508
+ if (!config.colors) {
3509
+ return text;
3510
+ }
3511
+ return `${ANSI.bold}${text}${ANSI.reset}`;
3512
+ }
3317
3513
 
3318
3514
  // src/outdent.ts
3319
3515
  var DEFAULT_OPTIONS = {
@@ -3452,123 +3648,682 @@ function createOutdent(options = {}) {
3452
3648
  return outdent;
3453
3649
  }
3454
3650
 
3455
- // src/parse.ts
3456
- import { jsonrepair as jsonrepair2 } from "jsonrepair";
3457
- function parseLLMOutput(output, schema, options = {}) {
3458
- const sanitized = sanitizeThink(output);
3459
- const parseOptions = {
3460
- repair: options.repair ?? true,
3461
- maxCandidates: options.maxCandidates ?? 5,
3462
- acceptArrays: options.acceptArrays ?? true,
3463
- extraction: options.extraction,
3464
- onTrace: options.onTrace
3465
- };
3466
- const candidates = extractJsonCandidates(sanitized.visibleText, {
3467
- maxCandidates: parseOptions.maxCandidates,
3468
- acceptArrays: parseOptions.acceptArrays,
3469
- allowRepairHints: parseOptions.repair,
3470
- heuristics: parseOptions.extraction
3471
- });
3472
- emitTrace(parseOptions.onTrace, {
3473
- stage: "extract",
3474
- level: "info",
3475
- message: `Extracted ${candidates.length} candidate(s).`,
3476
- details: {
3477
- maxCandidates: parseOptions.maxCandidates,
3478
- thinkBlocks: sanitized.thinkBlocks.length,
3479
- thinkDiagnostics: sanitized.diagnostics
3480
- }
3481
- });
3482
- const errors = [];
3483
- const diagnostics = [];
3484
- let bestIssues = [];
3485
- let bestCandidate = candidates[0] ?? null;
3486
- let bestParsed = null;
3487
- let bestRepaired = null;
3488
- for (const candidate of candidates) {
3489
- const parseAttempt = parseAttemptFromHint(candidate.parseHint, parseOptions.repair) ?? tryParseJsonCandidate(candidate.content, parseOptions.repair);
3490
- if (!parseAttempt.success) {
3491
- const diagnostic = {
3492
- candidateId: candidate.id,
3493
- source: candidate.source,
3494
- usedRepair: parseAttempt.usedRepair,
3495
- parseSuccess: false,
3496
- validationSuccess: false,
3497
- selected: false,
3498
- stage: parseAttempt.stage,
3499
- message: parseAttempt.error
3500
- };
3501
- diagnostics.push(diagnostic);
3502
- errors.push({
3503
- stage: parseAttempt.stage,
3504
- message: parseAttempt.error,
3505
- candidateId: candidate.id
3506
- });
3507
- emitTrace(parseOptions.onTrace, {
3508
- stage: parseAttempt.stage,
3509
- level: "error",
3510
- message: parseAttempt.error,
3511
- candidateId: candidate.id
3512
- });
3513
- continue;
3514
- }
3515
- emitTrace(parseOptions.onTrace, {
3516
- stage: "parse",
3517
- level: "info",
3518
- message: parseAttempt.usedRepair ? "Candidate parsed after repair." : "Candidate parsed without repair.",
3519
- candidateId: candidate.id,
3520
- details: {
3521
- usedRepair: parseAttempt.usedRepair
3522
- }
3523
- });
3524
- const validated = schema.safeParse(parseAttempt.parsed);
3525
- if (validated.success) {
3526
- const selectedDiagnostic = {
3527
- candidateId: candidate.id,
3528
- source: candidate.source,
3529
- usedRepair: parseAttempt.usedRepair,
3530
- parseSuccess: true,
3531
- validationSuccess: true,
3532
- selected: true,
3533
- stage: "success"
3534
- };
3535
- diagnostics.push(selectedDiagnostic);
3536
- emitTrace(parseOptions.onTrace, {
3537
- stage: "result",
3538
- level: "info",
3539
- message: `Validation succeeded on candidate ${candidate.id}.`,
3540
- candidateId: candidate.id
3541
- });
3542
- return {
3543
- success: true,
3544
- data: validated.data,
3545
- raw: output,
3546
- sanitizedRaw: sanitized.visibleText,
3547
- thinkBlocks: sanitized.thinkBlocks,
3548
- thinkDiagnostics: sanitized.diagnostics,
3549
- parsed: parseAttempt.parsed,
3550
- candidate,
3551
- repaired: parseAttempt.repaired,
3552
- candidates,
3553
- diagnostics,
3554
- errors,
3555
- zodIssues: []
3556
- };
3557
- }
3558
- const issues = validated.error.issues;
3559
- const message = formatZodIssues(issues);
3560
- const validationDiagnostic = {
3561
- candidateId: candidate.id,
3562
- source: candidate.source,
3563
- usedRepair: parseAttempt.usedRepair,
3564
- parseSuccess: true,
3565
- validationSuccess: false,
3566
- selected: false,
3567
- stage: "validate",
3568
- message,
3569
- zodIssues: issues
3651
+ // src/generate-shared.ts
3652
+ var sharedOutdent = createOutdent({
3653
+ trimLeadingNewline: true,
3654
+ trimTrailingNewline: true,
3655
+ newline: `
3656
+ `
3657
+ });
3658
+ var RE_THINK_TAGS = /<\/?think\s*>/gi;
3659
+ function resolvePrompt(prompt, context) {
3660
+ const resolved = typeof prompt === "function" ? prompt(context) : prompt;
3661
+ return normalizePromptValue(resolved, context);
3662
+ }
3663
+ function normalizePromptValue(value, _context) {
3664
+ if (typeof value === "string") {
3665
+ return {
3666
+ prompt: value
3570
3667
  };
3571
- diagnostics.push(validationDiagnostic);
3668
+ }
3669
+ if (isPromptResolver(value)) {
3670
+ return normalizePromptPayload(value.resolvePrompt(_context));
3671
+ }
3672
+ return normalizePromptPayload(value);
3673
+ }
3674
+ function normalizePromptPayload(value) {
3675
+ const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
3676
+ const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
3677
+ if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
3678
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
3679
+ }
3680
+ return {
3681
+ prompt,
3682
+ systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
3683
+ messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
3684
+ };
3685
+ }
3686
+ function applyPromptOutdent(payload, enabled) {
3687
+ if (!enabled) {
3688
+ return payload;
3689
+ }
3690
+ return {
3691
+ prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
3692
+ systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
3693
+ messages: payload.messages?.map((message) => ({
3694
+ ...message,
3695
+ content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
3696
+ }))
3697
+ };
3698
+ }
3699
+ function applyOutdentToOptionalPrompt(value, enabled) {
3700
+ if (!enabled || typeof value !== "string") {
3701
+ return value;
3702
+ }
3703
+ return sharedOutdent.string(value);
3704
+ }
3705
+ function mergeSystemPrompts(primary, secondary) {
3706
+ const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
3707
+ if (prompts.length === 0) {
3708
+ return;
3709
+ }
3710
+ return prompts.join(`
3711
+
3712
+ `);
3713
+ }
3714
+ function normalizeStreamConfig(option) {
3715
+ if (typeof option === "boolean") {
3716
+ return {
3717
+ enabled: option
3718
+ };
3719
+ }
3720
+ if (!option) {
3721
+ return {
3722
+ enabled: false
3723
+ };
3724
+ }
3725
+ return {
3726
+ enabled: option.enabled ?? true,
3727
+ onData: option.onData,
3728
+ to: option.to
3729
+ };
3730
+ }
3731
+ function normalizeDebugConfig(option) {
3732
+ if (typeof option === "boolean") {
3733
+ return {
3734
+ enabled: option,
3735
+ colors: true,
3736
+ verbose: false,
3737
+ logger: (line) => console.log(line)
3738
+ };
3739
+ }
3740
+ if (!option) {
3741
+ return {
3742
+ enabled: false,
3743
+ colors: true,
3744
+ verbose: false,
3745
+ logger: (line) => console.log(line)
3746
+ };
3747
+ }
3748
+ return {
3749
+ enabled: option.enabled ?? true,
3750
+ colors: option.colors ?? true,
3751
+ verbose: option.verbose ?? false,
3752
+ logger: option.logger ?? ((line) => console.log(line))
3753
+ };
3754
+ }
3755
+ function withToolTimeout(client, toolTimeoutMs) {
3756
+ return {
3757
+ id: client.id,
3758
+ listTools: client.listTools.bind(client),
3759
+ close: client.close?.bind(client),
3760
+ async callTool(params) {
3761
+ let timeoutId;
3762
+ const timeoutPromise = new Promise((_, reject) => {
3763
+ timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
3764
+ });
3765
+ try {
3766
+ return await Promise.race([client.callTool(params), timeoutPromise]);
3767
+ } finally {
3768
+ clearTimeout(timeoutId);
3769
+ }
3770
+ }
3771
+ };
3772
+ }
3773
+ function applyToolTimeout(clients, toolTimeoutMs) {
3774
+ return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
3775
+ }
3776
+ async function callModel(adapter, options) {
3777
+ const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
3778
+ const requestPayload = {
3779
+ prompt: options.prompt,
3780
+ messages: options.messages,
3781
+ systemPrompt: options.systemPrompt,
3782
+ temperature: options.request?.temperature,
3783
+ reasoningEffort: options.request?.reasoningEffort,
3784
+ maxTokens: options.request?.maxTokens,
3785
+ mcpClients: options.request?.mcpClients,
3786
+ toolChoice: options.request?.toolChoice,
3787
+ parallelToolCalls: options.request?.parallelToolCalls,
3788
+ maxToolRounds: options.request?.maxToolRounds,
3789
+ onToolExecution: options.request?.onToolExecution,
3790
+ transformToolOutput: options.request?.transformToolOutput,
3791
+ transformToolArguments: options.request?.transformToolArguments,
3792
+ transformToolCallParams: options.request?.transformToolCallParams,
3793
+ unknownToolError: options.request?.unknownToolError,
3794
+ toolDebug: options.request?.toolDebug,
3795
+ body: options.request?.body,
3796
+ signal: requestSignal
3797
+ };
3798
+ emitDebugRequest(options.debug, {
3799
+ label: options.debugLabel,
3800
+ provider: adapter.provider,
3801
+ model: adapter.model,
3802
+ attempt: options.attempt,
3803
+ selfHealAttempt: options.selfHeal,
3804
+ selfHealEnabled: options.selfHealEnabled,
3805
+ stream: options.stream.enabled && !!adapter.stream,
3806
+ requestPayload
3807
+ });
3808
+ options.observe?.(options.buildEvent({
3809
+ stage: "llm.request",
3810
+ message: "Sending LLM request.",
3811
+ details: {
3812
+ provider: adapter.provider,
3813
+ model: adapter.model,
3814
+ stream: options.stream.enabled && !!adapter.stream
3815
+ }
3816
+ }));
3817
+ if (options.stream.enabled && adapter.stream) {
3818
+ let latestUsage;
3819
+ let latestFinishReason;
3820
+ let streamedProviderText = "";
3821
+ let streamedDedicatedReasoning = "";
3822
+ let lastSnapshotFingerprint;
3823
+ let previousSnapshotText = "";
3824
+ let previousSnapshotReasoning = "";
3825
+ const emitStreamingData = (done, usage2, finishReason2) => {
3826
+ const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
3827
+ const snapshot = options.buildSnapshot(normalized2);
3828
+ const fingerprint = toStreamDataFingerprint(snapshot);
3829
+ if (!done && fingerprint === lastSnapshotFingerprint) {
3830
+ return;
3831
+ }
3832
+ const delta = {
3833
+ text: normalized2.text.startsWith(previousSnapshotText) ? normalized2.text.slice(previousSnapshotText.length) : "",
3834
+ reasoning: normalized2.reasoning.startsWith(previousSnapshotReasoning) ? normalized2.reasoning.slice(previousSnapshotReasoning.length) : ""
3835
+ };
3836
+ lastSnapshotFingerprint = fingerprint;
3837
+ previousSnapshotText = normalized2.text;
3838
+ previousSnapshotReasoning = normalized2.reasoning;
3839
+ options.stream.onData?.({
3840
+ delta,
3841
+ snapshot,
3842
+ done,
3843
+ usage: usage2,
3844
+ finishReason: finishReason2
3845
+ });
3846
+ if (options.stream.to === "stdout" && delta.text) {
3847
+ process.stdout.write(delta.text);
3848
+ }
3849
+ options.observe?.(options.buildEvent({
3850
+ stage: "llm.stream.data",
3851
+ message: done ? "Streaming response completed." : "Streaming response updated.",
3852
+ details: {
3853
+ done,
3854
+ finishReason: finishReason2
3855
+ }
3856
+ }));
3857
+ };
3858
+ const handleTextDelta = (delta) => {
3859
+ if (!delta) {
3860
+ return;
3861
+ }
3862
+ streamedProviderText += delta;
3863
+ options.observe?.(options.buildEvent({
3864
+ stage: "llm.stream.delta",
3865
+ message: "Received stream delta.",
3866
+ details: {
3867
+ chars: delta.length
3868
+ }
3869
+ }));
3870
+ emitStreamingData(false);
3871
+ };
3872
+ const handleReasoningDelta = (delta) => {
3873
+ if (!delta) {
3874
+ return;
3875
+ }
3876
+ streamedDedicatedReasoning += delta;
3877
+ emitStreamingData(false);
3878
+ };
3879
+ const response2 = await adapter.stream(requestPayload, {
3880
+ onChunk: (chunk) => {
3881
+ if (chunk.textDelta) {
3882
+ handleTextDelta(chunk.textDelta);
3883
+ }
3884
+ if (chunk.reasoningDelta) {
3885
+ handleReasoningDelta(chunk.reasoningDelta);
3886
+ }
3887
+ if (chunk.usage) {
3888
+ latestUsage = preferLatestUsage(latestUsage, chunk.usage);
3889
+ }
3890
+ if (chunk.finishReason) {
3891
+ latestFinishReason = chunk.finishReason;
3892
+ }
3893
+ }
3894
+ });
3895
+ streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
3896
+ streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
3897
+ const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
3898
+ const usage = preferLatestUsage(latestUsage, response2.usage);
3899
+ const finishReason = response2.finishReason ?? latestFinishReason;
3900
+ emitStreamingData(true, usage, finishReason);
3901
+ options.observe?.(options.buildEvent({
3902
+ stage: "llm.response",
3903
+ message: "Streaming response completed.",
3904
+ details: {
3905
+ via: "stream",
3906
+ chars: finalNormalized.parseSource.length,
3907
+ finishReason
3908
+ }
3909
+ }));
3910
+ emitDebugResponse(options.debug, {
3911
+ label: options.debugLabel,
3912
+ attempt: options.attempt,
3913
+ selfHealAttempt: options.selfHeal,
3914
+ selfHealEnabled: options.selfHealEnabled,
3915
+ via: "stream",
3916
+ text: finalNormalized.text,
3917
+ reasoning: finalNormalized.reasoning,
3918
+ parseSource: finalNormalized.parseSource,
3919
+ usage,
3920
+ finishReason
3921
+ });
3922
+ return {
3923
+ text: finalNormalized.text,
3924
+ reasoning: finalNormalized.reasoning,
3925
+ thinkBlocks: finalNormalized.thinkBlocks,
3926
+ parseSource: finalNormalized.parseSource,
3927
+ via: "stream",
3928
+ usage,
3929
+ finishReason
3930
+ };
3931
+ }
3932
+ const response = await adapter.complete(requestPayload);
3933
+ const normalized = normalizeModelOutput(response.text, response.reasoning);
3934
+ options.observe?.(options.buildEvent({
3935
+ stage: "llm.response",
3936
+ message: "Completion response received.",
3937
+ details: {
3938
+ via: "complete",
3939
+ chars: normalized.parseSource.length,
3940
+ finishReason: response.finishReason
3941
+ }
3942
+ }));
3943
+ emitDebugResponse(options.debug, {
3944
+ label: options.debugLabel,
3945
+ attempt: options.attempt,
3946
+ selfHealAttempt: options.selfHeal,
3947
+ selfHealEnabled: options.selfHealEnabled,
3948
+ via: "complete",
3949
+ text: normalized.text,
3950
+ reasoning: normalized.reasoning,
3951
+ parseSource: normalized.parseSource,
3952
+ usage: response.usage,
3953
+ finishReason: response.finishReason
3954
+ });
3955
+ return {
3956
+ text: normalized.text,
3957
+ reasoning: normalized.reasoning,
3958
+ thinkBlocks: normalized.thinkBlocks,
3959
+ parseSource: normalized.parseSource,
3960
+ via: "complete",
3961
+ usage: response.usage,
3962
+ finishReason: response.finishReason
3963
+ };
3964
+ }
3965
+ function normalizeModelOutput(text, dedicatedReasoning) {
3966
+ const sanitized = sanitizeThink(text);
3967
+ const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
3968
+ const reasoning = joinReasoningSegments([
3969
+ dedicatedReasoning,
3970
+ ...sanitized.thinkBlocks.map((block) => block.content)
3971
+ ]);
3972
+ return {
3973
+ text: visibleText,
3974
+ reasoning,
3975
+ thinkBlocks: sanitized.thinkBlocks,
3976
+ parseSource: composeParseSource(visibleText, reasoning)
3977
+ };
3978
+ }
3979
+ function composeParseSource(text, reasoning) {
3980
+ if (typeof reasoning !== "string" || reasoning.length === 0) {
3981
+ return text;
3982
+ }
3983
+ const sanitized = reasoning.replace(RE_THINK_TAGS, "");
3984
+ if (sanitized.length === 0) {
3985
+ return text;
3986
+ }
3987
+ return `<think>${sanitized}</think>${text}`;
3988
+ }
3989
+ function aggregateUsage(attempts) {
3990
+ let usage;
3991
+ for (const attempt of attempts) {
3992
+ usage = mergeUsage2(usage, attempt.usage);
3993
+ }
3994
+ return usage;
3995
+ }
3996
+ function mergeUsage2(base, next) {
3997
+ if (!base && !next) {
3998
+ return;
3999
+ }
4000
+ return {
4001
+ inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
4002
+ outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
4003
+ totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
4004
+ cost: (base?.cost ?? 0) + (next?.cost ?? 0)
4005
+ };
4006
+ }
4007
+ function isPromptResolver(value) {
4008
+ return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
4009
+ }
4010
+ function isLLMMessage(value) {
4011
+ if (typeof value !== "object" || value === null) {
4012
+ return false;
4013
+ }
4014
+ const candidate = value;
4015
+ if (candidate.role !== "system" && candidate.role !== "user" && candidate.role !== "assistant" && candidate.role !== "tool") {
4016
+ return false;
4017
+ }
4018
+ return "content" in candidate;
4019
+ }
4020
+ function joinReasoningSegments(parts) {
4021
+ return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
4022
+
4023
+ `);
4024
+ }
4025
+ function stripThinkBlocks(text, thinkBlocks) {
4026
+ if (thinkBlocks.length === 0) {
4027
+ return text;
4028
+ }
4029
+ let output = "";
4030
+ let cursor = 0;
4031
+ for (const block of thinkBlocks) {
4032
+ output += text.slice(cursor, block.start);
4033
+ cursor = block.end;
4034
+ }
4035
+ output += text.slice(cursor);
4036
+ return output;
4037
+ }
4038
+ function toStreamDataFingerprint(value) {
4039
+ try {
4040
+ return JSON.stringify(value);
4041
+ } catch {
4042
+ return "__unserializable__";
4043
+ }
4044
+ }
4045
+ function emitDebugRequest(config, input) {
4046
+ const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4047
+ const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4048
+ const lines = [
4049
+ color(config, title(config, [
4050
+ `[${input.label}][request]`,
4051
+ `attempt=${input.attempt}`,
4052
+ `selfHealEnabled=${input.selfHealEnabled}`,
4053
+ `selfHealAttempt=${input.selfHealAttempt}`
4054
+ ].join(" ")), "cyan"),
4055
+ dim(config, [
4056
+ `provider=${input.provider ?? "unknown"}`,
4057
+ `model=${input.model ?? "unknown"}`,
4058
+ `stream=${input.stream}`
4059
+ ].join(" ")),
4060
+ color(config, "prompt:", "yellow"),
4061
+ input.requestPayload.prompt ?? "(none)",
4062
+ color(config, "messages:", "yellow"),
4063
+ requestMessages,
4064
+ color(config, "systemPrompt:", "yellow"),
4065
+ input.requestPayload.systemPrompt ?? "(none)",
4066
+ color(config, "request.body:", "yellow"),
4067
+ requestBody
4068
+ ];
4069
+ emitDebug(config, lines.join(`
4070
+ `));
4071
+ }
4072
+ function emitDebugResponse(config, input) {
4073
+ const text = input.text.length > 0 ? input.text : "(none)";
4074
+ const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
4075
+ const metadata = [
4076
+ `via=${input.via}`,
4077
+ `textChars=${input.text.length}`,
4078
+ `reasoningChars=${input.reasoning.length}`
4079
+ ];
4080
+ if (config.verbose) {
4081
+ metadata.push(`parseSourceChars=${input.parseSource.length}`);
4082
+ }
4083
+ metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
4084
+ const lines = [
4085
+ color(config, title(config, [
4086
+ `[${input.label}][response]`,
4087
+ `attempt=${input.attempt}`,
4088
+ `selfHealEnabled=${input.selfHealEnabled}`,
4089
+ `selfHealAttempt=${input.selfHealAttempt}`
4090
+ ].join(" ")), "green"),
4091
+ dim(config, metadata.join(" ")),
4092
+ color(config, "text:", "yellow"),
4093
+ text,
4094
+ color(config, "reasoning:", "yellow"),
4095
+ reasoning
4096
+ ];
4097
+ if (config.verbose) {
4098
+ lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
4099
+ }
4100
+ emitDebug(config, lines.join(`
4101
+ `));
4102
+ }
4103
+ function emitDebug(config, message) {
4104
+ if (!config.enabled) {
4105
+ return;
4106
+ }
4107
+ config.logger(message);
4108
+ }
4109
+
4110
+ // src/generate.ts
4111
+ async function generate(adapter, promptOrOptions, callOptions) {
4112
+ const normalized = normalizeGenerateInput(promptOrOptions, callOptions);
4113
+ const useOutdent = normalized.outdent ?? true;
4114
+ const streamConfig = normalizeStreamConfig(normalized.stream);
4115
+ const debugConfig = normalizeDebugConfig(normalized.debug);
4116
+ const resolvedPrompt = applyPromptOutdent(resolvePrompt(normalized.prompt, { mode: "loose" }), useOutdent);
4117
+ const resolvedSystemPrompt = applyOutdentToOptionalPrompt(normalized.systemPrompt, useOutdent);
4118
+ const preparedPrompt = prepareGeneratePromptPayload(resolvedPrompt, resolvedSystemPrompt);
4119
+ const resolvedRequest = normalized.timeout?.tool !== undefined && normalized.request?.mcpClients !== undefined ? {
4120
+ ...normalized.request,
4121
+ mcpClients: applyToolTimeout(normalized.request.mcpClients, normalized.timeout.tool)
4122
+ } : normalized.request;
4123
+ const response = await callModel(adapter, {
4124
+ prompt: preparedPrompt.prompt,
4125
+ messages: preparedPrompt.messages,
4126
+ systemPrompt: preparedPrompt.systemPrompt,
4127
+ request: resolvedRequest,
4128
+ stream: streamConfig,
4129
+ observe: normalized.observe,
4130
+ buildEvent: ({ stage, message, details }) => ({
4131
+ stage,
4132
+ attempt: 1,
4133
+ message,
4134
+ details
4135
+ }),
4136
+ buildSnapshot: (model) => ({
4137
+ text: model.text,
4138
+ reasoning: model.reasoning
4139
+ }),
4140
+ debug: debugConfig,
4141
+ debugLabel: "generate",
4142
+ attempt: 1,
4143
+ selfHeal: false,
4144
+ selfHealEnabled: false,
4145
+ timeout: normalized.timeout
4146
+ });
4147
+ const attempt = {
4148
+ attempt: 1,
4149
+ via: response.via,
4150
+ text: response.text,
4151
+ reasoning: response.reasoning,
4152
+ usage: response.usage,
4153
+ finishReason: response.finishReason
4154
+ };
4155
+ const attempts = [attempt];
4156
+ normalized.observe?.({
4157
+ stage: "result",
4158
+ attempt: 1,
4159
+ message: "Text generation completed.",
4160
+ details: {
4161
+ via: response.via,
4162
+ finishReason: response.finishReason
4163
+ }
4164
+ });
4165
+ return {
4166
+ text: attempt.text,
4167
+ reasoning: attempt.reasoning,
4168
+ attempts,
4169
+ usage: aggregateUsage(attempts),
4170
+ finishReason: attempt.finishReason
4171
+ };
4172
+ }
4173
+ function normalizeGenerateInput(promptOrOptions, callOptions) {
4174
+ if (isGenerateOptions(promptOrOptions)) {
4175
+ return promptOrOptions;
4176
+ }
4177
+ if (!promptOrOptions) {
4178
+ throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
4179
+ }
4180
+ return {
4181
+ ...callOptions ?? {},
4182
+ prompt: promptOrOptions
4183
+ };
4184
+ }
4185
+ function isGenerateOptions(value) {
4186
+ return typeof value === "object" && value !== null && "prompt" in value;
4187
+ }
4188
+ function prepareGeneratePromptPayload(payload, systemPrompt) {
4189
+ if (Array.isArray(payload.messages) && payload.messages.length > 0) {
4190
+ const messages = payload.messages.map((message) => ({ ...message }));
4191
+ const mergedSystemPrompt = mergeSystemPrompts(payload.systemPrompt, systemPrompt);
4192
+ const systemMessages = mergedSystemPrompt ? [{ role: "system", content: mergedSystemPrompt }] : [];
4193
+ return {
4194
+ messages: [...systemMessages, ...messages]
4195
+ };
4196
+ }
4197
+ const resolvedPrompt = payload.prompt?.trim();
4198
+ if (!resolvedPrompt) {
4199
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4200
+ }
4201
+ return {
4202
+ prompt: resolvedPrompt,
4203
+ systemPrompt: mergeSystemPrompts(payload.systemPrompt, systemPrompt)
4204
+ };
4205
+ }
4206
+
4207
+ // src/structured.ts
4208
+ import { jsonrepair as jsonrepair3 } from "jsonrepair";
4209
+
4210
+ // src/parse.ts
4211
+ import { jsonrepair as jsonrepair2 } from "jsonrepair";
4212
+ function parseLLMOutput(output, schema, options = {}) {
4213
+ const sanitized = sanitizeThink(output);
4214
+ const parseOptions = {
4215
+ repair: options.repair ?? true,
4216
+ maxCandidates: options.maxCandidates ?? 5,
4217
+ acceptArrays: options.acceptArrays ?? true,
4218
+ extraction: options.extraction,
4219
+ onTrace: options.onTrace
4220
+ };
4221
+ const candidates = extractJsonCandidates(sanitized.visibleText, {
4222
+ maxCandidates: parseOptions.maxCandidates,
4223
+ acceptArrays: parseOptions.acceptArrays,
4224
+ allowRepairHints: parseOptions.repair,
4225
+ heuristics: parseOptions.extraction
4226
+ });
4227
+ emitTrace(parseOptions.onTrace, {
4228
+ stage: "extract",
4229
+ level: "info",
4230
+ message: `Extracted ${candidates.length} candidate(s).`,
4231
+ details: {
4232
+ maxCandidates: parseOptions.maxCandidates,
4233
+ thinkBlocks: sanitized.thinkBlocks.length,
4234
+ thinkDiagnostics: sanitized.diagnostics
4235
+ }
4236
+ });
4237
+ const errors = [];
4238
+ const diagnostics = [];
4239
+ let bestIssues = [];
4240
+ let bestCandidate = candidates[0] ?? null;
4241
+ let bestParsed = null;
4242
+ let bestRepaired = null;
4243
+ for (const candidate of candidates) {
4244
+ const parseAttempt = parseAttemptFromHint(candidate.parseHint, parseOptions.repair) ?? tryParseJsonCandidate(candidate.content, parseOptions.repair);
4245
+ if (!parseAttempt.success) {
4246
+ const diagnostic = {
4247
+ candidateId: candidate.id,
4248
+ source: candidate.source,
4249
+ usedRepair: parseAttempt.usedRepair,
4250
+ parseSuccess: false,
4251
+ validationSuccess: false,
4252
+ selected: false,
4253
+ stage: parseAttempt.stage,
4254
+ message: parseAttempt.error
4255
+ };
4256
+ diagnostics.push(diagnostic);
4257
+ errors.push({
4258
+ stage: parseAttempt.stage,
4259
+ message: parseAttempt.error,
4260
+ candidateId: candidate.id
4261
+ });
4262
+ emitTrace(parseOptions.onTrace, {
4263
+ stage: parseAttempt.stage,
4264
+ level: "error",
4265
+ message: parseAttempt.error,
4266
+ candidateId: candidate.id
4267
+ });
4268
+ continue;
4269
+ }
4270
+ emitTrace(parseOptions.onTrace, {
4271
+ stage: "parse",
4272
+ level: "info",
4273
+ message: parseAttempt.usedRepair ? "Candidate parsed after repair." : "Candidate parsed without repair.",
4274
+ candidateId: candidate.id,
4275
+ details: {
4276
+ usedRepair: parseAttempt.usedRepair
4277
+ }
4278
+ });
4279
+ const validated = schema.safeParse(parseAttempt.parsed);
4280
+ if (validated.success) {
4281
+ const selectedDiagnostic = {
4282
+ candidateId: candidate.id,
4283
+ source: candidate.source,
4284
+ usedRepair: parseAttempt.usedRepair,
4285
+ parseSuccess: true,
4286
+ validationSuccess: true,
4287
+ selected: true,
4288
+ stage: "success"
4289
+ };
4290
+ diagnostics.push(selectedDiagnostic);
4291
+ emitTrace(parseOptions.onTrace, {
4292
+ stage: "result",
4293
+ level: "info",
4294
+ message: `Validation succeeded on candidate ${candidate.id}.`,
4295
+ candidateId: candidate.id
4296
+ });
4297
+ return {
4298
+ success: true,
4299
+ data: validated.data,
4300
+ raw: output,
4301
+ sanitizedRaw: sanitized.visibleText,
4302
+ thinkBlocks: sanitized.thinkBlocks,
4303
+ thinkDiagnostics: sanitized.diagnostics,
4304
+ parsed: parseAttempt.parsed,
4305
+ candidate,
4306
+ repaired: parseAttempt.repaired,
4307
+ candidates,
4308
+ diagnostics,
4309
+ errors,
4310
+ zodIssues: []
4311
+ };
4312
+ }
4313
+ const issues = validated.error.issues;
4314
+ const message = formatZodIssues(issues);
4315
+ const validationDiagnostic = {
4316
+ candidateId: candidate.id,
4317
+ source: candidate.source,
4318
+ usedRepair: parseAttempt.usedRepair,
4319
+ parseSuccess: true,
4320
+ validationSuccess: false,
4321
+ selected: false,
4322
+ stage: "validate",
4323
+ message,
4324
+ zodIssues: issues
4325
+ };
4326
+ diagnostics.push(validationDiagnostic);
3572
4327
  if (bestIssues.length === 0 || issues.length < bestIssues.length) {
3573
4328
  bestIssues = issues;
3574
4329
  bestCandidate = candidate;
@@ -3751,48 +4506,19 @@ function formatZodIssues(issues) {
3751
4506
  `);
3752
4507
  }
3753
4508
 
3754
- // src/utils/debug-colors.ts
3755
- var ANSI = {
3756
- reset: "\x1B[0m",
3757
- bold: "\x1B[1m",
3758
- cyan: "\x1B[36m",
3759
- yellow: "\x1B[33m",
3760
- green: "\x1B[32m",
3761
- red: "\x1B[31m",
3762
- dim: "\x1B[2m"
3763
- };
3764
- function color(config, text, tone) {
3765
- if (!config.colors) {
3766
- return text;
3767
- }
3768
- return `${ANSI[tone]}${text}${ANSI.reset}`;
3769
- }
3770
- function dim(config, text) {
3771
- if (!config.colors) {
3772
- return text;
3773
- }
3774
- return `${ANSI.dim}${text}${ANSI.reset}`;
3775
- }
3776
- function title(config, text) {
3777
- if (!config.colors) {
3778
- return text;
3779
- }
3780
- return `${ANSI.bold}${text}${ANSI.reset}`;
3781
- }
3782
-
3783
4509
  // src/structured.ts
3784
4510
  class StructuredParseError extends Error {
3785
4511
  name = "StructuredParseError";
3786
- raw;
3787
- thinkBlocks;
4512
+ text;
4513
+ reasoning;
3788
4514
  candidates;
3789
4515
  zodIssues;
3790
4516
  repairLog;
3791
4517
  attempt;
3792
4518
  constructor(input) {
3793
4519
  super(input.message ?? `Structured parsing failed after ${input.attempt} attempt(s).`);
3794
- this.raw = input.raw;
3795
- this.thinkBlocks = input.thinkBlocks;
4520
+ this.text = input.text;
4521
+ this.reasoning = input.reasoning;
3796
4522
  this.candidates = input.candidates;
3797
4523
  this.zodIssues = input.zodIssues;
3798
4524
  this.repairLog = input.repairLog;
@@ -3823,12 +4549,6 @@ var RE_SIMPLE_IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3823
4549
  var RE_ESCAPE_QUOTE = /"/g;
3824
4550
  var RE_WHITESPACE2 = /\s+/g;
3825
4551
  var DEFAULT_SELF_HEAL_MAX_DIAGNOSTICS = 8;
3826
- var structuredOutdent = createOutdent({
3827
- trimLeadingNewline: true,
3828
- trimTrailingNewline: true,
3829
- newline: `
3830
- `
3831
- });
3832
4552
  var DEFAULT_STRICT_PARSE_OPTIONS = {
3833
4553
  repair: false,
3834
4554
  maxCandidates: 3,
@@ -3958,7 +4678,7 @@ async function structured(adapter, schemaOrOptions, promptInput, callOptions) {
3958
4678
  });
3959
4679
  const selfHealSource = resolveSelfHealSource(previous);
3960
4680
  const repairPrompt = buildSelfHealPrompt({
3961
- rawOutput: previous.raw,
4681
+ rawOutput: composeParseSource(previous.text, previous.reasoning),
3962
4682
  issues: previous.zodIssues,
3963
4683
  schema: normalized.schema,
3964
4684
  schemaInstruction: normalized.schemaInstruction,
@@ -4031,74 +4751,6 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
4031
4751
  function isStructuredOptions(value) {
4032
4752
  return typeof value === "object" && value !== null && "schema" in value && "prompt" in value;
4033
4753
  }
4034
- function resolvePrompt(prompt, context) {
4035
- const resolved = typeof prompt === "function" ? prompt(context) : prompt;
4036
- return normalizePromptValue(resolved, context);
4037
- }
4038
- function normalizePromptValue(value, context) {
4039
- if (typeof value === "string") {
4040
- return {
4041
- prompt: value
4042
- };
4043
- }
4044
- if (isPromptResolver(value)) {
4045
- return normalizePromptPayload(value.resolvePrompt(context));
4046
- }
4047
- return normalizePromptPayload(value);
4048
- }
4049
- function isPromptResolver(value) {
4050
- return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
4051
- }
4052
- function normalizePromptPayload(value) {
4053
- const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
4054
- const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
4055
- if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
4056
- throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4057
- }
4058
- return {
4059
- prompt,
4060
- systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
4061
- messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
4062
- };
4063
- }
4064
- function applyPromptOutdent(payload, enabled) {
4065
- if (!enabled) {
4066
- return payload;
4067
- }
4068
- return {
4069
- prompt: typeof payload.prompt === "string" ? structuredOutdent.string(payload.prompt) : undefined,
4070
- systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
4071
- messages: payload.messages?.map((message) => ({
4072
- ...message,
4073
- content: typeof message.content === "string" ? structuredOutdent.string(message.content) : message.content
4074
- }))
4075
- };
4076
- }
4077
- function isLLMMessage(value) {
4078
- if (typeof value !== "object" || value === null) {
4079
- return false;
4080
- }
4081
- const candidate = value;
4082
- if (candidate.role !== "system" && candidate.role !== "user" && candidate.role !== "assistant" && candidate.role !== "tool") {
4083
- return false;
4084
- }
4085
- return "content" in candidate;
4086
- }
4087
- function applyOutdentToOptionalPrompt(value, enabled) {
4088
- if (!enabled || typeof value !== "string") {
4089
- return value;
4090
- }
4091
- return structuredOutdent.string(value);
4092
- }
4093
- function mergeSystemPrompts(primary, secondary) {
4094
- const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
4095
- if (prompts.length === 0) {
4096
- return;
4097
- }
4098
- return prompts.join(`
4099
-
4100
- `);
4101
- }
4102
4754
  function prepareStructuredPromptPayload(payload, systemPrompt, schema, schemaInstruction) {
4103
4755
  if (Array.isArray(payload.messages) && payload.messages.length > 0) {
4104
4756
  const messages = payload.messages.map((message) => ({ ...message }));
@@ -4279,7 +4931,7 @@ function resolveSelfHealSource(attempt) {
4279
4931
  }
4280
4932
  return {
4281
4933
  kind: "raw",
4282
- text: attempt.raw
4934
+ text: composeParseSource(attempt.text, attempt.reasoning)
4283
4935
  };
4284
4936
  }
4285
4937
  function isSelfHealStalled(previous, current) {
@@ -4289,60 +4941,22 @@ function isSelfHealStalled(previous, current) {
4289
4941
  if (current.zodIssues.length < previous.zodIssues.length) {
4290
4942
  return false;
4291
4943
  }
4292
- if (current.parsed.errors.length < previous.parsed.errors.length) {
4293
- return false;
4294
- }
4295
- return buildSelfHealFailureFingerprint(previous) === buildSelfHealFailureFingerprint(current);
4296
- }
4297
- function buildSelfHealFailureFingerprint(attempt) {
4298
- const issues = attempt.zodIssues.map((issue) => `${formatIssuePath(issue.path)}:${issue.code}:${normalizeWhitespace(issue.message)}`).sort().join("|");
4299
- const errors = attempt.parsed.errors.map((error) => `${error.stage}:${error.candidateId ?? "-"}:${normalizeWhitespace(error.message)}`).sort().join("|");
4300
- const source = normalizeWhitespace(resolveSelfHealSource(attempt).text).slice(0, 512);
4301
- return [issues, errors, source].join("::");
4302
- }
4303
- function normalizeWhitespace(value) {
4304
- return value.replace(RE_WHITESPACE2, " ").trim();
4305
- }
4306
- function normalizeStreamConfig(option) {
4307
- if (typeof option === "boolean") {
4308
- return {
4309
- enabled: option
4310
- };
4311
- }
4312
- if (!option) {
4313
- return {
4314
- enabled: false
4315
- };
4316
- }
4317
- return {
4318
- enabled: option.enabled ?? true,
4319
- onData: option.onData,
4320
- to: option.to
4321
- };
4322
- }
4323
- function normalizeDebugConfig(option) {
4324
- if (typeof option === "boolean") {
4325
- return {
4326
- enabled: option,
4327
- colors: true,
4328
- logger: (line) => console.log(line)
4329
- };
4330
- }
4331
- if (!option) {
4332
- return {
4333
- enabled: false,
4334
- colors: true,
4335
- logger: (line) => console.log(line)
4336
- };
4944
+ if (current.parsed.errors.length < previous.parsed.errors.length) {
4945
+ return false;
4337
4946
  }
4338
- return {
4339
- enabled: option.enabled ?? true,
4340
- colors: option.colors ?? true,
4341
- logger: option.logger ?? ((line) => console.log(line))
4342
- };
4947
+ return buildSelfHealFailureFingerprint(previous) === buildSelfHealFailureFingerprint(current);
4948
+ }
4949
+ function buildSelfHealFailureFingerprint(attempt) {
4950
+ const issues = attempt.zodIssues.map((issue) => `${formatIssuePath(issue.path)}:${issue.code}:${normalizeWhitespace(issue.message)}`).sort().join("|");
4951
+ const errors = attempt.parsed.errors.map((error) => `${error.stage}:${error.candidateId ?? "-"}:${normalizeWhitespace(error.message)}`).sort().join("|");
4952
+ const source = normalizeWhitespace(resolveSelfHealSource(attempt).text).slice(0, 512);
4953
+ return [issues, errors, source].join("::");
4954
+ }
4955
+ function normalizeWhitespace(value) {
4956
+ return value.replace(RE_WHITESPACE2, " ").trim();
4343
4957
  }
4344
4958
  async function executeAttempt(adapter, input) {
4345
- const response = await callModel(adapter, {
4959
+ const response = await callModel2(adapter, {
4346
4960
  prompt: input.prompt,
4347
4961
  messages: input.messages,
4348
4962
  systemPrompt: input.systemPrompt,
@@ -4355,7 +4969,7 @@ async function executeAttempt(adapter, input) {
4355
4969
  selfHealEnabled: input.selfHealEnabled,
4356
4970
  timeout: input.timeout
4357
4971
  });
4358
- const parsed = parseWithObserve(response.text, input.schema, input.parseOptions, {
4972
+ const parsed = parseWithObserve(response.parseSource, input.schema, input.parseOptions, {
4359
4973
  observe: input.observe,
4360
4974
  attempt: input.attemptNumber,
4361
4975
  selfHeal: input.selfHeal
@@ -4364,8 +4978,8 @@ async function executeAttempt(adapter, input) {
4364
4978
  attempt: input.attemptNumber,
4365
4979
  selfHeal: input.selfHeal,
4366
4980
  via: response.via,
4367
- raw: response.text,
4368
- thinkBlocks: parsed.thinkBlocks,
4981
+ text: response.text,
4982
+ reasoning: response.reasoning,
4369
4983
  json: parsed.parsed,
4370
4984
  candidates: parsed.candidates.map((candidate) => candidate.content),
4371
4985
  repairLog: collectRepairLog(parsed),
@@ -4380,199 +4994,26 @@ async function executeAttempt(adapter, input) {
4380
4994
  trace
4381
4995
  };
4382
4996
  }
4383
- function withToolTimeout(client, toolTimeoutMs) {
4384
- return {
4385
- id: client.id,
4386
- listTools: client.listTools.bind(client),
4387
- close: client.close?.bind(client),
4388
- async callTool(params) {
4389
- let timeoutId;
4390
- const timeoutPromise = new Promise((_, reject) => {
4391
- timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
4392
- });
4393
- try {
4394
- return await Promise.race([client.callTool(params), timeoutPromise]);
4395
- } finally {
4396
- clearTimeout(timeoutId);
4397
- }
4398
- }
4399
- };
4400
- }
4401
- function applyToolTimeout(clients, toolTimeoutMs) {
4402
- return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
4403
- }
4404
- async function callModel(adapter, options) {
4405
- const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
4406
- const requestPayload = {
4407
- prompt: options.prompt,
4408
- messages: options.messages,
4409
- systemPrompt: options.systemPrompt,
4410
- temperature: options.request?.temperature,
4411
- maxTokens: options.request?.maxTokens,
4412
- mcpClients: options.request?.mcpClients,
4413
- toolChoice: options.request?.toolChoice,
4414
- parallelToolCalls: options.request?.parallelToolCalls,
4415
- maxToolRounds: options.request?.maxToolRounds,
4416
- onToolExecution: options.request?.onToolExecution,
4417
- transformToolOutput: options.request?.transformToolOutput,
4418
- transformToolArguments: options.request?.transformToolArguments,
4419
- transformToolCallParams: options.request?.transformToolCallParams,
4420
- unknownToolError: options.request?.unknownToolError,
4421
- toolDebug: options.request?.toolDebug,
4422
- body: options.request?.body,
4423
- signal: requestSignal
4424
- };
4425
- emitDebugRequest(options.debug, {
4426
- provider: adapter.provider,
4427
- model: adapter.model,
4428
- attempt: options.attempt,
4429
- selfHealAttempt: options.selfHeal,
4430
- selfHealEnabled: options.selfHealEnabled,
4431
- stream: options.stream.enabled && !!adapter.stream,
4432
- requestPayload
4433
- });
4434
- emitObserve(options.observe, {
4435
- stage: "llm.request",
4436
- attempt: options.attempt,
4437
- selfHeal: options.selfHeal,
4438
- message: "Sending LLM request.",
4439
- details: {
4440
- provider: adapter.provider,
4441
- model: adapter.model,
4442
- stream: options.stream.enabled && !!adapter.stream
4443
- }
4444
- });
4445
- if (options.stream.enabled && adapter.stream) {
4446
- let latestUsage;
4447
- let latestFinishReason;
4448
- let streamedRaw = "";
4449
- let sawToken = false;
4450
- let lastDataFingerprint;
4451
- const emitStreamingData = (raw, done, usage2, finishReason2) => {
4452
- const data = parseStreamingStructuredData(raw);
4453
- if (data === null && !done) {
4454
- return;
4455
- }
4456
- const fingerprint = toStreamDataFingerprint(data ?? null);
4457
- if (!done && fingerprint === lastDataFingerprint) {
4458
- return;
4459
- }
4460
- lastDataFingerprint = fingerprint;
4461
- options.stream.onData?.({
4462
- data: data ?? null,
4463
- raw,
4464
- done,
4465
- usage: usage2,
4466
- finishReason: finishReason2
4467
- });
4468
- emitObserve(options.observe, {
4469
- stage: "llm.stream.data",
4470
- attempt: options.attempt,
4471
- selfHeal: options.selfHeal,
4472
- message: done ? "Streaming structured data completed." : "Streaming structured data updated.",
4473
- details: {
4474
- done,
4475
- finishReason: finishReason2
4476
- }
4477
- });
4478
- };
4479
- const handleTextDelta = (delta) => {
4480
- if (!delta) {
4481
- return;
4482
- }
4483
- streamedRaw += delta;
4484
- if (options.stream.to === "stdout") {
4485
- process.stdout.write(delta);
4486
- }
4487
- emitObserve(options.observe, {
4488
- stage: "llm.stream.delta",
4489
- attempt: options.attempt,
4490
- selfHeal: options.selfHeal,
4491
- message: "Received stream delta.",
4492
- details: {
4493
- chars: delta.length
4494
- }
4495
- });
4496
- emitStreamingData(streamedRaw, false);
4497
- };
4498
- const response2 = await adapter.stream(requestPayload, {
4499
- onToken: (token) => {
4500
- sawToken = true;
4501
- handleTextDelta(token);
4502
- },
4503
- onChunk: (chunk) => {
4504
- if (!sawToken && chunk.textDelta) {
4505
- handleTextDelta(chunk.textDelta);
4506
- }
4507
- if (chunk.usage) {
4508
- latestUsage = preferLatestUsage(latestUsage, chunk.usage);
4509
- }
4510
- if (chunk.finishReason) {
4511
- latestFinishReason = chunk.finishReason;
4512
- }
4513
- }
4514
- });
4515
- const finalText = typeof response2.text === "string" && response2.text.length > 0 ? response2.text : streamedRaw;
4516
- const usage = preferLatestUsage(latestUsage, response2.usage);
4517
- const finishReason = response2.finishReason ?? latestFinishReason;
4518
- emitStreamingData(finalText, true, usage, finishReason);
4519
- emitObserve(options.observe, {
4520
- stage: "llm.response",
4997
+ async function callModel2(adapter, options) {
4998
+ return callModel(adapter, {
4999
+ ...options,
5000
+ buildEvent: ({ stage, message, details }) => ({
5001
+ stage,
4521
5002
  attempt: options.attempt,
4522
5003
  selfHeal: options.selfHeal,
4523
- message: "Streaming response completed.",
4524
- details: {
4525
- via: "stream",
4526
- chars: finalText.length,
4527
- finishReason
4528
- }
4529
- });
4530
- emitDebugResponse(options.debug, {
4531
- attempt: options.attempt,
4532
- selfHealAttempt: options.selfHeal,
4533
- selfHealEnabled: options.selfHealEnabled,
4534
- via: "stream",
4535
- responseText: finalText,
4536
- usage,
4537
- finishReason
4538
- });
4539
- return {
4540
- text: finalText,
4541
- via: "stream",
4542
- usage,
4543
- finishReason
4544
- };
4545
- }
4546
- const response = await adapter.complete(requestPayload);
4547
- emitObserve(options.observe, {
4548
- stage: "llm.response",
4549
- attempt: options.attempt,
4550
- selfHeal: options.selfHeal,
4551
- message: "Completion response received.",
4552
- details: {
4553
- via: "complete",
4554
- chars: response.text.length,
4555
- finishReason: response.finishReason
4556
- }
4557
- });
4558
- emitDebugResponse(options.debug, {
4559
- attempt: options.attempt,
4560
- selfHealAttempt: options.selfHeal,
4561
- selfHealEnabled: options.selfHealEnabled,
4562
- via: "complete",
4563
- responseText: response.text,
4564
- usage: response.usage,
4565
- finishReason: response.finishReason
5004
+ message,
5005
+ details
5006
+ }),
5007
+ buildSnapshot: (normalized) => ({
5008
+ text: normalized.text,
5009
+ reasoning: normalized.reasoning,
5010
+ data: parseStreamingStructuredData(normalized.parseSource) ?? null
5011
+ }),
5012
+ debugLabel: "structured"
4566
5013
  });
4567
- return {
4568
- text: response.text,
4569
- via: "complete",
4570
- usage: response.usage,
4571
- finishReason: response.finishReason
4572
- };
4573
5014
  }
4574
- function parseStreamingStructuredData(raw) {
4575
- const sanitized = sanitizeThink(raw);
5015
+ function parseStreamingStructuredData(parseSource) {
5016
+ const sanitized = sanitizeThink(parseSource);
4576
5017
  const start = findFirstJsonRootStart(sanitized.visibleText);
4577
5018
  if (start < 0) {
4578
5019
  return null;
@@ -4632,13 +5073,6 @@ function findFirstJsonRootStart(input) {
4632
5073
  }
4633
5074
  return Math.min(objectStart, arrayStart);
4634
5075
  }
4635
- function toStreamDataFingerprint(value) {
4636
- try {
4637
- return JSON.stringify(value);
4638
- } catch {
4639
- return "__unserializable__";
4640
- }
4641
- }
4642
5076
  function parseWithObserve(output, schema, parseOptions, context) {
4643
5077
  const userParseTrace = parseOptions.onTrace;
4644
5078
  return parseLLMOutput(output, schema, {
@@ -4668,38 +5102,20 @@ function buildSuccessResult(data, attempts) {
4668
5102
  const final = attempts.at(-1);
4669
5103
  return {
4670
5104
  data,
4671
- raw: final?.raw ?? "",
4672
- thinkBlocks: final?.thinkBlocks ?? [],
5105
+ text: final?.text ?? "",
5106
+ reasoning: final?.reasoning ?? "",
4673
5107
  json: final?.json ?? null,
4674
5108
  attempts,
4675
5109
  usage: aggregateUsage(attempts),
4676
5110
  finishReason: final?.finishReason
4677
5111
  };
4678
5112
  }
4679
- function aggregateUsage(attempts) {
4680
- let usage;
4681
- for (const attempt of attempts) {
4682
- usage = mergeUsage2(usage, attempt.usage);
4683
- }
4684
- return usage;
4685
- }
4686
- function mergeUsage2(base, next) {
4687
- if (!base && !next) {
4688
- return;
4689
- }
4690
- return {
4691
- inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
4692
- outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
4693
- totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
4694
- cost: (base?.cost ?? 0) + (next?.cost ?? 0)
4695
- };
4696
- }
4697
5113
  function toStructuredError(attempt) {
4698
5114
  if (!attempt) {
4699
5115
  return new StructuredParseError({
4700
5116
  message: "Structured parsing failed before any model response.",
4701
- raw: "",
4702
- thinkBlocks: [],
5117
+ text: "",
5118
+ reasoning: "",
4703
5119
  candidates: [],
4704
5120
  zodIssues: [],
4705
5121
  repairLog: [],
@@ -4707,8 +5123,8 @@ function toStructuredError(attempt) {
4707
5123
  });
4708
5124
  }
4709
5125
  return new StructuredParseError({
4710
- raw: attempt.raw,
4711
- thinkBlocks: attempt.thinkBlocks,
5126
+ text: attempt.text,
5127
+ reasoning: attempt.reasoning,
4712
5128
  candidates: attempt.candidates,
4713
5129
  zodIssues: attempt.zodIssues,
4714
5130
  repairLog: attempt.repairLog,
@@ -4718,59 +5134,6 @@ function toStructuredError(attempt) {
4718
5134
  function emitObserve(observe, event) {
4719
5135
  observe?.(event);
4720
5136
  }
4721
- function emitDebugRequest(config, input) {
4722
- const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4723
- const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4724
- const lines = [
4725
- color(config, title(config, [
4726
- "[structured][request]",
4727
- `attempt=${input.attempt}`,
4728
- `selfHealEnabled=${input.selfHealEnabled}`,
4729
- `selfHealAttempt=${input.selfHealAttempt}`
4730
- ].join(" ")), "cyan"),
4731
- dim(config, [
4732
- `provider=${input.provider ?? "unknown"}`,
4733
- `model=${input.model ?? "unknown"}`,
4734
- `stream=${input.stream}`
4735
- ].join(" ")),
4736
- color(config, "prompt:", "yellow"),
4737
- input.requestPayload.prompt ?? "(none)",
4738
- color(config, "messages:", "yellow"),
4739
- requestMessages,
4740
- color(config, "systemPrompt:", "yellow"),
4741
- input.requestPayload.systemPrompt ?? "(none)",
4742
- color(config, "request.body:", "yellow"),
4743
- requestBody
4744
- ];
4745
- emitDebug(config, lines.join(`
4746
- `));
4747
- }
4748
- function emitDebugResponse(config, input) {
4749
- const lines = [
4750
- color(config, title(config, [
4751
- "[structured][response]",
4752
- `attempt=${input.attempt}`,
4753
- `selfHealEnabled=${input.selfHealEnabled}`,
4754
- `selfHealAttempt=${input.selfHealAttempt}`
4755
- ].join(" ")), "green"),
4756
- dim(config, [
4757
- `via=${input.via}`,
4758
- `chars=${input.responseText.length}`,
4759
- `finishReason=${input.finishReason ?? "unknown"}`,
4760
- `usage=${JSON.stringify(input.usage ?? {})}`
4761
- ].join(" ")),
4762
- color(config, "text:", "yellow"),
4763
- input.responseText
4764
- ];
4765
- emitDebug(config, lines.join(`
4766
- `));
4767
- }
4768
- function emitDebug(config, message) {
4769
- if (!config.enabled) {
4770
- return;
4771
- }
4772
- config.logger(message);
4773
- }
4774
5137
 
4775
5138
  // src/llm.ts
4776
5139
  function createLLM(config, registry = createDefaultProviderRegistry()) {
@@ -4784,6 +5147,17 @@ function createLLM(config, registry = createDefaultProviderRegistry()) {
4784
5147
  const merged = mergeStructuredOptions(defaults, options);
4785
5148
  return structured(adapter, schema, prompt, merged);
4786
5149
  },
5150
+ async generate(promptOrOptions, options) {
5151
+ if (isGenerateOptions2(promptOrOptions)) {
5152
+ const merged2 = {
5153
+ ...mergeGenerateOptions(defaults, promptOrOptions),
5154
+ prompt: promptOrOptions.prompt
5155
+ };
5156
+ return generate(adapter, merged2);
5157
+ }
5158
+ const merged = mergeGenerateOptions(defaults, options);
5159
+ return generate(adapter, promptOrOptions, merged);
5160
+ },
4787
5161
  async embed(input, options = {}) {
4788
5162
  if (!adapter.embed) {
4789
5163
  throw new Error(`Provider "${adapter.provider ?? "unknown"}" does not support embeddings.`);
@@ -4813,6 +5187,26 @@ function mergeStructuredOptions(defaults, overrides) {
4813
5187
  timeout: mergeObjectLike(defaults?.timeout, overrides?.timeout)
4814
5188
  };
4815
5189
  }
5190
+ function mergeGenerateOptions(defaults, overrides) {
5191
+ if (!defaults && !overrides) {
5192
+ return {};
5193
+ }
5194
+ return {
5195
+ outdent: overrides?.outdent ?? defaults?.outdent,
5196
+ systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
5197
+ request: {
5198
+ ...defaults?.request ?? {},
5199
+ ...overrides?.request ?? {}
5200
+ },
5201
+ stream: mergeObjectLike(defaults?.stream, overrides?.stream),
5202
+ debug: mergeObjectLike(defaults?.debug, overrides?.debug),
5203
+ timeout: mergeObjectLike(defaults?.timeout, overrides?.timeout),
5204
+ observe: overrides?.observe ?? defaults?.observe
5205
+ };
5206
+ }
5207
+ function isGenerateOptions2(value) {
5208
+ return typeof value === "object" && value !== null && "prompt" in value;
5209
+ }
4816
5210
  function mergeObjectLike(defaults, overrides) {
4817
5211
  if (overrides === undefined) {
4818
5212
  return defaults;
@@ -5280,6 +5674,7 @@ export {
5280
5674
  inspectSchemaMetadata,
5281
5675
  inferSchemaExample,
5282
5676
  images,
5677
+ generate,
5283
5678
  formatZodIssues,
5284
5679
  formatPrompt,
5285
5680
  extractMarkdownCodeBlocks,