@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.js
CHANGED
|
@@ -1205,7 +1205,8 @@ var OpenAICompatibleProvider = class {
|
|
|
1205
1205
|
return this.name === "openrouter" && model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64"] } } : {};
|
|
1206
1206
|
}
|
|
1207
1207
|
validateInput(request) {
|
|
1208
|
-
const
|
|
1208
|
+
const modelOverride = request.providerOptions?.model;
|
|
1209
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
1209
1210
|
const fail = (source, feature) => {
|
|
1210
1211
|
throw new UnsupportedModelInputError({
|
|
1211
1212
|
provider: this.name,
|
|
@@ -1215,6 +1216,9 @@ var OpenAICompatibleProvider = class {
|
|
|
1215
1216
|
...feature ? { feature } : {}
|
|
1216
1217
|
});
|
|
1217
1218
|
};
|
|
1219
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
1220
|
+
fail(void 0, "invalid model providerOptions");
|
|
1221
|
+
}
|
|
1218
1222
|
if (this.name !== "openrouter" || effectiveModel.trim().length === 0) {
|
|
1219
1223
|
fail(void 0, "image input for this model");
|
|
1220
1224
|
}
|
|
@@ -2114,17 +2118,22 @@ function resolveOpenAIReasoningEffort(model, resolved) {
|
|
|
2114
2118
|
}
|
|
2115
2119
|
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
2116
2120
|
}
|
|
2117
|
-
var warnedChatMaxDowngrades = /* @__PURE__ */ new Set();
|
|
2118
2121
|
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
2119
2122
|
const effort = resolveOpenAIReasoningEffort(model, resolved);
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
return
|
|
2123
|
+
return effort === "max" ? "xhigh" : effort;
|
|
2124
|
+
}
|
|
2125
|
+
function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
|
|
2126
|
+
const resolved = resolveThinkingOptions(options);
|
|
2127
|
+
if (resolved.effort === void 0 || resolved.hasBudgetOverride) return void 0;
|
|
2128
|
+
const model = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
2129
|
+
const effective = resolveWireEffort(model, resolved);
|
|
2130
|
+
if (effective === void 0 || effective === resolved.effort) return void 0;
|
|
2131
|
+
return {
|
|
2132
|
+
requested: resolved.effort,
|
|
2133
|
+
effective,
|
|
2134
|
+
clamped: true,
|
|
2135
|
+
cause: `${endpoint} does not accept reasoning effort '${resolved.effort}' for ${model}; using '${effective}'`
|
|
2136
|
+
};
|
|
2128
2137
|
}
|
|
2129
2138
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
2130
2139
|
const oSeries = isOSeriesModel(model);
|
|
@@ -2149,19 +2158,21 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
2149
2158
|
constructor(options = {}) {
|
|
2150
2159
|
super({ profile: OPENAI_PROFILE, ...options });
|
|
2151
2160
|
}
|
|
2161
|
+
/** Report a clamped `effort` — Chat Completions caps `'max'` at `'xhigh'` and
|
|
2162
|
+
* rejects `'none'` before gpt-5.1 (clamped to `'minimal'`). */
|
|
2163
|
+
effortResolution(options) {
|
|
2164
|
+
return resolveOpenAIEffortResolution(
|
|
2165
|
+
options,
|
|
2166
|
+
resolveOpenAIChatReasoningEffort,
|
|
2167
|
+
"OpenAI Chat Completions"
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2152
2170
|
computeCost(model, usage, _reportedCost, context) {
|
|
2153
2171
|
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
2154
2172
|
}
|
|
2155
2173
|
};
|
|
2156
2174
|
|
|
2157
2175
|
// src/providers/openai-responses.ts
|
|
2158
|
-
var OPENAI_RESPONSES_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
2159
|
-
"gpt-4o",
|
|
2160
|
-
"gpt-4o-2024-08-06",
|
|
2161
|
-
"gpt-4o-2024-11-20",
|
|
2162
|
-
"gpt-4o-mini",
|
|
2163
|
-
"gpt-4o-mini-2024-07-18"
|
|
2164
|
-
]);
|
|
2165
2176
|
function base64FromSource(source) {
|
|
2166
2177
|
return source.type === "base64" ? source.data : Buffer.from(source.data.buffer, source.data.byteOffset, source.data.byteLength).toString(
|
|
2167
2178
|
"base64"
|
|
@@ -2200,10 +2211,17 @@ function responseImageParts(parts) {
|
|
|
2200
2211
|
var OpenAIResponsesProvider = class {
|
|
2201
2212
|
name = "openai-responses";
|
|
2202
2213
|
inputCapabilities(model) {
|
|
2203
|
-
return
|
|
2214
|
+
return model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
2215
|
+
}
|
|
2216
|
+
/** Report a clamped `effort`. Responses accepts the native `'max'` tier that
|
|
2217
|
+
* Chat Completions caps, but still clamps `'none'` before gpt-5.1, `'xhigh'`
|
|
2218
|
+
* below gpt-5.2, and everything to `'high'` on gpt-5-pro. */
|
|
2219
|
+
effortResolution(options) {
|
|
2220
|
+
return resolveOpenAIEffortResolution(options, resolveOpenAIReasoningEffort, "OpenAI Responses");
|
|
2204
2221
|
}
|
|
2205
2222
|
validateInput(request) {
|
|
2206
|
-
const
|
|
2223
|
+
const modelOverride = request.providerOptions?.model;
|
|
2224
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
2207
2225
|
const fail = (source, feature) => {
|
|
2208
2226
|
throw new UnsupportedModelInputError({
|
|
2209
2227
|
provider: this.name,
|
|
@@ -2213,7 +2231,10 @@ var OpenAIResponsesProvider = class {
|
|
|
2213
2231
|
...feature ? { feature } : {}
|
|
2214
2232
|
});
|
|
2215
2233
|
};
|
|
2216
|
-
if (
|
|
2234
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
2235
|
+
fail(void 0, "invalid model providerOptions");
|
|
2236
|
+
}
|
|
2237
|
+
if (effectiveModel.trim().length === 0) {
|
|
2217
2238
|
fail(void 0, "image input for this model");
|
|
2218
2239
|
}
|
|
2219
2240
|
if (request.providerOptions && "input" in request.providerOptions) {
|
|
@@ -2341,7 +2362,7 @@ var OpenAIResponsesProvider = class {
|
|
|
2341
2362
|
const resolved = resolveThinkingOptions(options);
|
|
2342
2363
|
const { includeThoughts } = resolved;
|
|
2343
2364
|
const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
|
|
2344
|
-
const stripTemp = oSeries || reasoningCapable && wireEffort !== void 0;
|
|
2365
|
+
const stripTemp = oSeries || supportsMaxReasoningEffort(effectiveModel) || reasoningCapable && wireEffort !== void 0;
|
|
2345
2366
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
2346
2367
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
2347
2368
|
const body = {
|
|
@@ -2689,7 +2710,6 @@ function anthropicImageBlocks(parts) {
|
|
|
2689
2710
|
}
|
|
2690
2711
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
2691
2712
|
var ANTHROPIC_FILES_BETA = "files-api-2025-04-14";
|
|
2692
|
-
var ANTHROPIC_IMAGE_MODELS = /* @__PURE__ */ new Set(["claude-sonnet-4-5", "claude-opus-4-8"]);
|
|
2693
2713
|
function hasAnthropicProviderFileImage(messages) {
|
|
2694
2714
|
return messages.some(
|
|
2695
2715
|
(message) => Array.isArray(message.content) && message.content.some(
|
|
@@ -2808,6 +2828,28 @@ var ANTHROPIC_RATES = {
|
|
|
2808
2828
|
function isValidTokenCount(value) {
|
|
2809
2829
|
return value !== void 0 && Number.isSafeInteger(value) && value >= 0;
|
|
2810
2830
|
}
|
|
2831
|
+
function anthropicStreamErrorStatus(type) {
|
|
2832
|
+
switch (type) {
|
|
2833
|
+
case "invalid_request_error":
|
|
2834
|
+
return 400;
|
|
2835
|
+
case "authentication_error":
|
|
2836
|
+
return 401;
|
|
2837
|
+
case "permission_error":
|
|
2838
|
+
return 403;
|
|
2839
|
+
case "not_found_error":
|
|
2840
|
+
return 404;
|
|
2841
|
+
case "request_too_large":
|
|
2842
|
+
return 413;
|
|
2843
|
+
case "rate_limit_error":
|
|
2844
|
+
return 429;
|
|
2845
|
+
case "api_error":
|
|
2846
|
+
return 500;
|
|
2847
|
+
case "overloaded_error":
|
|
2848
|
+
return 529;
|
|
2849
|
+
default:
|
|
2850
|
+
return 0;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2811
2853
|
function estimateAnthropicCost(model, usage, now = /* @__PURE__ */ new Date()) {
|
|
2812
2854
|
if (!model || !isValidTokenCount(usage.inputTokens) || !isValidTokenCount(usage.outputTokens)) {
|
|
2813
2855
|
return void 0;
|
|
@@ -2852,14 +2894,6 @@ function budgetToClaudeEffort(budget) {
|
|
|
2852
2894
|
if (budget <= THINKING_BUDGETS.xhigh) return "xhigh";
|
|
2853
2895
|
return "max";
|
|
2854
2896
|
}
|
|
2855
|
-
var warnedFableDisable = /* @__PURE__ */ new Set();
|
|
2856
|
-
function warnFableDisable(model) {
|
|
2857
|
-
if (warnedFableDisable.has(model)) return;
|
|
2858
|
-
warnedFableDisable.add(model);
|
|
2859
|
-
console.warn(
|
|
2860
|
-
`[axl] thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'.`
|
|
2861
|
-
);
|
|
2862
|
-
}
|
|
2863
2897
|
function resolveClaudeThinking(model, resolved) {
|
|
2864
2898
|
const capability = resolveClaudeCapability(model);
|
|
2865
2899
|
if (!capability) return { stripTemperature: false };
|
|
@@ -2879,11 +2913,14 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
2879
2913
|
}
|
|
2880
2914
|
if (resolved.thinkingDisabled) {
|
|
2881
2915
|
if (capability.thinking === "adaptive-always-on") {
|
|
2882
|
-
warnFableDisable(model);
|
|
2883
2916
|
return {
|
|
2884
2917
|
thinking: { type: "adaptive" },
|
|
2885
2918
|
outputConfig: { effort: "low" },
|
|
2886
|
-
stripTemperature: true
|
|
2919
|
+
stripTemperature: true,
|
|
2920
|
+
clamp: {
|
|
2921
|
+
effective: "low",
|
|
2922
|
+
cause: `thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'`
|
|
2923
|
+
}
|
|
2887
2924
|
};
|
|
2888
2925
|
}
|
|
2889
2926
|
if (activeEffort && capability.disableAt && !isEffortAtOrBelow(activeEffort, capability.disableAt)) {
|
|
@@ -2945,7 +2982,16 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
2945
2982
|
return {
|
|
2946
2983
|
thinking: { type: "enabled", budget_tokens: budget },
|
|
2947
2984
|
manualBudget: budget,
|
|
2948
|
-
stripTemperature: true
|
|
2985
|
+
stripTemperature: true,
|
|
2986
|
+
// Legacy models take `budget_tokens`, not an effort level, and have no
|
|
2987
|
+
// `effortLevels` — so `clampAnthropicEffort` folds 'max'/'xhigh' down to
|
|
2988
|
+
// the 'high' tier and the request silently carries that tier's budget.
|
|
2989
|
+
// Report it: an unreported budget-tier downgrade is exactly the silent
|
|
2990
|
+
// clamp this capability exists to expose.
|
|
2991
|
+
clamp: activeEffort === resolved.activeEffort ? void 0 : {
|
|
2992
|
+
effective: activeEffort,
|
|
2993
|
+
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}`
|
|
2994
|
+
}
|
|
2949
2995
|
};
|
|
2950
2996
|
}
|
|
2951
2997
|
return { stripTemperature: capability.stripTemperature ?? false };
|
|
@@ -3056,11 +3102,32 @@ function mergeAnthropicUsage(initial, update) {
|
|
|
3056
3102
|
}
|
|
3057
3103
|
var AnthropicProvider = class {
|
|
3058
3104
|
name = "anthropic";
|
|
3105
|
+
/** Report a clamped `effort`. Anthropic clamps to each model's supported
|
|
3106
|
+
* `output_config.effort` levels, and models that always think fall back to
|
|
3107
|
+
* adaptive `'low'` when asked to disable thinking. Only reported when the
|
|
3108
|
+
* effective level is what the request body actually carries; an explicit
|
|
3109
|
+
* `thinkingBudget` is documented to override `effort`, so it is not a clamp. */
|
|
3110
|
+
effortResolution(options) {
|
|
3111
|
+
const resolved = resolveThinkingOptions(options);
|
|
3112
|
+
if (resolved.effort === void 0 || resolved.hasBudgetOverride) return void 0;
|
|
3113
|
+
const model = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
3114
|
+
const config = resolveClaudeThinking(model, resolved);
|
|
3115
|
+
const wireEffort = typeof config.outputConfig?.effort === "string" ? config.outputConfig.effort : void 0;
|
|
3116
|
+
const effective = config.clamp?.effective ?? wireEffort;
|
|
3117
|
+
if (effective === void 0 || effective === resolved.effort) return void 0;
|
|
3118
|
+
return {
|
|
3119
|
+
requested: resolved.effort,
|
|
3120
|
+
effective,
|
|
3121
|
+
clamped: true,
|
|
3122
|
+
cause: config.clamp?.cause ?? `Anthropic ${model} does not support effort '${resolved.effort}'; using '${effective}'`
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3059
3125
|
inputCapabilities(model) {
|
|
3060
|
-
return
|
|
3126
|
+
return model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
3061
3127
|
}
|
|
3062
3128
|
validateInput(request) {
|
|
3063
|
-
const
|
|
3129
|
+
const modelOverride = request.providerOptions?.model;
|
|
3130
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
3064
3131
|
const fail = (source, feature) => {
|
|
3065
3132
|
throw new UnsupportedModelInputError({
|
|
3066
3133
|
provider: this.name,
|
|
@@ -3070,7 +3137,10 @@ var AnthropicProvider = class {
|
|
|
3070
3137
|
...feature ? { feature } : {}
|
|
3071
3138
|
});
|
|
3072
3139
|
};
|
|
3073
|
-
if (
|
|
3140
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
3141
|
+
fail(void 0, "invalid model providerOptions");
|
|
3142
|
+
}
|
|
3143
|
+
if (effectiveModel.trim().length === 0) {
|
|
3074
3144
|
fail(void 0, "image input for this model");
|
|
3075
3145
|
}
|
|
3076
3146
|
if (request.providerOptions && "messages" in request.providerOptions) {
|
|
@@ -3195,7 +3265,7 @@ var AnthropicProvider = class {
|
|
|
3195
3265
|
if (!res.body) {
|
|
3196
3266
|
throw new Error("Anthropic stream response has no body");
|
|
3197
3267
|
}
|
|
3198
|
-
yield* this.parseSSEStream(res.body, pricingContext);
|
|
3268
|
+
yield* this.parseSSEStream(res.body, pricingContext, res.headers);
|
|
3199
3269
|
}
|
|
3200
3270
|
// ---------------------------------------------------------------------------
|
|
3201
3271
|
// Internal: request building
|
|
@@ -3438,7 +3508,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
3438
3508
|
// ---------------------------------------------------------------------------
|
|
3439
3509
|
// Internal: SSE stream parsing
|
|
3440
3510
|
// ---------------------------------------------------------------------------
|
|
3441
|
-
async *parseSSEStream(body, pricingContext) {
|
|
3511
|
+
async *parseSSEStream(body, pricingContext, responseHeaders) {
|
|
3442
3512
|
const reader = body.getReader();
|
|
3443
3513
|
const decoder = new TextDecoder();
|
|
3444
3514
|
let buffer = "";
|
|
@@ -3484,6 +3554,15 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
3484
3554
|
continue;
|
|
3485
3555
|
}
|
|
3486
3556
|
switch (event.type) {
|
|
3557
|
+
case "error": {
|
|
3558
|
+
const status = anthropicStreamErrorStatus(event.error?.type);
|
|
3559
|
+
throw buildProviderError({
|
|
3560
|
+
provider: this.name,
|
|
3561
|
+
status,
|
|
3562
|
+
headers: responseHeaders,
|
|
3563
|
+
message: event.error?.message ?? "Anthropic stream failed"
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3487
3566
|
case "content_block_start": {
|
|
3488
3567
|
const block = event.content_block;
|
|
3489
3568
|
if (block?.type === "tool_use") {
|
|
@@ -3735,7 +3814,6 @@ var GEMINI_PRICING = {
|
|
|
3735
3814
|
"gemini-3.5-flash-lite": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3736
3815
|
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
3737
3816
|
};
|
|
3738
|
-
var GEMINI_INTERACTIONS_IMAGE_MODELS = /* @__PURE__ */ new Set(["gemini-3.7-flash"]);
|
|
3739
3817
|
function isValidTokenCount2(value) {
|
|
3740
3818
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3741
3819
|
}
|
|
@@ -3745,6 +3823,55 @@ function isStandardGeminiRequestTier(value) {
|
|
|
3745
3823
|
function isDefinitiveStandardGeminiResponseTier(value) {
|
|
3746
3824
|
return value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD" || value === "standard";
|
|
3747
3825
|
}
|
|
3826
|
+
function geminiInteractionErrorStatus(code) {
|
|
3827
|
+
if (typeof code === "number" && Number.isInteger(code) && code >= 100 && code <= 599) {
|
|
3828
|
+
return code;
|
|
3829
|
+
}
|
|
3830
|
+
switch (code) {
|
|
3831
|
+
case "invalid_request":
|
|
3832
|
+
case "invalid_argument":
|
|
3833
|
+
case "parameter_unknown":
|
|
3834
|
+
case "failed_precondition":
|
|
3835
|
+
return 400;
|
|
3836
|
+
case "authentication":
|
|
3837
|
+
case "unauthenticated":
|
|
3838
|
+
return 401;
|
|
3839
|
+
case "permission_denied":
|
|
3840
|
+
return 403;
|
|
3841
|
+
case "not_found":
|
|
3842
|
+
case "model_not_found":
|
|
3843
|
+
return 404;
|
|
3844
|
+
case "request_timeout":
|
|
3845
|
+
return 408;
|
|
3846
|
+
case "conflict":
|
|
3847
|
+
case "already_exists":
|
|
3848
|
+
return 409;
|
|
3849
|
+
case "request_too_large":
|
|
3850
|
+
return 413;
|
|
3851
|
+
case "out_of_range":
|
|
3852
|
+
return 416;
|
|
3853
|
+
case "quota_exceeded":
|
|
3854
|
+
case "resource_exhausted":
|
|
3855
|
+
case "rate_limit_exceeded":
|
|
3856
|
+
return 429;
|
|
3857
|
+
case "cancelled":
|
|
3858
|
+
return 499;
|
|
3859
|
+
case "api_error":
|
|
3860
|
+
case "internal":
|
|
3861
|
+
case "internal_server_error":
|
|
3862
|
+
return 500;
|
|
3863
|
+
case "bad_gateway":
|
|
3864
|
+
return 502;
|
|
3865
|
+
case "unavailable":
|
|
3866
|
+
case "service_unavailable":
|
|
3867
|
+
return 503;
|
|
3868
|
+
case "deadline_exceeded":
|
|
3869
|
+
case "gateway_timeout":
|
|
3870
|
+
return 504;
|
|
3871
|
+
default:
|
|
3872
|
+
return void 0;
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3748
3875
|
function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
|
|
3749
3876
|
return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
|
|
3750
3877
|
}
|
|
@@ -3826,7 +3953,9 @@ var GEMINI_3X_MIN_THINKING_LEVEL = /* @__PURE__ */ new Map([
|
|
|
3826
3953
|
["gemini-3.6-flash", "minimal"],
|
|
3827
3954
|
// L4 live evidence: Interactions gemini-3.7-flash accepts low/medium/high,
|
|
3828
3955
|
// but rejects minimal. Keep this exact entry isolated from legacy aliases.
|
|
3829
|
-
["gemini-3.7-flash", "low"]
|
|
3956
|
+
["gemini-3.7-flash", "low"],
|
|
3957
|
+
// Current-model live evidence: gemini-3.8-flash has the same low floor.
|
|
3958
|
+
["gemini-3.8-flash", "low"]
|
|
3830
3959
|
]);
|
|
3831
3960
|
var GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE = /* @__PURE__ */ new Set([
|
|
3832
3961
|
"gemini-3.5-flash-lite",
|
|
@@ -3843,21 +3972,80 @@ function budgetToThinkingLevel(budgetTokens) {
|
|
|
3843
3972
|
function minThinkingLevel(model) {
|
|
3844
3973
|
return GEMINI_3X_MIN_THINKING_LEVEL.get(model) ?? "minimal";
|
|
3845
3974
|
}
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
if (
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3975
|
+
function resolveGeminiThinking(model, resolved) {
|
|
3976
|
+
const is3x = isGemini3x(model);
|
|
3977
|
+
if (resolved.thinkingDisabled) {
|
|
3978
|
+
const minLevel = minThinkingLevel(model);
|
|
3979
|
+
if (is3x) {
|
|
3980
|
+
return {
|
|
3981
|
+
interactionLevel: minLevel,
|
|
3982
|
+
thinkingLevel: minLevel,
|
|
3983
|
+
thinkingBudget: void 0,
|
|
3984
|
+
attachThoughts: false,
|
|
3985
|
+
// A `thinkingBudget: 0` disable has no unified effort to report against;
|
|
3986
|
+
// only an explicit `effort: 'none'` is a clamp of a requested value.
|
|
3987
|
+
clamp: resolved.effort === "none" ? {
|
|
3988
|
+
effective: minLevel,
|
|
3989
|
+
cause: `Gemini 3.x models cannot disable thinking; using the model minimum thinking level '${minLevel}'`
|
|
3990
|
+
} : void 0
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
return {
|
|
3994
|
+
interactionLevel: "minimal",
|
|
3995
|
+
thinkingLevel: void 0,
|
|
3996
|
+
thinkingBudget: 0,
|
|
3997
|
+
attachThoughts: false
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
if (resolved.hasBudgetOverride) {
|
|
4001
|
+
const budget = resolved.thinkingBudget;
|
|
4002
|
+
const level = budgetToThinkingLevel(budget);
|
|
4003
|
+
return {
|
|
4004
|
+
interactionLevel: level,
|
|
4005
|
+
thinkingLevel: is3x ? level : void 0,
|
|
4006
|
+
thinkingBudget: is3x ? void 0 : budget,
|
|
4007
|
+
attachThoughts: resolved.includeThoughts
|
|
4008
|
+
};
|
|
4009
|
+
}
|
|
4010
|
+
if (resolved.activeEffort) {
|
|
4011
|
+
const effort = resolved.activeEffort;
|
|
4012
|
+
const budget = THINKING_BUDGETS2[effort] ?? 5e3;
|
|
4013
|
+
if (is3x) {
|
|
4014
|
+
const level = THINKING_LEVELS[effort] ?? "medium";
|
|
4015
|
+
return {
|
|
4016
|
+
interactionLevel: level,
|
|
4017
|
+
thinkingLevel: level,
|
|
4018
|
+
thinkingBudget: void 0,
|
|
4019
|
+
attachThoughts: resolved.includeThoughts,
|
|
4020
|
+
clamp: level === effort ? void 0 : {
|
|
4021
|
+
effective: level,
|
|
4022
|
+
cause: `Gemini 3.x thinking levels cap at 'high'; effort '${effort}' maps to '${level}'`
|
|
4023
|
+
}
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
return {
|
|
4027
|
+
interactionLevel: budgetToThinkingLevel(budget),
|
|
4028
|
+
thinkingLevel: void 0,
|
|
4029
|
+
// 2.5 Pro supports a higher max budget (32768) than other 2.5 models (24576).
|
|
4030
|
+
thinkingBudget: effort === "max" && model === "gemini-2.5-pro" ? 32768 : budget,
|
|
4031
|
+
attachThoughts: resolved.includeThoughts
|
|
4032
|
+
};
|
|
4033
|
+
}
|
|
4034
|
+
return {
|
|
4035
|
+
interactionLevel: void 0,
|
|
4036
|
+
thinkingLevel: void 0,
|
|
4037
|
+
thinkingBudget: void 0,
|
|
4038
|
+
attachThoughts: resolved.includeThoughts
|
|
4039
|
+
};
|
|
3853
4040
|
}
|
|
3854
4041
|
var GeminiProvider = class {
|
|
3855
4042
|
name = "google";
|
|
3856
4043
|
inputCapabilities(model) {
|
|
3857
|
-
return
|
|
4044
|
+
return model.trim().length > 0 ? { image: { sources: ["bytes", "base64", "provider-file"] } } : {};
|
|
3858
4045
|
}
|
|
3859
4046
|
validateInput(request) {
|
|
3860
|
-
const
|
|
4047
|
+
const modelOverride = request.providerOptions?.model;
|
|
4048
|
+
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
3861
4049
|
const fail = (source, feature) => {
|
|
3862
4050
|
throw new UnsupportedModelInputError({
|
|
3863
4051
|
provider: this.name,
|
|
@@ -3867,7 +4055,10 @@ var GeminiProvider = class {
|
|
|
3867
4055
|
...feature ? { feature } : {}
|
|
3868
4056
|
});
|
|
3869
4057
|
};
|
|
3870
|
-
if (
|
|
4058
|
+
if (request.providerOptions && "model" in request.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
4059
|
+
fail(void 0, "invalid model providerOptions");
|
|
4060
|
+
}
|
|
4061
|
+
if (effectiveModel.trim().length === 0) {
|
|
3871
4062
|
fail(void 0, "image input for this model");
|
|
3872
4063
|
}
|
|
3873
4064
|
if (request.providerOptions) {
|
|
@@ -3914,6 +4105,21 @@ var GeminiProvider = class {
|
|
|
3914
4105
|
nativeStructuredOutputSupport() {
|
|
3915
4106
|
return "lossy";
|
|
3916
4107
|
}
|
|
4108
|
+
/** Report a clamped `effort` (R7a). Gemini 3.x cannot disable thinking and
|
|
4109
|
+
* caps at `'high'`, so `'none'`, `'xhigh'` and `'max'` are not honored
|
|
4110
|
+
* verbatim there. Everything else — including 2.x disabled thinking — is
|
|
4111
|
+
* sent as asked, so there is nothing to report. */
|
|
4112
|
+
effortResolution(options) {
|
|
4113
|
+
const resolved = resolveThinkingOptions(options);
|
|
4114
|
+
if (resolved.effort === void 0) return void 0;
|
|
4115
|
+
const clamp = resolveGeminiThinking(this.requestModel(options), resolved).clamp;
|
|
4116
|
+
return clamp ? {
|
|
4117
|
+
requested: resolved.effort,
|
|
4118
|
+
effective: clamp.effective,
|
|
4119
|
+
clamped: true,
|
|
4120
|
+
cause: clamp.cause
|
|
4121
|
+
} : void 0;
|
|
4122
|
+
}
|
|
3917
4123
|
baseUrl;
|
|
3918
4124
|
apiKeySource;
|
|
3919
4125
|
callCounter = 0;
|
|
@@ -4059,7 +4265,7 @@ var GeminiProvider = class {
|
|
|
4059
4265
|
});
|
|
4060
4266
|
}
|
|
4061
4267
|
if (!res.body) throw new Error("Gemini Interactions stream has no body");
|
|
4062
|
-
yield* this.parseInteractionSSEStream(res.body);
|
|
4268
|
+
yield* this.parseInteractionSSEStream(res.body, res.headers);
|
|
4063
4269
|
}
|
|
4064
4270
|
buildInteractionRequestBody(messages, options, stream) {
|
|
4065
4271
|
const model = this.requestModel(options);
|
|
@@ -4088,12 +4294,9 @@ var GeminiProvider = class {
|
|
|
4088
4294
|
generationConfig.tool_choice = typeof options.toolChoice === "string" ? options.toolChoice === "required" ? "any" : options.toolChoice : { allowed_tools: { mode: "any", tools: [options.toolChoice.function.name] } };
|
|
4089
4295
|
}
|
|
4090
4296
|
const thinking = resolveThinkingOptions(options);
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
generationConfig.thinking_level = budgetToThinkingLevel(thinking.thinkingBudget);
|
|
4095
|
-
} else if (thinking.activeEffort) {
|
|
4096
|
-
generationConfig.thinking_level = isGemini3x(model) ? THINKING_LEVELS[thinking.activeEffort] ?? "medium" : budgetToThinkingLevel(THINKING_BUDGETS2[thinking.activeEffort] ?? 5e3);
|
|
4297
|
+
const thinkingPlan = resolveGeminiThinking(model, thinking);
|
|
4298
|
+
if (thinkingPlan.interactionLevel !== void 0) {
|
|
4299
|
+
generationConfig.thinking_level = thinkingPlan.interactionLevel;
|
|
4097
4300
|
}
|
|
4098
4301
|
if (thinking.includeThoughts) generationConfig.thinking_summaries = "auto";
|
|
4099
4302
|
if (Object.keys(generationConfig).length) body.generation_config = generationConfig;
|
|
@@ -4129,6 +4332,7 @@ var GeminiProvider = class {
|
|
|
4129
4332
|
return body;
|
|
4130
4333
|
}
|
|
4131
4334
|
assertSafeInteractionOptions(options) {
|
|
4335
|
+
const modelOverride = options.providerOptions?.model;
|
|
4132
4336
|
const model = this.requestModel(options);
|
|
4133
4337
|
const fail = (feature) => {
|
|
4134
4338
|
throw new UnsupportedModelInputError({
|
|
@@ -4138,7 +4342,10 @@ var GeminiProvider = class {
|
|
|
4138
4342
|
feature
|
|
4139
4343
|
});
|
|
4140
4344
|
};
|
|
4141
|
-
if (
|
|
4345
|
+
if (options.providerOptions && "model" in options.providerOptions && (typeof modelOverride !== "string" || modelOverride.trim().length === 0)) {
|
|
4346
|
+
fail("invalid model providerOptions");
|
|
4347
|
+
}
|
|
4348
|
+
if (model.trim().length === 0) fail("image input for this model");
|
|
4142
4349
|
const forbidden = [
|
|
4143
4350
|
"input",
|
|
4144
4351
|
"contents",
|
|
@@ -4214,7 +4421,7 @@ var GeminiProvider = class {
|
|
|
4214
4421
|
providerMetadata: json.steps?.length ? { geminiInteractionSteps: json.steps.filter(isGeminiInteractionStep) } : void 0
|
|
4215
4422
|
};
|
|
4216
4423
|
}
|
|
4217
|
-
async *parseInteractionSSEStream(body) {
|
|
4424
|
+
async *parseInteractionSSEStream(body, responseHeaders) {
|
|
4218
4425
|
const reader = body.getReader();
|
|
4219
4426
|
const decoder = new TextDecoder();
|
|
4220
4427
|
let buffer = "";
|
|
@@ -4240,7 +4447,22 @@ var GeminiProvider = class {
|
|
|
4240
4447
|
continue;
|
|
4241
4448
|
}
|
|
4242
4449
|
if (event.event_type === "error") {
|
|
4243
|
-
|
|
4450
|
+
const status = geminiInteractionErrorStatus(event.error?.code);
|
|
4451
|
+
const message = event.error?.message ?? "Gemini Interactions stream failed";
|
|
4452
|
+
if (status !== void 0) {
|
|
4453
|
+
throw buildProviderError({
|
|
4454
|
+
provider: this.name,
|
|
4455
|
+
status,
|
|
4456
|
+
headers: responseHeaders,
|
|
4457
|
+
message
|
|
4458
|
+
});
|
|
4459
|
+
}
|
|
4460
|
+
throw new ProviderError({
|
|
4461
|
+
provider: this.name,
|
|
4462
|
+
status: 0,
|
|
4463
|
+
retryable: false,
|
|
4464
|
+
message
|
|
4465
|
+
});
|
|
4244
4466
|
}
|
|
4245
4467
|
if (event.event_type === "step.start" && event.step) {
|
|
4246
4468
|
const index = event.index;
|
|
@@ -4510,50 +4732,17 @@ var GeminiProvider = class {
|
|
|
4510
4732
|
if (Object.keys(generationConfig).length > 0) {
|
|
4511
4733
|
body.generationConfig = generationConfig;
|
|
4512
4734
|
}
|
|
4513
|
-
const
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
}
|
|
4521
|
-
if (
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
warnGemini3xEffortNone(requestModel);
|
|
4525
|
-
}
|
|
4526
|
-
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(requestModel) };
|
|
4527
|
-
} else {
|
|
4528
|
-
generationConfig.thinkingConfig = { thinkingBudget: 0 };
|
|
4529
|
-
}
|
|
4530
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4531
|
-
} else if (hasBudgetOverride) {
|
|
4532
|
-
const config = {};
|
|
4533
|
-
if (isGemini3x(requestModel)) {
|
|
4534
|
-
config.thinkingLevel = budgetToThinkingLevel(thinkingBudget);
|
|
4535
|
-
} else {
|
|
4536
|
-
config.thinkingBudget = thinkingBudget;
|
|
4537
|
-
}
|
|
4538
|
-
if (includeThoughts) config.includeThoughts = true;
|
|
4539
|
-
generationConfig.thinkingConfig = config;
|
|
4540
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4541
|
-
} else if (activeEffort) {
|
|
4542
|
-
const config = {};
|
|
4543
|
-
if (isGemini3x(requestModel)) {
|
|
4544
|
-
config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
|
|
4545
|
-
} else {
|
|
4546
|
-
if (activeEffort === "max" && requestModel === "gemini-2.5-pro") {
|
|
4547
|
-
config.thinkingBudget = 32768;
|
|
4548
|
-
} else {
|
|
4549
|
-
config.thinkingBudget = THINKING_BUDGETS2[activeEffort] ?? 5e3;
|
|
4550
|
-
}
|
|
4551
|
-
}
|
|
4552
|
-
if (includeThoughts) config.includeThoughts = true;
|
|
4553
|
-
generationConfig.thinkingConfig = config;
|
|
4554
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4555
|
-
} else if (includeThoughts) {
|
|
4556
|
-
generationConfig.thinkingConfig = { includeThoughts: true };
|
|
4735
|
+
const thinkingPlan = resolveGeminiThinking(requestModel, resolveThinkingOptions(options));
|
|
4736
|
+
const thinkingConfig = {};
|
|
4737
|
+
if (thinkingPlan.thinkingLevel !== void 0) {
|
|
4738
|
+
thinkingConfig.thinkingLevel = thinkingPlan.thinkingLevel;
|
|
4739
|
+
}
|
|
4740
|
+
if (thinkingPlan.thinkingBudget !== void 0) {
|
|
4741
|
+
thinkingConfig.thinkingBudget = thinkingPlan.thinkingBudget;
|
|
4742
|
+
}
|
|
4743
|
+
if (thinkingPlan.attachThoughts) thinkingConfig.includeThoughts = true;
|
|
4744
|
+
if (Object.keys(thinkingConfig).length > 0) {
|
|
4745
|
+
generationConfig.thinkingConfig = thinkingConfig;
|
|
4557
4746
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4558
4747
|
}
|
|
4559
4748
|
if (options.toolChoice !== void 0) {
|
|
@@ -7188,6 +7377,9 @@ var AXL_EVENT_TYPES = [
|
|
|
7188
7377
|
// structured-output cliffs (oversized appended schema, dropped refinements,
|
|
7189
7378
|
// streaming disabled, zero-guidance `schemaPrompt:'none'`).
|
|
7190
7379
|
"schema_diagnostic",
|
|
7380
|
+
// Provider capability diagnostics — the resolved provider could not honor a
|
|
7381
|
+
// portable request knob verbatim (first kind: a clamped `effort`).
|
|
7382
|
+
"provider_diagnostic",
|
|
7191
7383
|
// Observability
|
|
7192
7384
|
"log",
|
|
7193
7385
|
"memory_remember",
|
|
@@ -7237,6 +7429,7 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
7237
7429
|
"pipeline",
|
|
7238
7430
|
"verify",
|
|
7239
7431
|
"schema_diagnostic",
|
|
7432
|
+
"provider_diagnostic",
|
|
7240
7433
|
"workflow_start",
|
|
7241
7434
|
"workflow_end",
|
|
7242
7435
|
"transcription_start",
|
|
@@ -8165,6 +8358,10 @@ var LEGACY_REDACTION_RULES = {
|
|
|
8165
8358
|
// which is user content or PII (field paths are schema-shape, preserved like
|
|
8166
8359
|
// `string_delta.path`). Passthrough, as an explicit reviewed decision.
|
|
8167
8360
|
schema_diagnostic: passthrough,
|
|
8361
|
+
// `provider_diagnostic` carries only provider capability metadata — a provider
|
|
8362
|
+
// name, a model id, and two effort level strings. No application data or user
|
|
8363
|
+
// content ever reaches it. Passthrough, as an explicit reviewed decision.
|
|
8364
|
+
provider_diagnostic: passthrough,
|
|
8168
8365
|
log: (e) => {
|
|
8169
8366
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
8170
8367
|
return { ...e, data: walkObjectOneLevel(e.data, LOG_PRESERVE_KEYS) };
|
|
@@ -8295,6 +8492,7 @@ var COMMON_V2_REDACTION_RULES = {
|
|
|
8295
8492
|
string_delta: reuseLegacyRule("string_delta"),
|
|
8296
8493
|
verify: reuseLegacyRule("verify"),
|
|
8297
8494
|
schema_diagnostic: reuseLegacyRule("schema_diagnostic"),
|
|
8495
|
+
provider_diagnostic: reuseLegacyRule("provider_diagnostic"),
|
|
8298
8496
|
log: reuseLegacyRule("log"),
|
|
8299
8497
|
memory_remember: reuseLegacyRule("memory_remember"),
|
|
8300
8498
|
memory_recall: reuseLegacyRule("memory_recall"),
|
|
@@ -8390,13 +8588,14 @@ function detectDroppedRefinements(schema) {
|
|
|
8390
8588
|
var DOCS_URL = "docs/observability.md#schema-diagnostics";
|
|
8391
8589
|
var MAX_WARNED_KEYS = 2048;
|
|
8392
8590
|
var warnedDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
8393
|
-
function
|
|
8591
|
+
function warnDiagnosticOnce(key, message, silent, docs = DOCS_URL) {
|
|
8394
8592
|
if (silent || process.env.AXL_DIAGNOSTICS_SILENT === "true") return;
|
|
8395
8593
|
if (warnedDiagnosticKeys.has(key)) return;
|
|
8396
8594
|
if (warnedDiagnosticKeys.size >= MAX_WARNED_KEYS) warnedDiagnosticKeys.clear();
|
|
8397
8595
|
warnedDiagnosticKeys.add(key);
|
|
8398
|
-
console.warn(`[axl] ${message} See ${
|
|
8596
|
+
console.warn(`[axl] ${message} See ${docs}.`);
|
|
8399
8597
|
}
|
|
8598
|
+
var warnSchemaDiagnosticOnce = warnDiagnosticOnce;
|
|
8400
8599
|
|
|
8401
8600
|
// src/config.ts
|
|
8402
8601
|
function defineConfig(config) {
|
|
@@ -9246,7 +9445,22 @@ function appendRetryMessages(messages, content, feedbackMessage, providerMetadat
|
|
|
9246
9445
|
content,
|
|
9247
9446
|
...providerMetadata ? { providerMetadata } : {}
|
|
9248
9447
|
});
|
|
9249
|
-
messages.push({ role: "
|
|
9448
|
+
messages.push({ role: "user", content: feedbackMessage });
|
|
9449
|
+
}
|
|
9450
|
+
function isRetryAbort(value) {
|
|
9451
|
+
return typeof value === "object" && value !== null && value.retry === false;
|
|
9452
|
+
}
|
|
9453
|
+
function describeValue(value) {
|
|
9454
|
+
if (value === null) return "null";
|
|
9455
|
+
if (Array.isArray(value)) return `an array of length ${value.length}`;
|
|
9456
|
+
if (typeof value === "object") {
|
|
9457
|
+
const keys = Object.keys(value);
|
|
9458
|
+
return keys.length > 0 ? `an object with keys [${keys.join(", ")}]` : "an empty object";
|
|
9459
|
+
}
|
|
9460
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
9461
|
+
return `the ${typeof value} ${JSON.stringify(value)}`;
|
|
9462
|
+
}
|
|
9463
|
+
return `a value of type ${typeof value}`;
|
|
9250
9464
|
}
|
|
9251
9465
|
function estimateMessagesTokens(messages) {
|
|
9252
9466
|
let total = 0;
|
|
@@ -10183,6 +10397,7 @@ ${appendedSchemaText}`;
|
|
|
10183
10397
|
provider,
|
|
10184
10398
|
model
|
|
10185
10399
|
});
|
|
10400
|
+
this.emitProviderDiagnostics(agent2, options, { provider, model, providerOptions });
|
|
10186
10401
|
if (handoffMessages && handoffMessages.length > 0) {
|
|
10187
10402
|
const handoffContext = handoffMessages.filter(
|
|
10188
10403
|
(m) => m.role === "assistant" || m.role === "tool"
|
|
@@ -10547,7 +10762,11 @@ ${appendedSchemaText}`;
|
|
|
10547
10762
|
retries: options.retries,
|
|
10548
10763
|
metadata: options.metadata,
|
|
10549
10764
|
validate: options.validate,
|
|
10550
|
-
validateRetries: options.validateRetries
|
|
10765
|
+
validateRetries: options.validateRetries,
|
|
10766
|
+
// This seam serves both callers of a handoff: a plain `ctx.ask()` on an
|
|
10767
|
+
// agent with `handoffs`, and the routed `ctx.delegate()` path, whose
|
|
10768
|
+
// terminal ask is the handoff target. The hook travels like `validate`.
|
|
10769
|
+
retryFeedback: options.retryFeedback
|
|
10551
10770
|
} : void 0;
|
|
10552
10771
|
const handoffFn = () => askStorage.run(targetFrame, async () => {
|
|
10553
10772
|
const delegateOriginal = agent2._delegateOriginalInput;
|
|
@@ -10696,9 +10915,10 @@ ${appendedSchemaText}`;
|
|
|
10696
10915
|
const attempt = guardrailOutputRetries + 1;
|
|
10697
10916
|
const maxAttempts = maxGuardrailRetries + 1;
|
|
10698
10917
|
const onBlock = guardrails.onBlock ?? "throw";
|
|
10918
|
+
const blockReason = outputResult.reason ?? "Output blocked by guardrail";
|
|
10699
10919
|
let feedbackMessage;
|
|
10700
10920
|
if (outputResult.block && onBlock === "retry" && guardrailOutputRetries < maxGuardrailRetries) {
|
|
10701
|
-
feedbackMessage = `Your previous response was blocked by a safety guardrail: ${
|
|
10921
|
+
feedbackMessage = `Your previous response was blocked by a safety guardrail: ${blockReason}. Please provide a different response that complies with the guidelines.`;
|
|
10702
10922
|
}
|
|
10703
10923
|
this.emitEvent({
|
|
10704
10924
|
type: "guardrail",
|
|
@@ -10721,31 +10941,43 @@ ${appendedSchemaText}`;
|
|
|
10721
10941
|
});
|
|
10722
10942
|
if (outputResult.block) {
|
|
10723
10943
|
if (feedbackMessage) {
|
|
10724
|
-
this.
|
|
10725
|
-
type: "pipeline",
|
|
10726
|
-
agent: agent2._name,
|
|
10727
|
-
status: "failed",
|
|
10944
|
+
const decision = await this.resolveRetryFeedback(options?.retryFeedback, {
|
|
10728
10945
|
stage: "guardrail",
|
|
10729
|
-
attempt
|
|
10730
|
-
maxAttempts
|
|
10731
|
-
|
|
10946
|
+
attempt,
|
|
10947
|
+
maxAttempts,
|
|
10948
|
+
output: content,
|
|
10949
|
+
reason: blockReason,
|
|
10950
|
+
defaultMessage: feedbackMessage
|
|
10732
10951
|
});
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
10952
|
+
if (decision.retry) {
|
|
10953
|
+
this.emitEvent({
|
|
10954
|
+
type: "pipeline",
|
|
10955
|
+
agent: agent2._name,
|
|
10956
|
+
status: "failed",
|
|
10957
|
+
stage: "guardrail",
|
|
10958
|
+
attempt,
|
|
10959
|
+
maxAttempts,
|
|
10960
|
+
reason: decision.message
|
|
10961
|
+
});
|
|
10962
|
+
guardrailOutputRetries++;
|
|
10963
|
+
appendRetryMessages(
|
|
10964
|
+
currentMessages,
|
|
10965
|
+
content,
|
|
10966
|
+
decision.message,
|
|
10967
|
+
response.providerMetadata
|
|
10968
|
+
);
|
|
10969
|
+
pendingRetryReason = "guardrail";
|
|
10970
|
+
continue;
|
|
10971
|
+
}
|
|
10972
|
+
this.currentSignal?.throwIfAborted();
|
|
10973
|
+
throw new GuardrailError("output", blockReason);
|
|
10742
10974
|
}
|
|
10743
10975
|
if (typeof onBlock === "function") {
|
|
10744
|
-
return onBlock(
|
|
10976
|
+
return onBlock(blockReason, {
|
|
10745
10977
|
metadata: this.metadata
|
|
10746
10978
|
});
|
|
10747
10979
|
}
|
|
10748
|
-
throw new GuardrailError("output",
|
|
10980
|
+
throw new GuardrailError("output", blockReason);
|
|
10749
10981
|
}
|
|
10750
10982
|
}
|
|
10751
10983
|
let validated = void 0;
|
|
@@ -10765,7 +10997,7 @@ ${appendedSchemaText}`;
|
|
|
10765
10997
|
schemaErr = err;
|
|
10766
10998
|
schemaReason = err instanceof Error ? err.message : String(err);
|
|
10767
10999
|
if (schemaRetries < maxSchemaRetries) {
|
|
10768
|
-
schemaFeedback = `Your response was not valid JSON or did not match the required schema: ${schemaReason}.
|
|
11000
|
+
schemaFeedback = `Your response was not valid JSON or did not match the required schema: ${schemaReason}. Return a corrected response that matches the required schema.`;
|
|
10769
11001
|
}
|
|
10770
11002
|
}
|
|
10771
11003
|
this.emitEvent({
|
|
@@ -10787,24 +11019,36 @@ ${appendedSchemaText}`;
|
|
|
10787
11019
|
});
|
|
10788
11020
|
if (!schemaValid) {
|
|
10789
11021
|
if (schemaFeedback) {
|
|
10790
|
-
this.
|
|
10791
|
-
type: "pipeline",
|
|
10792
|
-
agent: agent2._name,
|
|
10793
|
-
status: "failed",
|
|
11022
|
+
const decision = await this.resolveRetryFeedback(options.retryFeedback, {
|
|
10794
11023
|
stage: "schema",
|
|
10795
|
-
attempt:
|
|
10796
|
-
maxAttempts:
|
|
10797
|
-
|
|
11024
|
+
attempt: schemaAttempt,
|
|
11025
|
+
maxAttempts: schemaMaxAttempts,
|
|
11026
|
+
output: content,
|
|
11027
|
+
reason: schemaReason ?? "Schema parse failed",
|
|
11028
|
+
...schemaErr !== void 0 ? { error: schemaErr } : {},
|
|
11029
|
+
defaultMessage: schemaFeedback
|
|
10798
11030
|
});
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
11031
|
+
if (decision.retry) {
|
|
11032
|
+
this.emitEvent({
|
|
11033
|
+
type: "pipeline",
|
|
11034
|
+
agent: agent2._name,
|
|
11035
|
+
status: "failed",
|
|
11036
|
+
stage: "schema",
|
|
11037
|
+
attempt: schemaAttempt,
|
|
11038
|
+
maxAttempts: schemaMaxAttempts,
|
|
11039
|
+
reason: decision.message
|
|
11040
|
+
});
|
|
11041
|
+
schemaRetries++;
|
|
11042
|
+
appendRetryMessages(
|
|
11043
|
+
currentMessages,
|
|
11044
|
+
content,
|
|
11045
|
+
decision.message,
|
|
11046
|
+
response.providerMetadata
|
|
11047
|
+
);
|
|
11048
|
+
pendingRetryReason = "schema";
|
|
11049
|
+
continue;
|
|
11050
|
+
}
|
|
11051
|
+
this.currentSignal?.throwIfAborted();
|
|
10808
11052
|
}
|
|
10809
11053
|
const zodErr = schemaErr instanceof ZodError2 ? schemaErr : new ZodError2([
|
|
10810
11054
|
{
|
|
@@ -10818,12 +11062,14 @@ ${appendedSchemaText}`;
|
|
|
10818
11062
|
}
|
|
10819
11063
|
if (options?.schema && options.validate) {
|
|
10820
11064
|
let validateResult;
|
|
11065
|
+
let validateErr;
|
|
10821
11066
|
try {
|
|
10822
11067
|
validateResult = await options.validate(validated, {
|
|
10823
11068
|
metadata: this.metadata
|
|
10824
11069
|
});
|
|
10825
11070
|
} catch (err) {
|
|
10826
11071
|
rethrowEventStreamOverflow(err);
|
|
11072
|
+
validateErr = err;
|
|
10827
11073
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10828
11074
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
10829
11075
|
}
|
|
@@ -10832,7 +11078,7 @@ ${appendedSchemaText}`;
|
|
|
10832
11078
|
const validateMaxAttempts = maxValidateRetries + 1;
|
|
10833
11079
|
let validateFeedback;
|
|
10834
11080
|
if (!validateResult.valid && validateRetries < maxValidateRetries) {
|
|
10835
|
-
validateFeedback = `Your response
|
|
11081
|
+
validateFeedback = `Your previous response failed validation: ${validateResult.reason ?? "Validation failed"}. Return a corrected response that satisfies the required schema and this rule.`;
|
|
10836
11082
|
}
|
|
10837
11083
|
this.emitEvent({
|
|
10838
11084
|
type: "validate",
|
|
@@ -10853,24 +11099,37 @@ ${appendedSchemaText}`;
|
|
|
10853
11099
|
});
|
|
10854
11100
|
if (!validateResult.valid) {
|
|
10855
11101
|
if (validateFeedback) {
|
|
10856
|
-
this.
|
|
10857
|
-
type: "pipeline",
|
|
10858
|
-
agent: agent2._name,
|
|
10859
|
-
status: "failed",
|
|
11102
|
+
const decision = await this.resolveRetryFeedback(options.retryFeedback, {
|
|
10860
11103
|
stage: "validate",
|
|
10861
|
-
attempt:
|
|
10862
|
-
maxAttempts:
|
|
10863
|
-
|
|
11104
|
+
attempt: validateAttempt,
|
|
11105
|
+
maxAttempts: validateMaxAttempts,
|
|
11106
|
+
output: content,
|
|
11107
|
+
parsed: validated,
|
|
11108
|
+
reason: validateResult.reason ?? "Validation failed",
|
|
11109
|
+
...validateErr !== void 0 ? { error: validateErr } : {},
|
|
11110
|
+
defaultMessage: validateFeedback
|
|
10864
11111
|
});
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
|
|
11112
|
+
if (decision.retry) {
|
|
11113
|
+
this.emitEvent({
|
|
11114
|
+
type: "pipeline",
|
|
11115
|
+
agent: agent2._name,
|
|
11116
|
+
status: "failed",
|
|
11117
|
+
stage: "validate",
|
|
11118
|
+
attempt: validateAttempt,
|
|
11119
|
+
maxAttempts: validateMaxAttempts,
|
|
11120
|
+
reason: decision.message
|
|
11121
|
+
});
|
|
11122
|
+
validateRetries++;
|
|
11123
|
+
appendRetryMessages(
|
|
11124
|
+
currentMessages,
|
|
11125
|
+
content,
|
|
11126
|
+
decision.message,
|
|
11127
|
+
response.providerMetadata
|
|
11128
|
+
);
|
|
11129
|
+
pendingRetryReason = "validate";
|
|
11130
|
+
continue;
|
|
11131
|
+
}
|
|
11132
|
+
this.currentSignal?.throwIfAborted();
|
|
10874
11133
|
}
|
|
10875
11134
|
throw new ValidationError(
|
|
10876
11135
|
validated,
|
|
@@ -10892,6 +11151,31 @@ ${appendedSchemaText}`;
|
|
|
10892
11151
|
}
|
|
10893
11152
|
throw new MaxTurnsError("ctx.ask()", maxTurns);
|
|
10894
11153
|
}
|
|
11154
|
+
/**
|
|
11155
|
+
* Resolve the corrective feedback text for one rejected attempt, giving an optional
|
|
11156
|
+
* caller-supplied `retryFeedback` hook the chance to replace it or to stop retrying.
|
|
11157
|
+
*
|
|
11158
|
+
* Called by all three output gates, only while a retry is still permitted (never on the
|
|
11159
|
+
* exhausting attempt) and only after the gate's own event has been emitted, so a hook that
|
|
11160
|
+
* throws or aborts can never erase the evidence that the gate rejected. A non-empty string
|
|
11161
|
+
* replaces `defaultMessage` verbatim; `undefined` / `''` keeps it; `{ retry: false }` asks
|
|
11162
|
+
* the gate to throw its typed terminal error. Hook exceptions propagate — a bug in caller
|
|
11163
|
+
* code is not a model failure and must not be retried into silence, and neither is a
|
|
11164
|
+
* return value outside the contract: an unrecognized shape throws instead of silently
|
|
11165
|
+
* degrading to the default text, which would hide the defect behind extra token spend.
|
|
11166
|
+
*/
|
|
11167
|
+
async resolveRetryFeedback(hook, info) {
|
|
11168
|
+
if (!hook) return { retry: true, message: info.defaultMessage };
|
|
11169
|
+
const result = await hook(info, { metadata: this.metadata });
|
|
11170
|
+
if (result === void 0 || result === "") {
|
|
11171
|
+
return { retry: true, message: info.defaultMessage };
|
|
11172
|
+
}
|
|
11173
|
+
if (typeof result === "string") return { retry: true, message: result };
|
|
11174
|
+
if (isRetryAbort(result)) return { retry: false };
|
|
11175
|
+
throw new TypeError(
|
|
11176
|
+
`retryFeedback must return a string, undefined, or { retry: false }; received ${describeValue(result)}.`
|
|
11177
|
+
);
|
|
11178
|
+
}
|
|
10895
11179
|
/**
|
|
10896
11180
|
* Push the final assistant message into session history, preserving providerMetadata
|
|
10897
11181
|
* (e.g., Gemini thought signatures needed for multi-turn reasoning context) and
|
|
@@ -11047,6 +11331,57 @@ ${appendedSchemaText}`;
|
|
|
11047
11331
|
}
|
|
11048
11332
|
}
|
|
11049
11333
|
}
|
|
11334
|
+
/**
|
|
11335
|
+
* Emit a `provider_diagnostic` when the resolved provider reports that it
|
|
11336
|
+
* could not honor the unified `effort` verbatim (P3/R6/R7).
|
|
11337
|
+
*
|
|
11338
|
+
* The adapter only *resolves* — it returns the requested and effective levels
|
|
11339
|
+
* from the same pure resolver its request builder uses. The runtime decides
|
|
11340
|
+
* how to surface that, because only the runtime can see the event bus and
|
|
11341
|
+
* `AxlConfig.diagnostics.silent` (adapters receive `ProviderConfig` only).
|
|
11342
|
+
*
|
|
11343
|
+
* Called once per ask, so a tool loop with N provider round trips still emits
|
|
11344
|
+
* one event; the `console.warn` dedupes once per process per distinct clamp.
|
|
11345
|
+
* A throwing `effortResolution` is a provider bug and propagates.
|
|
11346
|
+
*/
|
|
11347
|
+
emitProviderDiagnostics(agent2, options, ctx) {
|
|
11348
|
+
if (!ctx.provider.effortResolution) return;
|
|
11349
|
+
const resolution = ctx.provider.effortResolution({
|
|
11350
|
+
model: ctx.model,
|
|
11351
|
+
effort: options?.effort ?? agent2._config.effort,
|
|
11352
|
+
thinkingBudget: options?.thinkingBudget ?? agent2._config.thinkingBudget,
|
|
11353
|
+
includeThoughts: options?.includeThoughts ?? agent2._config.includeThoughts,
|
|
11354
|
+
providerOptions: ctx.providerOptions
|
|
11355
|
+
});
|
|
11356
|
+
if (!resolution?.clamped) return;
|
|
11357
|
+
if (typeof resolution.effective !== "string" || resolution.effective.length === 0 || typeof resolution.requested !== "string" || resolution.requested.length === 0) {
|
|
11358
|
+
throw new TypeError(
|
|
11359
|
+
`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)}.`
|
|
11360
|
+
);
|
|
11361
|
+
}
|
|
11362
|
+
const cause = resolution.cause ?? `effort '${resolution.requested}' is not supported as asked`;
|
|
11363
|
+
this.emitEvent({
|
|
11364
|
+
type: "provider_diagnostic",
|
|
11365
|
+
agent: agent2._name,
|
|
11366
|
+
data: {
|
|
11367
|
+
kind: "effort_clamped",
|
|
11368
|
+
...ctx.provider.name ? { provider: ctx.provider.name } : {},
|
|
11369
|
+
model: ctx.model,
|
|
11370
|
+
requested: resolution.requested,
|
|
11371
|
+
effective: resolution.effective,
|
|
11372
|
+
cause
|
|
11373
|
+
}
|
|
11374
|
+
});
|
|
11375
|
+
warnDiagnosticOnce(
|
|
11376
|
+
// One warning per distinct clamp, not per ask: the same clamp across 500
|
|
11377
|
+
// asks is one process-level surprise, while a different requested/effective
|
|
11378
|
+
// pair is a genuinely new one.
|
|
11379
|
+
`effort_clamped\0${ctx.provider.name ?? ""}\0${ctx.model}\0${resolution.requested}\0${resolution.effective}`,
|
|
11380
|
+
`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.`,
|
|
11381
|
+
this.config.diagnostics?.silent,
|
|
11382
|
+
"docs/observability.md#provider-diagnostics"
|
|
11383
|
+
);
|
|
11384
|
+
}
|
|
11050
11385
|
buildToolDefs(agent2, resolvedHandoffs) {
|
|
11051
11386
|
const defs = [];
|
|
11052
11387
|
if (agent2._config.tools) {
|
|
@@ -12327,7 +12662,8 @@ ${summaryResponse.content}`
|
|
|
12327
12662
|
retries: options?.retries,
|
|
12328
12663
|
metadata: options?.metadata,
|
|
12329
12664
|
validate: options?.validate,
|
|
12330
|
-
validateRetries: options?.validateRetries
|
|
12665
|
+
validateRetries: options?.validateRetries,
|
|
12666
|
+
retryFeedback: options?.retryFeedback
|
|
12331
12667
|
});
|
|
12332
12668
|
}
|
|
12333
12669
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
@@ -12375,7 +12711,8 @@ ${summaryResponse.content}`
|
|
|
12375
12711
|
retries: options?.retries,
|
|
12376
12712
|
metadata: options?.metadata,
|
|
12377
12713
|
validate: options?.validate,
|
|
12378
|
-
validateRetries: options?.validateRetries
|
|
12714
|
+
validateRetries: options?.validateRetries,
|
|
12715
|
+
retryFeedback: options?.retryFeedback
|
|
12379
12716
|
});
|
|
12380
12717
|
}
|
|
12381
12718
|
/** Transcribe recorded audio through the dedicated transcription registry.
|