@axlsdk/axl 0.22.2 → 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 +1 -1
- package/dist/index.cjs +365 -136
- 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 +365 -136
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2340,17 +2340,22 @@ function resolveOpenAIReasoningEffort(model, resolved) {
|
|
|
2340
2340
|
}
|
|
2341
2341
|
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
2342
2342
|
}
|
|
2343
|
-
var warnedChatMaxDowngrades = /* @__PURE__ */ new Set();
|
|
2344
2343
|
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
2345
2344
|
const effort = resolveOpenAIReasoningEffort(model, resolved);
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
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
|
+
};
|
|
2354
2359
|
}
|
|
2355
2360
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
2356
2361
|
const oSeries = isOSeriesModel(model);
|
|
@@ -2375,6 +2380,15 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
2375
2380
|
constructor(options = {}) {
|
|
2376
2381
|
super({ profile: OPENAI_PROFILE, ...options });
|
|
2377
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
|
+
}
|
|
2378
2392
|
computeCost(model, usage, _reportedCost, context) {
|
|
2379
2393
|
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
2380
2394
|
}
|
|
@@ -2421,6 +2435,12 @@ var OpenAIResponsesProvider = class {
|
|
|
2421
2435
|
inputCapabilities(model) {
|
|
2422
2436
|
return model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
2423
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");
|
|
2443
|
+
}
|
|
2424
2444
|
validateInput(request) {
|
|
2425
2445
|
const modelOverride = request.providerOptions?.model;
|
|
2426
2446
|
const effectiveModel = typeof modelOverride === "string" ? modelOverride : request.model;
|
|
@@ -3096,14 +3116,6 @@ function budgetToClaudeEffort(budget) {
|
|
|
3096
3116
|
if (budget <= THINKING_BUDGETS.xhigh) return "xhigh";
|
|
3097
3117
|
return "max";
|
|
3098
3118
|
}
|
|
3099
|
-
var warnedFableDisable = /* @__PURE__ */ new Set();
|
|
3100
|
-
function warnFableDisable(model) {
|
|
3101
|
-
if (warnedFableDisable.has(model)) return;
|
|
3102
|
-
warnedFableDisable.add(model);
|
|
3103
|
-
console.warn(
|
|
3104
|
-
`[axl] thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'.`
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
3119
|
function resolveClaudeThinking(model, resolved) {
|
|
3108
3120
|
const capability = resolveClaudeCapability(model);
|
|
3109
3121
|
if (!capability) return { stripTemperature: false };
|
|
@@ -3123,11 +3135,14 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
3123
3135
|
}
|
|
3124
3136
|
if (resolved.thinkingDisabled) {
|
|
3125
3137
|
if (capability.thinking === "adaptive-always-on") {
|
|
3126
|
-
warnFableDisable(model);
|
|
3127
3138
|
return {
|
|
3128
3139
|
thinking: { type: "adaptive" },
|
|
3129
3140
|
outputConfig: { effort: "low" },
|
|
3130
|
-
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
|
+
}
|
|
3131
3146
|
};
|
|
3132
3147
|
}
|
|
3133
3148
|
if (activeEffort && capability.disableAt && !isEffortAtOrBelow(activeEffort, capability.disableAt)) {
|
|
@@ -3189,7 +3204,16 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
3189
3204
|
return {
|
|
3190
3205
|
thinking: { type: "enabled", budget_tokens: budget },
|
|
3191
3206
|
manualBudget: budget,
|
|
3192
|
-
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
|
+
}
|
|
3193
3217
|
};
|
|
3194
3218
|
}
|
|
3195
3219
|
return { stripTemperature: capability.stripTemperature ?? false };
|
|
@@ -3300,6 +3324,26 @@ function mergeAnthropicUsage(initial, update) {
|
|
|
3300
3324
|
}
|
|
3301
3325
|
var AnthropicProvider = class {
|
|
3302
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
|
+
}
|
|
3303
3347
|
inputCapabilities(model) {
|
|
3304
3348
|
return model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
3305
3349
|
}
|
|
@@ -4150,13 +4194,71 @@ function budgetToThinkingLevel(budgetTokens) {
|
|
|
4150
4194
|
function minThinkingLevel(model) {
|
|
4151
4195
|
return GEMINI_3X_MIN_THINKING_LEVEL.get(model) ?? "minimal";
|
|
4152
4196
|
}
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
if (
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
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
|
+
};
|
|
4160
4262
|
}
|
|
4161
4263
|
var GeminiProvider = class {
|
|
4162
4264
|
name = "google";
|
|
@@ -4225,6 +4327,21 @@ var GeminiProvider = class {
|
|
|
4225
4327
|
nativeStructuredOutputSupport() {
|
|
4226
4328
|
return "lossy";
|
|
4227
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
|
+
}
|
|
4228
4345
|
baseUrl;
|
|
4229
4346
|
apiKeySource;
|
|
4230
4347
|
callCounter = 0;
|
|
@@ -4399,12 +4516,9 @@ var GeminiProvider = class {
|
|
|
4399
4516
|
generationConfig.tool_choice = typeof options.toolChoice === "string" ? options.toolChoice === "required" ? "any" : options.toolChoice : { allowed_tools: { mode: "any", tools: [options.toolChoice.function.name] } };
|
|
4400
4517
|
}
|
|
4401
4518
|
const thinking = resolveThinkingOptions(options);
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
generationConfig.thinking_level = budgetToThinkingLevel(thinking.thinkingBudget);
|
|
4406
|
-
} else if (thinking.activeEffort) {
|
|
4407
|
-
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;
|
|
4408
4522
|
}
|
|
4409
4523
|
if (thinking.includeThoughts) generationConfig.thinking_summaries = "auto";
|
|
4410
4524
|
if (Object.keys(generationConfig).length) body.generation_config = generationConfig;
|
|
@@ -4840,50 +4954,17 @@ var GeminiProvider = class {
|
|
|
4840
4954
|
if (Object.keys(generationConfig).length > 0) {
|
|
4841
4955
|
body.generationConfig = generationConfig;
|
|
4842
4956
|
}
|
|
4843
|
-
const
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
}
|
|
4851
|
-
if (
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
warnGemini3xEffortNone(requestModel);
|
|
4855
|
-
}
|
|
4856
|
-
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(requestModel) };
|
|
4857
|
-
} else {
|
|
4858
|
-
generationConfig.thinkingConfig = { thinkingBudget: 0 };
|
|
4859
|
-
}
|
|
4860
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4861
|
-
} else if (hasBudgetOverride) {
|
|
4862
|
-
const config = {};
|
|
4863
|
-
if (isGemini3x(requestModel)) {
|
|
4864
|
-
config.thinkingLevel = budgetToThinkingLevel(thinkingBudget);
|
|
4865
|
-
} else {
|
|
4866
|
-
config.thinkingBudget = thinkingBudget;
|
|
4867
|
-
}
|
|
4868
|
-
if (includeThoughts) config.includeThoughts = true;
|
|
4869
|
-
generationConfig.thinkingConfig = config;
|
|
4870
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4871
|
-
} else if (activeEffort) {
|
|
4872
|
-
const config = {};
|
|
4873
|
-
if (isGemini3x(requestModel)) {
|
|
4874
|
-
config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
|
|
4875
|
-
} else {
|
|
4876
|
-
if (activeEffort === "max" && requestModel === "gemini-2.5-pro") {
|
|
4877
|
-
config.thinkingBudget = 32768;
|
|
4878
|
-
} else {
|
|
4879
|
-
config.thinkingBudget = THINKING_BUDGETS2[activeEffort] ?? 5e3;
|
|
4880
|
-
}
|
|
4881
|
-
}
|
|
4882
|
-
if (includeThoughts) config.includeThoughts = true;
|
|
4883
|
-
generationConfig.thinkingConfig = config;
|
|
4884
|
-
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4885
|
-
} else if (includeThoughts) {
|
|
4886
|
-
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;
|
|
4887
4968
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
4888
4969
|
}
|
|
4889
4970
|
if (options.toolChoice !== void 0) {
|
|
@@ -7518,6 +7599,9 @@ var AXL_EVENT_TYPES = [
|
|
|
7518
7599
|
// structured-output cliffs (oversized appended schema, dropped refinements,
|
|
7519
7600
|
// streaming disabled, zero-guidance `schemaPrompt:'none'`).
|
|
7520
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",
|
|
7521
7605
|
// Observability
|
|
7522
7606
|
"log",
|
|
7523
7607
|
"memory_remember",
|
|
@@ -7567,6 +7651,7 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
7567
7651
|
"pipeline",
|
|
7568
7652
|
"verify",
|
|
7569
7653
|
"schema_diagnostic",
|
|
7654
|
+
"provider_diagnostic",
|
|
7570
7655
|
"workflow_start",
|
|
7571
7656
|
"workflow_end",
|
|
7572
7657
|
"transcription_start",
|
|
@@ -8495,6 +8580,10 @@ var LEGACY_REDACTION_RULES = {
|
|
|
8495
8580
|
// which is user content or PII (field paths are schema-shape, preserved like
|
|
8496
8581
|
// `string_delta.path`). Passthrough, as an explicit reviewed decision.
|
|
8497
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,
|
|
8498
8587
|
log: (e) => {
|
|
8499
8588
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
8500
8589
|
return { ...e, data: walkObjectOneLevel(e.data, LOG_PRESERVE_KEYS) };
|
|
@@ -8625,6 +8714,7 @@ var COMMON_V2_REDACTION_RULES = {
|
|
|
8625
8714
|
string_delta: reuseLegacyRule("string_delta"),
|
|
8626
8715
|
verify: reuseLegacyRule("verify"),
|
|
8627
8716
|
schema_diagnostic: reuseLegacyRule("schema_diagnostic"),
|
|
8717
|
+
provider_diagnostic: reuseLegacyRule("provider_diagnostic"),
|
|
8628
8718
|
log: reuseLegacyRule("log"),
|
|
8629
8719
|
memory_remember: reuseLegacyRule("memory_remember"),
|
|
8630
8720
|
memory_recall: reuseLegacyRule("memory_recall"),
|
|
@@ -8720,13 +8810,14 @@ function detectDroppedRefinements(schema) {
|
|
|
8720
8810
|
var DOCS_URL = "docs/observability.md#schema-diagnostics";
|
|
8721
8811
|
var MAX_WARNED_KEYS = 2048;
|
|
8722
8812
|
var warnedDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
8723
|
-
function
|
|
8813
|
+
function warnDiagnosticOnce(key, message, silent, docs = DOCS_URL) {
|
|
8724
8814
|
if (silent || process.env.AXL_DIAGNOSTICS_SILENT === "true") return;
|
|
8725
8815
|
if (warnedDiagnosticKeys.has(key)) return;
|
|
8726
8816
|
if (warnedDiagnosticKeys.size >= MAX_WARNED_KEYS) warnedDiagnosticKeys.clear();
|
|
8727
8817
|
warnedDiagnosticKeys.add(key);
|
|
8728
|
-
console.warn(`[axl] ${message} See ${
|
|
8818
|
+
console.warn(`[axl] ${message} See ${docs}.`);
|
|
8729
8819
|
}
|
|
8820
|
+
var warnSchemaDiagnosticOnce = warnDiagnosticOnce;
|
|
8730
8821
|
|
|
8731
8822
|
// src/config.ts
|
|
8732
8823
|
function defineConfig(config) {
|
|
@@ -9576,7 +9667,22 @@ function appendRetryMessages(messages, content, feedbackMessage, providerMetadat
|
|
|
9576
9667
|
content,
|
|
9577
9668
|
...providerMetadata ? { providerMetadata } : {}
|
|
9578
9669
|
});
|
|
9579
|
-
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}`;
|
|
9580
9686
|
}
|
|
9581
9687
|
function estimateMessagesTokens(messages) {
|
|
9582
9688
|
let total = 0;
|
|
@@ -10513,6 +10619,7 @@ ${appendedSchemaText}`;
|
|
|
10513
10619
|
provider,
|
|
10514
10620
|
model
|
|
10515
10621
|
});
|
|
10622
|
+
this.emitProviderDiagnostics(agent2, options, { provider, model, providerOptions });
|
|
10516
10623
|
if (handoffMessages && handoffMessages.length > 0) {
|
|
10517
10624
|
const handoffContext = handoffMessages.filter(
|
|
10518
10625
|
(m) => m.role === "assistant" || m.role === "tool"
|
|
@@ -10877,7 +10984,11 @@ ${appendedSchemaText}`;
|
|
|
10877
10984
|
retries: options.retries,
|
|
10878
10985
|
metadata: options.metadata,
|
|
10879
10986
|
validate: options.validate,
|
|
10880
|
-
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
|
|
10881
10992
|
} : void 0;
|
|
10882
10993
|
const handoffFn = () => askStorage.run(targetFrame, async () => {
|
|
10883
10994
|
const delegateOriginal = agent2._delegateOriginalInput;
|
|
@@ -11026,9 +11137,10 @@ ${appendedSchemaText}`;
|
|
|
11026
11137
|
const attempt = guardrailOutputRetries + 1;
|
|
11027
11138
|
const maxAttempts = maxGuardrailRetries + 1;
|
|
11028
11139
|
const onBlock = guardrails.onBlock ?? "throw";
|
|
11140
|
+
const blockReason = outputResult.reason ?? "Output blocked by guardrail";
|
|
11029
11141
|
let feedbackMessage;
|
|
11030
11142
|
if (outputResult.block && onBlock === "retry" && guardrailOutputRetries < maxGuardrailRetries) {
|
|
11031
|
-
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.`;
|
|
11032
11144
|
}
|
|
11033
11145
|
this.emitEvent({
|
|
11034
11146
|
type: "guardrail",
|
|
@@ -11051,31 +11163,43 @@ ${appendedSchemaText}`;
|
|
|
11051
11163
|
});
|
|
11052
11164
|
if (outputResult.block) {
|
|
11053
11165
|
if (feedbackMessage) {
|
|
11054
|
-
this.
|
|
11055
|
-
type: "pipeline",
|
|
11056
|
-
agent: agent2._name,
|
|
11057
|
-
status: "failed",
|
|
11166
|
+
const decision = await this.resolveRetryFeedback(options?.retryFeedback, {
|
|
11058
11167
|
stage: "guardrail",
|
|
11059
|
-
attempt
|
|
11060
|
-
maxAttempts
|
|
11061
|
-
|
|
11168
|
+
attempt,
|
|
11169
|
+
maxAttempts,
|
|
11170
|
+
output: content,
|
|
11171
|
+
reason: blockReason,
|
|
11172
|
+
defaultMessage: feedbackMessage
|
|
11062
11173
|
});
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
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);
|
|
11072
11196
|
}
|
|
11073
11197
|
if (typeof onBlock === "function") {
|
|
11074
|
-
return onBlock(
|
|
11198
|
+
return onBlock(blockReason, {
|
|
11075
11199
|
metadata: this.metadata
|
|
11076
11200
|
});
|
|
11077
11201
|
}
|
|
11078
|
-
throw new GuardrailError("output",
|
|
11202
|
+
throw new GuardrailError("output", blockReason);
|
|
11079
11203
|
}
|
|
11080
11204
|
}
|
|
11081
11205
|
let validated = void 0;
|
|
@@ -11095,7 +11219,7 @@ ${appendedSchemaText}`;
|
|
|
11095
11219
|
schemaErr = err;
|
|
11096
11220
|
schemaReason = err instanceof Error ? err.message : String(err);
|
|
11097
11221
|
if (schemaRetries < maxSchemaRetries) {
|
|
11098
|
-
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.`;
|
|
11099
11223
|
}
|
|
11100
11224
|
}
|
|
11101
11225
|
this.emitEvent({
|
|
@@ -11117,24 +11241,36 @@ ${appendedSchemaText}`;
|
|
|
11117
11241
|
});
|
|
11118
11242
|
if (!schemaValid) {
|
|
11119
11243
|
if (schemaFeedback) {
|
|
11120
|
-
this.
|
|
11121
|
-
type: "pipeline",
|
|
11122
|
-
agent: agent2._name,
|
|
11123
|
-
status: "failed",
|
|
11244
|
+
const decision = await this.resolveRetryFeedback(options.retryFeedback, {
|
|
11124
11245
|
stage: "schema",
|
|
11125
|
-
attempt:
|
|
11126
|
-
maxAttempts:
|
|
11127
|
-
|
|
11246
|
+
attempt: schemaAttempt,
|
|
11247
|
+
maxAttempts: schemaMaxAttempts,
|
|
11248
|
+
output: content,
|
|
11249
|
+
reason: schemaReason ?? "Schema parse failed",
|
|
11250
|
+
...schemaErr !== void 0 ? { error: schemaErr } : {},
|
|
11251
|
+
defaultMessage: schemaFeedback
|
|
11128
11252
|
});
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
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();
|
|
11138
11274
|
}
|
|
11139
11275
|
const zodErr = schemaErr instanceof import_zod2.ZodError ? schemaErr : new import_zod2.ZodError([
|
|
11140
11276
|
{
|
|
@@ -11148,12 +11284,14 @@ ${appendedSchemaText}`;
|
|
|
11148
11284
|
}
|
|
11149
11285
|
if (options?.schema && options.validate) {
|
|
11150
11286
|
let validateResult;
|
|
11287
|
+
let validateErr;
|
|
11151
11288
|
try {
|
|
11152
11289
|
validateResult = await options.validate(validated, {
|
|
11153
11290
|
metadata: this.metadata
|
|
11154
11291
|
});
|
|
11155
11292
|
} catch (err) {
|
|
11156
11293
|
rethrowEventStreamOverflow(err);
|
|
11294
|
+
validateErr = err;
|
|
11157
11295
|
const reason = err instanceof Error ? err.message : String(err);
|
|
11158
11296
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
11159
11297
|
}
|
|
@@ -11162,7 +11300,7 @@ ${appendedSchemaText}`;
|
|
|
11162
11300
|
const validateMaxAttempts = maxValidateRetries + 1;
|
|
11163
11301
|
let validateFeedback;
|
|
11164
11302
|
if (!validateResult.valid && validateRetries < maxValidateRetries) {
|
|
11165
|
-
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.`;
|
|
11166
11304
|
}
|
|
11167
11305
|
this.emitEvent({
|
|
11168
11306
|
type: "validate",
|
|
@@ -11183,24 +11321,37 @@ ${appendedSchemaText}`;
|
|
|
11183
11321
|
});
|
|
11184
11322
|
if (!validateResult.valid) {
|
|
11185
11323
|
if (validateFeedback) {
|
|
11186
|
-
this.
|
|
11187
|
-
type: "pipeline",
|
|
11188
|
-
agent: agent2._name,
|
|
11189
|
-
status: "failed",
|
|
11324
|
+
const decision = await this.resolveRetryFeedback(options.retryFeedback, {
|
|
11190
11325
|
stage: "validate",
|
|
11191
|
-
attempt:
|
|
11192
|
-
maxAttempts:
|
|
11193
|
-
|
|
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
|
|
11194
11333
|
});
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
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();
|
|
11204
11355
|
}
|
|
11205
11356
|
throw new ValidationError(
|
|
11206
11357
|
validated,
|
|
@@ -11222,6 +11373,31 @@ ${appendedSchemaText}`;
|
|
|
11222
11373
|
}
|
|
11223
11374
|
throw new MaxTurnsError("ctx.ask()", maxTurns);
|
|
11224
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
|
+
}
|
|
11225
11401
|
/**
|
|
11226
11402
|
* Push the final assistant message into session history, preserving providerMetadata
|
|
11227
11403
|
* (e.g., Gemini thought signatures needed for multi-turn reasoning context) and
|
|
@@ -11377,6 +11553,57 @@ ${appendedSchemaText}`;
|
|
|
11377
11553
|
}
|
|
11378
11554
|
}
|
|
11379
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
|
+
}
|
|
11380
11607
|
buildToolDefs(agent2, resolvedHandoffs) {
|
|
11381
11608
|
const defs = [];
|
|
11382
11609
|
if (agent2._config.tools) {
|
|
@@ -12657,7 +12884,8 @@ ${summaryResponse.content}`
|
|
|
12657
12884
|
retries: options?.retries,
|
|
12658
12885
|
metadata: options?.metadata,
|
|
12659
12886
|
validate: options?.validate,
|
|
12660
|
-
validateRetries: options?.validateRetries
|
|
12887
|
+
validateRetries: options?.validateRetries,
|
|
12888
|
+
retryFeedback: options?.retryFeedback
|
|
12661
12889
|
});
|
|
12662
12890
|
}
|
|
12663
12891
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
@@ -12705,7 +12933,8 @@ ${summaryResponse.content}`
|
|
|
12705
12933
|
retries: options?.retries,
|
|
12706
12934
|
metadata: options?.metadata,
|
|
12707
12935
|
validate: options?.validate,
|
|
12708
|
-
validateRetries: options?.validateRetries
|
|
12936
|
+
validateRetries: options?.validateRetries,
|
|
12937
|
+
retryFeedback: options?.retryFeedback
|
|
12709
12938
|
});
|
|
12710
12939
|
}
|
|
12711
12940
|
/** Transcribe recorded audio through the dedicated transcription registry.
|