extrait 0.5.5 → 0.6.0

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.cjs CHANGED
@@ -52,6 +52,7 @@ __export(exports_src, {
52
52
  inspectSchemaMetadata: () => inspectSchemaMetadata,
53
53
  inferSchemaExample: () => inferSchemaExample,
54
54
  images: () => images,
55
+ generate: () => generate,
55
56
  formatZodIssues: () => formatZodIssues,
56
57
  formatPrompt: () => formatPrompt,
57
58
  extractMarkdownCodeBlocks: () => extractMarkdownCodeBlocks,
@@ -1317,6 +1318,17 @@ async function executeMCPToolCalls(calls, toolset, context) {
1317
1318
  remoteName: tool.remoteName,
1318
1319
  clientId: tool.clientId
1319
1320
  }) : rawArgs;
1321
+ const toolParams = context.request.transformToolCallParams ? await context.request.transformToolCallParams({
1322
+ name: tool.remoteName,
1323
+ arguments: args
1324
+ }, {
1325
+ name: toolName,
1326
+ remoteName: tool.remoteName,
1327
+ clientId: tool.clientId
1328
+ }) : {
1329
+ name: tool.remoteName,
1330
+ arguments: args
1331
+ };
1320
1332
  const metadata = {
1321
1333
  id: callId,
1322
1334
  type: call.type ?? "function",
@@ -1326,10 +1338,7 @@ async function executeMCPToolCalls(calls, toolset, context) {
1326
1338
  const startedAt = new Date().toISOString();
1327
1339
  const startedAtMs = Date.now();
1328
1340
  try {
1329
- const output = await tool.client.callTool({
1330
- name: tool.remoteName,
1331
- arguments: args
1332
- });
1341
+ const output = await tool.client.callTool(toolParams);
1333
1342
  const executionContext = {
1334
1343
  callId,
1335
1344
  type: call.type ?? "function",
@@ -1653,6 +1662,7 @@ function createOpenAICompatibleAdapter(options) {
1653
1662
  model: options.model,
1654
1663
  messages: buildMessages(request),
1655
1664
  temperature: request.temperature,
1665
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1656
1666
  max_tokens: request.maxTokens,
1657
1667
  stream: true
1658
1668
  })),
@@ -1664,6 +1674,7 @@ function createOpenAICompatibleAdapter(options) {
1664
1674
  }
1665
1675
  callbacks.onStart?.();
1666
1676
  let text = "";
1677
+ let reasoning = "";
1667
1678
  let usage;
1668
1679
  let finishReason;
1669
1680
  await consumeSSE(response, (data) => {
@@ -1675,6 +1686,7 @@ function createOpenAICompatibleAdapter(options) {
1675
1686
  return;
1676
1687
  }
1677
1688
  const delta = pickAssistantDelta(json);
1689
+ const reasoningDelta = pickAssistantReasoningDelta(json);
1678
1690
  const chunkUsage = pickUsage(json);
1679
1691
  const chunkFinishReason = pickFinishReason(json);
1680
1692
  usage = preferLatestUsage(usage, chunkUsage);
@@ -1685,9 +1697,13 @@ function createOpenAICompatibleAdapter(options) {
1685
1697
  text += delta;
1686
1698
  callbacks.onToken?.(delta);
1687
1699
  }
1688
- if (delta || chunkUsage || chunkFinishReason) {
1700
+ if (reasoningDelta) {
1701
+ reasoning += reasoningDelta;
1702
+ }
1703
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
1689
1704
  const chunk = {
1690
1705
  textDelta: delta,
1706
+ reasoningDelta: reasoningDelta || undefined,
1691
1707
  raw: json,
1692
1708
  usage: chunkUsage,
1693
1709
  finishReason: chunkFinishReason
@@ -1695,7 +1711,12 @@ function createOpenAICompatibleAdapter(options) {
1695
1711
  callbacks.onChunk?.(chunk);
1696
1712
  }
1697
1713
  });
1698
- const out = { text, usage, finishReason };
1714
+ const out = {
1715
+ text,
1716
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1717
+ usage,
1718
+ finishReason
1719
+ };
1699
1720
  callbacks.onComplete?.(out);
1700
1721
  return out;
1701
1722
  },
@@ -1755,6 +1776,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1755
1776
  model: options.model,
1756
1777
  messages: buildMessages(request),
1757
1778
  temperature: request.temperature,
1779
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1758
1780
  max_tokens: request.maxTokens,
1759
1781
  stream: false
1760
1782
  })),
@@ -1770,8 +1792,10 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1770
1792
  throw new Error("No assistant message in OpenAI-compatible response.");
1771
1793
  }
1772
1794
  const toolCalls = pickChatToolCalls(payload);
1795
+ const reasoning = pickAssistantReasoning(payload);
1773
1796
  return {
1774
1797
  text: pickAssistantText(payload),
1798
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1775
1799
  raw: payload,
1776
1800
  usage: pickUsage(payload),
1777
1801
  finishReason: pickFinishReason(payload),
@@ -1798,6 +1822,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1798
1822
  model: options.model,
1799
1823
  messages,
1800
1824
  temperature: request.temperature,
1825
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1801
1826
  max_tokens: request.maxTokens,
1802
1827
  tools: transportTools,
1803
1828
  tool_choice: request.toolChoice,
@@ -1819,8 +1844,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1819
1844
  throw new Error("No assistant message in OpenAI-compatible response.");
1820
1845
  }
1821
1846
  if (calledTools.length === 0) {
1847
+ const reasoning = pickAssistantReasoning(payload);
1822
1848
  return {
1823
1849
  text: pickAssistantText(payload),
1850
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1824
1851
  raw: payload,
1825
1852
  usage: aggregatedUsage,
1826
1853
  finishReason,
@@ -1848,6 +1875,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1848
1875
  }
1849
1876
  return {
1850
1877
  text: pickAssistantText(lastPayload ?? {}),
1878
+ reasoning: (() => {
1879
+ const value = pickAssistantReasoning(lastPayload ?? {});
1880
+ return value.length > 0 ? value : undefined;
1881
+ })(),
1851
1882
  raw: lastPayload,
1852
1883
  usage: aggregatedUsage,
1853
1884
  finishReason,
@@ -1867,6 +1898,7 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
1867
1898
  input: buildResponsesInput(request),
1868
1899
  previous_response_id: pickString(body?.previous_response_id),
1869
1900
  temperature: request.temperature,
1901
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1870
1902
  max_output_tokens: request.maxTokens
1871
1903
  })),
1872
1904
  signal: request.signal
@@ -1907,6 +1939,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1907
1939
  input,
1908
1940
  previous_response_id: previousResponseId,
1909
1941
  temperature: request.temperature,
1942
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1910
1943
  max_output_tokens: request.maxTokens,
1911
1944
  tools: transportTools,
1912
1945
  tool_choice: request.toolChoice,
@@ -1971,6 +2004,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1971
2004
  const executedToolCalls = [];
1972
2005
  const toolExecutions = [];
1973
2006
  callbacks.onStart?.();
2007
+ let lastRoundText = "";
2008
+ let lastRoundReasoning = "";
1974
2009
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1975
2010
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1976
2011
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -1983,6 +2018,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1983
2018
  model: options.model,
1984
2019
  messages,
1985
2020
  temperature: request.temperature,
2021
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1986
2022
  max_tokens: request.maxTokens,
1987
2023
  tools: transportTools,
1988
2024
  tool_choice: request.toolChoice,
@@ -1996,9 +2032,11 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1996
2032
  throw new Error(`HTTP ${response.status}: ${message}`);
1997
2033
  }
1998
2034
  let roundText = "";
2035
+ let roundReasoning = "";
1999
2036
  let roundUsage;
2000
2037
  let roundFinishReason;
2001
2038
  const streamedToolCalls = new Map;
2039
+ let reasoningFieldName;
2002
2040
  await consumeSSE(response, (data) => {
2003
2041
  if (data === "[DONE]") {
2004
2042
  return;
@@ -2009,6 +2047,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2009
2047
  }
2010
2048
  lastPayload = json;
2011
2049
  const delta = pickAssistantDelta(json);
2050
+ const reasoningDelta = pickAssistantReasoningDelta(json);
2012
2051
  const chunkUsage = pickUsage(json);
2013
2052
  const chunkFinishReason = pickFinishReason(json);
2014
2053
  collectOpenAIStreamToolCalls(json, streamedToolCalls);
@@ -2020,9 +2059,14 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2020
2059
  roundText += delta;
2021
2060
  callbacks.onToken?.(delta);
2022
2061
  }
2023
- if (delta || chunkUsage || chunkFinishReason) {
2062
+ if (reasoningDelta) {
2063
+ roundReasoning += reasoningDelta;
2064
+ reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
2065
+ }
2066
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2024
2067
  const chunk = {
2025
2068
  textDelta: delta,
2069
+ reasoningDelta: reasoningDelta || undefined,
2026
2070
  raw: json,
2027
2071
  usage: chunkUsage,
2028
2072
  finishReason: chunkFinishReason
@@ -2038,6 +2082,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2038
2082
  if (calledTools.length === 0) {
2039
2083
  const out2 = {
2040
2084
  text: roundText,
2085
+ reasoning: roundReasoning.length > 0 ? roundReasoning : undefined,
2041
2086
  raw: lastPayload,
2042
2087
  usage: aggregatedUsage,
2043
2088
  finishReason,
@@ -2058,7 +2103,12 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2058
2103
  });
2059
2104
  executedToolCalls.push(...outputs.map((entry) => entry.call));
2060
2105
  toolExecutions.push(...outputs.map((entry) => entry.execution));
2061
- const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools);
2106
+ lastRoundText = roundText;
2107
+ lastRoundReasoning = roundReasoning;
2108
+ const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
2109
+ reasoning: roundReasoning,
2110
+ reasoningFieldName
2111
+ });
2062
2112
  const toolMessages = outputs.map((entry) => ({
2063
2113
  role: "tool",
2064
2114
  tool_call_id: entry.call.id,
@@ -2067,7 +2117,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2067
2117
  messages = [...messages, assistantMessage, ...toolMessages];
2068
2118
  }
2069
2119
  const out = {
2070
- text: "",
2120
+ text: lastRoundText,
2121
+ reasoning: lastRoundReasoning.length > 0 ? lastRoundReasoning : undefined,
2071
2122
  raw: lastPayload,
2072
2123
  usage: aggregatedUsage,
2073
2124
  finishReason,
@@ -2089,6 +2140,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
2089
2140
  input: buildResponsesInput(request),
2090
2141
  previous_response_id: pickString(body?.previous_response_id),
2091
2142
  temperature: request.temperature,
2143
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2092
2144
  max_output_tokens: request.maxTokens,
2093
2145
  stream: true
2094
2146
  })),
@@ -2169,6 +2221,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2169
2221
  input,
2170
2222
  previous_response_id: previousResponseId,
2171
2223
  temperature: request.temperature,
2224
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2172
2225
  max_output_tokens: request.maxTokens,
2173
2226
  tools: transportTools,
2174
2227
  tool_choice: request.toolChoice,
@@ -2335,6 +2388,12 @@ function toResponsesTools(tools) {
2335
2388
  return { ...tool };
2336
2389
  });
2337
2390
  }
2391
+ function toOpenAIReasoningEffort(value) {
2392
+ if (!value) {
2393
+ return;
2394
+ }
2395
+ return value === "max" ? "xhigh" : value;
2396
+ }
2338
2397
  function pickChatToolCalls(payload) {
2339
2398
  const message = pickAssistantMessage(payload);
2340
2399
  if (!message) {
@@ -2416,20 +2475,50 @@ function pickAssistantDelta(payload) {
2416
2475
  if (!isRecord2(delta)) {
2417
2476
  return "";
2418
2477
  }
2419
- const content = delta.content;
2420
- if (typeof content === "string") {
2421
- return content;
2478
+ return pickTextFromOpenAIContent(delta.content);
2479
+ }
2480
+ function pickAssistantReasoning(payload) {
2481
+ const message = pickAssistantMessage(payload);
2482
+ if (!message) {
2483
+ return "";
2422
2484
  }
2423
- if (Array.isArray(content)) {
2424
- return content.map((part) => {
2425
- if (!isRecord2(part)) {
2426
- return "";
2427
- }
2428
- const text = part.text;
2429
- return typeof text === "string" ? text : "";
2430
- }).join("");
2485
+ return pickReasoningText(message);
2486
+ }
2487
+ function pickAssistantReasoningDelta(payload) {
2488
+ const choices = payload.choices;
2489
+ if (!Array.isArray(choices) || choices.length === 0) {
2490
+ return "";
2431
2491
  }
2432
- return "";
2492
+ const first = choices[0];
2493
+ if (!isRecord2(first)) {
2494
+ return "";
2495
+ }
2496
+ const delta = first.delta;
2497
+ if (!isRecord2(delta)) {
2498
+ return "";
2499
+ }
2500
+ return pickReasoningText(delta);
2501
+ }
2502
+ function pickAssistantReasoningDeltaFieldName(payload) {
2503
+ const choices = payload.choices;
2504
+ if (!Array.isArray(choices) || choices.length === 0) {
2505
+ return;
2506
+ }
2507
+ const first = choices[0];
2508
+ if (!isRecord2(first)) {
2509
+ return;
2510
+ }
2511
+ const delta = first.delta;
2512
+ if (!isRecord2(delta)) {
2513
+ return;
2514
+ }
2515
+ if (hasTextLikeValue(delta.reasoning)) {
2516
+ return "reasoning";
2517
+ }
2518
+ if (hasTextLikeValue(delta.reasoning_content)) {
2519
+ return "reasoning_content";
2520
+ }
2521
+ return;
2433
2522
  }
2434
2523
  function collectOpenAIStreamToolCalls(payload, state) {
2435
2524
  const choices = payload.choices;
@@ -2478,8 +2567,8 @@ function buildOpenAIStreamToolCalls(state) {
2478
2567
  arguments: entry.argumentsText.length > 0 ? entry.argumentsText : {}
2479
2568
  }));
2480
2569
  }
2481
- function buildOpenAIAssistantToolMessage(text, toolCalls) {
2482
- return {
2570
+ function buildOpenAIAssistantToolMessage(text, toolCalls, reasoning) {
2571
+ const message = {
2483
2572
  role: "assistant",
2484
2573
  content: text,
2485
2574
  tool_calls: toolCalls.map((call) => ({
@@ -2491,6 +2580,10 @@ function buildOpenAIAssistantToolMessage(text, toolCalls) {
2491
2580
  }
2492
2581
  }))
2493
2582
  };
2583
+ if (reasoning?.reasoning && reasoning.reasoning.length > 0) {
2584
+ message[reasoning.reasoningFieldName ?? "reasoning"] = reasoning.reasoning;
2585
+ }
2586
+ return message;
2494
2587
  }
2495
2588
  function pickResponsesStreamPayload(payload) {
2496
2589
  if (isRecord2(payload.response)) {
@@ -2652,21 +2745,9 @@ function pickResponsesText(payload) {
2652
2745
  function pickAssistantText(payload) {
2653
2746
  const message = pickAssistantMessage(payload);
2654
2747
  if (message) {
2655
- const content = message.content;
2656
- if (typeof content === "string") {
2657
- return content;
2658
- }
2659
- if (Array.isArray(content)) {
2660
- return content.map((part) => {
2661
- if (typeof part === "string") {
2662
- return part;
2663
- }
2664
- if (!isRecord2(part)) {
2665
- return "";
2666
- }
2667
- const text = part.text;
2668
- return typeof text === "string" ? text : "";
2669
- }).join("");
2748
+ const text = pickTextFromOpenAIContent(message.content);
2749
+ if (text.length > 0) {
2750
+ return text;
2670
2751
  }
2671
2752
  }
2672
2753
  const choices = payload.choices;
@@ -2678,6 +2759,36 @@ function pickAssistantText(payload) {
2678
2759
  }
2679
2760
  return "";
2680
2761
  }
2762
+ function pickReasoningText(value) {
2763
+ return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
2764
+ }
2765
+ function pickTextFromOpenAIContent(value) {
2766
+ return pickTextLike(value);
2767
+ }
2768
+ function pickTextLike(value) {
2769
+ if (typeof value === "string") {
2770
+ return value;
2771
+ }
2772
+ if (Array.isArray(value)) {
2773
+ return value.map((part) => pickTextLikePart(part)).join("");
2774
+ }
2775
+ if (!isRecord2(value)) {
2776
+ return "";
2777
+ }
2778
+ return pickTextLikePart(value);
2779
+ }
2780
+ function pickTextLikePart(value) {
2781
+ if (typeof value === "string") {
2782
+ return value;
2783
+ }
2784
+ if (!isRecord2(value)) {
2785
+ return "";
2786
+ }
2787
+ 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("") : "");
2788
+ }
2789
+ function hasTextLikeValue(value) {
2790
+ return pickTextLike(value).length > 0;
2791
+ }
2681
2792
  function pickUsage(payload) {
2682
2793
  const usage = payload.usage;
2683
2794
  if (!isRecord2(usage)) {
@@ -2731,14 +2842,13 @@ function createAnthropicCompatibleAdapter(options) {
2731
2842
  const response = await fetcher(buildURL(options.baseURL, path), {
2732
2843
  method: "POST",
2733
2844
  headers: buildHeaders2(options),
2734
- body: JSON.stringify(cleanUndefined({
2845
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2735
2846
  ...options.defaultBody,
2736
2847
  ...request.body,
2737
2848
  model: options.model,
2738
2849
  system: input.systemPrompt,
2739
2850
  messages: input.messages,
2740
2851
  temperature: request.temperature,
2741
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2742
2852
  stream: true
2743
2853
  })),
2744
2854
  signal: request.signal
@@ -2794,14 +2904,13 @@ async function completePassThrough(options, fetcher, path, request) {
2794
2904
  const response = await fetcher(buildURL(options.baseURL, path), {
2795
2905
  method: "POST",
2796
2906
  headers: buildHeaders2(options),
2797
- body: JSON.stringify(cleanUndefined({
2907
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2798
2908
  ...options.defaultBody,
2799
2909
  ...request.body,
2800
2910
  model: options.model,
2801
2911
  system: input.systemPrompt,
2802
2912
  messages: input.messages,
2803
2913
  temperature: request.temperature,
2804
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2805
2914
  stream: false
2806
2915
  })),
2807
2916
  signal: request.signal
@@ -2839,14 +2948,13 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2839
2948
  const response = await fetcher(buildURL(options.baseURL, path), {
2840
2949
  method: "POST",
2841
2950
  headers: buildHeaders2(options),
2842
- body: JSON.stringify(cleanUndefined({
2951
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2843
2952
  ...options.defaultBody,
2844
2953
  ...request.body,
2845
2954
  model: options.model,
2846
2955
  system: input.systemPrompt,
2847
2956
  messages,
2848
2957
  temperature: request.temperature,
2849
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2850
2958
  tools,
2851
2959
  tool_choice: toAnthropicToolChoice(request.toolChoice),
2852
2960
  stream: false
@@ -2924,14 +3032,13 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2924
3032
  const response = await fetcher(buildURL(options.baseURL, path), {
2925
3033
  method: "POST",
2926
3034
  headers: buildHeaders2(options),
2927
- body: JSON.stringify(cleanUndefined({
3035
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2928
3036
  ...options.defaultBody,
2929
3037
  ...request.body,
2930
3038
  model: options.model,
2931
3039
  system: input.systemPrompt,
2932
3040
  messages,
2933
3041
  temperature: request.temperature,
2934
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2935
3042
  tools,
2936
3043
  tool_choice: toAnthropicToolChoice(request.toolChoice),
2937
3044
  stream: true
@@ -3039,6 +3146,21 @@ function buildHeaders2(options) {
3039
3146
  ...options.headers
3040
3147
  };
3041
3148
  }
3149
+ function buildAnthropicRequestBody(options, request, body) {
3150
+ const bodyOutputConfig = isRecord2(body.output_config) ? body.output_config : undefined;
3151
+ const bodyThinking = body.thinking;
3152
+ const hasExplicitThinking = Object.prototype.hasOwnProperty.call(body, "thinking");
3153
+ const reasoningEffort = request.reasoningEffort;
3154
+ return cleanUndefined({
3155
+ ...body,
3156
+ max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
3157
+ output_config: reasoningEffort ? cleanUndefined({
3158
+ ...bodyOutputConfig,
3159
+ effort: reasoningEffort
3160
+ }) : bodyOutputConfig,
3161
+ thinking: reasoningEffort ? hasExplicitThinking ? bodyThinking : { type: "adaptive" } : bodyThinking
3162
+ });
3163
+ }
3042
3164
  function resolveAnthropicInput(request) {
3043
3165
  if (Array.isArray(request.messages) && request.messages.length > 0) {
3044
3166
  return toAnthropicInput(request.messages);
@@ -3393,8 +3515,34 @@ function buildProviderOptions(config) {
3393
3515
  };
3394
3516
  }
3395
3517
 
3396
- // src/structured.ts
3397
- var import_jsonrepair3 = require("jsonrepair");
3518
+ // src/utils/debug-colors.ts
3519
+ var ANSI = {
3520
+ reset: "\x1B[0m",
3521
+ bold: "\x1B[1m",
3522
+ cyan: "\x1B[36m",
3523
+ yellow: "\x1B[33m",
3524
+ green: "\x1B[32m",
3525
+ red: "\x1B[31m",
3526
+ dim: "\x1B[2m"
3527
+ };
3528
+ function color(config, text, tone) {
3529
+ if (!config.colors) {
3530
+ return text;
3531
+ }
3532
+ return `${ANSI[tone]}${text}${ANSI.reset}`;
3533
+ }
3534
+ function dim(config, text) {
3535
+ if (!config.colors) {
3536
+ return text;
3537
+ }
3538
+ return `${ANSI.dim}${text}${ANSI.reset}`;
3539
+ }
3540
+ function title(config, text) {
3541
+ if (!config.colors) {
3542
+ return text;
3543
+ }
3544
+ return `${ANSI.bold}${text}${ANSI.reset}`;
3545
+ }
3398
3546
 
3399
3547
  // src/outdent.ts
3400
3548
  var DEFAULT_OPTIONS = {
@@ -3533,39 +3681,598 @@ function createOutdent(options = {}) {
3533
3681
  return outdent;
3534
3682
  }
3535
3683
 
3536
- // src/parse.ts
3537
- var import_jsonrepair2 = require("jsonrepair");
3538
- function parseLLMOutput(output, schema, options = {}) {
3539
- const sanitized = sanitizeThink(output);
3540
- const parseOptions = {
3541
- repair: options.repair ?? true,
3542
- maxCandidates: options.maxCandidates ?? 5,
3543
- acceptArrays: options.acceptArrays ?? true,
3544
- extraction: options.extraction,
3545
- onTrace: options.onTrace
3684
+ // src/generate-shared.ts
3685
+ var sharedOutdent = createOutdent({
3686
+ trimLeadingNewline: true,
3687
+ trimTrailingNewline: true,
3688
+ newline: `
3689
+ `
3690
+ });
3691
+ var RE_THINK_TAGS = /<\/?think\s*>/gi;
3692
+ function resolvePrompt(prompt, context) {
3693
+ const resolved = typeof prompt === "function" ? prompt(context) : prompt;
3694
+ return normalizePromptValue(resolved, context);
3695
+ }
3696
+ function normalizePromptValue(value, _context) {
3697
+ if (typeof value === "string") {
3698
+ return {
3699
+ prompt: value
3700
+ };
3701
+ }
3702
+ if (isPromptResolver(value)) {
3703
+ return normalizePromptPayload(value.resolvePrompt(_context));
3704
+ }
3705
+ return normalizePromptPayload(value);
3706
+ }
3707
+ function normalizePromptPayload(value) {
3708
+ const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
3709
+ const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
3710
+ if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
3711
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
3712
+ }
3713
+ return {
3714
+ prompt,
3715
+ systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
3716
+ messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
3546
3717
  };
3547
- const candidates = extractJsonCandidates(sanitized.visibleText, {
3548
- maxCandidates: parseOptions.maxCandidates,
3549
- acceptArrays: parseOptions.acceptArrays,
3550
- allowRepairHints: parseOptions.repair,
3551
- heuristics: parseOptions.extraction
3552
- });
3553
- emitTrace(parseOptions.onTrace, {
3554
- stage: "extract",
3555
- level: "info",
3556
- message: `Extracted ${candidates.length} candidate(s).`,
3557
- details: {
3558
- maxCandidates: parseOptions.maxCandidates,
3559
- thinkBlocks: sanitized.thinkBlocks.length,
3560
- thinkDiagnostics: sanitized.diagnostics
3718
+ }
3719
+ function applyPromptOutdent(payload, enabled) {
3720
+ if (!enabled) {
3721
+ return payload;
3722
+ }
3723
+ return {
3724
+ prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
3725
+ systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
3726
+ messages: payload.messages?.map((message) => ({
3727
+ ...message,
3728
+ content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
3729
+ }))
3730
+ };
3731
+ }
3732
+ function applyOutdentToOptionalPrompt(value, enabled) {
3733
+ if (!enabled || typeof value !== "string") {
3734
+ return value;
3735
+ }
3736
+ return sharedOutdent.string(value);
3737
+ }
3738
+ function mergeSystemPrompts(primary, secondary) {
3739
+ const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
3740
+ if (prompts.length === 0) {
3741
+ return;
3742
+ }
3743
+ return prompts.join(`
3744
+
3745
+ `);
3746
+ }
3747
+ function normalizeStreamConfig(option) {
3748
+ if (typeof option === "boolean") {
3749
+ return {
3750
+ enabled: option
3751
+ };
3752
+ }
3753
+ if (!option) {
3754
+ return {
3755
+ enabled: false
3756
+ };
3757
+ }
3758
+ return {
3759
+ enabled: option.enabled ?? true,
3760
+ onData: option.onData,
3761
+ to: option.to
3762
+ };
3763
+ }
3764
+ function normalizeDebugConfig(option) {
3765
+ if (typeof option === "boolean") {
3766
+ return {
3767
+ enabled: option,
3768
+ colors: true,
3769
+ verbose: false,
3770
+ logger: (line) => console.log(line)
3771
+ };
3772
+ }
3773
+ if (!option) {
3774
+ return {
3775
+ enabled: false,
3776
+ colors: true,
3777
+ verbose: false,
3778
+ logger: (line) => console.log(line)
3779
+ };
3780
+ }
3781
+ return {
3782
+ enabled: option.enabled ?? true,
3783
+ colors: option.colors ?? true,
3784
+ verbose: option.verbose ?? false,
3785
+ logger: option.logger ?? ((line) => console.log(line))
3786
+ };
3787
+ }
3788
+ function withToolTimeout(client, toolTimeoutMs) {
3789
+ return {
3790
+ id: client.id,
3791
+ listTools: client.listTools.bind(client),
3792
+ close: client.close?.bind(client),
3793
+ async callTool(params) {
3794
+ let timeoutId;
3795
+ const timeoutPromise = new Promise((_, reject) => {
3796
+ timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
3797
+ });
3798
+ try {
3799
+ return await Promise.race([client.callTool(params), timeoutPromise]);
3800
+ } finally {
3801
+ clearTimeout(timeoutId);
3802
+ }
3561
3803
  }
3562
- });
3563
- const errors = [];
3564
- const diagnostics = [];
3565
- let bestIssues = [];
3566
- let bestCandidate = candidates[0] ?? null;
3567
- let bestParsed = null;
3568
- let bestRepaired = null;
3804
+ };
3805
+ }
3806
+ function applyToolTimeout(clients, toolTimeoutMs) {
3807
+ return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
3808
+ }
3809
+ async function callModel(adapter, options) {
3810
+ const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
3811
+ const requestPayload = {
3812
+ prompt: options.prompt,
3813
+ messages: options.messages,
3814
+ systemPrompt: options.systemPrompt,
3815
+ temperature: options.request?.temperature,
3816
+ reasoningEffort: options.request?.reasoningEffort,
3817
+ maxTokens: options.request?.maxTokens,
3818
+ mcpClients: options.request?.mcpClients,
3819
+ toolChoice: options.request?.toolChoice,
3820
+ parallelToolCalls: options.request?.parallelToolCalls,
3821
+ maxToolRounds: options.request?.maxToolRounds,
3822
+ onToolExecution: options.request?.onToolExecution,
3823
+ transformToolOutput: options.request?.transformToolOutput,
3824
+ transformToolArguments: options.request?.transformToolArguments,
3825
+ transformToolCallParams: options.request?.transformToolCallParams,
3826
+ unknownToolError: options.request?.unknownToolError,
3827
+ toolDebug: options.request?.toolDebug,
3828
+ body: options.request?.body,
3829
+ signal: requestSignal
3830
+ };
3831
+ emitDebugRequest(options.debug, {
3832
+ label: options.debugLabel,
3833
+ provider: adapter.provider,
3834
+ model: adapter.model,
3835
+ attempt: options.attempt,
3836
+ selfHealAttempt: options.selfHeal,
3837
+ selfHealEnabled: options.selfHealEnabled,
3838
+ stream: options.stream.enabled && !!adapter.stream,
3839
+ requestPayload
3840
+ });
3841
+ options.observe?.(options.buildEvent({
3842
+ stage: "llm.request",
3843
+ message: "Sending LLM request.",
3844
+ details: {
3845
+ provider: adapter.provider,
3846
+ model: adapter.model,
3847
+ stream: options.stream.enabled && !!adapter.stream
3848
+ }
3849
+ }));
3850
+ if (options.stream.enabled && adapter.stream) {
3851
+ let latestUsage;
3852
+ let latestFinishReason;
3853
+ let streamedProviderText = "";
3854
+ let streamedDedicatedReasoning = "";
3855
+ let lastSnapshotFingerprint;
3856
+ let previousSnapshotText = "";
3857
+ let previousSnapshotReasoning = "";
3858
+ const emitStreamingData = (done, usage2, finishReason2) => {
3859
+ const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
3860
+ const snapshot = options.buildSnapshot(normalized2);
3861
+ const fingerprint = toStreamDataFingerprint(snapshot);
3862
+ if (!done && fingerprint === lastSnapshotFingerprint) {
3863
+ return;
3864
+ }
3865
+ const delta = {
3866
+ text: normalized2.text.startsWith(previousSnapshotText) ? normalized2.text.slice(previousSnapshotText.length) : "",
3867
+ reasoning: normalized2.reasoning.startsWith(previousSnapshotReasoning) ? normalized2.reasoning.slice(previousSnapshotReasoning.length) : ""
3868
+ };
3869
+ lastSnapshotFingerprint = fingerprint;
3870
+ previousSnapshotText = normalized2.text;
3871
+ previousSnapshotReasoning = normalized2.reasoning;
3872
+ options.stream.onData?.({
3873
+ delta,
3874
+ snapshot,
3875
+ done,
3876
+ usage: usage2,
3877
+ finishReason: finishReason2
3878
+ });
3879
+ if (options.stream.to === "stdout" && delta.text) {
3880
+ process.stdout.write(delta.text);
3881
+ }
3882
+ options.observe?.(options.buildEvent({
3883
+ stage: "llm.stream.data",
3884
+ message: done ? "Streaming response completed." : "Streaming response updated.",
3885
+ details: {
3886
+ done,
3887
+ finishReason: finishReason2
3888
+ }
3889
+ }));
3890
+ };
3891
+ const handleTextDelta = (delta) => {
3892
+ if (!delta) {
3893
+ return;
3894
+ }
3895
+ streamedProviderText += delta;
3896
+ options.observe?.(options.buildEvent({
3897
+ stage: "llm.stream.delta",
3898
+ message: "Received stream delta.",
3899
+ details: {
3900
+ chars: delta.length
3901
+ }
3902
+ }));
3903
+ emitStreamingData(false);
3904
+ };
3905
+ const handleReasoningDelta = (delta) => {
3906
+ if (!delta) {
3907
+ return;
3908
+ }
3909
+ streamedDedicatedReasoning += delta;
3910
+ emitStreamingData(false);
3911
+ };
3912
+ const response2 = await adapter.stream(requestPayload, {
3913
+ onChunk: (chunk) => {
3914
+ if (chunk.textDelta) {
3915
+ handleTextDelta(chunk.textDelta);
3916
+ }
3917
+ if (chunk.reasoningDelta) {
3918
+ handleReasoningDelta(chunk.reasoningDelta);
3919
+ }
3920
+ if (chunk.usage) {
3921
+ latestUsage = preferLatestUsage(latestUsage, chunk.usage);
3922
+ }
3923
+ if (chunk.finishReason) {
3924
+ latestFinishReason = chunk.finishReason;
3925
+ }
3926
+ }
3927
+ });
3928
+ streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
3929
+ streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
3930
+ const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
3931
+ const usage = preferLatestUsage(latestUsage, response2.usage);
3932
+ const finishReason = response2.finishReason ?? latestFinishReason;
3933
+ emitStreamingData(true, usage, finishReason);
3934
+ options.observe?.(options.buildEvent({
3935
+ stage: "llm.response",
3936
+ message: "Streaming response completed.",
3937
+ details: {
3938
+ via: "stream",
3939
+ chars: finalNormalized.parseSource.length,
3940
+ 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: "stream",
3949
+ text: finalNormalized.text,
3950
+ reasoning: finalNormalized.reasoning,
3951
+ parseSource: finalNormalized.parseSource,
3952
+ usage,
3953
+ finishReason
3954
+ });
3955
+ return {
3956
+ text: finalNormalized.text,
3957
+ reasoning: finalNormalized.reasoning,
3958
+ thinkBlocks: finalNormalized.thinkBlocks,
3959
+ parseSource: finalNormalized.parseSource,
3960
+ via: "stream",
3961
+ usage,
3962
+ finishReason
3963
+ };
3964
+ }
3965
+ const response = await adapter.complete(requestPayload);
3966
+ const normalized = normalizeModelOutput(response.text, response.reasoning);
3967
+ options.observe?.(options.buildEvent({
3968
+ stage: "llm.response",
3969
+ message: "Completion response received.",
3970
+ details: {
3971
+ via: "complete",
3972
+ chars: normalized.parseSource.length,
3973
+ finishReason: response.finishReason
3974
+ }
3975
+ }));
3976
+ emitDebugResponse(options.debug, {
3977
+ label: options.debugLabel,
3978
+ attempt: options.attempt,
3979
+ selfHealAttempt: options.selfHeal,
3980
+ selfHealEnabled: options.selfHealEnabled,
3981
+ via: "complete",
3982
+ text: normalized.text,
3983
+ reasoning: normalized.reasoning,
3984
+ parseSource: normalized.parseSource,
3985
+ usage: response.usage,
3986
+ finishReason: response.finishReason
3987
+ });
3988
+ return {
3989
+ text: normalized.text,
3990
+ reasoning: normalized.reasoning,
3991
+ thinkBlocks: normalized.thinkBlocks,
3992
+ parseSource: normalized.parseSource,
3993
+ via: "complete",
3994
+ usage: response.usage,
3995
+ finishReason: response.finishReason
3996
+ };
3997
+ }
3998
+ function normalizeModelOutput(text, dedicatedReasoning) {
3999
+ const sanitized = sanitizeThink(text);
4000
+ const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
4001
+ const reasoning = joinReasoningSegments([
4002
+ dedicatedReasoning,
4003
+ ...sanitized.thinkBlocks.map((block) => block.content)
4004
+ ]);
4005
+ return {
4006
+ text: visibleText,
4007
+ reasoning,
4008
+ thinkBlocks: sanitized.thinkBlocks,
4009
+ parseSource: composeParseSource(visibleText, reasoning)
4010
+ };
4011
+ }
4012
+ function composeParseSource(text, reasoning) {
4013
+ if (typeof reasoning !== "string" || reasoning.length === 0) {
4014
+ return text;
4015
+ }
4016
+ const sanitized = reasoning.replace(RE_THINK_TAGS, "");
4017
+ if (sanitized.length === 0) {
4018
+ return text;
4019
+ }
4020
+ return `<think>${sanitized}</think>${text}`;
4021
+ }
4022
+ function aggregateUsage(attempts) {
4023
+ let usage;
4024
+ for (const attempt of attempts) {
4025
+ usage = mergeUsage2(usage, attempt.usage);
4026
+ }
4027
+ return usage;
4028
+ }
4029
+ function mergeUsage2(base, next) {
4030
+ if (!base && !next) {
4031
+ return;
4032
+ }
4033
+ return {
4034
+ inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
4035
+ outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
4036
+ totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
4037
+ cost: (base?.cost ?? 0) + (next?.cost ?? 0)
4038
+ };
4039
+ }
4040
+ function isPromptResolver(value) {
4041
+ return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
4042
+ }
4043
+ function isLLMMessage(value) {
4044
+ if (typeof value !== "object" || value === null) {
4045
+ return false;
4046
+ }
4047
+ const candidate = value;
4048
+ if (candidate.role !== "system" && candidate.role !== "user" && candidate.role !== "assistant" && candidate.role !== "tool") {
4049
+ return false;
4050
+ }
4051
+ return "content" in candidate;
4052
+ }
4053
+ function joinReasoningSegments(parts) {
4054
+ return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
4055
+
4056
+ `);
4057
+ }
4058
+ function stripThinkBlocks(text, thinkBlocks) {
4059
+ if (thinkBlocks.length === 0) {
4060
+ return text;
4061
+ }
4062
+ let output = "";
4063
+ let cursor = 0;
4064
+ for (const block of thinkBlocks) {
4065
+ output += text.slice(cursor, block.start);
4066
+ cursor = block.end;
4067
+ }
4068
+ output += text.slice(cursor);
4069
+ return output;
4070
+ }
4071
+ function toStreamDataFingerprint(value) {
4072
+ try {
4073
+ return JSON.stringify(value);
4074
+ } catch {
4075
+ return "__unserializable__";
4076
+ }
4077
+ }
4078
+ function emitDebugRequest(config, input) {
4079
+ const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4080
+ const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4081
+ const lines = [
4082
+ color(config, title(config, [
4083
+ `[${input.label}][request]`,
4084
+ `attempt=${input.attempt}`,
4085
+ `selfHealEnabled=${input.selfHealEnabled}`,
4086
+ `selfHealAttempt=${input.selfHealAttempt}`
4087
+ ].join(" ")), "cyan"),
4088
+ dim(config, [
4089
+ `provider=${input.provider ?? "unknown"}`,
4090
+ `model=${input.model ?? "unknown"}`,
4091
+ `stream=${input.stream}`
4092
+ ].join(" ")),
4093
+ color(config, "prompt:", "yellow"),
4094
+ input.requestPayload.prompt ?? "(none)",
4095
+ color(config, "messages:", "yellow"),
4096
+ requestMessages,
4097
+ color(config, "systemPrompt:", "yellow"),
4098
+ input.requestPayload.systemPrompt ?? "(none)",
4099
+ color(config, "request.body:", "yellow"),
4100
+ requestBody
4101
+ ];
4102
+ emitDebug(config, lines.join(`
4103
+ `));
4104
+ }
4105
+ function emitDebugResponse(config, input) {
4106
+ const text = input.text.length > 0 ? input.text : "(none)";
4107
+ const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
4108
+ const metadata = [
4109
+ `via=${input.via}`,
4110
+ `textChars=${input.text.length}`,
4111
+ `reasoningChars=${input.reasoning.length}`
4112
+ ];
4113
+ if (config.verbose) {
4114
+ metadata.push(`parseSourceChars=${input.parseSource.length}`);
4115
+ }
4116
+ metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
4117
+ const lines = [
4118
+ color(config, title(config, [
4119
+ `[${input.label}][response]`,
4120
+ `attempt=${input.attempt}`,
4121
+ `selfHealEnabled=${input.selfHealEnabled}`,
4122
+ `selfHealAttempt=${input.selfHealAttempt}`
4123
+ ].join(" ")), "green"),
4124
+ dim(config, metadata.join(" ")),
4125
+ color(config, "text:", "yellow"),
4126
+ text,
4127
+ color(config, "reasoning:", "yellow"),
4128
+ reasoning
4129
+ ];
4130
+ if (config.verbose) {
4131
+ lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
4132
+ }
4133
+ emitDebug(config, lines.join(`
4134
+ `));
4135
+ }
4136
+ function emitDebug(config, message) {
4137
+ if (!config.enabled) {
4138
+ return;
4139
+ }
4140
+ config.logger(message);
4141
+ }
4142
+
4143
+ // src/generate.ts
4144
+ async function generate(adapter, promptOrOptions, callOptions) {
4145
+ const normalized = normalizeGenerateInput(promptOrOptions, callOptions);
4146
+ const useOutdent = normalized.outdent ?? true;
4147
+ const streamConfig = normalizeStreamConfig(normalized.stream);
4148
+ const debugConfig = normalizeDebugConfig(normalized.debug);
4149
+ const resolvedPrompt = applyPromptOutdent(resolvePrompt(normalized.prompt, { mode: "loose" }), useOutdent);
4150
+ const resolvedSystemPrompt = applyOutdentToOptionalPrompt(normalized.systemPrompt, useOutdent);
4151
+ const preparedPrompt = prepareGeneratePromptPayload(resolvedPrompt, resolvedSystemPrompt);
4152
+ const resolvedRequest = normalized.timeout?.tool !== undefined && normalized.request?.mcpClients !== undefined ? {
4153
+ ...normalized.request,
4154
+ mcpClients: applyToolTimeout(normalized.request.mcpClients, normalized.timeout.tool)
4155
+ } : normalized.request;
4156
+ const response = await callModel(adapter, {
4157
+ prompt: preparedPrompt.prompt,
4158
+ messages: preparedPrompt.messages,
4159
+ systemPrompt: preparedPrompt.systemPrompt,
4160
+ request: resolvedRequest,
4161
+ stream: streamConfig,
4162
+ observe: normalized.observe,
4163
+ buildEvent: ({ stage, message, details }) => ({
4164
+ stage,
4165
+ attempt: 1,
4166
+ message,
4167
+ details
4168
+ }),
4169
+ buildSnapshot: (model) => ({
4170
+ text: model.text,
4171
+ reasoning: model.reasoning
4172
+ }),
4173
+ debug: debugConfig,
4174
+ debugLabel: "generate",
4175
+ attempt: 1,
4176
+ selfHeal: false,
4177
+ selfHealEnabled: false,
4178
+ timeout: normalized.timeout
4179
+ });
4180
+ const attempt = {
4181
+ attempt: 1,
4182
+ via: response.via,
4183
+ text: response.text,
4184
+ reasoning: response.reasoning,
4185
+ usage: response.usage,
4186
+ finishReason: response.finishReason
4187
+ };
4188
+ const attempts = [attempt];
4189
+ normalized.observe?.({
4190
+ stage: "result",
4191
+ attempt: 1,
4192
+ message: "Text generation completed.",
4193
+ details: {
4194
+ via: response.via,
4195
+ finishReason: response.finishReason
4196
+ }
4197
+ });
4198
+ return {
4199
+ text: attempt.text,
4200
+ reasoning: attempt.reasoning,
4201
+ attempts,
4202
+ usage: aggregateUsage(attempts),
4203
+ finishReason: attempt.finishReason
4204
+ };
4205
+ }
4206
+ function normalizeGenerateInput(promptOrOptions, callOptions) {
4207
+ if (isGenerateOptions(promptOrOptions)) {
4208
+ return promptOrOptions;
4209
+ }
4210
+ if (!promptOrOptions) {
4211
+ throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
4212
+ }
4213
+ return {
4214
+ ...callOptions ?? {},
4215
+ prompt: promptOrOptions
4216
+ };
4217
+ }
4218
+ function isGenerateOptions(value) {
4219
+ return typeof value === "object" && value !== null && "prompt" in value;
4220
+ }
4221
+ function prepareGeneratePromptPayload(payload, systemPrompt) {
4222
+ if (Array.isArray(payload.messages) && payload.messages.length > 0) {
4223
+ const messages = payload.messages.map((message) => ({ ...message }));
4224
+ const mergedSystemPrompt = mergeSystemPrompts(payload.systemPrompt, systemPrompt);
4225
+ const systemMessages = mergedSystemPrompt ? [{ role: "system", content: mergedSystemPrompt }] : [];
4226
+ return {
4227
+ messages: [...systemMessages, ...messages]
4228
+ };
4229
+ }
4230
+ const resolvedPrompt = payload.prompt?.trim();
4231
+ if (!resolvedPrompt) {
4232
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4233
+ }
4234
+ return {
4235
+ prompt: resolvedPrompt,
4236
+ systemPrompt: mergeSystemPrompts(payload.systemPrompt, systemPrompt)
4237
+ };
4238
+ }
4239
+
4240
+ // src/structured.ts
4241
+ var import_jsonrepair3 = require("jsonrepair");
4242
+
4243
+ // src/parse.ts
4244
+ var import_jsonrepair2 = require("jsonrepair");
4245
+ function parseLLMOutput(output, schema, options = {}) {
4246
+ const sanitized = sanitizeThink(output);
4247
+ const parseOptions = {
4248
+ repair: options.repair ?? true,
4249
+ maxCandidates: options.maxCandidates ?? 5,
4250
+ acceptArrays: options.acceptArrays ?? true,
4251
+ extraction: options.extraction,
4252
+ onTrace: options.onTrace
4253
+ };
4254
+ const candidates = extractJsonCandidates(sanitized.visibleText, {
4255
+ maxCandidates: parseOptions.maxCandidates,
4256
+ acceptArrays: parseOptions.acceptArrays,
4257
+ allowRepairHints: parseOptions.repair,
4258
+ heuristics: parseOptions.extraction
4259
+ });
4260
+ emitTrace(parseOptions.onTrace, {
4261
+ stage: "extract",
4262
+ level: "info",
4263
+ message: `Extracted ${candidates.length} candidate(s).`,
4264
+ details: {
4265
+ maxCandidates: parseOptions.maxCandidates,
4266
+ thinkBlocks: sanitized.thinkBlocks.length,
4267
+ thinkDiagnostics: sanitized.diagnostics
4268
+ }
4269
+ });
4270
+ const errors = [];
4271
+ const diagnostics = [];
4272
+ let bestIssues = [];
4273
+ let bestCandidate = candidates[0] ?? null;
4274
+ let bestParsed = null;
4275
+ let bestRepaired = null;
3569
4276
  for (const candidate of candidates) {
3570
4277
  const parseAttempt = parseAttemptFromHint(candidate.parseHint, parseOptions.repair) ?? tryParseJsonCandidate(candidate.content, parseOptions.repair);
3571
4278
  if (!parseAttempt.success) {
@@ -3832,48 +4539,19 @@ function formatZodIssues(issues) {
3832
4539
  `);
3833
4540
  }
3834
4541
 
3835
- // src/utils/debug-colors.ts
3836
- var ANSI = {
3837
- reset: "\x1B[0m",
3838
- bold: "\x1B[1m",
3839
- cyan: "\x1B[36m",
3840
- yellow: "\x1B[33m",
3841
- green: "\x1B[32m",
3842
- red: "\x1B[31m",
3843
- dim: "\x1B[2m"
3844
- };
3845
- function color(config, text, tone) {
3846
- if (!config.colors) {
3847
- return text;
3848
- }
3849
- return `${ANSI[tone]}${text}${ANSI.reset}`;
3850
- }
3851
- function dim(config, text) {
3852
- if (!config.colors) {
3853
- return text;
3854
- }
3855
- return `${ANSI.dim}${text}${ANSI.reset}`;
3856
- }
3857
- function title(config, text) {
3858
- if (!config.colors) {
3859
- return text;
3860
- }
3861
- return `${ANSI.bold}${text}${ANSI.reset}`;
3862
- }
3863
-
3864
4542
  // src/structured.ts
3865
4543
  class StructuredParseError extends Error {
3866
4544
  name = "StructuredParseError";
3867
- raw;
3868
- thinkBlocks;
4545
+ text;
4546
+ reasoning;
3869
4547
  candidates;
3870
4548
  zodIssues;
3871
4549
  repairLog;
3872
4550
  attempt;
3873
4551
  constructor(input) {
3874
4552
  super(input.message ?? `Structured parsing failed after ${input.attempt} attempt(s).`);
3875
- this.raw = input.raw;
3876
- this.thinkBlocks = input.thinkBlocks;
4553
+ this.text = input.text;
4554
+ this.reasoning = input.reasoning;
3877
4555
  this.candidates = input.candidates;
3878
4556
  this.zodIssues = input.zodIssues;
3879
4557
  this.repairLog = input.repairLog;
@@ -3904,12 +4582,6 @@ var RE_SIMPLE_IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3904
4582
  var RE_ESCAPE_QUOTE = /"/g;
3905
4583
  var RE_WHITESPACE2 = /\s+/g;
3906
4584
  var DEFAULT_SELF_HEAL_MAX_DIAGNOSTICS = 8;
3907
- var structuredOutdent = createOutdent({
3908
- trimLeadingNewline: true,
3909
- trimTrailingNewline: true,
3910
- newline: `
3911
- `
3912
- });
3913
4585
  var DEFAULT_STRICT_PARSE_OPTIONS = {
3914
4586
  repair: false,
3915
4587
  maxCandidates: 3,
@@ -4039,7 +4711,7 @@ async function structured(adapter, schemaOrOptions, promptInput, callOptions) {
4039
4711
  });
4040
4712
  const selfHealSource = resolveSelfHealSource(previous);
4041
4713
  const repairPrompt = buildSelfHealPrompt({
4042
- rawOutput: previous.raw,
4714
+ rawOutput: composeParseSource(previous.text, previous.reasoning),
4043
4715
  issues: previous.zodIssues,
4044
4716
  schema: normalized.schema,
4045
4717
  schemaInstruction: normalized.schemaInstruction,
@@ -4112,74 +4784,6 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
4112
4784
  function isStructuredOptions(value) {
4113
4785
  return typeof value === "object" && value !== null && "schema" in value && "prompt" in value;
4114
4786
  }
4115
- function resolvePrompt(prompt, context) {
4116
- const resolved = typeof prompt === "function" ? prompt(context) : prompt;
4117
- return normalizePromptValue(resolved, context);
4118
- }
4119
- function normalizePromptValue(value, context) {
4120
- if (typeof value === "string") {
4121
- return {
4122
- prompt: value
4123
- };
4124
- }
4125
- if (isPromptResolver(value)) {
4126
- return normalizePromptPayload(value.resolvePrompt(context));
4127
- }
4128
- return normalizePromptPayload(value);
4129
- }
4130
- function isPromptResolver(value) {
4131
- return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
4132
- }
4133
- function normalizePromptPayload(value) {
4134
- const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
4135
- const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
4136
- if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
4137
- throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4138
- }
4139
- return {
4140
- prompt,
4141
- systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
4142
- messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
4143
- };
4144
- }
4145
- function applyPromptOutdent(payload, enabled) {
4146
- if (!enabled) {
4147
- return payload;
4148
- }
4149
- return {
4150
- prompt: typeof payload.prompt === "string" ? structuredOutdent.string(payload.prompt) : undefined,
4151
- systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
4152
- messages: payload.messages?.map((message) => ({
4153
- ...message,
4154
- content: typeof message.content === "string" ? structuredOutdent.string(message.content) : message.content
4155
- }))
4156
- };
4157
- }
4158
- function isLLMMessage(value) {
4159
- if (typeof value !== "object" || value === null) {
4160
- return false;
4161
- }
4162
- const candidate = value;
4163
- if (candidate.role !== "system" && candidate.role !== "user" && candidate.role !== "assistant" && candidate.role !== "tool") {
4164
- return false;
4165
- }
4166
- return "content" in candidate;
4167
- }
4168
- function applyOutdentToOptionalPrompt(value, enabled) {
4169
- if (!enabled || typeof value !== "string") {
4170
- return value;
4171
- }
4172
- return structuredOutdent.string(value);
4173
- }
4174
- function mergeSystemPrompts(primary, secondary) {
4175
- const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
4176
- if (prompts.length === 0) {
4177
- return;
4178
- }
4179
- return prompts.join(`
4180
-
4181
- `);
4182
- }
4183
4787
  function prepareStructuredPromptPayload(payload, systemPrompt, schema, schemaInstruction) {
4184
4788
  if (Array.isArray(payload.messages) && payload.messages.length > 0) {
4185
4789
  const messages = payload.messages.map((message) => ({ ...message }));
@@ -4360,70 +4964,32 @@ function resolveSelfHealSource(attempt) {
4360
4964
  }
4361
4965
  return {
4362
4966
  kind: "raw",
4363
- text: attempt.raw
4364
- };
4365
- }
4366
- function isSelfHealStalled(previous, current) {
4367
- if (current.success) {
4368
- return false;
4369
- }
4370
- if (current.zodIssues.length < previous.zodIssues.length) {
4371
- return false;
4372
- }
4373
- if (current.parsed.errors.length < previous.parsed.errors.length) {
4374
- return false;
4375
- }
4376
- return buildSelfHealFailureFingerprint(previous) === buildSelfHealFailureFingerprint(current);
4377
- }
4378
- function buildSelfHealFailureFingerprint(attempt) {
4379
- const issues = attempt.zodIssues.map((issue) => `${formatIssuePath(issue.path)}:${issue.code}:${normalizeWhitespace(issue.message)}`).sort().join("|");
4380
- const errors = attempt.parsed.errors.map((error) => `${error.stage}:${error.candidateId ?? "-"}:${normalizeWhitespace(error.message)}`).sort().join("|");
4381
- const source = normalizeWhitespace(resolveSelfHealSource(attempt).text).slice(0, 512);
4382
- return [issues, errors, source].join("::");
4383
- }
4384
- function normalizeWhitespace(value) {
4385
- return value.replace(RE_WHITESPACE2, " ").trim();
4386
- }
4387
- function normalizeStreamConfig(option) {
4388
- if (typeof option === "boolean") {
4389
- return {
4390
- enabled: option
4391
- };
4392
- }
4393
- if (!option) {
4394
- return {
4395
- enabled: false
4396
- };
4397
- }
4398
- return {
4399
- enabled: option.enabled ?? true,
4400
- onData: option.onData,
4401
- to: option.to
4402
- };
4403
- }
4404
- function normalizeDebugConfig(option) {
4405
- if (typeof option === "boolean") {
4406
- return {
4407
- enabled: option,
4408
- colors: true,
4409
- logger: (line) => console.log(line)
4410
- };
4411
- }
4412
- if (!option) {
4413
- return {
4414
- enabled: false,
4415
- colors: true,
4416
- logger: (line) => console.log(line)
4417
- };
4418
- }
4419
- return {
4420
- enabled: option.enabled ?? true,
4421
- colors: option.colors ?? true,
4422
- logger: option.logger ?? ((line) => console.log(line))
4967
+ text: composeParseSource(attempt.text, attempt.reasoning)
4423
4968
  };
4424
4969
  }
4970
+ function isSelfHealStalled(previous, current) {
4971
+ if (current.success) {
4972
+ return false;
4973
+ }
4974
+ if (current.zodIssues.length < previous.zodIssues.length) {
4975
+ return false;
4976
+ }
4977
+ if (current.parsed.errors.length < previous.parsed.errors.length) {
4978
+ return false;
4979
+ }
4980
+ return buildSelfHealFailureFingerprint(previous) === buildSelfHealFailureFingerprint(current);
4981
+ }
4982
+ function buildSelfHealFailureFingerprint(attempt) {
4983
+ const issues = attempt.zodIssues.map((issue) => `${formatIssuePath(issue.path)}:${issue.code}:${normalizeWhitespace(issue.message)}`).sort().join("|");
4984
+ const errors = attempt.parsed.errors.map((error) => `${error.stage}:${error.candidateId ?? "-"}:${normalizeWhitespace(error.message)}`).sort().join("|");
4985
+ const source = normalizeWhitespace(resolveSelfHealSource(attempt).text).slice(0, 512);
4986
+ return [issues, errors, source].join("::");
4987
+ }
4988
+ function normalizeWhitespace(value) {
4989
+ return value.replace(RE_WHITESPACE2, " ").trim();
4990
+ }
4425
4991
  async function executeAttempt(adapter, input) {
4426
- const response = await callModel(adapter, {
4992
+ const response = await callModel2(adapter, {
4427
4993
  prompt: input.prompt,
4428
4994
  messages: input.messages,
4429
4995
  systemPrompt: input.systemPrompt,
@@ -4436,7 +5002,7 @@ async function executeAttempt(adapter, input) {
4436
5002
  selfHealEnabled: input.selfHealEnabled,
4437
5003
  timeout: input.timeout
4438
5004
  });
4439
- const parsed = parseWithObserve(response.text, input.schema, input.parseOptions, {
5005
+ const parsed = parseWithObserve(response.parseSource, input.schema, input.parseOptions, {
4440
5006
  observe: input.observe,
4441
5007
  attempt: input.attemptNumber,
4442
5008
  selfHeal: input.selfHeal
@@ -4445,8 +5011,8 @@ async function executeAttempt(adapter, input) {
4445
5011
  attempt: input.attemptNumber,
4446
5012
  selfHeal: input.selfHeal,
4447
5013
  via: response.via,
4448
- raw: response.text,
4449
- thinkBlocks: parsed.thinkBlocks,
5014
+ text: response.text,
5015
+ reasoning: response.reasoning,
4450
5016
  json: parsed.parsed,
4451
5017
  candidates: parsed.candidates.map((candidate) => candidate.content),
4452
5018
  repairLog: collectRepairLog(parsed),
@@ -4461,195 +5027,26 @@ async function executeAttempt(adapter, input) {
4461
5027
  trace
4462
5028
  };
4463
5029
  }
4464
- function withToolTimeout(client, toolTimeoutMs) {
4465
- return {
4466
- id: client.id,
4467
- listTools: client.listTools.bind(client),
4468
- close: client.close?.bind(client),
4469
- async callTool(params) {
4470
- let timeoutId;
4471
- const timeoutPromise = new Promise((_, reject) => {
4472
- timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
4473
- });
4474
- try {
4475
- return await Promise.race([client.callTool(params), timeoutPromise]);
4476
- } finally {
4477
- clearTimeout(timeoutId);
4478
- }
4479
- }
4480
- };
4481
- }
4482
- function applyToolTimeout(clients, toolTimeoutMs) {
4483
- return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
4484
- }
4485
- async function callModel(adapter, options) {
4486
- const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
4487
- const requestPayload = {
4488
- prompt: options.prompt,
4489
- messages: options.messages,
4490
- systemPrompt: options.systemPrompt,
4491
- temperature: options.request?.temperature,
4492
- maxTokens: options.request?.maxTokens,
4493
- mcpClients: options.request?.mcpClients,
4494
- toolChoice: options.request?.toolChoice,
4495
- parallelToolCalls: options.request?.parallelToolCalls,
4496
- maxToolRounds: options.request?.maxToolRounds,
4497
- onToolExecution: options.request?.onToolExecution,
4498
- toolDebug: options.request?.toolDebug,
4499
- body: options.request?.body,
4500
- signal: requestSignal
4501
- };
4502
- emitDebugRequest(options.debug, {
4503
- provider: adapter.provider,
4504
- model: adapter.model,
4505
- attempt: options.attempt,
4506
- selfHealAttempt: options.selfHeal,
4507
- selfHealEnabled: options.selfHealEnabled,
4508
- stream: options.stream.enabled && !!adapter.stream,
4509
- requestPayload
4510
- });
4511
- emitObserve(options.observe, {
4512
- stage: "llm.request",
4513
- attempt: options.attempt,
4514
- selfHeal: options.selfHeal,
4515
- message: "Sending LLM request.",
4516
- details: {
4517
- provider: adapter.provider,
4518
- model: adapter.model,
4519
- stream: options.stream.enabled && !!adapter.stream
4520
- }
4521
- });
4522
- if (options.stream.enabled && adapter.stream) {
4523
- let latestUsage;
4524
- let latestFinishReason;
4525
- let streamedRaw = "";
4526
- let sawToken = false;
4527
- let lastDataFingerprint;
4528
- const emitStreamingData = (raw, done, usage2, finishReason2) => {
4529
- const data = parseStreamingStructuredData(raw);
4530
- if (data === null && !done) {
4531
- return;
4532
- }
4533
- const fingerprint = toStreamDataFingerprint(data ?? null);
4534
- if (!done && fingerprint === lastDataFingerprint) {
4535
- return;
4536
- }
4537
- lastDataFingerprint = fingerprint;
4538
- options.stream.onData?.({
4539
- data: data ?? null,
4540
- raw,
4541
- done,
4542
- usage: usage2,
4543
- finishReason: finishReason2
4544
- });
4545
- emitObserve(options.observe, {
4546
- stage: "llm.stream.data",
4547
- attempt: options.attempt,
4548
- selfHeal: options.selfHeal,
4549
- message: done ? "Streaming structured data completed." : "Streaming structured data updated.",
4550
- details: {
4551
- done,
4552
- finishReason: finishReason2
4553
- }
4554
- });
4555
- };
4556
- const handleTextDelta = (delta) => {
4557
- if (!delta) {
4558
- return;
4559
- }
4560
- streamedRaw += delta;
4561
- if (options.stream.to === "stdout") {
4562
- process.stdout.write(delta);
4563
- }
4564
- emitObserve(options.observe, {
4565
- stage: "llm.stream.delta",
4566
- attempt: options.attempt,
4567
- selfHeal: options.selfHeal,
4568
- message: "Received stream delta.",
4569
- details: {
4570
- chars: delta.length
4571
- }
4572
- });
4573
- emitStreamingData(streamedRaw, false);
4574
- };
4575
- const response2 = await adapter.stream(requestPayload, {
4576
- onToken: (token) => {
4577
- sawToken = true;
4578
- handleTextDelta(token);
4579
- },
4580
- onChunk: (chunk) => {
4581
- if (!sawToken && chunk.textDelta) {
4582
- handleTextDelta(chunk.textDelta);
4583
- }
4584
- if (chunk.usage) {
4585
- latestUsage = preferLatestUsage(latestUsage, chunk.usage);
4586
- }
4587
- if (chunk.finishReason) {
4588
- latestFinishReason = chunk.finishReason;
4589
- }
4590
- }
4591
- });
4592
- const finalText = typeof response2.text === "string" && response2.text.length > 0 ? response2.text : streamedRaw;
4593
- const usage = preferLatestUsage(latestUsage, response2.usage);
4594
- const finishReason = response2.finishReason ?? latestFinishReason;
4595
- emitStreamingData(finalText, true, usage, finishReason);
4596
- emitObserve(options.observe, {
4597
- stage: "llm.response",
5030
+ async function callModel2(adapter, options) {
5031
+ return callModel(adapter, {
5032
+ ...options,
5033
+ buildEvent: ({ stage, message, details }) => ({
5034
+ stage,
4598
5035
  attempt: options.attempt,
4599
5036
  selfHeal: options.selfHeal,
4600
- message: "Streaming response completed.",
4601
- details: {
4602
- via: "stream",
4603
- chars: finalText.length,
4604
- finishReason
4605
- }
4606
- });
4607
- emitDebugResponse(options.debug, {
4608
- attempt: options.attempt,
4609
- selfHealAttempt: options.selfHeal,
4610
- selfHealEnabled: options.selfHealEnabled,
4611
- via: "stream",
4612
- responseText: finalText,
4613
- usage,
4614
- finishReason
4615
- });
4616
- return {
4617
- text: finalText,
4618
- via: "stream",
4619
- usage,
4620
- finishReason
4621
- };
4622
- }
4623
- const response = await adapter.complete(requestPayload);
4624
- emitObserve(options.observe, {
4625
- stage: "llm.response",
4626
- attempt: options.attempt,
4627
- selfHeal: options.selfHeal,
4628
- message: "Completion response received.",
4629
- details: {
4630
- via: "complete",
4631
- chars: response.text.length,
4632
- finishReason: response.finishReason
4633
- }
4634
- });
4635
- emitDebugResponse(options.debug, {
4636
- attempt: options.attempt,
4637
- selfHealAttempt: options.selfHeal,
4638
- selfHealEnabled: options.selfHealEnabled,
4639
- via: "complete",
4640
- responseText: response.text,
4641
- usage: response.usage,
4642
- finishReason: response.finishReason
5037
+ message,
5038
+ details
5039
+ }),
5040
+ buildSnapshot: (normalized) => ({
5041
+ text: normalized.text,
5042
+ reasoning: normalized.reasoning,
5043
+ data: parseStreamingStructuredData(normalized.parseSource) ?? null
5044
+ }),
5045
+ debugLabel: "structured"
4643
5046
  });
4644
- return {
4645
- text: response.text,
4646
- via: "complete",
4647
- usage: response.usage,
4648
- finishReason: response.finishReason
4649
- };
4650
5047
  }
4651
- function parseStreamingStructuredData(raw) {
4652
- const sanitized = sanitizeThink(raw);
5048
+ function parseStreamingStructuredData(parseSource) {
5049
+ const sanitized = sanitizeThink(parseSource);
4653
5050
  const start = findFirstJsonRootStart(sanitized.visibleText);
4654
5051
  if (start < 0) {
4655
5052
  return null;
@@ -4709,13 +5106,6 @@ function findFirstJsonRootStart(input) {
4709
5106
  }
4710
5107
  return Math.min(objectStart, arrayStart);
4711
5108
  }
4712
- function toStreamDataFingerprint(value) {
4713
- try {
4714
- return JSON.stringify(value);
4715
- } catch {
4716
- return "__unserializable__";
4717
- }
4718
- }
4719
5109
  function parseWithObserve(output, schema, parseOptions, context) {
4720
5110
  const userParseTrace = parseOptions.onTrace;
4721
5111
  return parseLLMOutput(output, schema, {
@@ -4745,38 +5135,20 @@ function buildSuccessResult(data, attempts) {
4745
5135
  const final = attempts.at(-1);
4746
5136
  return {
4747
5137
  data,
4748
- raw: final?.raw ?? "",
4749
- thinkBlocks: final?.thinkBlocks ?? [],
5138
+ text: final?.text ?? "",
5139
+ reasoning: final?.reasoning ?? "",
4750
5140
  json: final?.json ?? null,
4751
5141
  attempts,
4752
5142
  usage: aggregateUsage(attempts),
4753
5143
  finishReason: final?.finishReason
4754
5144
  };
4755
5145
  }
4756
- function aggregateUsage(attempts) {
4757
- let usage;
4758
- for (const attempt of attempts) {
4759
- usage = mergeUsage2(usage, attempt.usage);
4760
- }
4761
- return usage;
4762
- }
4763
- function mergeUsage2(base, next) {
4764
- if (!base && !next) {
4765
- return;
4766
- }
4767
- return {
4768
- inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
4769
- outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
4770
- totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
4771
- cost: (base?.cost ?? 0) + (next?.cost ?? 0)
4772
- };
4773
- }
4774
5146
  function toStructuredError(attempt) {
4775
5147
  if (!attempt) {
4776
5148
  return new StructuredParseError({
4777
5149
  message: "Structured parsing failed before any model response.",
4778
- raw: "",
4779
- thinkBlocks: [],
5150
+ text: "",
5151
+ reasoning: "",
4780
5152
  candidates: [],
4781
5153
  zodIssues: [],
4782
5154
  repairLog: [],
@@ -4784,8 +5156,8 @@ function toStructuredError(attempt) {
4784
5156
  });
4785
5157
  }
4786
5158
  return new StructuredParseError({
4787
- raw: attempt.raw,
4788
- thinkBlocks: attempt.thinkBlocks,
5159
+ text: attempt.text,
5160
+ reasoning: attempt.reasoning,
4789
5161
  candidates: attempt.candidates,
4790
5162
  zodIssues: attempt.zodIssues,
4791
5163
  repairLog: attempt.repairLog,
@@ -4795,59 +5167,6 @@ function toStructuredError(attempt) {
4795
5167
  function emitObserve(observe, event) {
4796
5168
  observe?.(event);
4797
5169
  }
4798
- function emitDebugRequest(config, input) {
4799
- const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4800
- const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4801
- const lines = [
4802
- color(config, title(config, [
4803
- "[structured][request]",
4804
- `attempt=${input.attempt}`,
4805
- `selfHealEnabled=${input.selfHealEnabled}`,
4806
- `selfHealAttempt=${input.selfHealAttempt}`
4807
- ].join(" ")), "cyan"),
4808
- dim(config, [
4809
- `provider=${input.provider ?? "unknown"}`,
4810
- `model=${input.model ?? "unknown"}`,
4811
- `stream=${input.stream}`
4812
- ].join(" ")),
4813
- color(config, "prompt:", "yellow"),
4814
- input.requestPayload.prompt ?? "(none)",
4815
- color(config, "messages:", "yellow"),
4816
- requestMessages,
4817
- color(config, "systemPrompt:", "yellow"),
4818
- input.requestPayload.systemPrompt ?? "(none)",
4819
- color(config, "request.body:", "yellow"),
4820
- requestBody
4821
- ];
4822
- emitDebug(config, lines.join(`
4823
- `));
4824
- }
4825
- function emitDebugResponse(config, input) {
4826
- const lines = [
4827
- color(config, title(config, [
4828
- "[structured][response]",
4829
- `attempt=${input.attempt}`,
4830
- `selfHealEnabled=${input.selfHealEnabled}`,
4831
- `selfHealAttempt=${input.selfHealAttempt}`
4832
- ].join(" ")), "green"),
4833
- dim(config, [
4834
- `via=${input.via}`,
4835
- `chars=${input.responseText.length}`,
4836
- `finishReason=${input.finishReason ?? "unknown"}`,
4837
- `usage=${JSON.stringify(input.usage ?? {})}`
4838
- ].join(" ")),
4839
- color(config, "text:", "yellow"),
4840
- input.responseText
4841
- ];
4842
- emitDebug(config, lines.join(`
4843
- `));
4844
- }
4845
- function emitDebug(config, message) {
4846
- if (!config.enabled) {
4847
- return;
4848
- }
4849
- config.logger(message);
4850
- }
4851
5170
 
4852
5171
  // src/llm.ts
4853
5172
  function createLLM(config, registry = createDefaultProviderRegistry()) {
@@ -4861,6 +5180,17 @@ function createLLM(config, registry = createDefaultProviderRegistry()) {
4861
5180
  const merged = mergeStructuredOptions(defaults, options);
4862
5181
  return structured(adapter, schema, prompt, merged);
4863
5182
  },
5183
+ async generate(promptOrOptions, options) {
5184
+ if (isGenerateOptions2(promptOrOptions)) {
5185
+ const merged2 = {
5186
+ ...mergeGenerateOptions(defaults, promptOrOptions),
5187
+ prompt: promptOrOptions.prompt
5188
+ };
5189
+ return generate(adapter, merged2);
5190
+ }
5191
+ const merged = mergeGenerateOptions(defaults, options);
5192
+ return generate(adapter, promptOrOptions, merged);
5193
+ },
4864
5194
  async embed(input, options = {}) {
4865
5195
  if (!adapter.embed) {
4866
5196
  throw new Error(`Provider "${adapter.provider ?? "unknown"}" does not support embeddings.`);
@@ -4890,6 +5220,26 @@ function mergeStructuredOptions(defaults, overrides) {
4890
5220
  timeout: mergeObjectLike(defaults?.timeout, overrides?.timeout)
4891
5221
  };
4892
5222
  }
5223
+ function mergeGenerateOptions(defaults, overrides) {
5224
+ if (!defaults && !overrides) {
5225
+ return {};
5226
+ }
5227
+ return {
5228
+ outdent: overrides?.outdent ?? defaults?.outdent,
5229
+ systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
5230
+ request: {
5231
+ ...defaults?.request ?? {},
5232
+ ...overrides?.request ?? {}
5233
+ },
5234
+ stream: mergeObjectLike(defaults?.stream, overrides?.stream),
5235
+ debug: mergeObjectLike(defaults?.debug, overrides?.debug),
5236
+ timeout: mergeObjectLike(defaults?.timeout, overrides?.timeout),
5237
+ observe: overrides?.observe ?? defaults?.observe
5238
+ };
5239
+ }
5240
+ function isGenerateOptions2(value) {
5241
+ return typeof value === "object" && value !== null && "prompt" in value;
5242
+ }
4893
5243
  function mergeObjectLike(defaults, overrides) {
4894
5244
  if (overrides === undefined) {
4895
5245
  return defaults;