@jaypie/llm 1.3.9 → 1.3.10

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.
@@ -14,35 +14,21 @@ var aws = require('@jaypie/aws');
14
14
  var openmeteo = require('openmeteo');
15
15
 
16
16
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
17
- const FIRST_CLASS_PROVIDER = {
18
- // https://docs.anthropic.com/en/docs/about-claude/models/overview
19
- ANTHROPIC: {
20
- DEFAULT: "claude-sonnet-4-6",
21
- LARGE: "claude-opus-4-8",
22
- SMALL: "claude-sonnet-4-6",
23
- TINY: "claude-haiku-4-5",
24
- },
25
- // https://ai.google.dev/gemini-api/docs/models
26
- GOOGLE: {
27
- DEFAULT: "gemini-3.1-pro-preview",
28
- LARGE: "gemini-3.1-pro-preview",
29
- SMALL: "gemini-3.5-flash",
30
- TINY: "gemini-3.1-flash-lite",
31
- },
32
- // https://developers.openai.com/api/docs/models
33
- OPENAI: {
34
- DEFAULT: "gpt-5.4",
35
- LARGE: "gpt-5.5",
36
- SMALL: "gpt-5.4-mini",
37
- TINY: "gpt-5.4-nano",
38
- },
39
- // https://docs.x.ai/developers/models
40
- XAI: {
41
- DEFAULT: "grok-latest",
42
- LARGE: "grok-latest",
43
- SMALL: "grok-4-1-fast-reasoning",
44
- TINY: "grok-4-1-fast-non-reasoning",
45
- },
17
+ /**
18
+ * Provider-neutral reasoning-effort levels — a five-point relative scale that
19
+ * deliberately borrows no provider's vocabulary. Each adapter translates these
20
+ * to its provider's native control (OpenAI `reasoning.effort`, Anthropic
21
+ * `output_config.effort`, Gemini `thinkingLevel`/`thinkingBudget`, Grok
22
+ * `reasoning_effort`, OpenRouter `reasoning.effort`), spreading the scale across
23
+ * the provider's available range. Omitting `effort` leaves the provider default
24
+ * untouched, so it is safe to set across a fallback chain.
25
+ */
26
+ const EFFORT = {
27
+ LOWEST: "lowest",
28
+ LOW: "low",
29
+ MEDIUM: "medium",
30
+ HIGH: "high",
31
+ HIGHEST: "highest",
46
32
  };
47
33
  const MODEL = {
48
34
  // Anthropic
@@ -56,19 +42,33 @@ const MODEL = {
56
42
  GEMINI_FLASH_LITE: "gemini-3.1-flash-lite",
57
43
  GEMINI_PRO: "gemini-3.1-pro-preview",
58
44
  // OpenAI
45
+ SOL: "gpt-5.6-sol",
46
+ TERRA: "gpt-5.6-terra",
47
+ LUNA: "gpt-5.6-luna",
48
+ /** @deprecated use MODEL.SOL (gpt-5.6-sol) */
59
49
  GPT: "gpt-5.5",
50
+ /** @deprecated use MODEL.TERRA (gpt-5.6-terra) */
60
51
  GPT_MINI: "gpt-5.4-mini",
52
+ /** @deprecated use MODEL.LUNA (gpt-5.6-luna) */
61
53
  GPT_NANO: "gpt-5.4-nano",
62
54
  // xAI
63
55
  GROK: "grok-latest",
56
+ // OpenRouter (provider-prefixed routes; traversed by the OpenRouter hot test)
57
+ OPENROUTER: {
58
+ GLM: "z-ai/glm-5.2",
59
+ LUNA: "openai/gpt-5.6-luna",
60
+ SONNET: "anthropic/claude-sonnet-5",
61
+ },
64
62
  };
65
63
  const GOOGLE_PROVIDER = {
66
64
  // https://ai.google.dev/gemini-api/docs/models
65
+ DEFAULT: MODEL.GEMINI_FLASH,
66
+ /** @deprecated Size tiers are retired in 2.0. Use PROVIDER.GOOGLE.DEFAULT, or pick a specific model from MODEL.*. */
67
67
  MODEL: {
68
- DEFAULT: FIRST_CLASS_PROVIDER.GOOGLE.DEFAULT,
69
- LARGE: FIRST_CLASS_PROVIDER.GOOGLE.LARGE,
70
- SMALL: FIRST_CLASS_PROVIDER.GOOGLE.SMALL,
71
- TINY: FIRST_CLASS_PROVIDER.GOOGLE.TINY,
68
+ DEFAULT: "gemini-3.1-pro-preview",
69
+ LARGE: "gemini-3.1-pro-preview",
70
+ SMALL: "gemini-3.5-flash",
71
+ TINY: "gemini-3.1-flash-lite",
72
72
  },
73
73
  MODEL_MATCH_WORDS: ["gemini", "google"],
74
74
  NAME: "google",
@@ -80,6 +80,10 @@ const GOOGLE_PROVIDER = {
80
80
  const PROVIDER = {
81
81
  // https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html
82
82
  BEDROCK: {
83
+ // Bedrock has no MODEL.* catalog entry yet; keep the literal default id.
84
+ // nova-pro is the Amazon-native model that reliably does tools+structured.
85
+ DEFAULT: "amazon.nova-pro-v1:0",
86
+ /** @deprecated Size tiers are retired in 2.0. Use PROVIDER.BEDROCK.DEFAULT. */
83
87
  MODEL: {
84
88
  DEFAULT: "amazon.nova-lite-v1:0",
85
89
  LARGE: "amazon.nova-pro-v1:0",
@@ -100,22 +104,26 @@ const PROVIDER = {
100
104
  },
101
105
  ANTHROPIC: {
102
106
  // https://docs.anthropic.com/en/docs/about-claude/models/overview
107
+ DEFAULT: MODEL.SONNET,
103
108
  MAX_TOKENS: {
104
109
  // Non-streaming ceiling: responses above ~16K output tokens risk HTTP
105
110
  // timeouts; streaming requests resolve to the model maximum instead
106
111
  // (see util/maxOutputTokens.ts)
107
112
  DEFAULT: 16384,
108
113
  },
114
+ /** @deprecated Size tiers are retired in 2.0. Use PROVIDER.ANTHROPIC.DEFAULT, or pick a specific model from MODEL.*. */
109
115
  MODEL: {
110
- DEFAULT: FIRST_CLASS_PROVIDER.ANTHROPIC.DEFAULT,
111
- LARGE: FIRST_CLASS_PROVIDER.ANTHROPIC.LARGE,
112
- SMALL: FIRST_CLASS_PROVIDER.ANTHROPIC.SMALL,
113
- TINY: FIRST_CLASS_PROVIDER.ANTHROPIC.TINY,
116
+ DEFAULT: "claude-sonnet-4-6",
117
+ LARGE: "claude-opus-4-8",
118
+ SMALL: "claude-sonnet-4-6",
119
+ TINY: "claude-haiku-4-5",
114
120
  },
115
121
  MODEL_MATCH_WORDS: [
116
122
  "anthropic",
117
123
  "claude",
124
+ "fable",
118
125
  "haiku",
126
+ "mythos",
119
127
  "opus",
120
128
  "sonnet",
121
129
  ],
@@ -138,21 +146,25 @@ const PROVIDER = {
138
146
  GOOGLE: GOOGLE_PROVIDER,
139
147
  OPENAI: {
140
148
  // https://platform.openai.com/docs/models
149
+ DEFAULT: MODEL.SOL,
150
+ /** @deprecated Size tiers are retired in 2.0. Use PROVIDER.OPENAI.DEFAULT, or pick a specific model from MODEL.*. */
141
151
  MODEL: {
142
- DEFAULT: FIRST_CLASS_PROVIDER.OPENAI.DEFAULT,
143
- LARGE: FIRST_CLASS_PROVIDER.OPENAI.LARGE,
144
- SMALL: FIRST_CLASS_PROVIDER.OPENAI.SMALL,
145
- TINY: FIRST_CLASS_PROVIDER.OPENAI.TINY,
152
+ DEFAULT: "gpt-5.4",
153
+ LARGE: "gpt-5.5",
154
+ SMALL: "gpt-5.4-mini",
155
+ TINY: "gpt-5.4-nano",
146
156
  },
147
- MODEL_MATCH_WORDS: ["openai", "gpt", /^o\d/],
157
+ MODEL_MATCH_WORDS: ["gpt", "luna", "openai", "sol", "terra", /^o\d/],
148
158
  NAME: "openai",
149
159
  },
150
160
  OPENROUTER: {
161
+ DEFAULT: MODEL.OPENROUTER.SONNET,
162
+ /** @deprecated Size tiers are retired in 2.0. Use PROVIDER.OPENROUTER.DEFAULT, or pick a specific route from MODEL.OPENROUTER.*. */
151
163
  MODEL: {
152
- DEFAULT: `anthropic/${FIRST_CLASS_PROVIDER.ANTHROPIC.DEFAULT}`,
153
- LARGE: `anthropic/${FIRST_CLASS_PROVIDER.ANTHROPIC.LARGE}`,
154
- SMALL: `anthropic/${FIRST_CLASS_PROVIDER.ANTHROPIC.SMALL}`,
155
- TINY: `anthropic/${FIRST_CLASS_PROVIDER.ANTHROPIC.TINY}`,
164
+ DEFAULT: "anthropic/claude-sonnet-4-6",
165
+ LARGE: "anthropic/claude-opus-4-8",
166
+ SMALL: "anthropic/claude-sonnet-4-6",
167
+ TINY: "anthropic/claude-haiku-4-5",
156
168
  },
157
169
  MODEL_MATCH_WORDS: ["openrouter"],
158
170
  NAME: "openrouter",
@@ -167,11 +179,13 @@ const PROVIDER = {
167
179
  // https://docs.x.ai/docs/models
168
180
  API_KEY: "XAI_API_KEY",
169
181
  BASE_URL: "https://api.x.ai/v1",
182
+ DEFAULT: MODEL.GROK,
183
+ /** @deprecated Size tiers are retired in 2.0. Use PROVIDER.XAI.DEFAULT, or pick a specific model from MODEL.*. */
170
184
  MODEL: {
171
- DEFAULT: FIRST_CLASS_PROVIDER.XAI.DEFAULT,
172
- LARGE: FIRST_CLASS_PROVIDER.XAI.LARGE,
173
- SMALL: FIRST_CLASS_PROVIDER.XAI.SMALL,
174
- TINY: FIRST_CLASS_PROVIDER.XAI.TINY,
185
+ DEFAULT: "grok-latest",
186
+ LARGE: "grok-latest",
187
+ SMALL: "grok-4-1-fast-reasoning",
188
+ TINY: "grok-4-1-fast-non-reasoning",
175
189
  },
176
190
  MODEL_MATCH_WORDS: ["grok", "xai"],
177
191
  NAME: "xai",
@@ -179,6 +193,7 @@ const PROVIDER = {
179
193
  };
180
194
  // Last: Defaults
181
195
  const DEFAULT = {
196
+ /** @deprecated Size tiers are retired in 2.0. Use DEFAULT.PROVIDER.DEFAULT, or pick a specific model from MODEL.*. */
182
197
  MODEL: {
183
198
  BASE: PROVIDER.OPENAI.MODEL.DEFAULT,
184
199
  LARGE: PROVIDER.OPENAI.MODEL.LARGE,
@@ -187,6 +202,10 @@ const DEFAULT = {
187
202
  },
188
203
  PROVIDER: PROVIDER.OPENAI,
189
204
  };
205
+ /**
206
+ * @deprecated Size-tier catalogs are retired in 2.0. Pick specific models from
207
+ * MODEL.* (grouped by provider via MODEL_MATCH_WORDS / determineModelProvider).
208
+ */
190
209
  // Only include "first class" models, not OpenRouter or other proxy services
191
210
  const ALL = {
192
211
  BASE: [
@@ -239,6 +258,7 @@ var constants = /*#__PURE__*/Object.freeze({
239
258
  __proto__: null,
240
259
  ALL: ALL,
241
260
  DEFAULT: DEFAULT,
261
+ EFFORT: EFFORT,
242
262
  MODEL: MODEL,
243
263
  PROVIDER: PROVIDER
244
264
  });
@@ -246,7 +266,7 @@ var constants = /*#__PURE__*/Object.freeze({
246
266
  function determineModelProvider(input) {
247
267
  if (!input) {
248
268
  return {
249
- model: DEFAULT.PROVIDER.MODEL.DEFAULT,
269
+ model: DEFAULT.PROVIDER.DEFAULT,
250
270
  provider: DEFAULT.PROVIDER.NAME,
251
271
  };
252
272
  }
@@ -269,85 +289,42 @@ function determineModelProvider(input) {
269
289
  // Check if input is a provider name
270
290
  if (input === PROVIDER.BEDROCK.NAME) {
271
291
  return {
272
- model: PROVIDER.BEDROCK.MODEL.DEFAULT,
292
+ model: PROVIDER.BEDROCK.DEFAULT,
273
293
  provider: PROVIDER.BEDROCK.NAME,
274
294
  };
275
295
  }
276
296
  if (input === PROVIDER.ANTHROPIC.NAME) {
277
297
  return {
278
- model: PROVIDER.ANTHROPIC.MODEL.DEFAULT,
298
+ model: PROVIDER.ANTHROPIC.DEFAULT,
279
299
  provider: PROVIDER.ANTHROPIC.NAME,
280
300
  };
281
301
  }
282
302
  if (input === PROVIDER.GOOGLE.NAME || input === "gemini") {
283
303
  return {
284
- model: PROVIDER.GOOGLE.MODEL.DEFAULT,
304
+ model: PROVIDER.GOOGLE.DEFAULT,
285
305
  provider: PROVIDER.GOOGLE.NAME,
286
306
  };
287
307
  }
288
308
  if (input === PROVIDER.OPENAI.NAME) {
289
309
  return {
290
- model: PROVIDER.OPENAI.MODEL.DEFAULT,
310
+ model: PROVIDER.OPENAI.DEFAULT,
291
311
  provider: PROVIDER.OPENAI.NAME,
292
312
  };
293
313
  }
294
314
  if (input === PROVIDER.OPENROUTER.NAME) {
295
315
  return {
296
- model: PROVIDER.OPENROUTER.MODEL.DEFAULT,
316
+ model: PROVIDER.OPENROUTER.DEFAULT,
297
317
  provider: PROVIDER.OPENROUTER.NAME,
298
318
  };
299
319
  }
300
320
  if (input === PROVIDER.XAI.NAME) {
301
321
  return {
302
- model: PROVIDER.XAI.MODEL.DEFAULT,
322
+ model: PROVIDER.XAI.DEFAULT,
303
323
  provider: PROVIDER.XAI.NAME,
304
324
  };
305
325
  }
306
- // Check if input matches an Anthropic model exactly
307
- for (const [, modelValue] of Object.entries(PROVIDER.ANTHROPIC.MODEL)) {
308
- if (input === modelValue) {
309
- return {
310
- model: input,
311
- provider: PROVIDER.ANTHROPIC.NAME,
312
- };
313
- }
314
- }
315
- // Check if input matches a Gemini model exactly
316
- for (const [, modelValue] of Object.entries(PROVIDER.GOOGLE.MODEL)) {
317
- if (input === modelValue) {
318
- return {
319
- model: input,
320
- provider: PROVIDER.GOOGLE.NAME,
321
- };
322
- }
323
- }
324
- // Check if input matches an OpenAI model exactly
325
- for (const [, modelValue] of Object.entries(PROVIDER.OPENAI.MODEL)) {
326
- if (input === modelValue) {
327
- return {
328
- model: input,
329
- provider: PROVIDER.OPENAI.NAME,
330
- };
331
- }
332
- }
333
- // Check if input matches an OpenRouter model exactly
334
- for (const [, modelValue] of Object.entries(PROVIDER.OPENROUTER.MODEL)) {
335
- if (input === modelValue) {
336
- return {
337
- model: input,
338
- provider: PROVIDER.OPENROUTER.NAME,
339
- };
340
- }
341
- }
342
- // Check if input matches an xAI model exactly
343
- for (const [, modelValue] of Object.entries(PROVIDER.XAI.MODEL)) {
344
- if (input === modelValue) {
345
- return {
346
- model: input,
347
- provider: PROVIDER.XAI.NAME,
348
- };
349
- }
350
- }
326
+ // Exact model ids are classified by the "/" rule and MODEL_MATCH_WORDS below,
327
+ // so no per-provider id catalog is consulted here.
351
328
  // Assume OpenRouter for models containing "/" (e.g., "openai/gpt-4", "anthropic/claude-3-opus")
352
329
  // This check must come before match words so that "openai/gpt-4" is not matched by "openai" keyword
353
330
  if (input.includes("/")) {
@@ -464,6 +441,125 @@ class BaseProviderAdapter {
464
441
  }
465
442
  }
466
443
 
444
+ /**
445
+ * Per-provider translation of the provider-neutral {@link LlmEffort} scale.
446
+ *
447
+ * The neutral enum (lowest → low → medium → high → highest) is a relative
448
+ * five-point scale. Each table below maps it onto the target provider's native
449
+ * effort control, keeping `medium`/`high` semantically aligned across providers
450
+ * and using each provider's extra bottom (`minimal`) or top (`xhigh`/`max`)
451
+ * rung where one exists. Providers with fewer levels collapse the ends and mark
452
+ * the result `papered`. A single `effort` value is therefore safe to reuse
453
+ * across providers and fallback chains.
454
+ */
455
+ /** Consistent debug message for a papered-over effort level. */
456
+ function paperedEffortMessage({ model, provider, requested, value, }) {
457
+ return `[llm] effort '${requested}' has no distinct tier on ${provider} model '${model}'; using '${value}'`;
458
+ }
459
+ // OpenAI Responses API `reasoning.effort`.
460
+ // Full ladder: minimal | low | medium | high | xhigh (plus `none`). Availability
461
+ // is not uniform across the gpt-5 line:
462
+ // - `xhigh` was introduced at gpt-5.2 and has been continuous since, so it is
463
+ // safe for our gpt-5.4 default and everything newer.
464
+ // - `minimal` shipped on gpt-5/5.1, was dropped at gpt-5.2, and returned on
465
+ // the current line; because that history is non-monotonic we only trust it
466
+ // from gpt-5.4 (our default floor) onward.
467
+ // Outside those windows (older gpt-5, o-series) the extreme rung is clamped and
468
+ // the mapping reports `papered: true`.
469
+ const OPENAI_EFFORT = {
470
+ [EFFORT.LOWEST]: "minimal",
471
+ [EFFORT.LOW]: "low",
472
+ [EFFORT.MEDIUM]: "medium",
473
+ [EFFORT.HIGH]: "high",
474
+ [EFFORT.HIGHEST]: "xhigh",
475
+ };
476
+ function openAiGptVersion(model) {
477
+ const match = model.match(/^gpt-(\d+)(?:\.(\d+))?/);
478
+ if (!match)
479
+ return null;
480
+ return { major: Number(match[1]), minor: match[2] ? Number(match[2]) : 0 };
481
+ }
482
+ function atLeast(version, major, minor) {
483
+ if (!version)
484
+ return false;
485
+ return (version.major > major || (version.major === major && version.minor >= minor));
486
+ }
487
+ function toOpenAiEffort(effort, { model }) {
488
+ const native = OPENAI_EFFORT[effort];
489
+ const version = openAiGptVersion(model);
490
+ // `minimal` only from gpt-5.4 (non-monotonic history; absent on o-series)
491
+ if (native === "minimal" && !atLeast(version, 5, 4)) {
492
+ return { papered: true, value: "low" };
493
+ }
494
+ // `xhigh` from gpt-5.2 onward (absent on older gpt-5 and o-series)
495
+ if (native === "xhigh" && !atLeast(version, 5, 2)) {
496
+ return { papered: true, value: "high" };
497
+ }
498
+ return { papered: false, value: native };
499
+ }
500
+ // xAI Grok `reasoning_effort` — low | medium | high. No sub-low or top rung, so
501
+ // `lowest` collapses onto `low` and `highest` onto `high`.
502
+ const XAI_EFFORT = {
503
+ [EFFORT.LOWEST]: { papered: true, value: "low" },
504
+ [EFFORT.LOW]: { papered: false, value: "low" },
505
+ [EFFORT.MEDIUM]: { papered: false, value: "medium" },
506
+ [EFFORT.HIGH]: { papered: false, value: "high" },
507
+ [EFFORT.HIGHEST]: { papered: true, value: "high" },
508
+ };
509
+ function toXaiEffort(effort) {
510
+ return XAI_EFFORT[effort];
511
+ }
512
+ // Anthropic `output_config.effort` — low | medium | high | xhigh | max. No
513
+ // sub-low rung, so `lowest` collapses onto `low`; `highest` reaches `max`.
514
+ const ANTHROPIC_EFFORT = {
515
+ [EFFORT.LOWEST]: { papered: true, value: "low" },
516
+ [EFFORT.LOW]: { papered: false, value: "low" },
517
+ [EFFORT.MEDIUM]: { papered: false, value: "medium" },
518
+ [EFFORT.HIGH]: { papered: false, value: "high" },
519
+ [EFFORT.HIGHEST]: { papered: false, value: "max" },
520
+ };
521
+ function toAnthropicEffort(effort) {
522
+ return ANTHROPIC_EFFORT[effort];
523
+ }
524
+ // Gemini 3.x `thinkingConfig.thinkingLevel` — MINIMAL | LOW | MEDIUM | HIGH. No
525
+ // top rung above HIGH, so `highest` collapses onto HIGH.
526
+ const GEMINI_THINKING_LEVEL = {
527
+ [EFFORT.LOWEST]: { papered: false, value: "MINIMAL" },
528
+ [EFFORT.LOW]: { papered: false, value: "LOW" },
529
+ [EFFORT.MEDIUM]: { papered: false, value: "MEDIUM" },
530
+ [EFFORT.HIGH]: { papered: false, value: "HIGH" },
531
+ [EFFORT.HIGHEST]: { papered: true, value: "HIGH" },
532
+ };
533
+ function toGeminiThinkingLevel(effort) {
534
+ return GEMINI_THINKING_LEVEL[effort];
535
+ }
536
+ // Gemini 2.5 `thinkingConfig.thinkingBudget` — every tier is a distinct token
537
+ // budget, so nothing is papered over. Floor (512) clears every 2.5 minimum;
538
+ // ceiling (24,576) is valid across 2.5 Pro (max 32,768) and Flash (max 24,576).
539
+ const GEMINI_THINKING_BUDGET = {
540
+ [EFFORT.LOWEST]: 512,
541
+ [EFFORT.LOW]: 4096,
542
+ [EFFORT.MEDIUM]: 8192,
543
+ [EFFORT.HIGH]: 16384,
544
+ [EFFORT.HIGHEST]: 24576,
545
+ };
546
+ function toGeminiThinkingBudget(effort) {
547
+ return { papered: false, value: GEMINI_THINKING_BUDGET[effort] };
548
+ }
549
+ // OpenRouter `reasoning.effort` — accepts the full minimal..xhigh ladder and
550
+ // maps to the routed provider's nearest supported level itself, so nothing is
551
+ // papered here.
552
+ const OPENROUTER_EFFORT = {
553
+ [EFFORT.LOWEST]: "minimal",
554
+ [EFFORT.LOW]: "low",
555
+ [EFFORT.MEDIUM]: "medium",
556
+ [EFFORT.HIGH]: "high",
557
+ [EFFORT.HIGHEST]: "xhigh",
558
+ };
559
+ function toOpenRouterEffort(effort) {
560
+ return { papered: false, value: OPENROUTER_EFFORT[effort] };
561
+ }
562
+
467
563
  // Enums
468
564
  exports.LlmMessageRole = void 0;
469
565
  (function (LlmMessageRole) {
@@ -1494,6 +1590,22 @@ function isTransientNetworkError(error) {
1494
1590
  // Constants
1495
1591
  //
1496
1592
  const STRUCTURED_OUTPUT_TOOL_NAME$3 = "structured_output";
1593
+ /**
1594
+ * Whether `output_config.effort` may be sent for this model. Anthropic added
1595
+ * the effort control on the Claude 4.5 line and up (the 5 family); older models
1596
+ * reject it. Parsing major/minor avoids matching a minor "5" in legacy ids
1597
+ * like `claude-3-5-sonnet`.
1598
+ */
1599
+ function supportsAnthropicEffort(model) {
1600
+ const match = model.match(/claude-[a-z]+-(\d+)(?:-(\d+))?/);
1601
+ if (!match)
1602
+ return false;
1603
+ const major = Number(match[1]);
1604
+ const minor = match[2] !== undefined ? Number(match[2]) : 0;
1605
+ if (major >= 5)
1606
+ return true;
1607
+ return major === 4 && minor >= 5;
1608
+ }
1497
1609
  const STRUCTURED_OUTPUT_NON_PARSE_STOP_REASONS = new Set([
1498
1610
  "refusal",
1499
1611
  "max_tokens",
@@ -1759,7 +1871,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
1759
1871
  constructor() {
1760
1872
  super(...arguments);
1761
1873
  this.name = PROVIDER.ANTHROPIC.NAME;
1762
- this.defaultModel = PROVIDER.ANTHROPIC.MODEL.DEFAULT;
1874
+ this.defaultModel = PROVIDER.ANTHROPIC.DEFAULT;
1763
1875
  // Session-level cache of models observed to reject `temperature` at runtime.
1764
1876
  // Populated by executeRequest on 400 errors so repeat calls skip the param.
1765
1877
  this.runtimeNoTemperatureModels = new Set();
@@ -1899,6 +2011,24 @@ class AnthropicAdapter extends BaseProviderAdapter {
1899
2011
  if (request.providerOptions) {
1900
2012
  Object.assign(anthropicRequest, request.providerOptions);
1901
2013
  }
2014
+ // Normalized reasoning effort -> output_config.effort (merged so a format
2015
+ // config above survives). First-class effort wins over providerOptions.
2016
+ if (request.effort &&
2017
+ supportsAnthropicEffort(anthropicRequest.model)) {
2018
+ const mapping = toAnthropicEffort(request.effort);
2019
+ if (mapping.papered) {
2020
+ log$1.log.debug(paperedEffortMessage({
2021
+ model: anthropicRequest.model,
2022
+ provider: this.name,
2023
+ requested: request.effort,
2024
+ value: mapping.value,
2025
+ }));
2026
+ }
2027
+ anthropicRequest.output_config = {
2028
+ ...anthropicRequest.output_config,
2029
+ effort: mapping.value,
2030
+ };
2031
+ }
1902
2032
  // First-class temperature takes precedence over providerOptions
1903
2033
  if (request.temperature !== undefined) {
1904
2034
  anthropicRequest.temperature = request.temperature;
@@ -1993,9 +2123,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
1993
2123
  */
1994
2124
  toFallbackStructuredOutputRequest(request) {
1995
2125
  const { output_config, ...rest } = request;
1996
- if (!output_config)
2126
+ if (!output_config?.format)
1997
2127
  return request;
1998
2128
  const fallbackRequest = { ...rest };
2129
+ // Preserve an effort setting; only the structured-output format falls back.
2130
+ if (output_config.effort) {
2131
+ fallbackRequest.output_config = { effort: output_config.effort };
2132
+ }
1999
2133
  const fakeTool = {
2000
2134
  name: STRUCTURED_OUTPUT_TOOL_NAME$3,
2001
2135
  description: "Output a structured JSON object, " +
@@ -2439,7 +2573,7 @@ class BedrockAdapter extends BaseProviderAdapter {
2439
2573
  constructor() {
2440
2574
  super(...arguments);
2441
2575
  this.name = PROVIDER.BEDROCK.NAME;
2442
- this.defaultModel = PROVIDER.BEDROCK.MODEL.DEFAULT;
2576
+ this.defaultModel = PROVIDER.BEDROCK.DEFAULT;
2443
2577
  this._modelsFallbackToStructuredOutputTool = new Set();
2444
2578
  this._modelsWithoutTemperature = new Set();
2445
2579
  }
@@ -2945,6 +3079,11 @@ const STRUCTURED_OUTPUT_TOOL_NAME$1 = "structured_output";
2945
3079
  // (including 2.5 thinking) do not support the combo and fall back to the
2946
3080
  // legacy `structured_output` fake-tool emulation.
2947
3081
  const GEMINI_3_PATTERN = /^gemini-3/;
3082
+ // Reasoning-effort control differs by generation: Gemini 3.x takes the
3083
+ // `thinkingLevel` enum, Gemini 2.5 takes an integer `thinkingBudget`. Sending
3084
+ // the wrong one errors, and models outside these families have no thinking
3085
+ // control, so effort is only applied when one of these matches.
3086
+ const GEMINI_25_PATTERN = /^gemini-2\.5/;
2948
3087
  /**
2949
3088
  * Detect 4xx errors that indicate the model itself does not support the
2950
3089
  * `responseJsonSchema` + tools combo. Triggers the runtime fallback to the
@@ -2991,7 +3130,7 @@ class GoogleAdapter extends BaseProviderAdapter {
2991
3130
  constructor() {
2992
3131
  super(...arguments);
2993
3132
  this.name = PROVIDER.GOOGLE.NAME;
2994
- this.defaultModel = PROVIDER.GOOGLE.MODEL.DEFAULT;
3133
+ this.defaultModel = PROVIDER.GOOGLE.DEFAULT;
2995
3134
  // Session-level cache of Gemini 3 models observed to reject the native
2996
3135
  // `responseJsonSchema` + tools combo. When a model is in this set,
2997
3136
  // buildRequest engages the legacy fake-tool path instead.
@@ -3119,6 +3258,42 @@ class GoogleAdapter extends BaseProviderAdapter {
3119
3258
  ...request.providerOptions,
3120
3259
  };
3121
3260
  }
3261
+ // Normalized reasoning effort -> thinkingConfig. Gemini 3.x uses the
3262
+ // thinkingLevel enum; 2.5 uses an integer thinkingBudget. Merged so a
3263
+ // providerOptions.thinkingConfig survives; first-class effort wins.
3264
+ if (request.effort) {
3265
+ const model = geminiRequest.model;
3266
+ let thinkingConfig;
3267
+ let papered = false;
3268
+ let value;
3269
+ if (GEMINI_3_PATTERN.test(model)) {
3270
+ const mapping = toGeminiThinkingLevel(request.effort);
3271
+ ({ papered, value } = mapping);
3272
+ thinkingConfig = { thinkingLevel: mapping.value };
3273
+ }
3274
+ else if (GEMINI_25_PATTERN.test(model)) {
3275
+ const mapping = toGeminiThinkingBudget(request.effort);
3276
+ ({ papered, value } = mapping);
3277
+ thinkingConfig = { thinkingBudget: mapping.value };
3278
+ }
3279
+ if (thinkingConfig) {
3280
+ if (papered) {
3281
+ log$1.log.debug(paperedEffortMessage({
3282
+ model,
3283
+ provider: this.name,
3284
+ requested: request.effort,
3285
+ value: value,
3286
+ }));
3287
+ }
3288
+ geminiRequest.config = {
3289
+ ...geminiRequest.config,
3290
+ thinkingConfig: {
3291
+ ...geminiRequest.config?.thinkingConfig,
3292
+ ...thinkingConfig,
3293
+ },
3294
+ };
3295
+ }
3296
+ }
3122
3297
  // First-class temperature takes precedence over providerOptions
3123
3298
  if (request.temperature !== undefined) {
3124
3299
  geminiRequest.config = {
@@ -4038,7 +4213,7 @@ class OpenAiAdapter extends BaseProviderAdapter {
4038
4213
  constructor() {
4039
4214
  super(...arguments);
4040
4215
  this.name = PROVIDER.OPENAI.NAME;
4041
- this.defaultModel = PROVIDER.OPENAI.MODEL.DEFAULT;
4216
+ this.defaultModel = PROVIDER.OPENAI.DEFAULT;
4042
4217
  // Session-level cache of models observed to reject `temperature` at runtime.
4043
4218
  // Populated by executeRequest on 400 errors so repeat calls skip the param.
4044
4219
  this.runtimeNoTemperatureModels = new Set();
@@ -4054,6 +4229,14 @@ class OpenAiAdapter extends BaseProviderAdapter {
4054
4229
  return false;
4055
4230
  return !MODELS_WITHOUT_TEMPERATURE$1.some((pattern) => pattern.test(model));
4056
4231
  }
4232
+ /** Whether `reasoning.effort` may be sent for this model. Overridden by xAI. */
4233
+ supportsReasoningEffort(model) {
4234
+ return isReasoningModel(model);
4235
+ }
4236
+ /** Translate a normalized effort to this provider's `reasoning.effort` value. */
4237
+ mapReasoningEffort(effort, model) {
4238
+ return toOpenAiEffort(effort, { model });
4239
+ }
4057
4240
  //
4058
4241
  // Request Building
4059
4242
  //
@@ -4092,6 +4275,24 @@ class OpenAiAdapter extends BaseProviderAdapter {
4092
4275
  if (request.providerOptions) {
4093
4276
  Object.assign(openaiRequest, request.providerOptions);
4094
4277
  }
4278
+ // Normalized reasoning effort -> reasoning.effort (merged so the
4279
+ // summary:auto above survives). First-class effort wins over providerOptions.
4280
+ if (request.effort && this.supportsReasoningEffort(model)) {
4281
+ const mapping = this.mapReasoningEffort(request.effort, model);
4282
+ if (mapping.papered) {
4283
+ log$1.log.debug(paperedEffortMessage({
4284
+ model,
4285
+ provider: this.name,
4286
+ requested: request.effort,
4287
+ value: mapping.value,
4288
+ }));
4289
+ }
4290
+ const existingReasoning = openaiRequest.reasoning ?? {};
4291
+ openaiRequest.reasoning = {
4292
+ ...existingReasoning,
4293
+ effort: mapping.value,
4294
+ };
4295
+ }
4095
4296
  // First-class temperature takes precedence over providerOptions
4096
4297
  if (request.temperature !== undefined) {
4097
4298
  openaiRequest.temperature = request.temperature;
@@ -4686,7 +4887,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4686
4887
  constructor() {
4687
4888
  super(...arguments);
4688
4889
  this.name = PROVIDER.OPENROUTER.NAME;
4689
- this.defaultModel = PROVIDER.OPENROUTER.MODEL.DEFAULT;
4890
+ this.defaultModel = PROVIDER.OPENROUTER.DEFAULT;
4690
4891
  // Session-level cache of models observed to reject native
4691
4892
  // `response_format: json_schema`. When a model is in this set, buildRequest
4692
4893
  // engages the legacy fake-tool path instead of native structured output.
@@ -4779,6 +4980,15 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4779
4980
  if (request.providerOptions) {
4780
4981
  Object.assign(openRouterRequest, request.providerOptions);
4781
4982
  }
4983
+ // Normalized reasoning effort -> reasoning.effort. OpenRouter accepts the
4984
+ // full ladder and maps to the routed provider's nearest supported level.
4985
+ // First-class effort wins over providerOptions.
4986
+ if (request.effort) {
4987
+ openRouterRequest.reasoning = {
4988
+ ...openRouterRequest.reasoning,
4989
+ effort: toOpenRouterEffort(request.effort).value,
4990
+ };
4991
+ }
4782
4992
  // First-class temperature takes precedence over providerOptions
4783
4993
  if (request.temperature !== undefined) {
4784
4994
  openRouterRequest.temperature =
@@ -5397,8 +5607,21 @@ class XaiAdapter extends OpenAiAdapter {
5397
5607
  super(...arguments);
5398
5608
  // @ts-expect-error Narrowing override: xAI name differs from parent's literal "openai"
5399
5609
  this.name = PROVIDER.XAI.NAME;
5400
- // @ts-expect-error Narrowing override: xAI default model differs from parent's literal
5401
- this.defaultModel = PROVIDER.XAI.MODEL.DEFAULT;
5610
+ this.defaultModel = PROVIDER.XAI.DEFAULT;
5611
+ }
5612
+ /**
5613
+ * Grok gates reasoning effort by model, not by the OpenAI `gpt-*`/`o*`
5614
+ * patterns. Only explicit `*-reasoning` models accept `reasoning_effort`;
5615
+ * bare grok-4 reasons implicitly and rejects it, so we stay conservative and
5616
+ * only opt in models whose name advertises reasoning.
5617
+ */
5618
+ supportsReasoningEffort(model) {
5619
+ if (/non-reasoning/.test(model))
5620
+ return false;
5621
+ return /reasoning/.test(model);
5622
+ }
5623
+ mapReasoningEffort(effort) {
5624
+ return toXaiEffort(effort);
5402
5625
  }
5403
5626
  classifyError(error) {
5404
5627
  if (isTransientIngestError(error)) {
@@ -6675,6 +6898,7 @@ class OperateLoop {
6675
6898
  }
6676
6899
  // Rebuild request with updated history for next turn
6677
6900
  request = {
6901
+ effort: options.effort,
6678
6902
  format: state.formattedFormat,
6679
6903
  instructions: options.instructions,
6680
6904
  messages: state.currentInput,
@@ -6778,6 +7002,7 @@ class OperateLoop {
6778
7002
  }
6779
7003
  buildInitialRequest(state, options) {
6780
7004
  return {
7005
+ effort: options.effort,
6781
7006
  format: state.formattedFormat,
6782
7007
  instructions: options.instructions,
6783
7008
  messages: state.currentInput,
@@ -7292,6 +7517,7 @@ class StreamLoop {
7292
7517
  }
7293
7518
  // Rebuild request with updated history for next turn
7294
7519
  request = {
7520
+ effort: options.effort,
7295
7521
  format: state.formattedFormat,
7296
7522
  instructions: options.instructions,
7297
7523
  messages: state.currentInput,
@@ -7367,6 +7593,7 @@ class StreamLoop {
7367
7593
  }
7368
7594
  buildInitialRequest(state, options) {
7369
7595
  return {
7596
+ effort: options.effort,
7370
7597
  format: state.formattedFormat,
7371
7598
  instructions: options.instructions,
7372
7599
  messages: state.currentInput,
@@ -7930,7 +8157,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
7930
8157
 
7931
8158
  // Main class implementation
7932
8159
  class AnthropicProvider {
7933
- constructor(model = PROVIDER.ANTHROPIC.MODEL.DEFAULT, { apiKey } = {}) {
8160
+ constructor(model = PROVIDER.ANTHROPIC.DEFAULT, { apiKey } = {}) {
7934
8161
  this.log = getLogger$5();
7935
8162
  this.conversationHistory = [];
7936
8163
  this.model = model;
@@ -8038,7 +8265,7 @@ async function initializeClient$4({ region, } = {}) {
8038
8265
  }
8039
8266
 
8040
8267
  class BedrockProvider {
8041
- constructor(model = PROVIDER.BEDROCK.MODEL.DEFAULT, { region } = {}) {
8268
+ constructor(model = PROVIDER.BEDROCK.DEFAULT, { region } = {}) {
8042
8269
  this.log = getLogger$4();
8043
8270
  this.conversationHistory = [];
8044
8271
  this.model = model;
@@ -8281,7 +8508,7 @@ function prepareMessages$2(message, { data, placeholders } = {}) {
8281
8508
  }
8282
8509
 
8283
8510
  class GoogleProvider {
8284
- constructor(model = PROVIDER.GOOGLE.MODEL.DEFAULT, { apiKey } = {}) {
8511
+ constructor(model = PROVIDER.GOOGLE.DEFAULT, { apiKey } = {}) {
8285
8512
  this.log = getLogger$3();
8286
8513
  this.conversationHistory = [];
8287
8514
  this.model = model;
@@ -8483,7 +8710,7 @@ async function createTextCompletion(client, { messages, model, }) {
8483
8710
  }
8484
8711
 
8485
8712
  class OpenAiProvider {
8486
- constructor(model = PROVIDER.OPENAI.MODEL.DEFAULT, { apiKey } = {}) {
8713
+ constructor(model = PROVIDER.OPENAI.DEFAULT, { apiKey } = {}) {
8487
8714
  this.log = getLogger$2();
8488
8715
  this.conversationHistory = [];
8489
8716
  this.model = model;
@@ -8714,7 +8941,7 @@ async function initializeClient$1({ apiKey, } = {}) {
8714
8941
  }
8715
8942
  // Get default model from environment or constants
8716
8943
  function getDefaultModel() {
8717
- return process.env.OPENROUTER_MODEL || PROVIDER.OPENROUTER.MODEL.DEFAULT;
8944
+ return process.env.OPENROUTER_MODEL || PROVIDER.OPENROUTER.DEFAULT;
8718
8945
  }
8719
8946
  function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
8720
8947
  const content = placeholders?.system === false
@@ -8866,7 +9093,7 @@ async function initializeClient({ apiKey, } = {}) {
8866
9093
  }
8867
9094
 
8868
9095
  class XaiProvider {
8869
- constructor(model = PROVIDER.XAI.MODEL.DEFAULT, { apiKey } = {}) {
9096
+ constructor(model = PROVIDER.XAI.DEFAULT, { apiKey } = {}) {
8870
9097
  this.log = getLogger$2();
8871
9098
  this.conversationHistory = [];
8872
9099
  this.model = model;
@@ -8997,23 +9224,25 @@ class Llm {
8997
9224
  const { apiKey, model } = options;
8998
9225
  switch (providerName) {
8999
9226
  case PROVIDER.ANTHROPIC.NAME:
9000
- return new AnthropicProvider(model || PROVIDER.ANTHROPIC.MODEL.DEFAULT, { apiKey });
9227
+ return new AnthropicProvider(model || PROVIDER.ANTHROPIC.DEFAULT, {
9228
+ apiKey,
9229
+ });
9001
9230
  case PROVIDER.BEDROCK.NAME:
9002
- return new BedrockProvider(model || PROVIDER.BEDROCK.MODEL.DEFAULT);
9231
+ return new BedrockProvider(model || PROVIDER.BEDROCK.DEFAULT);
9003
9232
  case PROVIDER.GOOGLE.NAME:
9004
- return new GoogleProvider(model || PROVIDER.GOOGLE.MODEL.DEFAULT, {
9233
+ return new GoogleProvider(model || PROVIDER.GOOGLE.DEFAULT, {
9005
9234
  apiKey,
9006
9235
  });
9007
9236
  case PROVIDER.OPENAI.NAME:
9008
- return new OpenAiProvider(model || PROVIDER.OPENAI.MODEL.DEFAULT, {
9237
+ return new OpenAiProvider(model || PROVIDER.OPENAI.DEFAULT, {
9009
9238
  apiKey,
9010
9239
  });
9011
9240
  case PROVIDER.OPENROUTER.NAME:
9012
- return new OpenRouterProvider(model || PROVIDER.OPENROUTER.MODEL.DEFAULT, {
9241
+ return new OpenRouterProvider(model || PROVIDER.OPENROUTER.DEFAULT, {
9013
9242
  apiKey,
9014
9243
  });
9015
9244
  case PROVIDER.XAI.NAME:
9016
- return new XaiProvider(model || PROVIDER.XAI.MODEL.DEFAULT, {
9245
+ return new XaiProvider(model || PROVIDER.XAI.DEFAULT, {
9017
9246
  apiKey,
9018
9247
  });
9019
9248
  default: