extrait 0.5.6 → 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,
@@ -1661,6 +1662,7 @@ function createOpenAICompatibleAdapter(options) {
1661
1662
  model: options.model,
1662
1663
  messages: buildMessages(request),
1663
1664
  temperature: request.temperature,
1665
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1664
1666
  max_tokens: request.maxTokens,
1665
1667
  stream: true
1666
1668
  })),
@@ -1672,6 +1674,7 @@ function createOpenAICompatibleAdapter(options) {
1672
1674
  }
1673
1675
  callbacks.onStart?.();
1674
1676
  let text = "";
1677
+ let reasoning = "";
1675
1678
  let usage;
1676
1679
  let finishReason;
1677
1680
  await consumeSSE(response, (data) => {
@@ -1683,6 +1686,7 @@ function createOpenAICompatibleAdapter(options) {
1683
1686
  return;
1684
1687
  }
1685
1688
  const delta = pickAssistantDelta(json);
1689
+ const reasoningDelta = pickAssistantReasoningDelta(json);
1686
1690
  const chunkUsage = pickUsage(json);
1687
1691
  const chunkFinishReason = pickFinishReason(json);
1688
1692
  usage = preferLatestUsage(usage, chunkUsage);
@@ -1693,9 +1697,13 @@ function createOpenAICompatibleAdapter(options) {
1693
1697
  text += delta;
1694
1698
  callbacks.onToken?.(delta);
1695
1699
  }
1696
- if (delta || chunkUsage || chunkFinishReason) {
1700
+ if (reasoningDelta) {
1701
+ reasoning += reasoningDelta;
1702
+ }
1703
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
1697
1704
  const chunk = {
1698
1705
  textDelta: delta,
1706
+ reasoningDelta: reasoningDelta || undefined,
1699
1707
  raw: json,
1700
1708
  usage: chunkUsage,
1701
1709
  finishReason: chunkFinishReason
@@ -1703,7 +1711,12 @@ function createOpenAICompatibleAdapter(options) {
1703
1711
  callbacks.onChunk?.(chunk);
1704
1712
  }
1705
1713
  });
1706
- const out = { text, usage, finishReason };
1714
+ const out = {
1715
+ text,
1716
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1717
+ usage,
1718
+ finishReason
1719
+ };
1707
1720
  callbacks.onComplete?.(out);
1708
1721
  return out;
1709
1722
  },
@@ -1763,6 +1776,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1763
1776
  model: options.model,
1764
1777
  messages: buildMessages(request),
1765
1778
  temperature: request.temperature,
1779
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1766
1780
  max_tokens: request.maxTokens,
1767
1781
  stream: false
1768
1782
  })),
@@ -1778,8 +1792,10 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1778
1792
  throw new Error("No assistant message in OpenAI-compatible response.");
1779
1793
  }
1780
1794
  const toolCalls = pickChatToolCalls(payload);
1795
+ const reasoning = pickAssistantReasoning(payload);
1781
1796
  return {
1782
1797
  text: pickAssistantText(payload),
1798
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1783
1799
  raw: payload,
1784
1800
  usage: pickUsage(payload),
1785
1801
  finishReason: pickFinishReason(payload),
@@ -1806,6 +1822,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1806
1822
  model: options.model,
1807
1823
  messages,
1808
1824
  temperature: request.temperature,
1825
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1809
1826
  max_tokens: request.maxTokens,
1810
1827
  tools: transportTools,
1811
1828
  tool_choice: request.toolChoice,
@@ -1827,8 +1844,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1827
1844
  throw new Error("No assistant message in OpenAI-compatible response.");
1828
1845
  }
1829
1846
  if (calledTools.length === 0) {
1847
+ const reasoning = pickAssistantReasoning(payload);
1830
1848
  return {
1831
1849
  text: pickAssistantText(payload),
1850
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1832
1851
  raw: payload,
1833
1852
  usage: aggregatedUsage,
1834
1853
  finishReason,
@@ -1856,6 +1875,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1856
1875
  }
1857
1876
  return {
1858
1877
  text: pickAssistantText(lastPayload ?? {}),
1878
+ reasoning: (() => {
1879
+ const value = pickAssistantReasoning(lastPayload ?? {});
1880
+ return value.length > 0 ? value : undefined;
1881
+ })(),
1859
1882
  raw: lastPayload,
1860
1883
  usage: aggregatedUsage,
1861
1884
  finishReason,
@@ -1875,6 +1898,7 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
1875
1898
  input: buildResponsesInput(request),
1876
1899
  previous_response_id: pickString(body?.previous_response_id),
1877
1900
  temperature: request.temperature,
1901
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1878
1902
  max_output_tokens: request.maxTokens
1879
1903
  })),
1880
1904
  signal: request.signal
@@ -1915,6 +1939,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1915
1939
  input,
1916
1940
  previous_response_id: previousResponseId,
1917
1941
  temperature: request.temperature,
1942
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1918
1943
  max_output_tokens: request.maxTokens,
1919
1944
  tools: transportTools,
1920
1945
  tool_choice: request.toolChoice,
@@ -1979,6 +2004,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1979
2004
  const executedToolCalls = [];
1980
2005
  const toolExecutions = [];
1981
2006
  callbacks.onStart?.();
2007
+ let lastRoundText = "";
2008
+ let lastRoundReasoning = "";
1982
2009
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1983
2010
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1984
2011
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -1991,6 +2018,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1991
2018
  model: options.model,
1992
2019
  messages,
1993
2020
  temperature: request.temperature,
2021
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1994
2022
  max_tokens: request.maxTokens,
1995
2023
  tools: transportTools,
1996
2024
  tool_choice: request.toolChoice,
@@ -2004,9 +2032,11 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2004
2032
  throw new Error(`HTTP ${response.status}: ${message}`);
2005
2033
  }
2006
2034
  let roundText = "";
2035
+ let roundReasoning = "";
2007
2036
  let roundUsage;
2008
2037
  let roundFinishReason;
2009
2038
  const streamedToolCalls = new Map;
2039
+ let reasoningFieldName;
2010
2040
  await consumeSSE(response, (data) => {
2011
2041
  if (data === "[DONE]") {
2012
2042
  return;
@@ -2017,6 +2047,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2017
2047
  }
2018
2048
  lastPayload = json;
2019
2049
  const delta = pickAssistantDelta(json);
2050
+ const reasoningDelta = pickAssistantReasoningDelta(json);
2020
2051
  const chunkUsage = pickUsage(json);
2021
2052
  const chunkFinishReason = pickFinishReason(json);
2022
2053
  collectOpenAIStreamToolCalls(json, streamedToolCalls);
@@ -2028,9 +2059,14 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2028
2059
  roundText += delta;
2029
2060
  callbacks.onToken?.(delta);
2030
2061
  }
2031
- if (delta || chunkUsage || chunkFinishReason) {
2062
+ if (reasoningDelta) {
2063
+ roundReasoning += reasoningDelta;
2064
+ reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
2065
+ }
2066
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2032
2067
  const chunk = {
2033
2068
  textDelta: delta,
2069
+ reasoningDelta: reasoningDelta || undefined,
2034
2070
  raw: json,
2035
2071
  usage: chunkUsage,
2036
2072
  finishReason: chunkFinishReason
@@ -2046,6 +2082,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2046
2082
  if (calledTools.length === 0) {
2047
2083
  const out2 = {
2048
2084
  text: roundText,
2085
+ reasoning: roundReasoning.length > 0 ? roundReasoning : undefined,
2049
2086
  raw: lastPayload,
2050
2087
  usage: aggregatedUsage,
2051
2088
  finishReason,
@@ -2066,7 +2103,12 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2066
2103
  });
2067
2104
  executedToolCalls.push(...outputs.map((entry) => entry.call));
2068
2105
  toolExecutions.push(...outputs.map((entry) => entry.execution));
2069
- const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools);
2106
+ lastRoundText = roundText;
2107
+ lastRoundReasoning = roundReasoning;
2108
+ const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
2109
+ reasoning: roundReasoning,
2110
+ reasoningFieldName
2111
+ });
2070
2112
  const toolMessages = outputs.map((entry) => ({
2071
2113
  role: "tool",
2072
2114
  tool_call_id: entry.call.id,
@@ -2075,7 +2117,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2075
2117
  messages = [...messages, assistantMessage, ...toolMessages];
2076
2118
  }
2077
2119
  const out = {
2078
- text: "",
2120
+ text: lastRoundText,
2121
+ reasoning: lastRoundReasoning.length > 0 ? lastRoundReasoning : undefined,
2079
2122
  raw: lastPayload,
2080
2123
  usage: aggregatedUsage,
2081
2124
  finishReason,
@@ -2097,6 +2140,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
2097
2140
  input: buildResponsesInput(request),
2098
2141
  previous_response_id: pickString(body?.previous_response_id),
2099
2142
  temperature: request.temperature,
2143
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2100
2144
  max_output_tokens: request.maxTokens,
2101
2145
  stream: true
2102
2146
  })),
@@ -2177,6 +2221,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2177
2221
  input,
2178
2222
  previous_response_id: previousResponseId,
2179
2223
  temperature: request.temperature,
2224
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2180
2225
  max_output_tokens: request.maxTokens,
2181
2226
  tools: transportTools,
2182
2227
  tool_choice: request.toolChoice,
@@ -2343,6 +2388,12 @@ function toResponsesTools(tools) {
2343
2388
  return { ...tool };
2344
2389
  });
2345
2390
  }
2391
+ function toOpenAIReasoningEffort(value) {
2392
+ if (!value) {
2393
+ return;
2394
+ }
2395
+ return value === "max" ? "xhigh" : value;
2396
+ }
2346
2397
  function pickChatToolCalls(payload) {
2347
2398
  const message = pickAssistantMessage(payload);
2348
2399
  if (!message) {
@@ -2424,20 +2475,50 @@ function pickAssistantDelta(payload) {
2424
2475
  if (!isRecord2(delta)) {
2425
2476
  return "";
2426
2477
  }
2427
- const content = delta.content;
2428
- if (typeof content === "string") {
2429
- return content;
2478
+ return pickTextFromOpenAIContent(delta.content);
2479
+ }
2480
+ function pickAssistantReasoning(payload) {
2481
+ const message = pickAssistantMessage(payload);
2482
+ if (!message) {
2483
+ return "";
2430
2484
  }
2431
- if (Array.isArray(content)) {
2432
- return content.map((part) => {
2433
- if (!isRecord2(part)) {
2434
- return "";
2435
- }
2436
- const text = part.text;
2437
- return typeof text === "string" ? text : "";
2438
- }).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 "";
2439
2491
  }
2440
- 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;
2441
2522
  }
2442
2523
  function collectOpenAIStreamToolCalls(payload, state) {
2443
2524
  const choices = payload.choices;
@@ -2486,8 +2567,8 @@ function buildOpenAIStreamToolCalls(state) {
2486
2567
  arguments: entry.argumentsText.length > 0 ? entry.argumentsText : {}
2487
2568
  }));
2488
2569
  }
2489
- function buildOpenAIAssistantToolMessage(text, toolCalls) {
2490
- return {
2570
+ function buildOpenAIAssistantToolMessage(text, toolCalls, reasoning) {
2571
+ const message = {
2491
2572
  role: "assistant",
2492
2573
  content: text,
2493
2574
  tool_calls: toolCalls.map((call) => ({
@@ -2499,6 +2580,10 @@ function buildOpenAIAssistantToolMessage(text, toolCalls) {
2499
2580
  }
2500
2581
  }))
2501
2582
  };
2583
+ if (reasoning?.reasoning && reasoning.reasoning.length > 0) {
2584
+ message[reasoning.reasoningFieldName ?? "reasoning"] = reasoning.reasoning;
2585
+ }
2586
+ return message;
2502
2587
  }
2503
2588
  function pickResponsesStreamPayload(payload) {
2504
2589
  if (isRecord2(payload.response)) {
@@ -2660,21 +2745,9 @@ function pickResponsesText(payload) {
2660
2745
  function pickAssistantText(payload) {
2661
2746
  const message = pickAssistantMessage(payload);
2662
2747
  if (message) {
2663
- const content = message.content;
2664
- if (typeof content === "string") {
2665
- return content;
2666
- }
2667
- if (Array.isArray(content)) {
2668
- return content.map((part) => {
2669
- if (typeof part === "string") {
2670
- return part;
2671
- }
2672
- if (!isRecord2(part)) {
2673
- return "";
2674
- }
2675
- const text = part.text;
2676
- return typeof text === "string" ? text : "";
2677
- }).join("");
2748
+ const text = pickTextFromOpenAIContent(message.content);
2749
+ if (text.length > 0) {
2750
+ return text;
2678
2751
  }
2679
2752
  }
2680
2753
  const choices = payload.choices;
@@ -2686,6 +2759,36 @@ function pickAssistantText(payload) {
2686
2759
  }
2687
2760
  return "";
2688
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
+ }
2689
2792
  function pickUsage(payload) {
2690
2793
  const usage = payload.usage;
2691
2794
  if (!isRecord2(usage)) {
@@ -2739,14 +2842,13 @@ function createAnthropicCompatibleAdapter(options) {
2739
2842
  const response = await fetcher(buildURL(options.baseURL, path), {
2740
2843
  method: "POST",
2741
2844
  headers: buildHeaders2(options),
2742
- body: JSON.stringify(cleanUndefined({
2845
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2743
2846
  ...options.defaultBody,
2744
2847
  ...request.body,
2745
2848
  model: options.model,
2746
2849
  system: input.systemPrompt,
2747
2850
  messages: input.messages,
2748
2851
  temperature: request.temperature,
2749
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2750
2852
  stream: true
2751
2853
  })),
2752
2854
  signal: request.signal
@@ -2802,14 +2904,13 @@ async function completePassThrough(options, fetcher, path, request) {
2802
2904
  const response = await fetcher(buildURL(options.baseURL, path), {
2803
2905
  method: "POST",
2804
2906
  headers: buildHeaders2(options),
2805
- body: JSON.stringify(cleanUndefined({
2907
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2806
2908
  ...options.defaultBody,
2807
2909
  ...request.body,
2808
2910
  model: options.model,
2809
2911
  system: input.systemPrompt,
2810
2912
  messages: input.messages,
2811
2913
  temperature: request.temperature,
2812
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2813
2914
  stream: false
2814
2915
  })),
2815
2916
  signal: request.signal
@@ -2847,14 +2948,13 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2847
2948
  const response = await fetcher(buildURL(options.baseURL, path), {
2848
2949
  method: "POST",
2849
2950
  headers: buildHeaders2(options),
2850
- body: JSON.stringify(cleanUndefined({
2951
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2851
2952
  ...options.defaultBody,
2852
2953
  ...request.body,
2853
2954
  model: options.model,
2854
2955
  system: input.systemPrompt,
2855
2956
  messages,
2856
2957
  temperature: request.temperature,
2857
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2858
2958
  tools,
2859
2959
  tool_choice: toAnthropicToolChoice(request.toolChoice),
2860
2960
  stream: false
@@ -2932,14 +3032,13 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2932
3032
  const response = await fetcher(buildURL(options.baseURL, path), {
2933
3033
  method: "POST",
2934
3034
  headers: buildHeaders2(options),
2935
- body: JSON.stringify(cleanUndefined({
3035
+ body: JSON.stringify(buildAnthropicRequestBody(options, request, {
2936
3036
  ...options.defaultBody,
2937
3037
  ...request.body,
2938
3038
  model: options.model,
2939
3039
  system: input.systemPrompt,
2940
3040
  messages,
2941
3041
  temperature: request.temperature,
2942
- max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2943
3042
  tools,
2944
3043
  tool_choice: toAnthropicToolChoice(request.toolChoice),
2945
3044
  stream: true
@@ -3047,6 +3146,21 @@ function buildHeaders2(options) {
3047
3146
  ...options.headers
3048
3147
  };
3049
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
+ }
3050
3164
  function resolveAnthropicInput(request) {
3051
3165
  if (Array.isArray(request.messages) && request.messages.length > 0) {
3052
3166
  return toAnthropicInput(request.messages);
@@ -3401,8 +3515,34 @@ function buildProviderOptions(config) {
3401
3515
  };
3402
3516
  }
3403
3517
 
3404
- // src/structured.ts
3405
- 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
+ }
3406
3546
 
3407
3547
  // src/outdent.ts
3408
3548
  var DEFAULT_OPTIONS = {
@@ -3541,44 +3681,603 @@ function createOutdent(options = {}) {
3541
3681
  return outdent;
3542
3682
  }
3543
3683
 
3544
- // src/parse.ts
3545
- var import_jsonrepair2 = require("jsonrepair");
3546
- function parseLLMOutput(output, schema, options = {}) {
3547
- const sanitized = sanitizeThink(output);
3548
- const parseOptions = {
3549
- repair: options.repair ?? true,
3550
- maxCandidates: options.maxCandidates ?? 5,
3551
- acceptArrays: options.acceptArrays ?? true,
3552
- extraction: options.extraction,
3553
- 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
3554
3717
  };
3555
- const candidates = extractJsonCandidates(sanitized.visibleText, {
3556
- maxCandidates: parseOptions.maxCandidates,
3557
- acceptArrays: parseOptions.acceptArrays,
3558
- allowRepairHints: parseOptions.repair,
3559
- heuristics: parseOptions.extraction
3560
- });
3561
- emitTrace(parseOptions.onTrace, {
3562
- stage: "extract",
3563
- level: "info",
3564
- message: `Extracted ${candidates.length} candidate(s).`,
3565
- details: {
3566
- maxCandidates: parseOptions.maxCandidates,
3567
- thinkBlocks: sanitized.thinkBlocks.length,
3568
- 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
+ }
3569
3803
  }
3570
- });
3571
- const errors = [];
3572
- const diagnostics = [];
3573
- let bestIssues = [];
3574
- let bestCandidate = candidates[0] ?? null;
3575
- let bestParsed = null;
3576
- let bestRepaired = null;
3577
- for (const candidate of candidates) {
3578
- const parseAttempt = parseAttemptFromHint(candidate.parseHint, parseOptions.repair) ?? tryParseJsonCandidate(candidate.content, parseOptions.repair);
3579
- if (!parseAttempt.success) {
3580
- const diagnostic = {
3581
- candidateId: candidate.id,
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;
4276
+ for (const candidate of candidates) {
4277
+ const parseAttempt = parseAttemptFromHint(candidate.parseHint, parseOptions.repair) ?? tryParseJsonCandidate(candidate.content, parseOptions.repair);
4278
+ if (!parseAttempt.success) {
4279
+ const diagnostic = {
4280
+ candidateId: candidate.id,
3582
4281
  source: candidate.source,
3583
4282
  usedRepair: parseAttempt.usedRepair,
3584
4283
  parseSuccess: false,
@@ -3840,48 +4539,19 @@ function formatZodIssues(issues) {
3840
4539
  `);
3841
4540
  }
3842
4541
 
3843
- // src/utils/debug-colors.ts
3844
- var ANSI = {
3845
- reset: "\x1B[0m",
3846
- bold: "\x1B[1m",
3847
- cyan: "\x1B[36m",
3848
- yellow: "\x1B[33m",
3849
- green: "\x1B[32m",
3850
- red: "\x1B[31m",
3851
- dim: "\x1B[2m"
3852
- };
3853
- function color(config, text, tone) {
3854
- if (!config.colors) {
3855
- return text;
3856
- }
3857
- return `${ANSI[tone]}${text}${ANSI.reset}`;
3858
- }
3859
- function dim(config, text) {
3860
- if (!config.colors) {
3861
- return text;
3862
- }
3863
- return `${ANSI.dim}${text}${ANSI.reset}`;
3864
- }
3865
- function title(config, text) {
3866
- if (!config.colors) {
3867
- return text;
3868
- }
3869
- return `${ANSI.bold}${text}${ANSI.reset}`;
3870
- }
3871
-
3872
4542
  // src/structured.ts
3873
4543
  class StructuredParseError extends Error {
3874
4544
  name = "StructuredParseError";
3875
- raw;
3876
- thinkBlocks;
4545
+ text;
4546
+ reasoning;
3877
4547
  candidates;
3878
4548
  zodIssues;
3879
4549
  repairLog;
3880
4550
  attempt;
3881
4551
  constructor(input) {
3882
4552
  super(input.message ?? `Structured parsing failed after ${input.attempt} attempt(s).`);
3883
- this.raw = input.raw;
3884
- this.thinkBlocks = input.thinkBlocks;
4553
+ this.text = input.text;
4554
+ this.reasoning = input.reasoning;
3885
4555
  this.candidates = input.candidates;
3886
4556
  this.zodIssues = input.zodIssues;
3887
4557
  this.repairLog = input.repairLog;
@@ -3912,12 +4582,6 @@ var RE_SIMPLE_IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3912
4582
  var RE_ESCAPE_QUOTE = /"/g;
3913
4583
  var RE_WHITESPACE2 = /\s+/g;
3914
4584
  var DEFAULT_SELF_HEAL_MAX_DIAGNOSTICS = 8;
3915
- var structuredOutdent = createOutdent({
3916
- trimLeadingNewline: true,
3917
- trimTrailingNewline: true,
3918
- newline: `
3919
- `
3920
- });
3921
4585
  var DEFAULT_STRICT_PARSE_OPTIONS = {
3922
4586
  repair: false,
3923
4587
  maxCandidates: 3,
@@ -4047,7 +4711,7 @@ async function structured(adapter, schemaOrOptions, promptInput, callOptions) {
4047
4711
  });
4048
4712
  const selfHealSource = resolveSelfHealSource(previous);
4049
4713
  const repairPrompt = buildSelfHealPrompt({
4050
- rawOutput: previous.raw,
4714
+ rawOutput: composeParseSource(previous.text, previous.reasoning),
4051
4715
  issues: previous.zodIssues,
4052
4716
  schema: normalized.schema,
4053
4717
  schemaInstruction: normalized.schemaInstruction,
@@ -4120,74 +4784,6 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
4120
4784
  function isStructuredOptions(value) {
4121
4785
  return typeof value === "object" && value !== null && "schema" in value && "prompt" in value;
4122
4786
  }
4123
- function resolvePrompt(prompt, context) {
4124
- const resolved = typeof prompt === "function" ? prompt(context) : prompt;
4125
- return normalizePromptValue(resolved, context);
4126
- }
4127
- function normalizePromptValue(value, context) {
4128
- if (typeof value === "string") {
4129
- return {
4130
- prompt: value
4131
- };
4132
- }
4133
- if (isPromptResolver(value)) {
4134
- return normalizePromptPayload(value.resolvePrompt(context));
4135
- }
4136
- return normalizePromptPayload(value);
4137
- }
4138
- function isPromptResolver(value) {
4139
- return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
4140
- }
4141
- function normalizePromptPayload(value) {
4142
- const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
4143
- const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
4144
- if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
4145
- throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4146
- }
4147
- return {
4148
- prompt,
4149
- systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
4150
- messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
4151
- };
4152
- }
4153
- function applyPromptOutdent(payload, enabled) {
4154
- if (!enabled) {
4155
- return payload;
4156
- }
4157
- return {
4158
- prompt: typeof payload.prompt === "string" ? structuredOutdent.string(payload.prompt) : undefined,
4159
- systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
4160
- messages: payload.messages?.map((message) => ({
4161
- ...message,
4162
- content: typeof message.content === "string" ? structuredOutdent.string(message.content) : message.content
4163
- }))
4164
- };
4165
- }
4166
- function isLLMMessage(value) {
4167
- if (typeof value !== "object" || value === null) {
4168
- return false;
4169
- }
4170
- const candidate = value;
4171
- if (candidate.role !== "system" && candidate.role !== "user" && candidate.role !== "assistant" && candidate.role !== "tool") {
4172
- return false;
4173
- }
4174
- return "content" in candidate;
4175
- }
4176
- function applyOutdentToOptionalPrompt(value, enabled) {
4177
- if (!enabled || typeof value !== "string") {
4178
- return value;
4179
- }
4180
- return structuredOutdent.string(value);
4181
- }
4182
- function mergeSystemPrompts(primary, secondary) {
4183
- const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
4184
- if (prompts.length === 0) {
4185
- return;
4186
- }
4187
- return prompts.join(`
4188
-
4189
- `);
4190
- }
4191
4787
  function prepareStructuredPromptPayload(payload, systemPrompt, schema, schemaInstruction) {
4192
4788
  if (Array.isArray(payload.messages) && payload.messages.length > 0) {
4193
4789
  const messages = payload.messages.map((message) => ({ ...message }));
@@ -4368,7 +4964,7 @@ function resolveSelfHealSource(attempt) {
4368
4964
  }
4369
4965
  return {
4370
4966
  kind: "raw",
4371
- text: attempt.raw
4967
+ text: composeParseSource(attempt.text, attempt.reasoning)
4372
4968
  };
4373
4969
  }
4374
4970
  function isSelfHealStalled(previous, current) {
@@ -4378,60 +4974,22 @@ function isSelfHealStalled(previous, current) {
4378
4974
  if (current.zodIssues.length < previous.zodIssues.length) {
4379
4975
  return false;
4380
4976
  }
4381
- if (current.parsed.errors.length < previous.parsed.errors.length) {
4382
- return false;
4383
- }
4384
- return buildSelfHealFailureFingerprint(previous) === buildSelfHealFailureFingerprint(current);
4385
- }
4386
- function buildSelfHealFailureFingerprint(attempt) {
4387
- const issues = attempt.zodIssues.map((issue) => `${formatIssuePath(issue.path)}:${issue.code}:${normalizeWhitespace(issue.message)}`).sort().join("|");
4388
- const errors = attempt.parsed.errors.map((error) => `${error.stage}:${error.candidateId ?? "-"}:${normalizeWhitespace(error.message)}`).sort().join("|");
4389
- const source = normalizeWhitespace(resolveSelfHealSource(attempt).text).slice(0, 512);
4390
- return [issues, errors, source].join("::");
4391
- }
4392
- function normalizeWhitespace(value) {
4393
- return value.replace(RE_WHITESPACE2, " ").trim();
4394
- }
4395
- function normalizeStreamConfig(option) {
4396
- if (typeof option === "boolean") {
4397
- return {
4398
- enabled: option
4399
- };
4400
- }
4401
- if (!option) {
4402
- return {
4403
- enabled: false
4404
- };
4405
- }
4406
- return {
4407
- enabled: option.enabled ?? true,
4408
- onData: option.onData,
4409
- to: option.to
4410
- };
4411
- }
4412
- function normalizeDebugConfig(option) {
4413
- if (typeof option === "boolean") {
4414
- return {
4415
- enabled: option,
4416
- colors: true,
4417
- logger: (line) => console.log(line)
4418
- };
4419
- }
4420
- if (!option) {
4421
- return {
4422
- enabled: false,
4423
- colors: true,
4424
- logger: (line) => console.log(line)
4425
- };
4977
+ if (current.parsed.errors.length < previous.parsed.errors.length) {
4978
+ return false;
4426
4979
  }
4427
- return {
4428
- enabled: option.enabled ?? true,
4429
- colors: option.colors ?? true,
4430
- logger: option.logger ?? ((line) => console.log(line))
4431
- };
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();
4432
4990
  }
4433
4991
  async function executeAttempt(adapter, input) {
4434
- const response = await callModel(adapter, {
4992
+ const response = await callModel2(adapter, {
4435
4993
  prompt: input.prompt,
4436
4994
  messages: input.messages,
4437
4995
  systemPrompt: input.systemPrompt,
@@ -4444,7 +5002,7 @@ async function executeAttempt(adapter, input) {
4444
5002
  selfHealEnabled: input.selfHealEnabled,
4445
5003
  timeout: input.timeout
4446
5004
  });
4447
- const parsed = parseWithObserve(response.text, input.schema, input.parseOptions, {
5005
+ const parsed = parseWithObserve(response.parseSource, input.schema, input.parseOptions, {
4448
5006
  observe: input.observe,
4449
5007
  attempt: input.attemptNumber,
4450
5008
  selfHeal: input.selfHeal
@@ -4453,8 +5011,8 @@ async function executeAttempt(adapter, input) {
4453
5011
  attempt: input.attemptNumber,
4454
5012
  selfHeal: input.selfHeal,
4455
5013
  via: response.via,
4456
- raw: response.text,
4457
- thinkBlocks: parsed.thinkBlocks,
5014
+ text: response.text,
5015
+ reasoning: response.reasoning,
4458
5016
  json: parsed.parsed,
4459
5017
  candidates: parsed.candidates.map((candidate) => candidate.content),
4460
5018
  repairLog: collectRepairLog(parsed),
@@ -4469,199 +5027,26 @@ async function executeAttempt(adapter, input) {
4469
5027
  trace
4470
5028
  };
4471
5029
  }
4472
- function withToolTimeout(client, toolTimeoutMs) {
4473
- return {
4474
- id: client.id,
4475
- listTools: client.listTools.bind(client),
4476
- close: client.close?.bind(client),
4477
- async callTool(params) {
4478
- let timeoutId;
4479
- const timeoutPromise = new Promise((_, reject) => {
4480
- timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
4481
- });
4482
- try {
4483
- return await Promise.race([client.callTool(params), timeoutPromise]);
4484
- } finally {
4485
- clearTimeout(timeoutId);
4486
- }
4487
- }
4488
- };
4489
- }
4490
- function applyToolTimeout(clients, toolTimeoutMs) {
4491
- return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
4492
- }
4493
- async function callModel(adapter, options) {
4494
- const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
4495
- const requestPayload = {
4496
- prompt: options.prompt,
4497
- messages: options.messages,
4498
- systemPrompt: options.systemPrompt,
4499
- temperature: options.request?.temperature,
4500
- maxTokens: options.request?.maxTokens,
4501
- mcpClients: options.request?.mcpClients,
4502
- toolChoice: options.request?.toolChoice,
4503
- parallelToolCalls: options.request?.parallelToolCalls,
4504
- maxToolRounds: options.request?.maxToolRounds,
4505
- onToolExecution: options.request?.onToolExecution,
4506
- transformToolOutput: options.request?.transformToolOutput,
4507
- transformToolArguments: options.request?.transformToolArguments,
4508
- transformToolCallParams: options.request?.transformToolCallParams,
4509
- unknownToolError: options.request?.unknownToolError,
4510
- toolDebug: options.request?.toolDebug,
4511
- body: options.request?.body,
4512
- signal: requestSignal
4513
- };
4514
- emitDebugRequest(options.debug, {
4515
- provider: adapter.provider,
4516
- model: adapter.model,
4517
- attempt: options.attempt,
4518
- selfHealAttempt: options.selfHeal,
4519
- selfHealEnabled: options.selfHealEnabled,
4520
- stream: options.stream.enabled && !!adapter.stream,
4521
- requestPayload
4522
- });
4523
- emitObserve(options.observe, {
4524
- stage: "llm.request",
4525
- attempt: options.attempt,
4526
- selfHeal: options.selfHeal,
4527
- message: "Sending LLM request.",
4528
- details: {
4529
- provider: adapter.provider,
4530
- model: adapter.model,
4531
- stream: options.stream.enabled && !!adapter.stream
4532
- }
4533
- });
4534
- if (options.stream.enabled && adapter.stream) {
4535
- let latestUsage;
4536
- let latestFinishReason;
4537
- let streamedRaw = "";
4538
- let sawToken = false;
4539
- let lastDataFingerprint;
4540
- const emitStreamingData = (raw, done, usage2, finishReason2) => {
4541
- const data = parseStreamingStructuredData(raw);
4542
- if (data === null && !done) {
4543
- return;
4544
- }
4545
- const fingerprint = toStreamDataFingerprint(data ?? null);
4546
- if (!done && fingerprint === lastDataFingerprint) {
4547
- return;
4548
- }
4549
- lastDataFingerprint = fingerprint;
4550
- options.stream.onData?.({
4551
- data: data ?? null,
4552
- raw,
4553
- done,
4554
- usage: usage2,
4555
- finishReason: finishReason2
4556
- });
4557
- emitObserve(options.observe, {
4558
- stage: "llm.stream.data",
4559
- attempt: options.attempt,
4560
- selfHeal: options.selfHeal,
4561
- message: done ? "Streaming structured data completed." : "Streaming structured data updated.",
4562
- details: {
4563
- done,
4564
- finishReason: finishReason2
4565
- }
4566
- });
4567
- };
4568
- const handleTextDelta = (delta) => {
4569
- if (!delta) {
4570
- return;
4571
- }
4572
- streamedRaw += delta;
4573
- if (options.stream.to === "stdout") {
4574
- process.stdout.write(delta);
4575
- }
4576
- emitObserve(options.observe, {
4577
- stage: "llm.stream.delta",
4578
- attempt: options.attempt,
4579
- selfHeal: options.selfHeal,
4580
- message: "Received stream delta.",
4581
- details: {
4582
- chars: delta.length
4583
- }
4584
- });
4585
- emitStreamingData(streamedRaw, false);
4586
- };
4587
- const response2 = await adapter.stream(requestPayload, {
4588
- onToken: (token) => {
4589
- sawToken = true;
4590
- handleTextDelta(token);
4591
- },
4592
- onChunk: (chunk) => {
4593
- if (!sawToken && chunk.textDelta) {
4594
- handleTextDelta(chunk.textDelta);
4595
- }
4596
- if (chunk.usage) {
4597
- latestUsage = preferLatestUsage(latestUsage, chunk.usage);
4598
- }
4599
- if (chunk.finishReason) {
4600
- latestFinishReason = chunk.finishReason;
4601
- }
4602
- }
4603
- });
4604
- const finalText = typeof response2.text === "string" && response2.text.length > 0 ? response2.text : streamedRaw;
4605
- const usage = preferLatestUsage(latestUsage, response2.usage);
4606
- const finishReason = response2.finishReason ?? latestFinishReason;
4607
- emitStreamingData(finalText, true, usage, finishReason);
4608
- emitObserve(options.observe, {
4609
- stage: "llm.response",
5030
+ async function callModel2(adapter, options) {
5031
+ return callModel(adapter, {
5032
+ ...options,
5033
+ buildEvent: ({ stage, message, details }) => ({
5034
+ stage,
4610
5035
  attempt: options.attempt,
4611
5036
  selfHeal: options.selfHeal,
4612
- message: "Streaming response completed.",
4613
- details: {
4614
- via: "stream",
4615
- chars: finalText.length,
4616
- finishReason
4617
- }
4618
- });
4619
- emitDebugResponse(options.debug, {
4620
- attempt: options.attempt,
4621
- selfHealAttempt: options.selfHeal,
4622
- selfHealEnabled: options.selfHealEnabled,
4623
- via: "stream",
4624
- responseText: finalText,
4625
- usage,
4626
- finishReason
4627
- });
4628
- return {
4629
- text: finalText,
4630
- via: "stream",
4631
- usage,
4632
- finishReason
4633
- };
4634
- }
4635
- const response = await adapter.complete(requestPayload);
4636
- emitObserve(options.observe, {
4637
- stage: "llm.response",
4638
- attempt: options.attempt,
4639
- selfHeal: options.selfHeal,
4640
- message: "Completion response received.",
4641
- details: {
4642
- via: "complete",
4643
- chars: response.text.length,
4644
- finishReason: response.finishReason
4645
- }
4646
- });
4647
- emitDebugResponse(options.debug, {
4648
- attempt: options.attempt,
4649
- selfHealAttempt: options.selfHeal,
4650
- selfHealEnabled: options.selfHealEnabled,
4651
- via: "complete",
4652
- responseText: response.text,
4653
- usage: response.usage,
4654
- 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"
4655
5046
  });
4656
- return {
4657
- text: response.text,
4658
- via: "complete",
4659
- usage: response.usage,
4660
- finishReason: response.finishReason
4661
- };
4662
5047
  }
4663
- function parseStreamingStructuredData(raw) {
4664
- const sanitized = sanitizeThink(raw);
5048
+ function parseStreamingStructuredData(parseSource) {
5049
+ const sanitized = sanitizeThink(parseSource);
4665
5050
  const start = findFirstJsonRootStart(sanitized.visibleText);
4666
5051
  if (start < 0) {
4667
5052
  return null;
@@ -4721,13 +5106,6 @@ function findFirstJsonRootStart(input) {
4721
5106
  }
4722
5107
  return Math.min(objectStart, arrayStart);
4723
5108
  }
4724
- function toStreamDataFingerprint(value) {
4725
- try {
4726
- return JSON.stringify(value);
4727
- } catch {
4728
- return "__unserializable__";
4729
- }
4730
- }
4731
5109
  function parseWithObserve(output, schema, parseOptions, context) {
4732
5110
  const userParseTrace = parseOptions.onTrace;
4733
5111
  return parseLLMOutput(output, schema, {
@@ -4757,38 +5135,20 @@ function buildSuccessResult(data, attempts) {
4757
5135
  const final = attempts.at(-1);
4758
5136
  return {
4759
5137
  data,
4760
- raw: final?.raw ?? "",
4761
- thinkBlocks: final?.thinkBlocks ?? [],
5138
+ text: final?.text ?? "",
5139
+ reasoning: final?.reasoning ?? "",
4762
5140
  json: final?.json ?? null,
4763
5141
  attempts,
4764
5142
  usage: aggregateUsage(attempts),
4765
5143
  finishReason: final?.finishReason
4766
5144
  };
4767
5145
  }
4768
- function aggregateUsage(attempts) {
4769
- let usage;
4770
- for (const attempt of attempts) {
4771
- usage = mergeUsage2(usage, attempt.usage);
4772
- }
4773
- return usage;
4774
- }
4775
- function mergeUsage2(base, next) {
4776
- if (!base && !next) {
4777
- return;
4778
- }
4779
- return {
4780
- inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
4781
- outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
4782
- totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
4783
- cost: (base?.cost ?? 0) + (next?.cost ?? 0)
4784
- };
4785
- }
4786
5146
  function toStructuredError(attempt) {
4787
5147
  if (!attempt) {
4788
5148
  return new StructuredParseError({
4789
5149
  message: "Structured parsing failed before any model response.",
4790
- raw: "",
4791
- thinkBlocks: [],
5150
+ text: "",
5151
+ reasoning: "",
4792
5152
  candidates: [],
4793
5153
  zodIssues: [],
4794
5154
  repairLog: [],
@@ -4796,8 +5156,8 @@ function toStructuredError(attempt) {
4796
5156
  });
4797
5157
  }
4798
5158
  return new StructuredParseError({
4799
- raw: attempt.raw,
4800
- thinkBlocks: attempt.thinkBlocks,
5159
+ text: attempt.text,
5160
+ reasoning: attempt.reasoning,
4801
5161
  candidates: attempt.candidates,
4802
5162
  zodIssues: attempt.zodIssues,
4803
5163
  repairLog: attempt.repairLog,
@@ -4807,59 +5167,6 @@ function toStructuredError(attempt) {
4807
5167
  function emitObserve(observe, event) {
4808
5168
  observe?.(event);
4809
5169
  }
4810
- function emitDebugRequest(config, input) {
4811
- const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4812
- const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4813
- const lines = [
4814
- color(config, title(config, [
4815
- "[structured][request]",
4816
- `attempt=${input.attempt}`,
4817
- `selfHealEnabled=${input.selfHealEnabled}`,
4818
- `selfHealAttempt=${input.selfHealAttempt}`
4819
- ].join(" ")), "cyan"),
4820
- dim(config, [
4821
- `provider=${input.provider ?? "unknown"}`,
4822
- `model=${input.model ?? "unknown"}`,
4823
- `stream=${input.stream}`
4824
- ].join(" ")),
4825
- color(config, "prompt:", "yellow"),
4826
- input.requestPayload.prompt ?? "(none)",
4827
- color(config, "messages:", "yellow"),
4828
- requestMessages,
4829
- color(config, "systemPrompt:", "yellow"),
4830
- input.requestPayload.systemPrompt ?? "(none)",
4831
- color(config, "request.body:", "yellow"),
4832
- requestBody
4833
- ];
4834
- emitDebug(config, lines.join(`
4835
- `));
4836
- }
4837
- function emitDebugResponse(config, input) {
4838
- const lines = [
4839
- color(config, title(config, [
4840
- "[structured][response]",
4841
- `attempt=${input.attempt}`,
4842
- `selfHealEnabled=${input.selfHealEnabled}`,
4843
- `selfHealAttempt=${input.selfHealAttempt}`
4844
- ].join(" ")), "green"),
4845
- dim(config, [
4846
- `via=${input.via}`,
4847
- `chars=${input.responseText.length}`,
4848
- `finishReason=${input.finishReason ?? "unknown"}`,
4849
- `usage=${JSON.stringify(input.usage ?? {})}`
4850
- ].join(" ")),
4851
- color(config, "text:", "yellow"),
4852
- input.responseText
4853
- ];
4854
- emitDebug(config, lines.join(`
4855
- `));
4856
- }
4857
- function emitDebug(config, message) {
4858
- if (!config.enabled) {
4859
- return;
4860
- }
4861
- config.logger(message);
4862
- }
4863
5170
 
4864
5171
  // src/llm.ts
4865
5172
  function createLLM(config, registry = createDefaultProviderRegistry()) {
@@ -4873,6 +5180,17 @@ function createLLM(config, registry = createDefaultProviderRegistry()) {
4873
5180
  const merged = mergeStructuredOptions(defaults, options);
4874
5181
  return structured(adapter, schema, prompt, merged);
4875
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
+ },
4876
5194
  async embed(input, options = {}) {
4877
5195
  if (!adapter.embed) {
4878
5196
  throw new Error(`Provider "${adapter.provider ?? "unknown"}" does not support embeddings.`);
@@ -4902,6 +5220,26 @@ function mergeStructuredOptions(defaults, overrides) {
4902
5220
  timeout: mergeObjectLike(defaults?.timeout, overrides?.timeout)
4903
5221
  };
4904
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
+ }
4905
5243
  function mergeObjectLike(defaults, overrides) {
4906
5244
  if (overrides === undefined) {
4907
5245
  return defaults;