@axlsdk/axl 0.22.1 → 0.22.3
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/README.md +2 -2
- package/dist/index.cjs +500 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +164 -3
- package/dist/index.d.ts +164 -3
- package/dist/index.js +500 -163
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1427,7 +1427,8 @@ var OpenAICompatibleProvider = class {
|
|
|
1427
1427
|
return this.name === "openrouter" && model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64"] } } : {};
|
|
1428
1428
|
}
|
|
1429
1429
|
validateInput(request) {
|
|
1430
|
-
const
|
|
1430
|
+
const modelOverride = request.providerOptions?.model;
|
|
1431
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
1431
1432
|
const fail = (source, feature) => {
|
|
1432
1433
|
throw new UnsupportedModelInputError({
|
|
1433
1434
|
provider: this.name,
|
|
@@ -1437,6 +1438,9 @@ var OpenAICompatibleProvider = class {
|
|
|
1437
1438
|
...feature ? { feature } : {}
|
|
1438
1439
|
});
|
|
1439
1440
|
};
|
|
1441
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
1442
|
+
fail(void 0, "invalid model providerOptions");
|
|
1443
|
+
}
|
|
1440
1444
|
if (this.name !== "openrouter" || effectiveModel.trim().length === 0) {
|
|
1441
1445
|
fail(void 0, "image input for this model");
|
|
1442
1446
|
}
|
|
@@ -2336,17 +2340,22 @@ function resolveOpenAIReasoningEffort(model, resolved) {
|
|
|
2336
2340
|
}
|
|
2337
2341
|
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
2338
2342
|
}
|
|
2339
|
-
var warnedChatMaxDowngrades = /* @__PURE__ */ new Set();
|
|
2340
2343
|
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
2341
2344
|
const effort = resolveOpenAIReasoningEffort(model, resolved);
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
return
|
|
2345
|
+
return effort === "max" ? "xhigh" : effort;
|
|
2346
|
+
}
|
|
2347
|
+
function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
|
|
2348
|
+
const resolved = resolveThinkingOptions(options);
|
|
2349
|
+
if (resolved.effort === void 0 || resolved.hasBudgetOverride) return void 0;
|
|
2350
|
+
const model = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
2351
|
+
const effective = resolveWireEffort(model, resolved);
|
|
2352
|
+
if (effective === void 0 || effective === resolved.effort) return void 0;
|
|
2353
|
+
return {
|
|
2354
|
+
requested: resolved.effort,
|
|
2355
|
+
effective,
|
|
2356
|
+
clamped: true,
|
|
2357
|
+
cause: `${endpoint} does not accept reasoning effort '${resolved.effort}' for ${model}; using '${effective}'`
|
|
2358
|
+
};
|
|
2350
2359
|
}
|
|
2351
2360
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
2352
2361
|
const oSeries = isOSeriesModel(model);
|
|
@@ -2371,19 +2380,21 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
2371
2380
|
constructor(options = {}) {
|
|
2372
2381
|
super({ profile: OPENAI_PROFILE, ...options });
|
|
2373
2382
|
}
|
|
2383
|
+
/** Report a clamped `effort` — Chat Completions caps `'max'` at `'xhigh'` and
|
|
2384
|
+
* rejects `'none'` before gpt-5.1 (clamped to `'minimal'`). */
|
|
2385
|
+
effortResolution(options) {
|
|
2386
|
+
return resolveOpenAIEffortResolution(
|
|
2387
|
+
options,
|
|
2388
|
+
resolveOpenAIChatReasoningEffort,
|
|
2389
|
+
"OpenAI Chat Completions"
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2374
2392
|
computeCost(model, usage, _reportedCost, context) {
|
|
2375
2393
|
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
2376
2394
|
}
|
|
2377
2395
|
};
|
|
2378
2396
|
|
|
2379
2397
|
// src/providers/openai-responses.ts
|
|
2380
|
-
var OPENAI_RESPONSES_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
2381
|
-
"gpt-4o",
|
|
2382
|
-
"gpt-4o-2024-08-06",
|
|
2383
|
-
"gpt-4o-2024-11-20",
|
|
2384
|
-
"gpt-4o-mini",
|
|
2385
|
-
"gpt-4o-mini-2024-07-18"
|
|
2386
|
-
]);
|
|
2387
2398
|
function base64FromSource(source) {
|
|
2388
2399
|
return source.type === "base64" ? source.data : Buffer.from(source.data.buffer, source.data.byteOffset, source.data.byteLength).toString(
|
|
2389
2400
|
"base64"
|
|
@@ -2422,10 +2433,17 @@ function responseImageParts(parts) {
|
|
|
2422
2433
|
var OpenAIResponsesProvider = class {
|
|
2423
2434
|
name = "openai-responses";
|
|
2424
2435
|
inputCapabilities(model) {
|
|
2425
|
-
return
|
|
2436
|
+
return model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
2437
|
+
}
|
|
2438
|
+
/** Report a clamped `effort`. Responses accepts the native `'max'` tier that
|
|
2439
|
+
* Chat Completions caps, but still clamps `'none'` before gpt-5.1, `'xhigh'`
|
|
2440
|
+
* below gpt-5.2, and everything to `'high'` on gpt-5-pro. */
|
|
2441
|
+
effortResolution(options) {
|
|
2442
|
+
return resolveOpenAIEffortResolution(options, resolveOpenAIReasoningEffort, "OpenAI Responses");
|
|
2426
2443
|
}
|
|
2427
2444
|
validateInput(request) {
|
|
2428
|
-
const
|
|
2445
|
+
const modelOverride = request.providerOptions?.model;
|
|
2446
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
2429
2447
|
const fail = (source, feature) => {
|
|
2430
2448
|
throw new UnsupportedModelInputError({
|
|
2431
2449
|
provider: this.name,
|
|
@@ -2435,7 +2453,10 @@ var OpenAIResponsesProvider = class {
|
|
|
2435
2453
|
...feature ? { feature } : {}
|
|
2436
2454
|
});
|
|
2437
2455
|
};
|
|
2438
|
-
if (
|
|
2456
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
2457
|
+
fail(void 0, "invalid model providerOptions");
|
|
2458
|
+
}
|
|
2459
|
+
if (effectiveModel.trim().length === 0) {
|
|
2439
2460
|
fail(void 0, "image input for this model");
|
|
2440
2461
|
}
|
|
2441
2462
|
if (request.providerOptions && "input" in request.providerOptions) {
|
|
@@ -2563,7 +2584,7 @@ var OpenAIResponsesProvider = class {
|
|
|
2563
2584
|
const resolved = resolveThinkingOptions(options);
|
|
2564
2585
|
const { includeThoughts } = resolved;
|
|
2565
2586
|
const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
|
|
2566
|
-
const stripTemp = oSeries || reasoningCapable && wireEffort !== void 0;
|
|
2587
|
+
const stripTemp = oSeries || supportsMaxReasoningEffort(effectiveModel) || reasoningCapable && wireEffort !== void 0;
|
|
2567
2588
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
2568
2589
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
2569
2590
|
const body = {
|
|
@@ -2911,7 +2932,6 @@ function anthropicImageBlocks(parts) {
|
|
|
2911
2932
|
}
|
|
2912
2933
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
2913
2934
|
var ANTHROPIC_FILES_BETA = "files-api-2025-04-14";
|
|
2914
|
-
var ANTHROPIC_IMAGE_MODELS = /* @__PURE__ */ new Set(["claude-sonnet-4-5", "claude-opus-4-8"]);
|
|
2915
2935
|
function hasAnthropicProviderFileImage(messages) {
|
|
2916
2936
|
return messages.some(
|
|
2917
2937
|
(message) => Array.isArray(message.content) && message.content.some(
|
|
@@ -3030,6 +3050,28 @@ var ANTHROPIC_RATES = {
|
|
|
3030
3050
|
function isValidTokenCount(value) {
|
|
3031
3051
|
return value !== void 0 && Number.isSafeInteger(value) && value >= 0;
|
|
3032
3052
|
}
|
|
3053
|
+
function anthropicStreamErrorStatus(type) {
|
|
3054
|
+
switch (type) {
|
|
3055
|
+
case "invalid_request_error":
|
|
3056
|
+
return 400;
|
|
3057
|
+
case "authentication_error":
|
|
3058
|
+
return 401;
|
|
3059
|
+
case "permission_error":
|
|
3060
|
+
return 403;
|
|
3061
|
+
case "not_found_error":
|
|
3062
|
+
return 404;
|
|
3063
|
+
case "request_too_large":
|
|
3064
|
+
return 413;
|
|
3065
|
+
case "rate_limit_error":
|
|
3066
|
+
return 429;
|
|
3067
|
+
case "api_error":
|
|
3068
|
+
return 500;
|
|
3069
|
+
case "overloaded_error":
|
|
3070
|
+
return 529;
|
|
3071
|
+
default:
|
|
3072
|
+
return 0;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3033
3075
|
function estimateAnthropicCost(model, usage, now = /* @__PURE__ */ new Date()) {
|
|
3034
3076
|
if (!model || !isValidTokenCount(usage.inputTokens) || !isValidTokenCount(usage.outputTokens)) {
|
|
3035
3077
|
return void 0;
|
|
@@ -3074,14 +3116,6 @@ function budgetToClaudeEffort(budget) {
|
|
|
3074
3116
|
if (budget <= THINKING_BUDGETS.xhigh) return "xhigh";
|
|
3075
3117
|
return "max";
|
|
3076
3118
|
}
|
|
3077
|
-
var warnedFableDisable = /* @__PURE__ */ new Set();
|
|
3078
|
-
function warnFableDisable(model) {
|
|
3079
|
-
if (warnedFableDisable.has(model)) return;
|
|
3080
|
-
warnedFableDisable.add(model);
|
|
3081
|
-
console.warn(
|
|
3082
|
-
`[axl] thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'.`
|
|
3083
|
-
);
|
|
3084
|
-
}
|
|
3085
3119
|
function resolveClaudeThinking(model, resolved) {
|
|
3086
3120
|
const capability = resolveClaudeCapability(model);
|
|
3087
3121
|
if (!capability) return { stripTemperature: false };
|
|
@@ -3101,11 +3135,14 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
3101
3135
|
}
|
|
3102
3136
|
if (resolved.thinkingDisabled) {
|
|
3103
3137
|
if (capability.thinking === "adaptive-always-on") {
|
|
3104
|
-
warnFableDisable(model);
|
|
3105
3138
|
return {
|
|
3106
3139
|
thinking: { type: "adaptive" },
|
|
3107
3140
|
outputConfig: { effort: "low" },
|
|
3108
|
-
stripTemperature: true
|
|
3141
|
+
stripTemperature: true,
|
|
3142
|
+
clamp: {
|
|
3143
|
+
effective: "low",
|
|
3144
|
+
cause: `thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'`
|
|
3145
|
+
}
|
|
3109
3146
|
};
|
|
3110
3147
|
}
|
|
3111
3148
|
if (activeEffort && capability.disableAt && !isEffortAtOrBelow(activeEffort, capability.disableAt)) {
|
|
@@ -3167,7 +3204,16 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
3167
3204
|
return {
|
|
3168
3205
|
thinking: { type: "enabled", budget_tokens: budget },
|
|
3169
3206
|
manualBudget: budget,
|
|
3170
|
-
stripTemperature: true
|
|
3207
|
+
stripTemperature: true,
|
|
3208
|
+
// Legacy models take `budget_tokens`, not an effort level, and have no
|
|
3209
|
+
// `effortLevels` — so `clampAnthropicEffort` folds 'max'/'xhigh' down to
|
|
3210
|
+
// the 'high' tier and the request silently carries that tier's budget.
|
|
3211
|
+
// Report it: an unreported budget-tier downgrade is exactly the silent
|
|
3212
|
+
// clamp this capability exists to expose.
|
|
3213
|
+
clamp: activeEffort === resolved.activeEffort ? void 0 : {
|
|
3214
|
+
effective: activeEffort,
|
|
3215
|
+
cause: `Anthropic ${model} takes thinking budget_tokens rather than an effort level, and its ceiling tier is '${activeEffort}'; effort '${resolved.activeEffort}' sends budget_tokens ${budget}`
|
|
3216
|
+
}
|
|
3171
3217
|
};
|
|
3172
3218
|
}
|
|
3173
3219
|
return { stripTemperature: capability.stripTemperature ?? false };
|
|
@@ -3278,11 +3324,32 @@ function mergeAnthropicUsage(initial, update) {
|
|
|
3278
3324
|
}
|
|
3279
3325
|
var AnthropicProvider = class {
|
|
3280
3326
|
name = "anthropic";
|
|
3327
|
+
/** Report a clamped `effort`. Anthropic clamps to each model's supported
|
|
3328
|
+
* `output_config.effort` levels, and models that always think fall back to
|
|
3329
|
+
* adaptive `'low'` when asked to disable thinking. Only reported when the
|
|
3330
|
+
* effective level is what the request body actually carries; an explicit
|
|
3331
|
+
* `thinkingBudget` is documented to override `effort`, so it is not a clamp. */
|
|
3332
|
+
effortResolution(options) {
|
|
3333
|
+
const resolved = resolveThinkingOptions(options);
|
|
3334
|
+
if (resolved.effort === void 0 || resolved.hasBudgetOverride) return void 0;
|
|
3335
|
+
const model = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
3336
|
+
const config = resolveClaudeThinking(model, resolved);
|
|
3337
|
+
const wireEffort = typeof config.outputConfig?.effort === "string" ? config.outputConfig.effort : void 0;
|
|
3338
|
+
const effective = config.clamp?.effective ?? wireEffort;
|
|
3339
|
+
if (effective === void 0 || effective === resolved.effort) return void 0;
|
|
3340
|
+
return {
|
|
3341
|
+
requested: resolved.effort,
|
|
3342
|
+
effective,
|
|
3343
|
+
clamped: true,
|
|
3344
|
+
cause: config.clamp?.cause ?? `Anthropic ${model} does not support effort '${resolved.effort}'; using '${effective}'`
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3281
3347
|
inputCapabilities(model) {
|
|
3282
|
-
return
|
|
3348
|
+
return model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
3283
3349
|
}
|
|
3284
3350
|
validateInput(request) {
|
|
3285
|
-
const
|
|
3351
|
+
const modelOverride = request.providerOptions?.model;
|
|
3352
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
3286
3353
|
const fail = (source, feature) => {
|
|
3287
3354
|
throw new UnsupportedModelInputError({
|
|
3288
3355
|
provider: this.name,
|
|
@@ -3292,7 +3359,10 @@ var AnthropicProvider = class {
|
|
|
3292
3359
|
...feature ? { feature } : {}
|
|
3293
3360
|
});
|
|
3294
3361
|
};
|
|
3295
|
-
if (
|
|
3362
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
3363
|
+
fail(void 0, "invalid model providerOptions");
|
|
3364
|
+
}
|
|
3365
|
+
if (effectiveModel.trim().length === 0) {
|
|
3296
3366
|
fail(void 0, "image input for this model");
|
|
3297
3367
|
}
|
|
3298
3368
|
if (request.providerOptions && "messages" in request.providerOptions) {
|
|
@@ -3417,7 +3487,7 @@ var AnthropicProvider = class {
|
|
|
3417
3487
|
if (!res.body) {
|
|
3418
3488
|
throw new Error("Anthropic stream response has no body");
|
|
3419
3489
|
}
|
|
3420
|
-
yield* this.parseSSEStream(res.body, pricingContext);
|
|
3490
|
+
yield* this.parseSSEStream(res.body, pricingContext, res.headers);
|
|
3421
3491
|
}
|
|
3422
3492
|
// ---------------------------------------------------------------------------
|
|
3423
3493
|
// Internal: request building
|
|
@@ -3660,7 +3730,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
3660
3730
|
// ---------------------------------------------------------------------------
|
|
3661
3731
|
// Internal: SSE stream parsing
|
|
3662
3732
|
// ---------------------------------------------------------------------------
|
|
3663
|
-
async *parseSSEStream(body, pricingContext) {
|
|
3733
|
+
async *parseSSEStream(body, pricingContext, responseHeaders) {
|
|
3664
3734
|
const reader = body.getReader();
|
|
3665
3735
|
const decoder = new TextDecoder();
|
|
3666
3736
|
let buffer = "";
|
|
@@ -3706,6 +3776,15 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
3706
3776
|
continue;
|
|
3707
3777
|
}
|
|
3708
3778
|
switch (event.type) {
|
|
3779
|
+
case "error": {
|
|
3780
|
+
const status = anthropicStreamErrorStatus(event.error?.type);
|
|
3781
|
+
throw buildProviderError({
|
|
3782
|
+
provider: this.name,
|
|
3783
|
+
status,
|
|
3784
|
+
headers: responseHeaders,
|
|
3785
|
+
message: event.error?.message ?? "Anthropic stream failed"
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3709
3788
|
case "content_block_start": {
|
|
3710
3789
|
const block = event.content_block;
|
|
3711
3790
|
if (block?.type === "tool_use") {
|
|
@@ -3957,7 +4036,6 @@ var GEMINI_PRICING = {
|
|
|
3957
4036
|
"gemini-3.5-flash-lite": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3958
4037
|
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
3959
4038
|
};
|
|
3960
|
-
var GEMINI_INTERACTIONS_IMAGE_MODELS = /* @__PURE__ */ new Set(["gemini-3.7-flash"]);
|
|
3961
4039
|
function isValidTokenCount2(value) {
|
|
3962
4040
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3963
4041
|
}
|
|
@@ -3967,6 +4045,55 @@ function isStandardGeminiRequestTier(value) {
|
|
|
3967
4045
|
function isDefinitiveStandardGeminiResponseTier(value) {
|
|
3968
4046
|
return value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD" || value === "standard";
|
|
3969
4047
|
}
|
|
4048
|
+
function geminiInteractionErrorStatus(code) {
|
|
4049
|
+
if (typeof code === "number" && Number.isInteger(code) && code >= 100 && code <= 599) {
|
|
4050
|
+
return code;
|
|
4051
|
+
}
|
|
4052
|
+
switch (code) {
|
|
4053
|
+
case "invalid_request":
|
|
4054
|
+
case "invalid_argument":
|
|
4055
|
+
case "parameter_unknown":
|
|
4056
|
+
case "failed_precondition":
|
|
4057
|
+
return 400;
|
|
4058
|
+
case "authentication":
|
|
4059
|
+
case "unauthenticated":
|
|
4060
|
+
return 401;
|
|
4061
|
+
case "permission_denied":
|
|
4062
|
+
return 403;
|
|
4063
|
+
case "not_found":
|
|
4064
|
+
case "model_not_found":
|
|
4065
|
+
return 404;
|
|
4066
|
+
case "request_timeout":
|
|
4067
|
+
return 408;
|
|
4068
|
+
case "conflict":
|
|
4069
|
+
case "already_exists":
|
|
4070
|
+
return 409;
|
|
4071
|
+
case "request_too_large":
|
|
4072
|
+
return 413;
|
|
4073
|
+
case "out_of_range":
|
|
4074
|
+
return 416;
|
|
4075
|
+
case "quota_exceeded":
|
|
4076
|
+
case "resource_exhausted":
|
|
4077
|
+
case "rate_limit_exceeded":
|
|
4078
|
+
return 429;
|
|
4079
|
+
case "cancelled":
|
|
4080
|
+
return 499;
|
|
4081
|
+
case "api_error":
|
|
4082
|
+
case "internal":
|
|
4083
|
+
case "internal_server_error":
|
|
4084
|
+
return 500;
|
|
4085
|
+
case "bad_gateway":
|
|
4086
|
+
return 502;
|
|
4087
|
+
case "unavailable":
|
|
4088
|
+
case "service_unavailable":
|
|
4089
|
+
return 503;
|
|
4090
|
+
case "deadline_exceeded":
|
|
4091
|
+
case "gateway_timeout":
|
|
4092
|
+
return 504;
|
|
4093
|
+
default:
|
|
4094
|
+
return void 0;
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
3970
4097
|
function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
|
|
3971
4098
|
return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
|
|
3972
4099
|
}
|
|
@@ -4048,7 +4175,9 @@ var GEMINI_3X_MIN_THINKING_LEVEL = /* @__PURE__ */ new Map([
|
|
|
4048
4175
|
["gemini-3.6-flash", "minimal"],
|
|
4049
4176
|
// L4 live evidence: Interactions gemini-3.7-flash accepts low/medium/high,
|
|
4050
4177
|
// but rejects minimal. Keep this exact entry isolated from legacy aliases.
|
|
4051
|
-
["gemini-3.7-flash", "low"]
|
|
4178
|
+
["gemini-3.7-flash", "low"],
|
|
4179
|
+
// Current-model live evidence: gemini-3.8-flash has the same low floor.
|
|
4180
|
+
["gemini-3.8-flash", "low"]
|
|
4052
4181
|
]);
|
|
4053
4182
|
var GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE = /* @__PURE__ */ new Set([
|
|
4054
4183
|
"gemini-3.5-flash-lite",
|
|
@@ -4065,21 +4194,80 @@ function budgetToThinkingLevel(budgetTokens) {
|
|
|
4065
4194
|
function minThinkingLevel(model) {
|
|
4066
4195
|
return GEMINI_3X_MIN_THINKING_LEVEL.get(model) ?? "minimal";
|
|
4067
4196
|
}
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
if (
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4197
|
+
function resolveGeminiThinking(model, resolved) {
|
|
4198
|
+
const is3x = isGemini3x(model);
|
|
4199
|
+
if (resolved.thinkingDisabled) {
|
|
4200
|
+
const minLevel = minThinkingLevel(model);
|
|
4201
|
+
if (is3x) {
|
|
4202
|
+
return {
|
|
4203
|
+
interactionLevel: minLevel,
|
|
4204
|
+
thinkingLevel: minLevel,
|
|
4205
|
+
thinkingBudget: void 0,
|
|
4206
|
+
attachThoughts: false,
|
|
4207
|
+
// A `thinkingBudget: 0` disable has no unified effort to report against;
|
|
4208
|
+
// only an explicit `effort: 'none'` is a clamp of a requested value.
|
|
4209
|
+
clamp: resolved.effort === "none" ? {
|
|
4210
|
+
effective: minLevel,
|
|
4211
|
+
cause: `Gemini 3.x models cannot disable thinking; using the model minimum thinking level '${minLevel}'`
|
|
4212
|
+
} : void 0
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
return {
|
|
4216
|
+
interactionLevel: "minimal",
|
|
4217
|
+
thinkingLevel: void 0,
|
|
4218
|
+
thinkingBudget: 0,
|
|
4219
|
+
attachThoughts: false
|
|
4220
|
+
};
|
|
4221
|
+
}
|
|
4222
|
+
if (resolved.hasBudgetOverride) {
|
|
4223
|
+
const budget = resolved.thinkingBudget;
|
|
4224
|
+
const level = budgetToThinkingLevel(budget);
|
|
4225
|
+
return {
|
|
4226
|
+
interactionLevel: level,
|
|
4227
|
+
thinkingLevel: is3x ? level : void 0,
|
|
4228
|
+
thinkingBudget: is3x ? void 0 : budget,
|
|
4229
|
+
attachThoughts: resolved.includeThoughts
|
|
4230
|
+
};
|
|
4231
|
+
}
|
|
4232
|
+
if (resolved.activeEffort) {
|
|
4233
|
+
const effort = resolved.activeEffort;
|
|
4234
|
+
const budget = THINKING_BUDGETS2[effort] ?? 5e3;
|
|
4235
|
+
if (is3x) {
|
|
4236
|
+
const level = THINKING_LEVELS[effort] ?? "medium";
|
|
4237
|
+
return {
|
|
4238
|
+
interactionLevel: level,
|
|
4239
|
+
thinkingLevel: level,
|
|
4240
|
+
thinkingBudget: void 0,
|
|
4241
|
+
attachThoughts: resolved.includeThoughts,
|
|
4242
|
+
clamp: level === effort ? void 0 : {
|
|
4243
|
+
effective: level,
|
|
4244
|
+
cause: `Gemini 3.x thinking levels cap at 'high'; effort '${effort}' maps to '${level}'`
|
|
4245
|
+
}
|
|
4246
|
+
};
|
|
4247
|
+
}
|
|
4248
|
+
return {
|
|
4249
|
+
interactionLevel: budgetToThinkingLevel(budget),
|
|
4250
|
+
thinkingLevel: void 0,
|
|
4251
|
+
// 2.5 Pro supports a higher max budget (32768) than other 2.5 models (24576).
|
|
4252
|
+
thinkingBudget: effort === "max" && model === "gemini-2.5-pro" ? 32768 : budget,
|
|
4253
|
+
attachThoughts: resolved.includeThoughts
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
4256
|
+
return {
|
|
4257
|
+
interactionLevel: void 0,
|
|
4258
|
+
thinkingLevel: void 0,
|
|
4259
|
+
thinkingBudget: void 0,
|
|
4260
|
+
attachThoughts: resolved.includeThoughts
|
|
4261
|
+
};
|
|
4075
4262
|
}
|
|
4076
4263
|
var GeminiProvider = class {
|
|
4077
4264
|
name = "google";
|
|
4078
4265
|
inputCapabilities(model) {
|
|
4079
|
-
return
|
|
4266
|
+
return model.trim().length > 0 ? { image: { sources: ["bytes", "base64", "provider-file"] } } : {};
|
|
4080
4267
|
}
|
|
4081
4268
|
validateInput(request) {
|
|
4082
|
-
const
|
|
4269
|
+
const modelOverride = request.providerOptions?.model;
|
|
4270
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
4083
4271
|
const fail = (source, feature) => {
|
|
4084
4272
|
throw new UnsupportedModelInputError({
|
|
4085
4273
|
provider: this.name,
|
|
@@ -4089,7 +4277,10 @@ var GeminiProvider = class {
|
|
|
4089
4277
|
...feature ? { feature } : {}
|
|
4090
4278
|
});
|
|
4091
4279
|
};
|
|
4092
|
-
if (
|
|
4280
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
4281
|
+
fail(void 0, "invalid model providerOptions");
|
|
4282
|
+
}
|
|
4283
|
+
if (effectiveModel.trim().length === 0) {
|
|
4093
4284
|
fail(void 0, "image input for this model");
|
|
4094
4285
|
}
|
|
4095
4286
|
if (request.providerOptions) {
|
|
@@ -4136,6 +4327,21 @@ var GeminiProvider = class {
|
|
|
4136
4327
|
nativeStructuredOutputSupport() {
|
|
4137
4328
|
return "lossy";
|
|
4138
4329
|
}
|
|
4330
|
+
/** Report a clamped `effort` (R7a). Gemini 3.x cannot disable thinking and
|
|
4331
|
+
* caps at `'high'`, so `'none'`, `'xhigh'` and `'max'` are not honored
|
|
4332
|
+
* verbatim there. Everything else — including 2.x disabled thinking — is
|
|
4333
|
+
* sent as asked, so there is nothing to report. */
|
|
4334
|
+
effortResolution(options) {
|
|
4335
|
+
const resolved = resolveThinkingOptions(options);
|
|
4336
|
+
if (resolved.effort === void 0) return void 0;
|
|
4337
|
+
const clamp = resolveGeminiThinking(this.requestModel(options), resolved).clamp;
|
|
4338
|
+
return clamp ? {
|
|
4339
|
+
requested: resolved.effort,
|
|
4340
|
+
effective: clamp.effective,
|
|
4341
|
+
clamped: true,
|
|
4342
|
+
cause: clamp.cause
|
|
4343
|
+
} : void 0;
|
|
4344
|
+
}
|
|
4139
4345
|
baseUrl;
|
|
4140
4346
|
apiKeySource;
|
|
4141
4347
|
callCounter = 0;
|
|
@@ -4281,7 +4487,7 @@ var GeminiProvider = class {
|
|
|
4281
4487
|
});
|
|
4282
4488
|
}
|
|
4283
4489
|
if (!res.body) throw new Error("Gemini Interactions stream has no body");
|
|
4284
|
-
yield* this.parseInteractionSSEStream(res.body);
|
|
4490
|
+
yield* this.parseInteractionSSEStream(res.body, res.headers);
|
|
4285
4491
|
}
|
|
4286
4492
|
buildInteractionRequestBody(messages, options, stream) {
|
|
4287
4493
|
const model = this.requestModel(options);
|
|
@@ -4310,12 +4516,9 @@ var GeminiProvider = class {
|
|
|
4310
4516
|
generationConfig.tool_choice = typeof options.toolChoice === "string" ? options.toolChoice === "required" ? "any" : options.toolChoice : { allowed_tools: { mode: "any", tools: [options.toolChoice.function.name] } };
|
|
4311
4517
|
}
|
|
4312
4518
|
const thinking = resolveThinkingOptions(options);
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
generationConfig.thinking_level = budgetToThinkingLevel(thinking.thinkingBudget);
|
|
4317
|
-
} else if (thinking.activeEffort) {
|
|
4318
|
-
generationConfig.thinking_level = isGemini3x(model) ? THINKING_LEVELS[thinking.activeEffort] ?? "medium" : budgetToThinkingLevel(THINKING_BUDGETS2[thinking.activeEffort] ?? 5e3);
|
|
4519
|
+
const thinkingPlan = resolveGeminiThinking(model, thinking);
|
|
4520
|
+
if (thinkingPlan.interactionLevel !== void 0) {
|
|
4521
|
+
generationConfig.thinking_level = thinkingPlan.interactionLevel;
|
|
4319
4522
|
}
|
|
4320
4523
|
if (thinking.includeThoughts) generationConfig.thinking_summaries = "auto";
|
|
4321
4524
|
if (Object.keys(generationConfig).length) body.generation_config = generationConfig;
|
|
@@ -4351,6 +4554,7 @@ var GeminiProvider = class {
|
|
|
4351
4554
|
return body;
|
|
4352
4555
|
}
|
|
4353
4556
|
assertSafeInteractionOptions(options) {
|
|
4557
|
+
const modelOverride = options.providerOptions?.model;
|
|
4354
4558
|
const model = this.requestModel(options);
|
|
4355
4559
|
const fail = (feature) => {
|
|
4356
4560
|
throw new UnsupportedModelInputError({
|
|
@@ -4360,7 +4564,10 @@ var GeminiProvider = class {
|
|
|
4360
4564
|
feature
|
|
4361
4565
|
});
|
|
4362
4566
|
};
|
|
4363
|
-
if (
|
|
4567
|
+
if (options.providerOptions && "model" in options.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
4568
|
+
fail("invalid model providerOptions");
|
|
4569
|
+
}
|
|
4570
|
+
if (model.trim().length === 0) fail("image input for this model");
|
|
4364
4571
|
const forbidden = [
|
|
4365
4572
|
"input",
|
|
4366
4573
|
"contents",
|
|
@@ -4436,7 +4643,7 @@ var GeminiProvider = class {
|
|
|
4436
4643
|
providerMetadata: json.steps?.length ? { geminiInteractionSteps: json.steps.filter(isGeminiInteractionStep) } : void 0
|
|
4437
4644
|
};
|
|
4438
4645
|
}
|
|
4439
|
-
async *parseInteractionSSEStream(body) {
|
|
4646
|
+
async *parseInteractionSSEStream(body, responseHeaders) {
|
|
4440
4647
|
const reader = body.getReader();
|
|
4441
4648
|
const decoder = new TextDecoder();
|
|
4442
4649
|
let buffer = "";
|
|
@@ -4462,7 +4669,22 @@ var GeminiProvider = class {
|
|
|
4462
4669
|
continue;
|
|
4463
4670
|
}
|
|
4464
4671
|
if (event.event_type === "error") {
|
|
4465
|
-
|
|
4672
|
+
const status = geminiInteractionErrorStatus(event.error?.code);
|
|
4673
|
+
const message = event.error?.message ?? "Gemini Interactions stream failed";
|
|
4674
|
+
if (status !== void 0) {
|
|
4675
|
+
throw buildProviderError({
|
|
4676
|
+
provider: this.name,
|
|
4677
|
+
status,
|
|
4678
|
+
headers: responseHeaders,
|
|
4679
|
+
message
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
throw new ProviderError({
|
|
4683
|
+
provider: this.name,
|
|
4684
|
+
status: 0,
|
|
4685
|
+
retryable: false,
|
|
4686
|
+
message
|
|
4687
|
+
});
|
|
4466
4688
|
}
|
|
4467
4689
|
if (event.event_type === "step.start" && event.step) {
|
|
4468
4690
|
const index = event.index;
|
|
@@ -4732,50 +4954,17 @@ var GeminiProvider = class {
|
|
|
4732
4954
|
if (Object.keys(generationConfig).length > 0) {
|
|
4733
4955
|
body.generationConfig = generationConfig;
|
|
4734
4956
|
}
|
|
4735
|
-
const
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
}
|
|
4743
|
-
if (
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
warnGemini3xEffortNone(requestModel);
|
|
4747
|
-
}
|
|
4748
|
-
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(requestModel) };
|
|
4749
|
-
} else {
|
|
4750
|
-
generationConfig.thinkingConfig = { thinkingBudget: 0 };
|
|
4751
|
-
}
|
|
4752
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4753
|
-
} else if (hasBudgetOverride) {
|
|
4754
|
-
const config = {};
|
|
4755
|
-
if (isGemini3x(requestModel)) {
|
|
4756
|
-
config.thinkingLevel = budgetToThinkingLevel(thinkingBudget);
|
|
4757
|
-
} else {
|
|
4758
|
-
config.thinkingBudget = thinkingBudget;
|
|
4759
|
-
}
|
|
4760
|
-
if (includeThoughts) config.includeThoughts = true;
|
|
4761
|
-
generationConfig.thinkingConfig = config;
|
|
4762
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4763
|
-
} else if (activeEffort) {
|
|
4764
|
-
const config = {};
|
|
4765
|
-
if (isGemini3x(requestModel)) {
|
|
4766
|
-
config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
|
|
4767
|
-
} else {
|
|
4768
|
-
if (activeEffort === "max" && requestModel === "gemini-2.5-pro") {
|
|
4769
|
-
config.thinkingBudget = 32768;
|
|
4770
|
-
} else {
|
|
4771
|
-
config.thinkingBudget = THINKING_BUDGETS2[activeEffort] ?? 5e3;
|
|
4772
|
-
}
|
|
4773
|
-
}
|
|
4774
|
-
if (includeThoughts) config.includeThoughts = true;
|
|
4775
|
-
generationConfig.thinkingConfig = config;
|
|
4776
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4777
|
-
} else if (includeThoughts) {
|
|
4778
|
-
generationConfig.thinkingConfig = { includeThoughts: true };
|
|
4957
|
+
const thinkingPlan = resolveGeminiThinking(requestModel, resolveThinkingOptions(options));
|
|
4958
|
+
const thinkingConfig = {};
|
|
4959
|
+
if (thinkingPlan.thinkingLevel !== void 0) {
|
|
4960
|
+
thinkingConfig.thinkingLevel = thinkingPlan.thinkingLevel;
|
|
4961
|
+
}
|
|
4962
|
+
if (thinkingPlan.thinkingBudget !== void 0) {
|
|
4963
|
+
thinkingConfig.thinkingBudget = thinkingPlan.thinkingBudget;
|
|
4964
|
+
}
|
|
4965
|
+
if (thinkingPlan.attachThoughts) thinkingConfig.includeThoughts = true;
|
|
4966
|
+
if (Object.keys(thinkingConfig).length > 0) {
|
|
4967
|
+
generationConfig.thinkingConfig = thinkingConfig;
|
|
4779
4968
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4780
4969
|
}
|
|
4781
4970
|
if (options.toolChoice !== void 0) {
|
|
@@ -7410,6 +7599,9 @@ var AXL_EVENT_TYPES = [
|
|
|
7410
7599
|
// structured-output cliffs (oversized appended schema, dropped refinements,
|
|
7411
7600
|
// streaming disabled, zero-guidance `schemaPrompt:'none'`).
|
|
7412
7601
|
"schema_diagnostic",
|
|
7602
|
+
// Provider capability diagnostics — the resolved provider could not honor a
|
|
7603
|
+
// portable request knob verbatim (first kind: a clamped `effort`).
|
|
7604
|
+
"provider_diagnostic",
|
|
7413
7605
|
// Observability
|
|
7414
7606
|
"log",
|
|
7415
7607
|
"memory_remember",
|
|
@@ -7459,6 +7651,7 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
7459
7651
|
"pipeline",
|
|
7460
7652
|
"verify",
|
|
7461
7653
|
"schema_diagnostic",
|
|
7654
|
+
"provider_diagnostic",
|
|
7462
7655
|
"workflow_start",
|
|
7463
7656
|
"workflow_end",
|
|
7464
7657
|
"transcription_start",
|
|
@@ -8387,6 +8580,10 @@ var LEGACY_REDACTION_RULES = {
|
|
|
8387
8580
|
// which is user content or PII (field paths are schema-shape, preserved like
|
|
8388
8581
|
// `string_delta.path`). Passthrough, as an explicit reviewed decision.
|
|
8389
8582
|
schema_diagnostic: passthrough,
|
|
8583
|
+
// `provider_diagnostic` carries only provider capability metadata — a provider
|
|
8584
|
+
// name, a model id, and two effort level strings. No application data or user
|
|
8585
|
+
// content ever reaches it. Passthrough, as an explicit reviewed decision.
|
|
8586
|
+
provider_diagnostic: passthrough,
|
|
8390
8587
|
log: (e) => {
|
|
8391
8588
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
8392
8589
|
return { ...e, data: walkObjectOneLevel(e.data, LOG_PRESERVE_KEYS) };
|
|
@@ -8517,6 +8714,7 @@ var COMMON_V2_REDACTION_RULES = {
|
|
|
8517
8714
|
string_delta: reuseLegacyRule("string_delta"),
|
|
8518
8715
|
verify: reuseLegacyRule("verify"),
|
|
8519
8716
|
schema_diagnostic: reuseLegacyRule("schema_diagnostic"),
|
|
8717
|
+
provider_diagnostic: reuseLegacyRule("provider_diagnostic"),
|
|
8520
8718
|
log: reuseLegacyRule("log"),
|
|
8521
8719
|
memory_remember: reuseLegacyRule("memory_remember"),
|
|
8522
8720
|
memory_recall: reuseLegacyRule("memory_recall"),
|
|
@@ -8612,13 +8810,14 @@ function detectDroppedRefinements(schema) {
|
|
|
8612
8810
|
var DOCS_URL = "docs/observability.md#schema-diagnostics";
|
|
8613
8811
|
var MAX_WARNED_KEYS = 2048;
|
|
8614
8812
|
var warnedDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
8615
|
-
function
|
|
8813
|
+
function warnDiagnosticOnce(key, message, silent, docs = DOCS_URL) {
|
|
8616
8814
|
if (silent || process.env.AXL_DIAGNOSTICS_SILENT === "true") return;
|
|
8617
8815
|
if (warnedDiagnosticKeys.has(key)) return;
|
|
8618
8816
|
if (warnedDiagnosticKeys.size >= MAX_WARNED_KEYS) warnedDiagnosticKeys.clear();
|
|
8619
8817
|
warnedDiagnosticKeys.add(key);
|
|
8620
|
-
console.warn(`[axl] ${message} See ${
|
|
8818
|
+
console.warn(`[axl] ${message} See ${docs}.`);
|
|
8621
8819
|
}
|
|
8820
|
+
var warnSchemaDiagnosticOnce = warnDiagnosticOnce;
|
|
8622
8821
|
|
|
8623
8822
|
// src/config.ts
|
|
8624
8823
|
function defineConfig(config) {
|
|
@@ -9468,7 +9667,22 @@ function appendRetryMessages(messages, content, feedbackMessage, providerMetadat
|
|
|
9468
9667
|
content,
|
|
9469
9668
|
...providerMetadata ? { providerMetadata } : {}
|
|
9470
9669
|
});
|
|
9471
|
-
messages.push({ role: "
|
|
9670
|
+
messages.push({ role: "user", content: feedbackMessage });
|
|
9671
|
+
}
|
|
9672
|
+
function isRetryAbort(value) {
|
|
9673
|
+
return typeof value === "object" && value !== null && value.retry === false;
|
|
9674
|
+
}
|
|
9675
|
+
function describeValue(value) {
|
|
9676
|
+
if (value === null) return "null";
|
|
9677
|
+
if (Array.isArray(value)) return `an array of length ${value.length}`;
|
|
9678
|
+
if (typeof value === "object") {
|
|
9679
|
+
const keys = Object.keys(value);
|
|
9680
|
+
return keys.length > 0 ? `an object with keys [${keys.join(", ")}]` : "an empty object";
|
|
9681
|
+
}
|
|
9682
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
9683
|
+
return `the ${typeof value} ${JSON.stringify(value)}`;
|
|
9684
|
+
}
|
|
9685
|
+
return `a value of type ${typeof value}`;
|
|
9472
9686
|
}
|
|
9473
9687
|
function estimateMessagesTokens(messages) {
|
|
9474
9688
|
let total = 0;
|
|
@@ -10405,6 +10619,7 @@ ${appendedSchemaText}`;
|
|
|
10405
10619
|
provider,
|
|
10406
10620
|
model
|
|
10407
10621
|
});
|
|
10622
|
+
this.emitProviderDiagnostics(agent2, options, { provider, model, providerOptions });
|
|
10408
10623
|
if (handoffMessages && handoffMessages.length > 0) {
|
|
10409
10624
|
const handoffContext = handoffMessages.filter(
|
|
10410
10625
|
(m) => m.role === "assistant" || m.role === "tool"
|
|
@@ -10769,7 +10984,11 @@ ${appendedSchemaText}`;
|
|
|
10769
10984
|
retries: options.retries,
|
|
10770
10985
|
metadata: options.metadata,
|
|
10771
10986
|
validate: options.validate,
|
|
10772
|
-
validateRetries: options.validateRetries
|
|
10987
|
+
validateRetries: options.validateRetries,
|
|
10988
|
+
// This seam serves both callers of a handoff: a plain `ctx.ask()` on an
|
|
10989
|
+
// agent with `handoffs`, and the routed `ctx.delegate()` path, whose
|
|
10990
|
+
// terminal ask is the handoff target. The hook travels like `validate`.
|
|
10991
|
+
retryFeedback: options.retryFeedback
|
|
10773
10992
|
} : void 0;
|
|
10774
10993
|
const handoffFn = () => askStorage.run(targetFrame, async () => {
|
|
10775
10994
|
const delegateOriginal = agent2._delegateOriginalInput;
|
|
@@ -10918,9 +11137,10 @@ ${appendedSchemaText}`;
|
|
|
10918
11137
|
const attempt = guardrailOutputRetries + 1;
|
|
10919
11138
|
const maxAttempts = maxGuardrailRetries + 1;
|
|
10920
11139
|
const onBlock = guardrails.onBlock ?? "throw";
|
|
11140
|
+
const blockReason = outputResult.reason ?? "Output blocked by guardrail";
|
|
10921
11141
|
let feedbackMessage;
|
|
10922
11142
|
if (outputResult.block && onBlock === "retry" && guardrailOutputRetries < maxGuardrailRetries) {
|
|
10923
|
-
feedbackMessage = `Your previous response was blocked by a safety guardrail: ${
|
|
11143
|
+
feedbackMessage = `Your previous response was blocked by a safety guardrail: ${blockReason}. Please provide a different response that complies with the guidelines.`;
|
|
10924
11144
|
}
|
|
10925
11145
|
this.emitEvent({
|
|
10926
11146
|
type: "guardrail",
|
|
@@ -10943,31 +11163,43 @@ ${appendedSchemaText}`;
|
|
|
10943
11163
|
});
|
|
10944
11164
|
if (outputResult.block) {
|
|
10945
11165
|
if (feedbackMessage) {
|
|
10946
|
-
this.
|
|
10947
|
-
type: "pipeline",
|
|
10948
|
-
agent: agent2._name,
|
|
10949
|
-
status: "failed",
|
|
11166
|
+
const decision = await this.resolveRetryFeedback(options?.retryFeedback, {
|
|
10950
11167
|
stage: "guardrail",
|
|
10951
|
-
attempt
|
|
10952
|
-
maxAttempts
|
|
10953
|
-
|
|
11168
|
+
attempt,
|
|
11169
|
+
maxAttempts,
|
|
11170
|
+
output: content,
|
|
11171
|
+
reason: blockReason,
|
|
11172
|
+
defaultMessage: feedbackMessage
|
|
10954
11173
|
});
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
11174
|
+
if (decision.retry) {
|
|
11175
|
+
this.emitEvent({
|
|
11176
|
+
type: "pipeline",
|
|
11177
|
+
agent: agent2._name,
|
|
11178
|
+
status: "failed",
|
|
11179
|
+
stage: "guardrail",
|
|
11180
|
+
attempt,
|
|
11181
|
+
maxAttempts,
|
|
11182
|
+
reason: decision.message
|
|
11183
|
+
});
|
|
11184
|
+
guardrailOutputRetries++;
|
|
11185
|
+
appendRetryMessages(
|
|
11186
|
+
currentMessages,
|
|
11187
|
+
content,
|
|
11188
|
+
decision.message,
|
|
11189
|
+
response.providerMetadata
|
|
11190
|
+
);
|
|
11191
|
+
pendingRetryReason = "guardrail";
|
|
11192
|
+
continue;
|
|
11193
|
+
}
|
|
11194
|
+
this.currentSignal?.throwIfAborted();
|
|
11195
|
+
throw new GuardrailError("output", blockReason);
|
|
10964
11196
|
}
|
|
10965
11197
|
if (typeof onBlock === "function") {
|
|
10966
|
-
return onBlock(
|
|
11198
|
+
return onBlock(blockReason, {
|
|
10967
11199
|
metadata: this.metadata
|
|
10968
11200
|
});
|
|
10969
11201
|
}
|
|
10970
|
-
throw new GuardrailError("output",
|
|
11202
|
+
throw new GuardrailError("output", blockReason);
|
|
10971
11203
|
}
|
|
10972
11204
|
}
|
|
10973
11205
|
let validated = void 0;
|
|
@@ -10987,7 +11219,7 @@ ${appendedSchemaText}`;
|
|
|
10987
11219
|
schemaErr = err;
|
|
10988
11220
|
schemaReason = err instanceof Error ? err.message : String(err);
|
|
10989
11221
|
if (schemaRetries < maxSchemaRetries) {
|
|
10990
|
-
schemaFeedback = `Your response was not valid JSON or did not match the required schema: ${schemaReason}.
|
|
11222
|
+
schemaFeedback = `Your response was not valid JSON or did not match the required schema: ${schemaReason}. Return a corrected response that matches the required schema.`;
|
|
10991
11223
|
}
|
|
10992
11224
|
}
|
|
10993
11225
|
this.emitEvent({
|
|
@@ -11009,24 +11241,36 @@ ${appendedSchemaText}`;
|
|
|
11009
11241
|
});
|
|
11010
11242
|
if (!schemaValid) {
|
|
11011
11243
|
if (schemaFeedback) {
|
|
11012
|
-
this.
|
|
11013
|
-
type: "pipeline",
|
|
11014
|
-
agent: agent2._name,
|
|
11015
|
-
status: "failed",
|
|
11244
|
+
const decision = await this.resolveRetryFeedback(options.retryFeedback, {
|
|
11016
11245
|
stage: "schema",
|
|
11017
|
-
attempt:
|
|
11018
|
-
maxAttempts:
|
|
11019
|
-
|
|
11246
|
+
attempt: schemaAttempt,
|
|
11247
|
+
maxAttempts: schemaMaxAttempts,
|
|
11248
|
+
output: content,
|
|
11249
|
+
reason: schemaReason ?? "Schema parse failed",
|
|
11250
|
+
...schemaErr !== void 0 ? { error: schemaErr } : {},
|
|
11251
|
+
defaultMessage: schemaFeedback
|
|
11020
11252
|
});
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11253
|
+
if (decision.retry) {
|
|
11254
|
+
this.emitEvent({
|
|
11255
|
+
type: "pipeline",
|
|
11256
|
+
agent: agent2._name,
|
|
11257
|
+
status: "failed",
|
|
11258
|
+
stage: "schema",
|
|
11259
|
+
attempt: schemaAttempt,
|
|
11260
|
+
maxAttempts: schemaMaxAttempts,
|
|
11261
|
+
reason: decision.message
|
|
11262
|
+
});
|
|
11263
|
+
schemaRetries++;
|
|
11264
|
+
appendRetryMessages(
|
|
11265
|
+
currentMessages,
|
|
11266
|
+
content,
|
|
11267
|
+
decision.message,
|
|
11268
|
+
response.providerMetadata
|
|
11269
|
+
);
|
|
11270
|
+
pendingRetryReason = "schema";
|
|
11271
|
+
continue;
|
|
11272
|
+
}
|
|
11273
|
+
this.currentSignal?.throwIfAborted();
|
|
11030
11274
|
}
|
|
11031
11275
|
const zodErr = schemaErr instanceof import_zod2.ZodError ? schemaErr : new import_zod2.ZodError([
|
|
11032
11276
|
{
|
|
@@ -11040,12 +11284,14 @@ ${appendedSchemaText}`;
|
|
|
11040
11284
|
}
|
|
11041
11285
|
if (options?.schema && options.validate) {
|
|
11042
11286
|
let validateResult;
|
|
11287
|
+
let validateErr;
|
|
11043
11288
|
try {
|
|
11044
11289
|
validateResult = await options.validate(validated, {
|
|
11045
11290
|
metadata: this.metadata
|
|
11046
11291
|
});
|
|
11047
11292
|
} catch (err) {
|
|
11048
11293
|
rethrowEventStreamOverflow(err);
|
|
11294
|
+
validateErr = err;
|
|
11049
11295
|
const reason = err instanceof Error ? err.message : String(err);
|
|
11050
11296
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
11051
11297
|
}
|
|
@@ -11054,7 +11300,7 @@ ${appendedSchemaText}`;
|
|
|
11054
11300
|
const validateMaxAttempts = maxValidateRetries + 1;
|
|
11055
11301
|
let validateFeedback;
|
|
11056
11302
|
if (!validateResult.valid && validateRetries < maxValidateRetries) {
|
|
11057
|
-
validateFeedback = `Your response
|
|
11303
|
+
validateFeedback = `Your previous response failed validation: ${validateResult.reason ?? "Validation failed"}. Return a corrected response that satisfies the required schema and this rule.`;
|
|
11058
11304
|
}
|
|
11059
11305
|
this.emitEvent({
|
|
11060
11306
|
type: "validate",
|
|
@@ -11075,24 +11321,37 @@ ${appendedSchemaText}`;
|
|
|
11075
11321
|
});
|
|
11076
11322
|
if (!validateResult.valid) {
|
|
11077
11323
|
if (validateFeedback) {
|
|
11078
|
-
this.
|
|
11079
|
-
type: "pipeline",
|
|
11080
|
-
agent: agent2._name,
|
|
11081
|
-
status: "failed",
|
|
11324
|
+
const decision = await this.resolveRetryFeedback(options.retryFeedback, {
|
|
11082
11325
|
stage: "validate",
|
|
11083
|
-
attempt:
|
|
11084
|
-
maxAttempts:
|
|
11085
|
-
|
|
11326
|
+
attempt: validateAttempt,
|
|
11327
|
+
maxAttempts: validateMaxAttempts,
|
|
11328
|
+
output: content,
|
|
11329
|
+
parsed: validated,
|
|
11330
|
+
reason: validateResult.reason ?? "Validation failed",
|
|
11331
|
+
...validateErr !== void 0 ? { error: validateErr } : {},
|
|
11332
|
+
defaultMessage: validateFeedback
|
|
11086
11333
|
});
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11334
|
+
if (decision.retry) {
|
|
11335
|
+
this.emitEvent({
|
|
11336
|
+
type: "pipeline",
|
|
11337
|
+
agent: agent2._name,
|
|
11338
|
+
status: "failed",
|
|
11339
|
+
stage: "validate",
|
|
11340
|
+
attempt: validateAttempt,
|
|
11341
|
+
maxAttempts: validateMaxAttempts,
|
|
11342
|
+
reason: decision.message
|
|
11343
|
+
});
|
|
11344
|
+
validateRetries++;
|
|
11345
|
+
appendRetryMessages(
|
|
11346
|
+
currentMessages,
|
|
11347
|
+
content,
|
|
11348
|
+
decision.message,
|
|
11349
|
+
response.providerMetadata
|
|
11350
|
+
);
|
|
11351
|
+
pendingRetryReason = "validate";
|
|
11352
|
+
continue;
|
|
11353
|
+
}
|
|
11354
|
+
this.currentSignal?.throwIfAborted();
|
|
11096
11355
|
}
|
|
11097
11356
|
throw new ValidationError(
|
|
11098
11357
|
validated,
|
|
@@ -11114,6 +11373,31 @@ ${appendedSchemaText}`;
|
|
|
11114
11373
|
}
|
|
11115
11374
|
throw new MaxTurnsError("ctx.ask()", maxTurns);
|
|
11116
11375
|
}
|
|
11376
|
+
/**
|
|
11377
|
+
* Resolve the corrective feedback text for one rejected attempt, giving an optional
|
|
11378
|
+
* caller-supplied `retryFeedback` hook the chance to replace it or to stop retrying.
|
|
11379
|
+
*
|
|
11380
|
+
* Called by all three output gates, only while a retry is still permitted (never on the
|
|
11381
|
+
* exhausting attempt) and only after the gate's own event has been emitted, so a hook that
|
|
11382
|
+
* throws or aborts can never erase the evidence that the gate rejected. A non-empty string
|
|
11383
|
+
* replaces `defaultMessage` verbatim; `undefined` / `''` keeps it; `{ retry: false }` asks
|
|
11384
|
+
* the gate to throw its typed terminal error. Hook exceptions propagate — a bug in caller
|
|
11385
|
+
* code is not a model failure and must not be retried into silence, and neither is a
|
|
11386
|
+
* return value outside the contract: an unrecognized shape throws instead of silently
|
|
11387
|
+
* degrading to the default text, which would hide the defect behind extra token spend.
|
|
11388
|
+
*/
|
|
11389
|
+
async resolveRetryFeedback(hook, info) {
|
|
11390
|
+
if (!hook) return { retry: true, message: info.defaultMessage };
|
|
11391
|
+
const result = await hook(info, { metadata: this.metadata });
|
|
11392
|
+
if (result === void 0 || result === "") {
|
|
11393
|
+
return { retry: true, message: info.defaultMessage };
|
|
11394
|
+
}
|
|
11395
|
+
if (typeof result === "string") return { retry: true, message: result };
|
|
11396
|
+
if (isRetryAbort(result)) return { retry: false };
|
|
11397
|
+
throw new TypeError(
|
|
11398
|
+
`retryFeedback must return a string, undefined, or { retry: false }; received ${describeValue(result)}.`
|
|
11399
|
+
);
|
|
11400
|
+
}
|
|
11117
11401
|
/**
|
|
11118
11402
|
* Push the final assistant message into session history, preserving providerMetadata
|
|
11119
11403
|
* (e.g., Gemini thought signatures needed for multi-turn reasoning context) and
|
|
@@ -11269,6 +11553,57 @@ ${appendedSchemaText}`;
|
|
|
11269
11553
|
}
|
|
11270
11554
|
}
|
|
11271
11555
|
}
|
|
11556
|
+
/**
|
|
11557
|
+
* Emit a `provider_diagnostic` when the resolved provider reports that it
|
|
11558
|
+
* could not honor the unified `effort` verbatim (P3/R6/R7).
|
|
11559
|
+
*
|
|
11560
|
+
* The adapter only *resolves* — it returns the requested and effective levels
|
|
11561
|
+
* from the same pure resolver its request builder uses. The runtime decides
|
|
11562
|
+
* how to surface that, because only the runtime can see the event bus and
|
|
11563
|
+
* `AxlConfig.diagnostics.silent` (adapters receive `ProviderConfig` only).
|
|
11564
|
+
*
|
|
11565
|
+
* Called once per ask, so a tool loop with N provider round trips still emits
|
|
11566
|
+
* one event; the `console.warn` dedupes once per process per distinct clamp.
|
|
11567
|
+
* A throwing `effortResolution` is a provider bug and propagates.
|
|
11568
|
+
*/
|
|
11569
|
+
emitProviderDiagnostics(agent2, options, ctx) {
|
|
11570
|
+
if (!ctx.provider.effortResolution) return;
|
|
11571
|
+
const resolution = ctx.provider.effortResolution({
|
|
11572
|
+
model: ctx.model,
|
|
11573
|
+
effort: options?.effort ?? agent2._config.effort,
|
|
11574
|
+
thinkingBudget: options?.thinkingBudget ?? agent2._config.thinkingBudget,
|
|
11575
|
+
includeThoughts: options?.includeThoughts ?? agent2._config.includeThoughts,
|
|
11576
|
+
providerOptions: ctx.providerOptions
|
|
11577
|
+
});
|
|
11578
|
+
if (!resolution?.clamped) return;
|
|
11579
|
+
if (typeof resolution.effective !== "string" || resolution.effective.length === 0 || typeof resolution.requested !== "string" || resolution.requested.length === 0) {
|
|
11580
|
+
throw new TypeError(
|
|
11581
|
+
`Provider '${ctx.provider.name ?? "unknown"}' returned a malformed EffortResolution for model '${ctx.model}': 'requested' and 'effective' must be non-empty strings when 'clamped' is true. Received ${JSON.stringify(resolution)}.`
|
|
11582
|
+
);
|
|
11583
|
+
}
|
|
11584
|
+
const cause = resolution.cause ?? `effort '${resolution.requested}' is not supported as asked`;
|
|
11585
|
+
this.emitEvent({
|
|
11586
|
+
type: "provider_diagnostic",
|
|
11587
|
+
agent: agent2._name,
|
|
11588
|
+
data: {
|
|
11589
|
+
kind: "effort_clamped",
|
|
11590
|
+
...ctx.provider.name ? { provider: ctx.provider.name } : {},
|
|
11591
|
+
model: ctx.model,
|
|
11592
|
+
requested: resolution.requested,
|
|
11593
|
+
effective: resolution.effective,
|
|
11594
|
+
cause
|
|
11595
|
+
}
|
|
11596
|
+
});
|
|
11597
|
+
warnDiagnosticOnce(
|
|
11598
|
+
// One warning per distinct clamp, not per ask: the same clamp across 500
|
|
11599
|
+
// asks is one process-level surprise, while a different requested/effective
|
|
11600
|
+
// pair is a genuinely new one.
|
|
11601
|
+
`effort_clamped\0${ctx.provider.name ?? ""}\0${ctx.model}\0${resolution.requested}\0${resolution.effective}`,
|
|
11602
|
+
`effort '${resolution.requested}' was clamped to '${resolution.effective}' on ${ctx.provider.name ? `${ctx.provider.name} ` : ""}${ctx.model} \u2014 ${cause}. A 'provider_diagnostic' event carries the same detail per ask.`,
|
|
11603
|
+
this.config.diagnostics?.silent,
|
|
11604
|
+
"docs/observability.md#provider-diagnostics"
|
|
11605
|
+
);
|
|
11606
|
+
}
|
|
11272
11607
|
buildToolDefs(agent2, resolvedHandoffs) {
|
|
11273
11608
|
const defs = [];
|
|
11274
11609
|
if (agent2._config.tools) {
|
|
@@ -12549,7 +12884,8 @@ ${summaryResponse.content}`
|
|
|
12549
12884
|
retries: options?.retries,
|
|
12550
12885
|
metadata: options?.metadata,
|
|
12551
12886
|
validate: options?.validate,
|
|
12552
|
-
validateRetries: options?.validateRetries
|
|
12887
|
+
validateRetries: options?.validateRetries,
|
|
12888
|
+
retryFeedback: options?.retryFeedback
|
|
12553
12889
|
});
|
|
12554
12890
|
}
|
|
12555
12891
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
@@ -12597,7 +12933,8 @@ ${summaryResponse.content}`
|
|
|
12597
12933
|
retries: options?.retries,
|
|
12598
12934
|
metadata: options?.metadata,
|
|
12599
12935
|
validate: options?.validate,
|
|
12600
|
-
validateRetries: options?.validateRetries
|
|
12936
|
+
validateRetries: options?.validateRetries,
|
|
12937
|
+
retryFeedback: options?.retryFeedback
|
|
12601
12938
|
});
|
|
12602
12939
|
}
|
|
12603
12940
|
/** Transcribe recorded audio through the dedicated transcription registry.
|