@jaypie/llm 1.3.9 → 1.3.11
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/cjs/Llm.d.ts +19 -11
- package/dist/cjs/constants.d.ts +53 -4
- package/dist/cjs/errors/LlmError.d.ts +62 -0
- package/dist/cjs/errors/toLlmError.d.ts +11 -0
- package/dist/cjs/index.cjs +739 -177
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +177 -19
- package/dist/cjs/index.d.ts +5 -1
- package/dist/cjs/operate/adapters/AnthropicAdapter.d.ts +3 -2
- package/dist/cjs/operate/adapters/BedrockAdapter.d.ts +1 -1
- package/dist/cjs/operate/adapters/GoogleAdapter.d.ts +1 -1
- package/dist/cjs/operate/adapters/OpenAiAdapter.d.ts +7 -1
- package/dist/cjs/operate/adapters/OpenRouterAdapter.d.ts +5 -1
- package/dist/cjs/operate/adapters/XaiAdapter.d.ts +11 -1
- package/dist/cjs/operate/retry/RetryExecutor.d.ts +13 -0
- package/dist/cjs/operate/types.d.ts +15 -1
- package/dist/cjs/providers/google/types.d.ts +5 -0
- package/dist/cjs/types/LlmProvider.interface.d.ts +18 -0
- package/dist/cjs/util/classifyProviderError.d.ts +13 -0
- package/dist/cjs/util/effort.d.ts +42 -0
- package/dist/cjs/util/resolveModelChain.d.ts +17 -0
- package/dist/esm/Llm.d.ts +19 -11
- package/dist/esm/constants.d.ts +53 -4
- package/dist/esm/errors/LlmError.d.ts +62 -0
- package/dist/esm/errors/toLlmError.d.ts +11 -0
- package/dist/esm/index.d.ts +177 -19
- package/dist/esm/index.js +705 -148
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/adapters/AnthropicAdapter.d.ts +3 -2
- package/dist/esm/operate/adapters/BedrockAdapter.d.ts +1 -1
- package/dist/esm/operate/adapters/GoogleAdapter.d.ts +1 -1
- package/dist/esm/operate/adapters/OpenAiAdapter.d.ts +7 -1
- package/dist/esm/operate/adapters/OpenRouterAdapter.d.ts +5 -1
- package/dist/esm/operate/adapters/XaiAdapter.d.ts +11 -1
- package/dist/esm/operate/retry/RetryExecutor.d.ts +13 -0
- package/dist/esm/operate/types.d.ts +15 -1
- package/dist/esm/providers/google/types.d.ts +5 -0
- package/dist/esm/types/LlmProvider.interface.d.ts +18 -0
- package/dist/esm/util/classifyProviderError.d.ts +13 -0
- package/dist/esm/util/effort.d.ts +42 -0
- package/dist/esm/util/resolveModelChain.d.ts +17 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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:
|
|
69
|
-
LARGE:
|
|
70
|
-
SMALL:
|
|
71
|
-
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:
|
|
111
|
-
LARGE:
|
|
112
|
-
SMALL:
|
|
113
|
-
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:
|
|
143
|
-
LARGE:
|
|
144
|
-
SMALL:
|
|
145
|
-
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", "
|
|
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:
|
|
153
|
-
LARGE:
|
|
154
|
-
SMALL:
|
|
155
|
-
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:
|
|
172
|
-
LARGE:
|
|
173
|
-
SMALL:
|
|
174
|
-
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
322
|
+
model: PROVIDER.XAI.DEFAULT,
|
|
303
323
|
provider: PROVIDER.XAI.NAME,
|
|
304
324
|
};
|
|
305
325
|
}
|
|
306
|
-
//
|
|
307
|
-
|
|
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("/")) {
|
|
@@ -427,6 +404,36 @@ function determineModelProvider(input) {
|
|
|
427
404
|
};
|
|
428
405
|
}
|
|
429
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Normalizes a `model` option that may be a single model name or a
|
|
409
|
+
* preference-ordered array of model names.
|
|
410
|
+
*
|
|
411
|
+
* A `string[]` is interpreted as a fallback chain: index `0` is the primary
|
|
412
|
+
* model, indices `1..` become fallback entries with an auto-detected provider
|
|
413
|
+
* (reusing `determineModelProvider`, exactly as the constructor's first
|
|
414
|
+
* argument does).
|
|
415
|
+
*
|
|
416
|
+
* Returns the primary model plus the derived fallback chain. A bare string
|
|
417
|
+
* yields an empty chain and is unchanged.
|
|
418
|
+
*/
|
|
419
|
+
function resolveModelChain(model) {
|
|
420
|
+
if (!Array.isArray(model)) {
|
|
421
|
+
return { fallback: [], model };
|
|
422
|
+
}
|
|
423
|
+
if (model.length === 0) {
|
|
424
|
+
throw new errors.ConfigurationError("model array must contain at least one model name");
|
|
425
|
+
}
|
|
426
|
+
const [primary, ...rest] = model;
|
|
427
|
+
const fallback = rest.map((name) => {
|
|
428
|
+
const determined = determineModelProvider(name);
|
|
429
|
+
if (!determined.provider) {
|
|
430
|
+
throw new errors.ConfigurationError(`Unable to determine provider from model: ${name}`);
|
|
431
|
+
}
|
|
432
|
+
return { model: determined.model, provider: determined.provider };
|
|
433
|
+
});
|
|
434
|
+
return { fallback, model: primary };
|
|
435
|
+
}
|
|
436
|
+
|
|
430
437
|
//
|
|
431
438
|
//
|
|
432
439
|
// Abstract Base Adapter
|
|
@@ -464,6 +471,125 @@ class BaseProviderAdapter {
|
|
|
464
471
|
}
|
|
465
472
|
}
|
|
466
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Per-provider translation of the provider-neutral {@link LlmEffort} scale.
|
|
476
|
+
*
|
|
477
|
+
* The neutral enum (lowest → low → medium → high → highest) is a relative
|
|
478
|
+
* five-point scale. Each table below maps it onto the target provider's native
|
|
479
|
+
* effort control, keeping `medium`/`high` semantically aligned across providers
|
|
480
|
+
* and using each provider's extra bottom (`minimal`) or top (`xhigh`/`max`)
|
|
481
|
+
* rung where one exists. Providers with fewer levels collapse the ends and mark
|
|
482
|
+
* the result `papered`. A single `effort` value is therefore safe to reuse
|
|
483
|
+
* across providers and fallback chains.
|
|
484
|
+
*/
|
|
485
|
+
/** Consistent debug message for a papered-over effort level. */
|
|
486
|
+
function paperedEffortMessage({ model, provider, requested, value, }) {
|
|
487
|
+
return `[llm] effort '${requested}' has no distinct tier on ${provider} model '${model}'; using '${value}'`;
|
|
488
|
+
}
|
|
489
|
+
// OpenAI Responses API `reasoning.effort`.
|
|
490
|
+
// Full ladder: minimal | low | medium | high | xhigh (plus `none`). Availability
|
|
491
|
+
// is not uniform across the gpt-5 line:
|
|
492
|
+
// - `xhigh` was introduced at gpt-5.2 and has been continuous since, so it is
|
|
493
|
+
// safe for our gpt-5.4 default and everything newer.
|
|
494
|
+
// - `minimal` shipped on gpt-5/5.1, was dropped at gpt-5.2, and returned on
|
|
495
|
+
// the current line; because that history is non-monotonic we only trust it
|
|
496
|
+
// from gpt-5.4 (our default floor) onward.
|
|
497
|
+
// Outside those windows (older gpt-5, o-series) the extreme rung is clamped and
|
|
498
|
+
// the mapping reports `papered: true`.
|
|
499
|
+
const OPENAI_EFFORT = {
|
|
500
|
+
[EFFORT.LOWEST]: "minimal",
|
|
501
|
+
[EFFORT.LOW]: "low",
|
|
502
|
+
[EFFORT.MEDIUM]: "medium",
|
|
503
|
+
[EFFORT.HIGH]: "high",
|
|
504
|
+
[EFFORT.HIGHEST]: "xhigh",
|
|
505
|
+
};
|
|
506
|
+
function openAiGptVersion(model) {
|
|
507
|
+
const match = model.match(/^gpt-(\d+)(?:\.(\d+))?/);
|
|
508
|
+
if (!match)
|
|
509
|
+
return null;
|
|
510
|
+
return { major: Number(match[1]), minor: match[2] ? Number(match[2]) : 0 };
|
|
511
|
+
}
|
|
512
|
+
function atLeast(version, major, minor) {
|
|
513
|
+
if (!version)
|
|
514
|
+
return false;
|
|
515
|
+
return (version.major > major || (version.major === major && version.minor >= minor));
|
|
516
|
+
}
|
|
517
|
+
function toOpenAiEffort(effort, { model }) {
|
|
518
|
+
const native = OPENAI_EFFORT[effort];
|
|
519
|
+
const version = openAiGptVersion(model);
|
|
520
|
+
// `minimal` only from gpt-5.4 (non-monotonic history; absent on o-series)
|
|
521
|
+
if (native === "minimal" && !atLeast(version, 5, 4)) {
|
|
522
|
+
return { papered: true, value: "low" };
|
|
523
|
+
}
|
|
524
|
+
// `xhigh` from gpt-5.2 onward (absent on older gpt-5 and o-series)
|
|
525
|
+
if (native === "xhigh" && !atLeast(version, 5, 2)) {
|
|
526
|
+
return { papered: true, value: "high" };
|
|
527
|
+
}
|
|
528
|
+
return { papered: false, value: native };
|
|
529
|
+
}
|
|
530
|
+
// xAI Grok `reasoning_effort` — low | medium | high. No sub-low or top rung, so
|
|
531
|
+
// `lowest` collapses onto `low` and `highest` onto `high`.
|
|
532
|
+
const XAI_EFFORT = {
|
|
533
|
+
[EFFORT.LOWEST]: { papered: true, value: "low" },
|
|
534
|
+
[EFFORT.LOW]: { papered: false, value: "low" },
|
|
535
|
+
[EFFORT.MEDIUM]: { papered: false, value: "medium" },
|
|
536
|
+
[EFFORT.HIGH]: { papered: false, value: "high" },
|
|
537
|
+
[EFFORT.HIGHEST]: { papered: true, value: "high" },
|
|
538
|
+
};
|
|
539
|
+
function toXaiEffort(effort) {
|
|
540
|
+
return XAI_EFFORT[effort];
|
|
541
|
+
}
|
|
542
|
+
// Anthropic `output_config.effort` — low | medium | high | xhigh | max. No
|
|
543
|
+
// sub-low rung, so `lowest` collapses onto `low`; `highest` reaches `max`.
|
|
544
|
+
const ANTHROPIC_EFFORT = {
|
|
545
|
+
[EFFORT.LOWEST]: { papered: true, value: "low" },
|
|
546
|
+
[EFFORT.LOW]: { papered: false, value: "low" },
|
|
547
|
+
[EFFORT.MEDIUM]: { papered: false, value: "medium" },
|
|
548
|
+
[EFFORT.HIGH]: { papered: false, value: "high" },
|
|
549
|
+
[EFFORT.HIGHEST]: { papered: false, value: "max" },
|
|
550
|
+
};
|
|
551
|
+
function toAnthropicEffort(effort) {
|
|
552
|
+
return ANTHROPIC_EFFORT[effort];
|
|
553
|
+
}
|
|
554
|
+
// Gemini 3.x `thinkingConfig.thinkingLevel` — MINIMAL | LOW | MEDIUM | HIGH. No
|
|
555
|
+
// top rung above HIGH, so `highest` collapses onto HIGH.
|
|
556
|
+
const GEMINI_THINKING_LEVEL = {
|
|
557
|
+
[EFFORT.LOWEST]: { papered: false, value: "MINIMAL" },
|
|
558
|
+
[EFFORT.LOW]: { papered: false, value: "LOW" },
|
|
559
|
+
[EFFORT.MEDIUM]: { papered: false, value: "MEDIUM" },
|
|
560
|
+
[EFFORT.HIGH]: { papered: false, value: "HIGH" },
|
|
561
|
+
[EFFORT.HIGHEST]: { papered: true, value: "HIGH" },
|
|
562
|
+
};
|
|
563
|
+
function toGeminiThinkingLevel(effort) {
|
|
564
|
+
return GEMINI_THINKING_LEVEL[effort];
|
|
565
|
+
}
|
|
566
|
+
// Gemini 2.5 `thinkingConfig.thinkingBudget` — every tier is a distinct token
|
|
567
|
+
// budget, so nothing is papered over. Floor (512) clears every 2.5 minimum;
|
|
568
|
+
// ceiling (24,576) is valid across 2.5 Pro (max 32,768) and Flash (max 24,576).
|
|
569
|
+
const GEMINI_THINKING_BUDGET = {
|
|
570
|
+
[EFFORT.LOWEST]: 512,
|
|
571
|
+
[EFFORT.LOW]: 4096,
|
|
572
|
+
[EFFORT.MEDIUM]: 8192,
|
|
573
|
+
[EFFORT.HIGH]: 16384,
|
|
574
|
+
[EFFORT.HIGHEST]: 24576,
|
|
575
|
+
};
|
|
576
|
+
function toGeminiThinkingBudget(effort) {
|
|
577
|
+
return { papered: false, value: GEMINI_THINKING_BUDGET[effort] };
|
|
578
|
+
}
|
|
579
|
+
// OpenRouter `reasoning.effort` — accepts the full minimal..xhigh ladder and
|
|
580
|
+
// maps to the routed provider's nearest supported level itself, so nothing is
|
|
581
|
+
// papered here.
|
|
582
|
+
const OPENROUTER_EFFORT = {
|
|
583
|
+
[EFFORT.LOWEST]: "minimal",
|
|
584
|
+
[EFFORT.LOW]: "low",
|
|
585
|
+
[EFFORT.MEDIUM]: "medium",
|
|
586
|
+
[EFFORT.HIGH]: "high",
|
|
587
|
+
[EFFORT.HIGHEST]: "xhigh",
|
|
588
|
+
};
|
|
589
|
+
function toOpenRouterEffort(effort) {
|
|
590
|
+
return { papered: false, value: OPENROUTER_EFFORT[effort] };
|
|
591
|
+
}
|
|
592
|
+
|
|
467
593
|
// Enums
|
|
468
594
|
exports.LlmMessageRole = void 0;
|
|
469
595
|
(function (LlmMessageRole) {
|
|
@@ -1393,17 +1519,23 @@ function tryParseNumber(input, options) {
|
|
|
1393
1519
|
/**
|
|
1394
1520
|
* Categories of errors for retry logic
|
|
1395
1521
|
*/
|
|
1396
|
-
|
|
1522
|
+
exports.ErrorCategory = void 0;
|
|
1397
1523
|
(function (ErrorCategory) {
|
|
1398
1524
|
/** Error is transient and can be retried */
|
|
1399
1525
|
ErrorCategory["Retryable"] = "retryable";
|
|
1400
|
-
/** Error is due to rate limiting */
|
|
1526
|
+
/** Error is due to short-term rate limiting (retry after a delay) */
|
|
1401
1527
|
ErrorCategory["RateLimit"] = "rate_limit";
|
|
1528
|
+
/**
|
|
1529
|
+
* Provider quota is exhausted or the account cannot be billed
|
|
1530
|
+
* (insufficient funds, daily quota, plan limit). Terminal and actionable —
|
|
1531
|
+
* retrying within the request budget will not help.
|
|
1532
|
+
*/
|
|
1533
|
+
ErrorCategory["Quota"] = "quota";
|
|
1402
1534
|
/** Error cannot be recovered from */
|
|
1403
1535
|
ErrorCategory["Unrecoverable"] = "unrecoverable";
|
|
1404
1536
|
/** Error type is unknown */
|
|
1405
1537
|
ErrorCategory["Unknown"] = "unknown";
|
|
1406
|
-
})(ErrorCategory || (ErrorCategory = {}));
|
|
1538
|
+
})(exports.ErrorCategory || (exports.ErrorCategory = {}));
|
|
1407
1539
|
|
|
1408
1540
|
/**
|
|
1409
1541
|
* Transient network error detection utility.
|
|
@@ -1489,11 +1621,120 @@ function isTransientNetworkError(error) {
|
|
|
1489
1621
|
return false;
|
|
1490
1622
|
}
|
|
1491
1623
|
|
|
1624
|
+
//
|
|
1625
|
+
//
|
|
1626
|
+
// Constants
|
|
1627
|
+
//
|
|
1628
|
+
/**
|
|
1629
|
+
* Transient structured-output compile failures. The provider caches the
|
|
1630
|
+
* compiled grammar after a successful compile, so an immediate retry of the
|
|
1631
|
+
* identical request typically succeeds (issue #422).
|
|
1632
|
+
*/
|
|
1633
|
+
const RETRYABLE_MESSAGE_PATTERNS = [
|
|
1634
|
+
"grammar compilation timed out",
|
|
1635
|
+
"grammar compilation timeout",
|
|
1636
|
+
];
|
|
1637
|
+
/** Billing / insufficient-funds signals — the account cannot be charged. */
|
|
1638
|
+
const BILLING_MESSAGE_PATTERNS = [
|
|
1639
|
+
"insufficient_quota",
|
|
1640
|
+
"insufficient funds",
|
|
1641
|
+
"insufficient credit",
|
|
1642
|
+
"credit balance",
|
|
1643
|
+
"billing",
|
|
1644
|
+
"payment required",
|
|
1645
|
+
"plan and billing",
|
|
1646
|
+
];
|
|
1647
|
+
/** Exhausted usage quota (per-day / per-model limits). */
|
|
1648
|
+
const QUOTA_MESSAGE_PATTERNS = [
|
|
1649
|
+
"quota exceeded",
|
|
1650
|
+
"exceeded your current quota",
|
|
1651
|
+
"resource_exhausted",
|
|
1652
|
+
"quota_exceeded",
|
|
1653
|
+
];
|
|
1654
|
+
//
|
|
1655
|
+
//
|
|
1656
|
+
// Helpers
|
|
1657
|
+
//
|
|
1658
|
+
function extractMessage(error) {
|
|
1659
|
+
if (error instanceof Error)
|
|
1660
|
+
return error.message.toLowerCase();
|
|
1661
|
+
if (typeof error === "string")
|
|
1662
|
+
return error.toLowerCase();
|
|
1663
|
+
if (error && typeof error === "object") {
|
|
1664
|
+
const message = error.message;
|
|
1665
|
+
if (typeof message === "string")
|
|
1666
|
+
return message.toLowerCase();
|
|
1667
|
+
}
|
|
1668
|
+
return "";
|
|
1669
|
+
}
|
|
1670
|
+
//
|
|
1671
|
+
//
|
|
1672
|
+
// Main
|
|
1673
|
+
//
|
|
1674
|
+
/**
|
|
1675
|
+
* Shared, provider-agnostic first pass over an error. Returns a
|
|
1676
|
+
* {@link ClassifiedError} when the message unambiguously identifies a
|
|
1677
|
+
* cross-provider condition (retryable structured-output timeout, exhausted
|
|
1678
|
+
* quota, or a billing failure), or `undefined` to defer to the adapter's own
|
|
1679
|
+
* status/name classification.
|
|
1680
|
+
*
|
|
1681
|
+
* Adapters call this before their existing logic so that, for example, a
|
|
1682
|
+
* `429` carrying a daily-quota message is classified as {@link
|
|
1683
|
+
* ErrorCategory.Quota} rather than {@link ErrorCategory.RateLimit}.
|
|
1684
|
+
*/
|
|
1685
|
+
function classifyProviderError(error) {
|
|
1686
|
+
const message = extractMessage(error);
|
|
1687
|
+
if (!message)
|
|
1688
|
+
return undefined;
|
|
1689
|
+
for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
|
|
1690
|
+
if (message.includes(pattern)) {
|
|
1691
|
+
return { category: exports.ErrorCategory.Retryable, error, shouldRetry: true };
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
for (const pattern of BILLING_MESSAGE_PATTERNS) {
|
|
1695
|
+
if (message.includes(pattern)) {
|
|
1696
|
+
return {
|
|
1697
|
+
category: exports.ErrorCategory.Quota,
|
|
1698
|
+
error,
|
|
1699
|
+
reason: "billing",
|
|
1700
|
+
shouldRetry: false,
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
for (const pattern of QUOTA_MESSAGE_PATTERNS) {
|
|
1705
|
+
if (message.includes(pattern)) {
|
|
1706
|
+
return {
|
|
1707
|
+
category: exports.ErrorCategory.Quota,
|
|
1708
|
+
error,
|
|
1709
|
+
reason: "quota",
|
|
1710
|
+
shouldRetry: false,
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
return undefined;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1492
1717
|
//
|
|
1493
1718
|
//
|
|
1494
1719
|
// Constants
|
|
1495
1720
|
//
|
|
1496
1721
|
const STRUCTURED_OUTPUT_TOOL_NAME$3 = "structured_output";
|
|
1722
|
+
/**
|
|
1723
|
+
* Whether `output_config.effort` may be sent for this model. Anthropic added
|
|
1724
|
+
* the effort control on the Claude 4.5 line and up (the 5 family); older models
|
|
1725
|
+
* reject it. Parsing major/minor avoids matching a minor "5" in legacy ids
|
|
1726
|
+
* like `claude-3-5-sonnet`.
|
|
1727
|
+
*/
|
|
1728
|
+
function supportsAnthropicEffort(model) {
|
|
1729
|
+
const match = model.match(/claude-[a-z]+-(\d+)(?:-(\d+))?/);
|
|
1730
|
+
if (!match)
|
|
1731
|
+
return false;
|
|
1732
|
+
const major = Number(match[1]);
|
|
1733
|
+
const minor = match[2] !== undefined ? Number(match[2]) : 0;
|
|
1734
|
+
if (major >= 5)
|
|
1735
|
+
return true;
|
|
1736
|
+
return major === 4 && minor >= 5;
|
|
1737
|
+
}
|
|
1497
1738
|
const STRUCTURED_OUTPUT_NON_PARSE_STOP_REASONS = new Set([
|
|
1498
1739
|
"refusal",
|
|
1499
1740
|
"max_tokens",
|
|
@@ -1759,7 +2000,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
1759
2000
|
constructor() {
|
|
1760
2001
|
super(...arguments);
|
|
1761
2002
|
this.name = PROVIDER.ANTHROPIC.NAME;
|
|
1762
|
-
this.defaultModel = PROVIDER.ANTHROPIC.
|
|
2003
|
+
this.defaultModel = PROVIDER.ANTHROPIC.DEFAULT;
|
|
1763
2004
|
// Session-level cache of models observed to reject `temperature` at runtime.
|
|
1764
2005
|
// Populated by executeRequest on 400 errors so repeat calls skip the param.
|
|
1765
2006
|
this.runtimeNoTemperatureModels = new Set();
|
|
@@ -1899,6 +2140,24 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
1899
2140
|
if (request.providerOptions) {
|
|
1900
2141
|
Object.assign(anthropicRequest, request.providerOptions);
|
|
1901
2142
|
}
|
|
2143
|
+
// Normalized reasoning effort -> output_config.effort (merged so a format
|
|
2144
|
+
// config above survives). First-class effort wins over providerOptions.
|
|
2145
|
+
if (request.effort &&
|
|
2146
|
+
supportsAnthropicEffort(anthropicRequest.model)) {
|
|
2147
|
+
const mapping = toAnthropicEffort(request.effort);
|
|
2148
|
+
if (mapping.papered) {
|
|
2149
|
+
log$1.log.debug(paperedEffortMessage({
|
|
2150
|
+
model: anthropicRequest.model,
|
|
2151
|
+
provider: this.name,
|
|
2152
|
+
requested: request.effort,
|
|
2153
|
+
value: mapping.value,
|
|
2154
|
+
}));
|
|
2155
|
+
}
|
|
2156
|
+
anthropicRequest.output_config = {
|
|
2157
|
+
...anthropicRequest.output_config,
|
|
2158
|
+
effort: mapping.value,
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
1902
2161
|
// First-class temperature takes precedence over providerOptions
|
|
1903
2162
|
if (request.temperature !== undefined) {
|
|
1904
2163
|
anthropicRequest.temperature = request.temperature;
|
|
@@ -1993,9 +2252,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
1993
2252
|
*/
|
|
1994
2253
|
toFallbackStructuredOutputRequest(request) {
|
|
1995
2254
|
const { output_config, ...rest } = request;
|
|
1996
|
-
if (!output_config)
|
|
2255
|
+
if (!output_config?.format)
|
|
1997
2256
|
return request;
|
|
1998
2257
|
const fallbackRequest = { ...rest };
|
|
2258
|
+
// Preserve an effort setting; only the structured-output format falls back.
|
|
2259
|
+
if (output_config.effort) {
|
|
2260
|
+
fallbackRequest.output_config = { effort: output_config.effort };
|
|
2261
|
+
}
|
|
1999
2262
|
const fakeTool = {
|
|
2000
2263
|
name: STRUCTURED_OUTPUT_TOOL_NAME$3,
|
|
2001
2264
|
description: "Output a structured JSON object, " +
|
|
@@ -2224,12 +2487,17 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2224
2487
|
// Error Classification
|
|
2225
2488
|
//
|
|
2226
2489
|
classifyError(error) {
|
|
2490
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
2491
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
2492
|
+
const shared = classifyProviderError(error);
|
|
2493
|
+
if (shared)
|
|
2494
|
+
return shared;
|
|
2227
2495
|
const errorName = error?.constructor?.name;
|
|
2228
2496
|
// Check for rate limit error
|
|
2229
2497
|
if (errorName === "RateLimitError") {
|
|
2230
2498
|
return {
|
|
2231
2499
|
error,
|
|
2232
|
-
category: ErrorCategory.RateLimit,
|
|
2500
|
+
category: exports.ErrorCategory.RateLimit,
|
|
2233
2501
|
shouldRetry: false,
|
|
2234
2502
|
suggestedDelayMs: 60000,
|
|
2235
2503
|
};
|
|
@@ -2238,7 +2506,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2238
2506
|
if (RETRYABLE_ERROR_NAMES.includes(errorName)) {
|
|
2239
2507
|
return {
|
|
2240
2508
|
error,
|
|
2241
|
-
category: ErrorCategory.Retryable,
|
|
2509
|
+
category: exports.ErrorCategory.Retryable,
|
|
2242
2510
|
shouldRetry: true,
|
|
2243
2511
|
};
|
|
2244
2512
|
}
|
|
@@ -2246,7 +2514,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2246
2514
|
if (NOT_RETRYABLE_ERROR_NAMES.includes(errorName)) {
|
|
2247
2515
|
return {
|
|
2248
2516
|
error,
|
|
2249
|
-
category: ErrorCategory.Unrecoverable,
|
|
2517
|
+
category: exports.ErrorCategory.Unrecoverable,
|
|
2250
2518
|
shouldRetry: false,
|
|
2251
2519
|
};
|
|
2252
2520
|
}
|
|
@@ -2254,14 +2522,14 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2254
2522
|
if (isTransientNetworkError(error)) {
|
|
2255
2523
|
return {
|
|
2256
2524
|
error,
|
|
2257
|
-
category: ErrorCategory.Retryable,
|
|
2525
|
+
category: exports.ErrorCategory.Retryable,
|
|
2258
2526
|
shouldRetry: true,
|
|
2259
2527
|
};
|
|
2260
2528
|
}
|
|
2261
2529
|
// Unknown error - treat as potentially retryable
|
|
2262
2530
|
return {
|
|
2263
2531
|
error,
|
|
2264
|
-
category: ErrorCategory.Unknown,
|
|
2532
|
+
category: exports.ErrorCategory.Unknown,
|
|
2265
2533
|
shouldRetry: true,
|
|
2266
2534
|
};
|
|
2267
2535
|
}
|
|
@@ -2439,7 +2707,7 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2439
2707
|
constructor() {
|
|
2440
2708
|
super(...arguments);
|
|
2441
2709
|
this.name = PROVIDER.BEDROCK.NAME;
|
|
2442
|
-
this.defaultModel = PROVIDER.BEDROCK.
|
|
2710
|
+
this.defaultModel = PROVIDER.BEDROCK.DEFAULT;
|
|
2443
2711
|
this._modelsFallbackToStructuredOutputTool = new Set();
|
|
2444
2712
|
this._modelsWithoutTemperature = new Set();
|
|
2445
2713
|
}
|
|
@@ -2832,6 +3100,11 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2832
3100
|
// Error Classification
|
|
2833
3101
|
//
|
|
2834
3102
|
classifyError(error) {
|
|
3103
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
3104
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
3105
|
+
const shared = classifyProviderError(error);
|
|
3106
|
+
if (shared)
|
|
3107
|
+
return shared;
|
|
2835
3108
|
const errorName = error?.constructor?.name;
|
|
2836
3109
|
const errorMessage = error?.message ?? "";
|
|
2837
3110
|
if (errorName === "ThrottlingException" ||
|
|
@@ -2839,7 +3112,7 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2839
3112
|
errorMessage.includes("Too Many Requests")) {
|
|
2840
3113
|
return {
|
|
2841
3114
|
error,
|
|
2842
|
-
category: ErrorCategory.RateLimit,
|
|
3115
|
+
category: exports.ErrorCategory.RateLimit,
|
|
2843
3116
|
shouldRetry: false,
|
|
2844
3117
|
suggestedDelayMs: 60000,
|
|
2845
3118
|
};
|
|
@@ -2850,7 +3123,7 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2850
3123
|
errorMessage.includes("InternalServerException")) {
|
|
2851
3124
|
return {
|
|
2852
3125
|
error,
|
|
2853
|
-
category: ErrorCategory.Retryable,
|
|
3126
|
+
category: exports.ErrorCategory.Retryable,
|
|
2854
3127
|
shouldRetry: true,
|
|
2855
3128
|
};
|
|
2856
3129
|
}
|
|
@@ -2861,20 +3134,20 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2861
3134
|
errorMessage.includes("ValidationException")) {
|
|
2862
3135
|
return {
|
|
2863
3136
|
error,
|
|
2864
|
-
category: ErrorCategory.Unrecoverable,
|
|
3137
|
+
category: exports.ErrorCategory.Unrecoverable,
|
|
2865
3138
|
shouldRetry: false,
|
|
2866
3139
|
};
|
|
2867
3140
|
}
|
|
2868
3141
|
if (isTransientNetworkError(error)) {
|
|
2869
3142
|
return {
|
|
2870
3143
|
error,
|
|
2871
|
-
category: ErrorCategory.Retryable,
|
|
3144
|
+
category: exports.ErrorCategory.Retryable,
|
|
2872
3145
|
shouldRetry: true,
|
|
2873
3146
|
};
|
|
2874
3147
|
}
|
|
2875
3148
|
return {
|
|
2876
3149
|
error,
|
|
2877
|
-
category: ErrorCategory.Unknown,
|
|
3150
|
+
category: exports.ErrorCategory.Unknown,
|
|
2878
3151
|
shouldRetry: true,
|
|
2879
3152
|
};
|
|
2880
3153
|
}
|
|
@@ -2945,6 +3218,11 @@ const STRUCTURED_OUTPUT_TOOL_NAME$1 = "structured_output";
|
|
|
2945
3218
|
// (including 2.5 thinking) do not support the combo and fall back to the
|
|
2946
3219
|
// legacy `structured_output` fake-tool emulation.
|
|
2947
3220
|
const GEMINI_3_PATTERN = /^gemini-3/;
|
|
3221
|
+
// Reasoning-effort control differs by generation: Gemini 3.x takes the
|
|
3222
|
+
// `thinkingLevel` enum, Gemini 2.5 takes an integer `thinkingBudget`. Sending
|
|
3223
|
+
// the wrong one errors, and models outside these families have no thinking
|
|
3224
|
+
// control, so effort is only applied when one of these matches.
|
|
3225
|
+
const GEMINI_25_PATTERN = /^gemini-2\.5/;
|
|
2948
3226
|
/**
|
|
2949
3227
|
* Detect 4xx errors that indicate the model itself does not support the
|
|
2950
3228
|
* `responseJsonSchema` + tools combo. Triggers the runtime fallback to the
|
|
@@ -2991,7 +3269,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
2991
3269
|
constructor() {
|
|
2992
3270
|
super(...arguments);
|
|
2993
3271
|
this.name = PROVIDER.GOOGLE.NAME;
|
|
2994
|
-
this.defaultModel = PROVIDER.GOOGLE.
|
|
3272
|
+
this.defaultModel = PROVIDER.GOOGLE.DEFAULT;
|
|
2995
3273
|
// Session-level cache of Gemini 3 models observed to reject the native
|
|
2996
3274
|
// `responseJsonSchema` + tools combo. When a model is in this set,
|
|
2997
3275
|
// buildRequest engages the legacy fake-tool path instead.
|
|
@@ -3119,6 +3397,42 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3119
3397
|
...request.providerOptions,
|
|
3120
3398
|
};
|
|
3121
3399
|
}
|
|
3400
|
+
// Normalized reasoning effort -> thinkingConfig. Gemini 3.x uses the
|
|
3401
|
+
// thinkingLevel enum; 2.5 uses an integer thinkingBudget. Merged so a
|
|
3402
|
+
// providerOptions.thinkingConfig survives; first-class effort wins.
|
|
3403
|
+
if (request.effort) {
|
|
3404
|
+
const model = geminiRequest.model;
|
|
3405
|
+
let thinkingConfig;
|
|
3406
|
+
let papered = false;
|
|
3407
|
+
let value;
|
|
3408
|
+
if (GEMINI_3_PATTERN.test(model)) {
|
|
3409
|
+
const mapping = toGeminiThinkingLevel(request.effort);
|
|
3410
|
+
({ papered, value } = mapping);
|
|
3411
|
+
thinkingConfig = { thinkingLevel: mapping.value };
|
|
3412
|
+
}
|
|
3413
|
+
else if (GEMINI_25_PATTERN.test(model)) {
|
|
3414
|
+
const mapping = toGeminiThinkingBudget(request.effort);
|
|
3415
|
+
({ papered, value } = mapping);
|
|
3416
|
+
thinkingConfig = { thinkingBudget: mapping.value };
|
|
3417
|
+
}
|
|
3418
|
+
if (thinkingConfig) {
|
|
3419
|
+
if (papered) {
|
|
3420
|
+
log$1.log.debug(paperedEffortMessage({
|
|
3421
|
+
model,
|
|
3422
|
+
provider: this.name,
|
|
3423
|
+
requested: request.effort,
|
|
3424
|
+
value: value,
|
|
3425
|
+
}));
|
|
3426
|
+
}
|
|
3427
|
+
geminiRequest.config = {
|
|
3428
|
+
...geminiRequest.config,
|
|
3429
|
+
thinkingConfig: {
|
|
3430
|
+
...geminiRequest.config?.thinkingConfig,
|
|
3431
|
+
...thinkingConfig,
|
|
3432
|
+
},
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3122
3436
|
// First-class temperature takes precedence over providerOptions
|
|
3123
3437
|
if (request.temperature !== undefined) {
|
|
3124
3438
|
geminiRequest.config = {
|
|
@@ -3460,6 +3774,11 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3460
3774
|
// Error Classification
|
|
3461
3775
|
//
|
|
3462
3776
|
classifyError(error) {
|
|
3777
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
3778
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
3779
|
+
const shared = classifyProviderError(error);
|
|
3780
|
+
if (shared)
|
|
3781
|
+
return shared;
|
|
3463
3782
|
const geminiError = error;
|
|
3464
3783
|
// Extract status code from error
|
|
3465
3784
|
const statusCode = geminiError.status || geminiError.code;
|
|
@@ -3467,7 +3786,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3467
3786
|
if (statusCode === 429) {
|
|
3468
3787
|
return {
|
|
3469
3788
|
error,
|
|
3470
|
-
category: ErrorCategory.RateLimit,
|
|
3789
|
+
category: exports.ErrorCategory.RateLimit,
|
|
3471
3790
|
shouldRetry: false,
|
|
3472
3791
|
suggestedDelayMs: 60000,
|
|
3473
3792
|
};
|
|
@@ -3477,7 +3796,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3477
3796
|
RETRYABLE_STATUS_CODES$1.includes(statusCode)) {
|
|
3478
3797
|
return {
|
|
3479
3798
|
error,
|
|
3480
|
-
category: ErrorCategory.Retryable,
|
|
3799
|
+
category: exports.ErrorCategory.Retryable,
|
|
3481
3800
|
shouldRetry: true,
|
|
3482
3801
|
};
|
|
3483
3802
|
}
|
|
@@ -3486,7 +3805,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3486
3805
|
NOT_RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
3487
3806
|
return {
|
|
3488
3807
|
error,
|
|
3489
|
-
category: ErrorCategory.Unrecoverable,
|
|
3808
|
+
category: exports.ErrorCategory.Unrecoverable,
|
|
3490
3809
|
shouldRetry: false,
|
|
3491
3810
|
};
|
|
3492
3811
|
}
|
|
@@ -3496,7 +3815,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3496
3815
|
errorMessage.includes("quota exceeded")) {
|
|
3497
3816
|
return {
|
|
3498
3817
|
error,
|
|
3499
|
-
category: ErrorCategory.RateLimit,
|
|
3818
|
+
category: exports.ErrorCategory.RateLimit,
|
|
3500
3819
|
shouldRetry: false,
|
|
3501
3820
|
suggestedDelayMs: 60000,
|
|
3502
3821
|
};
|
|
@@ -3506,7 +3825,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3506
3825
|
errorMessage.includes("ECONNREFUSED")) {
|
|
3507
3826
|
return {
|
|
3508
3827
|
error,
|
|
3509
|
-
category: ErrorCategory.Retryable,
|
|
3828
|
+
category: exports.ErrorCategory.Retryable,
|
|
3510
3829
|
shouldRetry: true,
|
|
3511
3830
|
};
|
|
3512
3831
|
}
|
|
@@ -3514,14 +3833,14 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3514
3833
|
if (isTransientNetworkError(error)) {
|
|
3515
3834
|
return {
|
|
3516
3835
|
error,
|
|
3517
|
-
category: ErrorCategory.Retryable,
|
|
3836
|
+
category: exports.ErrorCategory.Retryable,
|
|
3518
3837
|
shouldRetry: true,
|
|
3519
3838
|
};
|
|
3520
3839
|
}
|
|
3521
3840
|
// Unknown error - treat as potentially retryable
|
|
3522
3841
|
return {
|
|
3523
3842
|
error,
|
|
3524
|
-
category: ErrorCategory.Unknown,
|
|
3843
|
+
category: exports.ErrorCategory.Unknown,
|
|
3525
3844
|
shouldRetry: true,
|
|
3526
3845
|
};
|
|
3527
3846
|
}
|
|
@@ -4038,7 +4357,7 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4038
4357
|
constructor() {
|
|
4039
4358
|
super(...arguments);
|
|
4040
4359
|
this.name = PROVIDER.OPENAI.NAME;
|
|
4041
|
-
this.defaultModel = PROVIDER.OPENAI.
|
|
4360
|
+
this.defaultModel = PROVIDER.OPENAI.DEFAULT;
|
|
4042
4361
|
// Session-level cache of models observed to reject `temperature` at runtime.
|
|
4043
4362
|
// Populated by executeRequest on 400 errors so repeat calls skip the param.
|
|
4044
4363
|
this.runtimeNoTemperatureModels = new Set();
|
|
@@ -4054,6 +4373,14 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4054
4373
|
return false;
|
|
4055
4374
|
return !MODELS_WITHOUT_TEMPERATURE$1.some((pattern) => pattern.test(model));
|
|
4056
4375
|
}
|
|
4376
|
+
/** Whether `reasoning.effort` may be sent for this model. Overridden by xAI. */
|
|
4377
|
+
supportsReasoningEffort(model) {
|
|
4378
|
+
return isReasoningModel(model);
|
|
4379
|
+
}
|
|
4380
|
+
/** Translate a normalized effort to this provider's `reasoning.effort` value. */
|
|
4381
|
+
mapReasoningEffort(effort, model) {
|
|
4382
|
+
return toOpenAiEffort(effort, { model });
|
|
4383
|
+
}
|
|
4057
4384
|
//
|
|
4058
4385
|
// Request Building
|
|
4059
4386
|
//
|
|
@@ -4092,6 +4419,24 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4092
4419
|
if (request.providerOptions) {
|
|
4093
4420
|
Object.assign(openaiRequest, request.providerOptions);
|
|
4094
4421
|
}
|
|
4422
|
+
// Normalized reasoning effort -> reasoning.effort (merged so the
|
|
4423
|
+
// summary:auto above survives). First-class effort wins over providerOptions.
|
|
4424
|
+
if (request.effort && this.supportsReasoningEffort(model)) {
|
|
4425
|
+
const mapping = this.mapReasoningEffort(request.effort, model);
|
|
4426
|
+
if (mapping.papered) {
|
|
4427
|
+
log$1.log.debug(paperedEffortMessage({
|
|
4428
|
+
model,
|
|
4429
|
+
provider: this.name,
|
|
4430
|
+
requested: request.effort,
|
|
4431
|
+
value: mapping.value,
|
|
4432
|
+
}));
|
|
4433
|
+
}
|
|
4434
|
+
const existingReasoning = openaiRequest.reasoning ?? {};
|
|
4435
|
+
openaiRequest.reasoning = {
|
|
4436
|
+
...existingReasoning,
|
|
4437
|
+
effort: mapping.value,
|
|
4438
|
+
};
|
|
4439
|
+
}
|
|
4095
4440
|
// First-class temperature takes precedence over providerOptions
|
|
4096
4441
|
if (request.temperature !== undefined) {
|
|
4097
4442
|
openaiRequest.temperature = request.temperature;
|
|
@@ -4382,11 +4727,16 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4382
4727
|
// Error Classification
|
|
4383
4728
|
//
|
|
4384
4729
|
classifyError(error) {
|
|
4730
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
4731
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
4732
|
+
const shared = classifyProviderError(error);
|
|
4733
|
+
if (shared)
|
|
4734
|
+
return shared;
|
|
4385
4735
|
// Check for rate limit error
|
|
4386
4736
|
if (error instanceof RateLimitError$1) {
|
|
4387
4737
|
return {
|
|
4388
4738
|
error,
|
|
4389
|
-
category: ErrorCategory.RateLimit,
|
|
4739
|
+
category: exports.ErrorCategory.RateLimit,
|
|
4390
4740
|
shouldRetry: false, // Rate limit requires waiting, not immediate retry
|
|
4391
4741
|
suggestedDelayMs: 60000, // 1 minute default
|
|
4392
4742
|
};
|
|
@@ -4396,7 +4746,7 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4396
4746
|
if (error instanceof ErrorType) {
|
|
4397
4747
|
return {
|
|
4398
4748
|
error,
|
|
4399
|
-
category: ErrorCategory.Retryable,
|
|
4749
|
+
category: exports.ErrorCategory.Retryable,
|
|
4400
4750
|
shouldRetry: true,
|
|
4401
4751
|
};
|
|
4402
4752
|
}
|
|
@@ -4406,7 +4756,7 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4406
4756
|
if (error instanceof ErrorType) {
|
|
4407
4757
|
return {
|
|
4408
4758
|
error,
|
|
4409
|
-
category: ErrorCategory.Unrecoverable,
|
|
4759
|
+
category: exports.ErrorCategory.Unrecoverable,
|
|
4410
4760
|
shouldRetry: false,
|
|
4411
4761
|
};
|
|
4412
4762
|
}
|
|
@@ -4415,14 +4765,14 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
4415
4765
|
if (isTransientNetworkError(error)) {
|
|
4416
4766
|
return {
|
|
4417
4767
|
error,
|
|
4418
|
-
category: ErrorCategory.Retryable,
|
|
4768
|
+
category: exports.ErrorCategory.Retryable,
|
|
4419
4769
|
shouldRetry: true,
|
|
4420
4770
|
};
|
|
4421
4771
|
}
|
|
4422
4772
|
// Unknown error - treat as potentially retryable
|
|
4423
4773
|
return {
|
|
4424
4774
|
error,
|
|
4425
|
-
category: ErrorCategory.Unknown,
|
|
4775
|
+
category: exports.ErrorCategory.Unknown,
|
|
4426
4776
|
shouldRetry: true,
|
|
4427
4777
|
};
|
|
4428
4778
|
}
|
|
@@ -4686,7 +5036,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4686
5036
|
constructor() {
|
|
4687
5037
|
super(...arguments);
|
|
4688
5038
|
this.name = PROVIDER.OPENROUTER.NAME;
|
|
4689
|
-
this.defaultModel = PROVIDER.OPENROUTER.
|
|
5039
|
+
this.defaultModel = PROVIDER.OPENROUTER.DEFAULT;
|
|
4690
5040
|
// Session-level cache of models observed to reject native
|
|
4691
5041
|
// `response_format: json_schema`. When a model is in this set, buildRequest
|
|
4692
5042
|
// engages the legacy fake-tool path instead of native structured output.
|
|
@@ -4779,6 +5129,15 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4779
5129
|
if (request.providerOptions) {
|
|
4780
5130
|
Object.assign(openRouterRequest, request.providerOptions);
|
|
4781
5131
|
}
|
|
5132
|
+
// Normalized reasoning effort -> reasoning.effort. OpenRouter accepts the
|
|
5133
|
+
// full ladder and maps to the routed provider's nearest supported level.
|
|
5134
|
+
// First-class effort wins over providerOptions.
|
|
5135
|
+
if (request.effort) {
|
|
5136
|
+
openRouterRequest.reasoning = {
|
|
5137
|
+
...openRouterRequest.reasoning,
|
|
5138
|
+
effort: toOpenRouterEffort(request.effort).value,
|
|
5139
|
+
};
|
|
5140
|
+
}
|
|
4782
5141
|
// First-class temperature takes precedence over providerOptions
|
|
4783
5142
|
if (request.temperature !== undefined) {
|
|
4784
5143
|
openRouterRequest.temperature =
|
|
@@ -5141,6 +5500,11 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5141
5500
|
// Error Classification
|
|
5142
5501
|
//
|
|
5143
5502
|
classifyError(error) {
|
|
5503
|
+
// Shared first pass: retryable structured-output timeouts (#422),
|
|
5504
|
+
// quota exhaustion, and billing failures classify the same across providers.
|
|
5505
|
+
const shared = classifyProviderError(error);
|
|
5506
|
+
if (shared)
|
|
5507
|
+
return shared;
|
|
5144
5508
|
// Check if error has a status code (HTTP error)
|
|
5145
5509
|
const errorWithStatus = error;
|
|
5146
5510
|
const statusCode = errorWithStatus.status || errorWithStatus.statusCode;
|
|
@@ -5149,7 +5513,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5149
5513
|
if (statusCode === RATE_LIMIT_STATUS_CODE) {
|
|
5150
5514
|
return {
|
|
5151
5515
|
error,
|
|
5152
|
-
category: ErrorCategory.RateLimit,
|
|
5516
|
+
category: exports.ErrorCategory.RateLimit,
|
|
5153
5517
|
shouldRetry: false,
|
|
5154
5518
|
suggestedDelayMs: 60000,
|
|
5155
5519
|
};
|
|
@@ -5158,7 +5522,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5158
5522
|
if (RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
5159
5523
|
return {
|
|
5160
5524
|
error,
|
|
5161
|
-
category: ErrorCategory.Retryable,
|
|
5525
|
+
category: exports.ErrorCategory.Retryable,
|
|
5162
5526
|
shouldRetry: true,
|
|
5163
5527
|
};
|
|
5164
5528
|
}
|
|
@@ -5166,7 +5530,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5166
5530
|
if (statusCode >= 400 && statusCode < 500) {
|
|
5167
5531
|
return {
|
|
5168
5532
|
error,
|
|
5169
|
-
category: ErrorCategory.Unrecoverable,
|
|
5533
|
+
category: exports.ErrorCategory.Unrecoverable,
|
|
5170
5534
|
shouldRetry: false,
|
|
5171
5535
|
};
|
|
5172
5536
|
}
|
|
@@ -5177,7 +5541,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5177
5541
|
errorMessage.includes("too many requests")) {
|
|
5178
5542
|
return {
|
|
5179
5543
|
error,
|
|
5180
|
-
category: ErrorCategory.RateLimit,
|
|
5544
|
+
category: exports.ErrorCategory.RateLimit,
|
|
5181
5545
|
shouldRetry: false,
|
|
5182
5546
|
suggestedDelayMs: 60000,
|
|
5183
5547
|
};
|
|
@@ -5186,14 +5550,14 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
5186
5550
|
if (isTransientNetworkError(error)) {
|
|
5187
5551
|
return {
|
|
5188
5552
|
error,
|
|
5189
|
-
category: ErrorCategory.Retryable,
|
|
5553
|
+
category: exports.ErrorCategory.Retryable,
|
|
5190
5554
|
shouldRetry: true,
|
|
5191
5555
|
};
|
|
5192
5556
|
}
|
|
5193
5557
|
// Unknown error - treat as potentially retryable
|
|
5194
5558
|
return {
|
|
5195
5559
|
error,
|
|
5196
|
-
category: ErrorCategory.Unknown,
|
|
5560
|
+
category: exports.ErrorCategory.Unknown,
|
|
5197
5561
|
shouldRetry: true,
|
|
5198
5562
|
};
|
|
5199
5563
|
}
|
|
@@ -5397,14 +5761,27 @@ class XaiAdapter extends OpenAiAdapter {
|
|
|
5397
5761
|
super(...arguments);
|
|
5398
5762
|
// @ts-expect-error Narrowing override: xAI name differs from parent's literal "openai"
|
|
5399
5763
|
this.name = PROVIDER.XAI.NAME;
|
|
5400
|
-
|
|
5401
|
-
|
|
5764
|
+
this.defaultModel = PROVIDER.XAI.DEFAULT;
|
|
5765
|
+
}
|
|
5766
|
+
/**
|
|
5767
|
+
* Grok gates reasoning effort by model, not by the OpenAI `gpt-*`/`o*`
|
|
5768
|
+
* patterns. Only explicit `*-reasoning` models accept `reasoning_effort`;
|
|
5769
|
+
* bare grok-4 reasons implicitly and rejects it, so we stay conservative and
|
|
5770
|
+
* only opt in models whose name advertises reasoning.
|
|
5771
|
+
*/
|
|
5772
|
+
supportsReasoningEffort(model) {
|
|
5773
|
+
if (/non-reasoning/.test(model))
|
|
5774
|
+
return false;
|
|
5775
|
+
return /reasoning/.test(model);
|
|
5776
|
+
}
|
|
5777
|
+
mapReasoningEffort(effort) {
|
|
5778
|
+
return toXaiEffort(effort);
|
|
5402
5779
|
}
|
|
5403
5780
|
classifyError(error) {
|
|
5404
5781
|
if (isTransientIngestError(error)) {
|
|
5405
5782
|
return {
|
|
5406
5783
|
error,
|
|
5407
|
-
category: ErrorCategory.Retryable,
|
|
5784
|
+
category: exports.ErrorCategory.Retryable,
|
|
5408
5785
|
shouldRetry: true,
|
|
5409
5786
|
};
|
|
5410
5787
|
}
|
|
@@ -6492,6 +6869,122 @@ function createStaleRejectionGuard() {
|
|
|
6492
6869
|
};
|
|
6493
6870
|
}
|
|
6494
6871
|
|
|
6872
|
+
//
|
|
6873
|
+
//
|
|
6874
|
+
// Base
|
|
6875
|
+
//
|
|
6876
|
+
/**
|
|
6877
|
+
* Normalized, provider-agnostic LLM error. Thrown by the operate/stream retry
|
|
6878
|
+
* layer when a request cannot be completed, so consumers can `catch` a stable
|
|
6879
|
+
* type regardless of which provider failed. Extends {@link JaypieError} so
|
|
6880
|
+
* `isJaypieError()` and `.status` continue to work; the original provider error
|
|
6881
|
+
* is preserved on `.cause`.
|
|
6882
|
+
*/
|
|
6883
|
+
class LlmError extends errors.JaypieError {
|
|
6884
|
+
constructor(message, category, { status = 502, title = "Bad Gateway", provider, model, retryAfterMs, cause, } = {}) {
|
|
6885
|
+
super(message, { status, title }, { _type: "LlmError" });
|
|
6886
|
+
this.name = "LlmError";
|
|
6887
|
+
this.category = category;
|
|
6888
|
+
this.provider = provider;
|
|
6889
|
+
this.model = model;
|
|
6890
|
+
this.retryAfterMs = retryAfterMs;
|
|
6891
|
+
this.cause = cause;
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
//
|
|
6895
|
+
//
|
|
6896
|
+
// Subclasses
|
|
6897
|
+
//
|
|
6898
|
+
/**
|
|
6899
|
+
* Short-term rate limiting (per-minute 429). Terminal within the request
|
|
6900
|
+
* budget; `retryAfterMs` carries the provider's suggested wait when available.
|
|
6901
|
+
*/
|
|
6902
|
+
class LlmRateLimitError extends LlmError {
|
|
6903
|
+
constructor(message, options = {}) {
|
|
6904
|
+
super(message, exports.ErrorCategory.RateLimit, {
|
|
6905
|
+
status: 429,
|
|
6906
|
+
title: "Too Many Requests",
|
|
6907
|
+
...options,
|
|
6908
|
+
});
|
|
6909
|
+
this.name = "LlmRateLimitError";
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
/**
|
|
6913
|
+
* Provider quota is exhausted or the account cannot be billed. Terminal and
|
|
6914
|
+
* actionable. `reason` distinguishes an exhausted usage quota from insufficient
|
|
6915
|
+
* funds.
|
|
6916
|
+
*/
|
|
6917
|
+
class LlmQuotaError extends LlmError {
|
|
6918
|
+
constructor(message, { reason = "quota", ...options } = {}) {
|
|
6919
|
+
super(message, exports.ErrorCategory.Quota, {
|
|
6920
|
+
status: 402,
|
|
6921
|
+
title: "Payment Required",
|
|
6922
|
+
...options,
|
|
6923
|
+
});
|
|
6924
|
+
this.name = "LlmQuotaError";
|
|
6925
|
+
this.reason = reason;
|
|
6926
|
+
}
|
|
6927
|
+
}
|
|
6928
|
+
/**
|
|
6929
|
+
* The request cannot be recovered from (bad request, authentication,
|
|
6930
|
+
* permission, not found). Terminal.
|
|
6931
|
+
*/
|
|
6932
|
+
class LlmUnrecoverableError extends LlmError {
|
|
6933
|
+
constructor(message, options = {}) {
|
|
6934
|
+
super(message, exports.ErrorCategory.Unrecoverable, {
|
|
6935
|
+
status: 502,
|
|
6936
|
+
title: "Bad Gateway",
|
|
6937
|
+
...options,
|
|
6938
|
+
});
|
|
6939
|
+
this.name = "LlmUnrecoverableError";
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
/**
|
|
6943
|
+
* A transient or unknown error survived the retry budget. Terminal only because
|
|
6944
|
+
* retries were exhausted; the underlying condition may succeed later.
|
|
6945
|
+
*/
|
|
6946
|
+
class LlmTransientError extends LlmError {
|
|
6947
|
+
constructor(message, options = {}) {
|
|
6948
|
+
super(message, exports.ErrorCategory.Retryable, {
|
|
6949
|
+
status: 504,
|
|
6950
|
+
title: "Gateway Timeout",
|
|
6951
|
+
...options,
|
|
6952
|
+
});
|
|
6953
|
+
this.name = "LlmTransientError";
|
|
6954
|
+
}
|
|
6955
|
+
}
|
|
6956
|
+
|
|
6957
|
+
/**
|
|
6958
|
+
* Map a {@link ClassifiedError} to the matching {@link LlmError} subclass,
|
|
6959
|
+
* preserving the original error as `cause`. Used by the retry layer to throw a
|
|
6960
|
+
* stable, catchable type instead of a bare gateway error.
|
|
6961
|
+
*/
|
|
6962
|
+
function toLlmError(classified, context = {}) {
|
|
6963
|
+
const original = classified.error;
|
|
6964
|
+
const message = original instanceof Error ? original.message : String(original);
|
|
6965
|
+
const options = {
|
|
6966
|
+
cause: original,
|
|
6967
|
+
model: context.model,
|
|
6968
|
+
provider: context.provider,
|
|
6969
|
+
retryAfterMs: classified.suggestedDelayMs,
|
|
6970
|
+
};
|
|
6971
|
+
switch (classified.category) {
|
|
6972
|
+
case exports.ErrorCategory.RateLimit:
|
|
6973
|
+
return new LlmRateLimitError(message, options);
|
|
6974
|
+
case exports.ErrorCategory.Quota:
|
|
6975
|
+
return new LlmQuotaError(message, {
|
|
6976
|
+
...options,
|
|
6977
|
+
reason: classified.reason ?? "quota",
|
|
6978
|
+
});
|
|
6979
|
+
case exports.ErrorCategory.Unrecoverable:
|
|
6980
|
+
return new LlmUnrecoverableError(message, options);
|
|
6981
|
+
case exports.ErrorCategory.Retryable:
|
|
6982
|
+
case exports.ErrorCategory.Unknown:
|
|
6983
|
+
default:
|
|
6984
|
+
return new LlmTransientError(message, options);
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
|
|
6495
6988
|
//
|
|
6496
6989
|
//
|
|
6497
6990
|
// Main
|
|
@@ -6506,6 +6999,18 @@ class RetryExecutor {
|
|
|
6506
6999
|
this.hookRunner = config.hookRunner ?? hookRunner;
|
|
6507
7000
|
this.errorClassifier = config.errorClassifier;
|
|
6508
7001
|
}
|
|
7002
|
+
/**
|
|
7003
|
+
* Build the typed, provider-agnostic error thrown when a request cannot be
|
|
7004
|
+
* completed — classified (rate limit / quota / unrecoverable / transient)
|
|
7005
|
+
* and carrying the provider, model, and original error as `cause`.
|
|
7006
|
+
*/
|
|
7007
|
+
toTerminalError(error, context) {
|
|
7008
|
+
const classified = this.errorClassifier.classify(error);
|
|
7009
|
+
return toLlmError(classified, {
|
|
7010
|
+
model: context.model,
|
|
7011
|
+
provider: context.provider,
|
|
7012
|
+
});
|
|
7013
|
+
}
|
|
6509
7014
|
/**
|
|
6510
7015
|
* Execute an operation with retry logic.
|
|
6511
7016
|
* Each attempt receives an AbortSignal. On failure, the signal is aborted
|
|
@@ -6549,8 +7054,7 @@ class RetryExecutor {
|
|
|
6549
7054
|
providerRequest: options.context.providerRequest,
|
|
6550
7055
|
error,
|
|
6551
7056
|
});
|
|
6552
|
-
|
|
6553
|
-
throw new errors.BadGatewayError(errorMessage);
|
|
7057
|
+
throw this.toTerminalError(error, options.context);
|
|
6554
7058
|
}
|
|
6555
7059
|
// Check if error is not retryable
|
|
6556
7060
|
if (!this.errorClassifier.isRetryable(error)) {
|
|
@@ -6562,8 +7066,7 @@ class RetryExecutor {
|
|
|
6562
7066
|
providerRequest: options.context.providerRequest,
|
|
6563
7067
|
error,
|
|
6564
7068
|
});
|
|
6565
|
-
|
|
6566
|
-
throw new errors.BadGatewayError(errorMessage);
|
|
7069
|
+
throw this.toTerminalError(error, options.context);
|
|
6567
7070
|
}
|
|
6568
7071
|
// Warn if this is an unknown error type
|
|
6569
7072
|
if (!this.errorClassifier.isKnownError(error)) {
|
|
@@ -6606,6 +7109,7 @@ const MAX_CONSECUTIVE_TOOL_ERRORS = 6;
|
|
|
6606
7109
|
*/
|
|
6607
7110
|
function createErrorClassifier(adapter) {
|
|
6608
7111
|
return {
|
|
7112
|
+
classify: (error) => adapter.classifyError(error),
|
|
6609
7113
|
isRetryable: (error) => adapter.isRetryableError(error),
|
|
6610
7114
|
isKnownError: (error) => {
|
|
6611
7115
|
const classified = adapter.classifyError(error);
|
|
@@ -6675,6 +7179,7 @@ class OperateLoop {
|
|
|
6675
7179
|
}
|
|
6676
7180
|
// Rebuild request with updated history for next turn
|
|
6677
7181
|
request = {
|
|
7182
|
+
effort: options.effort,
|
|
6678
7183
|
format: state.formattedFormat,
|
|
6679
7184
|
instructions: options.instructions,
|
|
6680
7185
|
messages: state.currentInput,
|
|
@@ -6778,6 +7283,7 @@ class OperateLoop {
|
|
|
6778
7283
|
}
|
|
6779
7284
|
buildInitialRequest(state, options) {
|
|
6780
7285
|
return {
|
|
7286
|
+
effort: options.effort,
|
|
6781
7287
|
format: state.formattedFormat,
|
|
6782
7288
|
instructions: options.instructions,
|
|
6783
7289
|
messages: state.currentInput,
|
|
@@ -6857,7 +7363,9 @@ class OperateLoop {
|
|
|
6857
7363
|
const response = await retryExecutor.execute((signal) => this.adapter.executeRequest(this.client, providerRequest, signal), {
|
|
6858
7364
|
context: {
|
|
6859
7365
|
input: state.currentInput,
|
|
7366
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
6860
7367
|
options,
|
|
7368
|
+
provider: this.adapter.name,
|
|
6861
7369
|
providerRequest,
|
|
6862
7370
|
},
|
|
6863
7371
|
hooks: hooksWithProgress,
|
|
@@ -7292,6 +7800,7 @@ class StreamLoop {
|
|
|
7292
7800
|
}
|
|
7293
7801
|
// Rebuild request with updated history for next turn
|
|
7294
7802
|
request = {
|
|
7803
|
+
effort: options.effort,
|
|
7295
7804
|
format: state.formattedFormat,
|
|
7296
7805
|
instructions: options.instructions,
|
|
7297
7806
|
messages: state.currentInput,
|
|
@@ -7367,6 +7876,7 @@ class StreamLoop {
|
|
|
7367
7876
|
}
|
|
7368
7877
|
buildInitialRequest(state, options) {
|
|
7369
7878
|
return {
|
|
7879
|
+
effort: options.effort,
|
|
7370
7880
|
format: state.formattedFormat,
|
|
7371
7881
|
instructions: options.instructions,
|
|
7372
7882
|
messages: state.currentInput,
|
|
@@ -7478,9 +7988,11 @@ class StreamLoop {
|
|
|
7478
7988
|
!this.adapter.isRetryableError(error)) {
|
|
7479
7989
|
log.error(`Stream request failed after ${this.retryPolicy.maxRetries} retries`);
|
|
7480
7990
|
log.var({ error });
|
|
7481
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7482
7991
|
llmSpan?.finish();
|
|
7483
|
-
throw
|
|
7992
|
+
throw toLlmError(this.adapter.classifyError(error), {
|
|
7993
|
+
model: options.model ?? this.adapter.defaultModel,
|
|
7994
|
+
provider: this.adapter.name,
|
|
7995
|
+
});
|
|
7484
7996
|
}
|
|
7485
7997
|
const delay = this.retryPolicy.getDelayForAttempt(attempt);
|
|
7486
7998
|
log.warn(`Stream request failed. Retrying in ${delay}ms...`);
|
|
@@ -7930,7 +8442,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
|
|
|
7930
8442
|
|
|
7931
8443
|
// Main class implementation
|
|
7932
8444
|
class AnthropicProvider {
|
|
7933
|
-
constructor(model = PROVIDER.ANTHROPIC.
|
|
8445
|
+
constructor(model = PROVIDER.ANTHROPIC.DEFAULT, { apiKey } = {}) {
|
|
7934
8446
|
this.log = getLogger$5();
|
|
7935
8447
|
this.conversationHistory = [];
|
|
7936
8448
|
this.model = model;
|
|
@@ -8038,7 +8550,7 @@ async function initializeClient$4({ region, } = {}) {
|
|
|
8038
8550
|
}
|
|
8039
8551
|
|
|
8040
8552
|
class BedrockProvider {
|
|
8041
|
-
constructor(model = PROVIDER.BEDROCK.
|
|
8553
|
+
constructor(model = PROVIDER.BEDROCK.DEFAULT, { region } = {}) {
|
|
8042
8554
|
this.log = getLogger$4();
|
|
8043
8555
|
this.conversationHistory = [];
|
|
8044
8556
|
this.model = model;
|
|
@@ -8281,7 +8793,7 @@ function prepareMessages$2(message, { data, placeholders } = {}) {
|
|
|
8281
8793
|
}
|
|
8282
8794
|
|
|
8283
8795
|
class GoogleProvider {
|
|
8284
|
-
constructor(model = PROVIDER.GOOGLE.
|
|
8796
|
+
constructor(model = PROVIDER.GOOGLE.DEFAULT, { apiKey } = {}) {
|
|
8285
8797
|
this.log = getLogger$3();
|
|
8286
8798
|
this.conversationHistory = [];
|
|
8287
8799
|
this.model = model;
|
|
@@ -8483,7 +8995,7 @@ async function createTextCompletion(client, { messages, model, }) {
|
|
|
8483
8995
|
}
|
|
8484
8996
|
|
|
8485
8997
|
class OpenAiProvider {
|
|
8486
|
-
constructor(model = PROVIDER.OPENAI.
|
|
8998
|
+
constructor(model = PROVIDER.OPENAI.DEFAULT, { apiKey } = {}) {
|
|
8487
8999
|
this.log = getLogger$2();
|
|
8488
9000
|
this.conversationHistory = [];
|
|
8489
9001
|
this.model = model;
|
|
@@ -8714,7 +9226,7 @@ async function initializeClient$1({ apiKey, } = {}) {
|
|
|
8714
9226
|
}
|
|
8715
9227
|
// Get default model from environment or constants
|
|
8716
9228
|
function getDefaultModel() {
|
|
8717
|
-
return process.env.OPENROUTER_MODEL || PROVIDER.OPENROUTER.
|
|
9229
|
+
return process.env.OPENROUTER_MODEL || PROVIDER.OPENROUTER.DEFAULT;
|
|
8718
9230
|
}
|
|
8719
9231
|
function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
|
|
8720
9232
|
const content = placeholders?.system === false
|
|
@@ -8866,7 +9378,7 @@ async function initializeClient({ apiKey, } = {}) {
|
|
|
8866
9378
|
}
|
|
8867
9379
|
|
|
8868
9380
|
class XaiProvider {
|
|
8869
|
-
constructor(model = PROVIDER.XAI.
|
|
9381
|
+
constructor(model = PROVIDER.XAI.DEFAULT, { apiKey } = {}) {
|
|
8870
9382
|
this.log = getLogger$2();
|
|
8871
9383
|
this.conversationHistory = [];
|
|
8872
9384
|
this.model = model;
|
|
@@ -8951,7 +9463,14 @@ class XaiProvider {
|
|
|
8951
9463
|
|
|
8952
9464
|
class Llm {
|
|
8953
9465
|
constructor(providerName = DEFAULT.PROVIDER.NAME, options = {}) {
|
|
8954
|
-
const { fallback, model } = options;
|
|
9466
|
+
const { fallback: fallbackOption, model: modelOption } = options;
|
|
9467
|
+
// A `model` array is sugar for a preference-ordered fallback chain:
|
|
9468
|
+
// index 0 is primary, the rest become fallback entries (provider
|
|
9469
|
+
// auto-detected). Any explicit `fallback` is appended after the chain.
|
|
9470
|
+
const { fallback: modelFallback, model } = resolveModelChain(modelOption);
|
|
9471
|
+
const fallback = modelFallback.length || fallbackOption
|
|
9472
|
+
? [...modelFallback, ...(fallbackOption ?? [])]
|
|
9473
|
+
: undefined;
|
|
8955
9474
|
let finalProvider = providerName;
|
|
8956
9475
|
let finalModel = model;
|
|
8957
9476
|
// Legacy: accept "gemini" but warn
|
|
@@ -8997,23 +9516,25 @@ class Llm {
|
|
|
8997
9516
|
const { apiKey, model } = options;
|
|
8998
9517
|
switch (providerName) {
|
|
8999
9518
|
case PROVIDER.ANTHROPIC.NAME:
|
|
9000
|
-
return new AnthropicProvider(model || PROVIDER.ANTHROPIC.
|
|
9519
|
+
return new AnthropicProvider(model || PROVIDER.ANTHROPIC.DEFAULT, {
|
|
9520
|
+
apiKey,
|
|
9521
|
+
});
|
|
9001
9522
|
case PROVIDER.BEDROCK.NAME:
|
|
9002
|
-
return new BedrockProvider(model || PROVIDER.BEDROCK.
|
|
9523
|
+
return new BedrockProvider(model || PROVIDER.BEDROCK.DEFAULT);
|
|
9003
9524
|
case PROVIDER.GOOGLE.NAME:
|
|
9004
|
-
return new GoogleProvider(model || PROVIDER.GOOGLE.
|
|
9525
|
+
return new GoogleProvider(model || PROVIDER.GOOGLE.DEFAULT, {
|
|
9005
9526
|
apiKey,
|
|
9006
9527
|
});
|
|
9007
9528
|
case PROVIDER.OPENAI.NAME:
|
|
9008
|
-
return new OpenAiProvider(model || PROVIDER.OPENAI.
|
|
9529
|
+
return new OpenAiProvider(model || PROVIDER.OPENAI.DEFAULT, {
|
|
9009
9530
|
apiKey,
|
|
9010
9531
|
});
|
|
9011
9532
|
case PROVIDER.OPENROUTER.NAME:
|
|
9012
|
-
return new OpenRouterProvider(model || PROVIDER.OPENROUTER.
|
|
9533
|
+
return new OpenRouterProvider(model || PROVIDER.OPENROUTER.DEFAULT, {
|
|
9013
9534
|
apiKey,
|
|
9014
9535
|
});
|
|
9015
9536
|
case PROVIDER.XAI.NAME:
|
|
9016
|
-
return new XaiProvider(model || PROVIDER.XAI.
|
|
9537
|
+
return new XaiProvider(model || PROVIDER.XAI.DEFAULT, {
|
|
9017
9538
|
apiKey,
|
|
9018
9539
|
});
|
|
9019
9540
|
default:
|
|
@@ -9021,6 +9542,11 @@ class Llm {
|
|
|
9021
9542
|
}
|
|
9022
9543
|
}
|
|
9023
9544
|
async send(message, options) {
|
|
9545
|
+
if (options && Array.isArray(options.model)) {
|
|
9546
|
+
// `send` has no fallback machinery; use the primary model only
|
|
9547
|
+
const { model } = resolveModelChain(options.model);
|
|
9548
|
+
return this._llm.send(message, { ...options, model });
|
|
9549
|
+
}
|
|
9024
9550
|
return this._llm.send(message, options);
|
|
9025
9551
|
}
|
|
9026
9552
|
/**
|
|
@@ -9053,8 +9579,21 @@ class Llm {
|
|
|
9053
9579
|
if (!this._llm.operate) {
|
|
9054
9580
|
throw new errors.NotImplementedError(`Provider ${this._provider} does not support operate method`);
|
|
9055
9581
|
}
|
|
9056
|
-
|
|
9057
|
-
|
|
9582
|
+
// A per-call `model` array becomes primary + a derived fallback chain
|
|
9583
|
+
// prepended to any resolved chain.
|
|
9584
|
+
const { fallback: modelFallback, model: perCallModel } = resolveModelChain(options.model);
|
|
9585
|
+
const resolvedOptions = {
|
|
9586
|
+
...options,
|
|
9587
|
+
model: perCallModel,
|
|
9588
|
+
};
|
|
9589
|
+
const fallbackChain = [
|
|
9590
|
+
...modelFallback,
|
|
9591
|
+
...this.resolveFallbackChain(resolvedOptions),
|
|
9592
|
+
];
|
|
9593
|
+
const optionsWithoutFallback = {
|
|
9594
|
+
...resolvedOptions,
|
|
9595
|
+
fallback: false,
|
|
9596
|
+
};
|
|
9058
9597
|
let lastError;
|
|
9059
9598
|
let attempts = 0;
|
|
9060
9599
|
// Try primary provider first
|
|
@@ -9103,33 +9642,45 @@ class Llm {
|
|
|
9103
9642
|
if (!this._llm.stream) {
|
|
9104
9643
|
throw new errors.NotImplementedError(`Provider ${this._provider} does not support stream method`);
|
|
9105
9644
|
}
|
|
9106
|
-
|
|
9645
|
+
// `stream` has no instance-level fallback machinery; an array model uses
|
|
9646
|
+
// the primary and ignores the rest.
|
|
9647
|
+
const { model } = resolveModelChain(options.model);
|
|
9648
|
+
const streamOptions = { ...options, model };
|
|
9649
|
+
yield* this._llm.stream(input, streamOptions);
|
|
9107
9650
|
}
|
|
9108
9651
|
static async send(message, options) {
|
|
9109
9652
|
const { llm, apiKey, model, ...messageOptions } = options || {};
|
|
9110
|
-
|
|
9653
|
+
// A `model` array derives a fallback chain; `send` has no fallback
|
|
9654
|
+
// machinery, so the primary model is used and the chain is ignored.
|
|
9655
|
+
const { model: primaryModel } = resolveModelChain(model);
|
|
9656
|
+
const instance = new Llm(llm, { apiKey, model: primaryModel });
|
|
9111
9657
|
return instance.send(message, messageOptions);
|
|
9112
9658
|
}
|
|
9113
9659
|
static async operate(input, options) {
|
|
9114
9660
|
const { apiKey, fallback, llm, model, ...operateOptions } = options || {};
|
|
9661
|
+
// A `model` array becomes primary + derived fallback chain
|
|
9662
|
+
const { fallback: modelFallback, model: primaryModel } = resolveModelChain(model);
|
|
9115
9663
|
let finalLlm = llm;
|
|
9116
|
-
let finalModel =
|
|
9117
|
-
if (!llm &&
|
|
9118
|
-
const determined = determineModelProvider(
|
|
9664
|
+
let finalModel = primaryModel;
|
|
9665
|
+
if (!llm && primaryModel) {
|
|
9666
|
+
const determined = determineModelProvider(primaryModel);
|
|
9119
9667
|
if (determined.provider) {
|
|
9120
9668
|
finalLlm = determined.provider;
|
|
9121
9669
|
}
|
|
9122
9670
|
}
|
|
9123
|
-
else if (llm &&
|
|
9671
|
+
else if (llm && primaryModel) {
|
|
9124
9672
|
// When both llm and model are provided, check if they conflict
|
|
9125
|
-
const determined = determineModelProvider(
|
|
9673
|
+
const determined = determineModelProvider(primaryModel);
|
|
9126
9674
|
if (determined.provider && determined.provider !== llm) {
|
|
9127
9675
|
// Don't pass the conflicting model to the constructor
|
|
9128
9676
|
finalModel = undefined;
|
|
9129
9677
|
}
|
|
9130
9678
|
}
|
|
9131
9679
|
// Resolve fallback for static method: pass to instance if array, pass to operate options if false
|
|
9132
|
-
const
|
|
9680
|
+
const explicitFallback = Array.isArray(fallback) ? fallback : [];
|
|
9681
|
+
const instanceFallback = modelFallback.length || explicitFallback.length
|
|
9682
|
+
? [...modelFallback, ...explicitFallback]
|
|
9683
|
+
: undefined;
|
|
9133
9684
|
const operateFallback = fallback === false ? false : undefined;
|
|
9134
9685
|
const instance = new Llm(finalLlm, {
|
|
9135
9686
|
apiKey,
|
|
@@ -9143,23 +9694,29 @@ class Llm {
|
|
|
9143
9694
|
}
|
|
9144
9695
|
static stream(input, options) {
|
|
9145
9696
|
const { llm, apiKey, model, ...streamOptions } = options || {};
|
|
9697
|
+
// A `model` array becomes primary + derived fallback chain
|
|
9698
|
+
const { fallback: modelFallback, model: primaryModel } = resolveModelChain(model);
|
|
9146
9699
|
let finalLlm = llm;
|
|
9147
|
-
let finalModel =
|
|
9148
|
-
if (!llm &&
|
|
9149
|
-
const determined = determineModelProvider(
|
|
9700
|
+
let finalModel = primaryModel;
|
|
9701
|
+
if (!llm && primaryModel) {
|
|
9702
|
+
const determined = determineModelProvider(primaryModel);
|
|
9150
9703
|
if (determined.provider) {
|
|
9151
9704
|
finalLlm = determined.provider;
|
|
9152
9705
|
}
|
|
9153
9706
|
}
|
|
9154
|
-
else if (llm &&
|
|
9707
|
+
else if (llm && primaryModel) {
|
|
9155
9708
|
// When both llm and model are provided, check if they conflict
|
|
9156
|
-
const determined = determineModelProvider(
|
|
9709
|
+
const determined = determineModelProvider(primaryModel);
|
|
9157
9710
|
if (determined.provider && determined.provider !== llm) {
|
|
9158
9711
|
// Don't pass the conflicting model to the constructor
|
|
9159
9712
|
finalModel = undefined;
|
|
9160
9713
|
}
|
|
9161
9714
|
}
|
|
9162
|
-
const instance = new Llm(finalLlm, {
|
|
9715
|
+
const instance = new Llm(finalLlm, {
|
|
9716
|
+
apiKey,
|
|
9717
|
+
fallback: modelFallback.length ? modelFallback : undefined,
|
|
9718
|
+
model: finalModel,
|
|
9719
|
+
});
|
|
9163
9720
|
return instance.stream(input, streamOptions);
|
|
9164
9721
|
}
|
|
9165
9722
|
}
|
|
@@ -9463,6 +10020,11 @@ exports.GoogleProvider = GoogleProvider;
|
|
|
9463
10020
|
exports.JaypieToolkit = JaypieToolkit;
|
|
9464
10021
|
exports.LLM = constants;
|
|
9465
10022
|
exports.Llm = Llm;
|
|
10023
|
+
exports.LlmError = LlmError;
|
|
10024
|
+
exports.LlmQuotaError = LlmQuotaError;
|
|
10025
|
+
exports.LlmRateLimitError = LlmRateLimitError;
|
|
10026
|
+
exports.LlmTransientError = LlmTransientError;
|
|
10027
|
+
exports.LlmUnrecoverableError = LlmUnrecoverableError;
|
|
9466
10028
|
exports.OpenRouterProvider = OpenRouterProvider;
|
|
9467
10029
|
exports.Toolkit = Toolkit;
|
|
9468
10030
|
exports.XaiProvider = XaiProvider;
|