@axlsdk/axl 0.25.0 → 0.25.2
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 +14 -15
- package/dist/index.cjs +831 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -19
- package/dist/index.d.ts +106 -19
- package/dist/index.js +816 -143
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -40,6 +40,23 @@ var UnsupportedModelInputError = class extends AxlError {
|
|
|
40
40
|
this.source = options.source;
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
+
var UnsupportedModelOptionError = class extends AxlError {
|
|
44
|
+
provider;
|
|
45
|
+
model;
|
|
46
|
+
option;
|
|
47
|
+
remediation;
|
|
48
|
+
constructor(options) {
|
|
49
|
+
super(
|
|
50
|
+
"UNSUPPORTED_MODEL_OPTION",
|
|
51
|
+
`Provider '${options.provider}' model '${options.model}' does not support ${options.option}. ${options.remediation}`
|
|
52
|
+
);
|
|
53
|
+
this.name = "UnsupportedModelOptionError";
|
|
54
|
+
this.provider = options.provider;
|
|
55
|
+
this.model = options.model;
|
|
56
|
+
this.option = options.option;
|
|
57
|
+
this.remediation = options.remediation;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
43
60
|
var InvalidTranscriptionInputError = class extends AxlError {
|
|
44
61
|
constructor(message) {
|
|
45
62
|
super("INVALID_TRANSCRIPTION_INPUT", message);
|
|
@@ -331,7 +348,7 @@ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null
|
|
|
331
348
|
var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
|
|
332
349
|
var thisCopy = {
|
|
333
350
|
path: buildPath.startsWith("file:") ? fileURLToPath(buildPath) : buildPath,
|
|
334
|
-
version: true ? "0.25.
|
|
351
|
+
version: true ? "0.25.2" : "unknown"
|
|
335
352
|
};
|
|
336
353
|
function warnCrossCopy(other, joined) {
|
|
337
354
|
if (other === thisCopy) return;
|
|
@@ -3204,8 +3221,12 @@ var OpenAICompatibleProvider = class {
|
|
|
3204
3221
|
for (const key of forbidden) {
|
|
3205
3222
|
if (!userKeys.includes(key)) delete body[key];
|
|
3206
3223
|
}
|
|
3224
|
+
this.validateFinalRequestBody(body);
|
|
3207
3225
|
return body;
|
|
3208
3226
|
}
|
|
3227
|
+
/** Native subclasses may reject model-invalid fields after providerOptions merge. */
|
|
3228
|
+
validateFinalRequestBody(_body) {
|
|
3229
|
+
}
|
|
3209
3230
|
formatMessage(msg, model) {
|
|
3210
3231
|
const modalities = this.inputModalities(model);
|
|
3211
3232
|
const out = {
|
|
@@ -3613,6 +3634,24 @@ var withLongContext = (short) => ({
|
|
|
3613
3634
|
output: short.output * 1.5
|
|
3614
3635
|
});
|
|
3615
3636
|
var DIRECT_OPENAI_CATALOG = [
|
|
3637
|
+
{
|
|
3638
|
+
aliases: ["gpt-6-astra"],
|
|
3639
|
+
short: { input: 10 / M, cachedInput: 1 / M, cacheWrite: 12.5 / M, output: 50 / M },
|
|
3640
|
+
long: { input: 20 / M, cachedInput: 2 / M, cacheWrite: 25 / M, output: 75 / M },
|
|
3641
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
3642
|
+
},
|
|
3643
|
+
{
|
|
3644
|
+
aliases: ["gpt-6-sol"],
|
|
3645
|
+
short: { input: 2 / M, cachedInput: 0.2 / M, cacheWrite: 2.5 / M, output: 10 / M },
|
|
3646
|
+
long: { input: 4 / M, cachedInput: 0.4 / M, cacheWrite: 5 / M, output: 15 / M },
|
|
3647
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
3648
|
+
},
|
|
3649
|
+
{
|
|
3650
|
+
aliases: ["gpt-6-luna"],
|
|
3651
|
+
short: { input: 0.1 / M, cachedInput: 0.01 / M, cacheWrite: 0.125 / M, output: 0.5 / M },
|
|
3652
|
+
long: { input: 0.2 / M, cachedInput: 0.02 / M, cacheWrite: 0.25 / M, output: 0.75 / M },
|
|
3653
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
3654
|
+
},
|
|
3616
3655
|
{
|
|
3617
3656
|
aliases: ["gpt-5.6", "gpt-5.6-sol"],
|
|
3618
3657
|
snapshotBase: "gpt-5.6-sol",
|
|
@@ -3943,7 +3982,7 @@ function estimateDirectOpenAICost(model, usage, context) {
|
|
|
3943
3982
|
const audioOutput = audio_output_tokens ?? 0;
|
|
3944
3983
|
if (![prompt_tokens, completion_tokens, cached, cacheWrite, audioInput, audioOutput].every(
|
|
3945
3984
|
(count) => Number.isSafeInteger(count) && count >= 0
|
|
3946
|
-
) || cached + cacheWrite + audioInput > prompt_tokens || audioOutput > completion_tokens) {
|
|
3985
|
+
) || resolveOpenAIModelDescriptor(model).strictUsageTotals && (!Number.isSafeInteger(usage.total_tokens) || usage.total_tokens !== prompt_tokens + completion_tokens) || cached + cacheWrite + audioInput > prompt_tokens || audioOutput > completion_tokens) {
|
|
3947
3986
|
return void 0;
|
|
3948
3987
|
}
|
|
3949
3988
|
if (audioInput > 0 && entry.audio === void 0 || audioOutput > 0 && entry.audio?.output === void 0) {
|
|
@@ -3968,57 +4007,206 @@ function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens)
|
|
|
3968
4007
|
cached_tokens: cachedTokens
|
|
3969
4008
|
});
|
|
3970
4009
|
}
|
|
3971
|
-
|
|
3972
|
-
|
|
4010
|
+
var EFFORT_FALLBACK = {
|
|
4011
|
+
none: "minimal",
|
|
4012
|
+
minimal: "low",
|
|
4013
|
+
low: "medium",
|
|
4014
|
+
medium: "high",
|
|
4015
|
+
max: "xhigh",
|
|
4016
|
+
xhigh: "high"
|
|
4017
|
+
};
|
|
4018
|
+
var BASE_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
4019
|
+
function bothEndpoints(value) {
|
|
4020
|
+
return { chat: value, responses: value };
|
|
3973
4021
|
}
|
|
3974
|
-
function
|
|
3975
|
-
return
|
|
4022
|
+
function gpt6(id, efforts, chatTools) {
|
|
4023
|
+
return {
|
|
4024
|
+
id,
|
|
4025
|
+
family: "gpt-6",
|
|
4026
|
+
reasoning: true,
|
|
4027
|
+
efforts: bothEndpoints(efforts),
|
|
4028
|
+
defaultEffort: "medium",
|
|
4029
|
+
// Same policy as GPT-5.x: a portable `temperature` is dropped while
|
|
4030
|
+
// reasoning is active (including the active default). A raw
|
|
4031
|
+
// `providerOptions` sampling field is rejected by the final-body check.
|
|
4032
|
+
sampling: bothEndpoints("unless-effort-none"),
|
|
4033
|
+
chatTools,
|
|
4034
|
+
validatesFinalBody: true,
|
|
4035
|
+
strictUsageTotals: true
|
|
4036
|
+
};
|
|
3976
4037
|
}
|
|
3977
|
-
|
|
3978
|
-
"
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4038
|
+
function gpt56(id) {
|
|
4039
|
+
const chat = ["none", ...BASE_EFFORTS, "xhigh"];
|
|
4040
|
+
return {
|
|
4041
|
+
id,
|
|
4042
|
+
family: "gpt-5.6",
|
|
4043
|
+
reasoning: true,
|
|
4044
|
+
efforts: { chat, responses: [...chat, "max"] },
|
|
4045
|
+
// Responses rejects temperature even at the provider-default effort.
|
|
4046
|
+
sampling: { chat: "when-effort-sent", responses: "always" },
|
|
4047
|
+
chatTools: "allowed",
|
|
4048
|
+
validatesFinalBody: false,
|
|
4049
|
+
strictUsageTotals: false
|
|
4050
|
+
};
|
|
3985
4051
|
}
|
|
3986
|
-
|
|
3987
|
-
|
|
4052
|
+
var OPENAI_EXACT_MODELS = new Map(
|
|
4053
|
+
[
|
|
4054
|
+
gpt6("gpt-6-astra", ["low", "medium", "high", "xhigh", "max"], "never"),
|
|
4055
|
+
gpt6("gpt-6-sol", ["none", "low", "medium", "high", "xhigh", "max"], "effort-none-only"),
|
|
4056
|
+
gpt6("gpt-6-luna", ["none", "low", "medium", "high", "xhigh", "max"], "effort-none-only"),
|
|
4057
|
+
gpt56("gpt-5.6"),
|
|
4058
|
+
gpt56("gpt-5.6-sol"),
|
|
4059
|
+
gpt56("gpt-5.6-terra"),
|
|
4060
|
+
gpt56("gpt-5.6-luna")
|
|
4061
|
+
].map((descriptor) => [descriptor.id, descriptor])
|
|
4062
|
+
);
|
|
4063
|
+
var O_SERIES_DESCRIPTOR = {
|
|
4064
|
+
id: "o-series",
|
|
4065
|
+
family: "o-series",
|
|
4066
|
+
reasoning: true,
|
|
4067
|
+
efforts: bothEndpoints(BASE_EFFORTS),
|
|
4068
|
+
defaultEffort: "medium",
|
|
4069
|
+
sampling: bothEndpoints("always"),
|
|
4070
|
+
chatTools: "allowed",
|
|
4071
|
+
validatesFinalBody: false,
|
|
4072
|
+
strictUsageTotals: false
|
|
4073
|
+
};
|
|
4074
|
+
var GPT_5_PRO_DESCRIPTOR = {
|
|
4075
|
+
id: "gpt-5-pro",
|
|
4076
|
+
family: "gpt-5-pro",
|
|
4077
|
+
reasoning: true,
|
|
4078
|
+
efforts: bothEndpoints(["high"]),
|
|
4079
|
+
defaultEffort: "high",
|
|
4080
|
+
sampling: bothEndpoints("when-effort-sent"),
|
|
4081
|
+
chatTools: "allowed",
|
|
4082
|
+
validatesFinalBody: false,
|
|
4083
|
+
strictUsageTotals: false
|
|
4084
|
+
};
|
|
4085
|
+
var DEFAULT_DESCRIPTOR = {
|
|
4086
|
+
id: "default",
|
|
4087
|
+
family: "default",
|
|
4088
|
+
reasoning: false,
|
|
4089
|
+
// Never emitted (the model is not reasoning-capable); keeps the clamp total.
|
|
4090
|
+
efforts: bothEndpoints(BASE_EFFORTS),
|
|
4091
|
+
sampling: bothEndpoints("never"),
|
|
4092
|
+
chatTools: "allowed",
|
|
4093
|
+
validatesFinalBody: false,
|
|
4094
|
+
strictUsageTotals: false
|
|
4095
|
+
};
|
|
4096
|
+
function gpt5FamilyDescriptor(model) {
|
|
4097
|
+
const none = /^gpt-5\.[1-9]/.test(model);
|
|
4098
|
+
const xhigh = /^gpt-5\.([2-9]|\d{2,})/.test(model);
|
|
4099
|
+
const efforts = [
|
|
4100
|
+
...none ? ["none"] : [],
|
|
4101
|
+
...BASE_EFFORTS,
|
|
4102
|
+
...xhigh ? ["xhigh"] : []
|
|
4103
|
+
];
|
|
4104
|
+
return {
|
|
4105
|
+
id: "gpt-5",
|
|
4106
|
+
family: "gpt-5",
|
|
4107
|
+
reasoning: true,
|
|
4108
|
+
efforts: bothEndpoints(efforts),
|
|
4109
|
+
...none ? {} : { defaultEffort: "medium" },
|
|
4110
|
+
sampling: bothEndpoints("when-effort-sent"),
|
|
4111
|
+
chatTools: "allowed",
|
|
4112
|
+
validatesFinalBody: false,
|
|
4113
|
+
strictUsageTotals: false
|
|
4114
|
+
};
|
|
3988
4115
|
}
|
|
3989
|
-
|
|
3990
|
-
|
|
4116
|
+
var OPENAI_FAMILY_FALLBACKS = [
|
|
4117
|
+
{ match: /^(o1|o3|o4-mini)/, describe: () => O_SERIES_DESCRIPTOR },
|
|
4118
|
+
{ match: /^gpt-5-pro/, describe: () => GPT_5_PRO_DESCRIPTOR },
|
|
4119
|
+
{ match: /^gpt-5/, describe: gpt5FamilyDescriptor }
|
|
4120
|
+
];
|
|
4121
|
+
function resolveOpenAIModelDescriptor(model) {
|
|
4122
|
+
const exact = OPENAI_EXACT_MODELS.get(model);
|
|
4123
|
+
if (exact) return exact;
|
|
4124
|
+
const family = OPENAI_FAMILY_FALLBACKS.find((entry) => entry.match.test(model));
|
|
4125
|
+
return family ? family.describe(model) : DEFAULT_DESCRIPTOR;
|
|
4126
|
+
}
|
|
4127
|
+
function clampToDescriptor(descriptor, endpoint, effort) {
|
|
4128
|
+
const accepted = descriptor.efforts[endpoint];
|
|
4129
|
+
let candidate = effort;
|
|
4130
|
+
while (candidate !== void 0 && !accepted.includes(candidate)) {
|
|
4131
|
+
candidate = EFFORT_FALLBACK[candidate];
|
|
4132
|
+
}
|
|
4133
|
+
if (candidate === void 0) {
|
|
4134
|
+
throw new Error(
|
|
4135
|
+
`OpenAI descriptor '${descriptor.id}' accepts no ${endpoint} effort reachable from '${effort}'`
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
return candidate;
|
|
3991
4139
|
}
|
|
3992
|
-
function
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4140
|
+
function stripsPortableSampling(descriptor, endpoint, wireEffort) {
|
|
4141
|
+
switch (descriptor.sampling[endpoint]) {
|
|
4142
|
+
case "never":
|
|
4143
|
+
return false;
|
|
4144
|
+
case "always":
|
|
4145
|
+
return true;
|
|
4146
|
+
case "when-effort-sent":
|
|
4147
|
+
return wireEffort !== void 0;
|
|
4148
|
+
case "unless-effort-none":
|
|
4149
|
+
return (wireEffort ?? descriptor.defaultEffort) !== "none";
|
|
3997
4150
|
}
|
|
3998
|
-
if (effort === "xhigh" && !supportsXhigh(model)) return "high";
|
|
3999
|
-
return effort;
|
|
4000
4151
|
}
|
|
4001
|
-
function
|
|
4002
|
-
return
|
|
4152
|
+
function isOSeriesModel(model) {
|
|
4153
|
+
return resolveOpenAIModelDescriptor(model).family === "o-series";
|
|
4003
4154
|
}
|
|
4004
4155
|
function budgetToReasoningEffort(budget) {
|
|
4005
4156
|
if (budget <= 1024) return "low";
|
|
4006
4157
|
if (budget <= 8192) return "medium";
|
|
4007
4158
|
return "high";
|
|
4008
4159
|
}
|
|
4160
|
+
function resolveEndpointReasoningEffort(model, endpoint, resolved) {
|
|
4161
|
+
const descriptor = resolveOpenAIModelDescriptor(model);
|
|
4162
|
+
if (!descriptor.reasoning) return void 0;
|
|
4163
|
+
const requested = resolved.hasBudgetOverride ? budgetToReasoningEffort(resolved.thinkingBudget) : !resolved.thinkingDisabled && resolved.activeEffort ? resolved.activeEffort : resolved.thinkingDisabled ? "none" : void 0;
|
|
4164
|
+
return requested === void 0 ? void 0 : clampToDescriptor(descriptor, endpoint, requested);
|
|
4165
|
+
}
|
|
4009
4166
|
function resolveOpenAIReasoningEffort(model, resolved) {
|
|
4010
|
-
|
|
4011
|
-
if (resolved.hasBudgetOverride) {
|
|
4012
|
-
return clampReasoningEffort(model, budgetToReasoningEffort(resolved.thinkingBudget));
|
|
4013
|
-
}
|
|
4014
|
-
if (!resolved.thinkingDisabled && resolved.activeEffort) {
|
|
4015
|
-
return clampReasoningEffort(model, effortToReasoningEffort(resolved.activeEffort, model));
|
|
4016
|
-
}
|
|
4017
|
-
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
4167
|
+
return resolveEndpointReasoningEffort(model, "responses", resolved);
|
|
4018
4168
|
}
|
|
4019
4169
|
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
4020
|
-
|
|
4021
|
-
|
|
4170
|
+
return resolveEndpointReasoningEffort(model, "chat", resolved);
|
|
4171
|
+
}
|
|
4172
|
+
function validateOpenAIFinalRequestBody(body, endpoint) {
|
|
4173
|
+
const model = body.model;
|
|
4174
|
+
if (typeof model !== "string") return;
|
|
4175
|
+
const descriptor = resolveOpenAIModelDescriptor(model);
|
|
4176
|
+
if (!descriptor.validatesFinalBody) return;
|
|
4177
|
+
const provider = endpoint === "chat" ? "openai" : "openai-responses";
|
|
4178
|
+
const fail = (option, remediation) => {
|
|
4179
|
+
throw new UnsupportedModelOptionError({ provider, model, option, remediation });
|
|
4180
|
+
};
|
|
4181
|
+
const rawEffort = endpoint === "chat" ? body.reasoning_effort : body.reasoning !== null && typeof body.reasoning === "object" ? body.reasoning.effort : void 0;
|
|
4182
|
+
const accepted = descriptor.efforts[endpoint];
|
|
4183
|
+
if (rawEffort !== void 0 && !accepted.includes(rawEffort)) {
|
|
4184
|
+
fail("reasoning effort", `Use one of: ${accepted.join(", ")}.`);
|
|
4185
|
+
}
|
|
4186
|
+
const activeReasoning = (rawEffort ?? descriptor.defaultEffort) !== "none";
|
|
4187
|
+
if (endpoint === "chat" && descriptor.chatTools !== "allowed") {
|
|
4188
|
+
const toolChoice = body.tool_choice;
|
|
4189
|
+
const toolIntent = Array.isArray(body.tools) && body.tools.length > 0 || toolChoice !== void 0 && toolChoice !== null && toolChoice !== "none" && toolChoice !== "auto";
|
|
4190
|
+
if (toolIntent && (descriptor.chatTools === "never" || activeReasoning)) {
|
|
4191
|
+
fail(
|
|
4192
|
+
"Chat Completions tool calling",
|
|
4193
|
+
descriptor.chatTools === "never" ? `Use openai-responses:${model} for tools.` : `Use openai-responses:${model} for reasoning with tools, or set effective reasoning_effort to 'none'.`
|
|
4194
|
+
);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
if (activeReasoning) {
|
|
4198
|
+
const forbidden = endpoint === "chat" ? ["temperature", "top_p", "top_logprobs", "logprobs"] : ["temperature", "top_p", "top_logprobs"];
|
|
4199
|
+
for (const key of forbidden) {
|
|
4200
|
+
if (body[key] !== void 0)
|
|
4201
|
+
fail(key, `Remove ${key} or set reasoning effort to 'none' on Sol or Luna.`);
|
|
4202
|
+
}
|
|
4203
|
+
if (endpoint === "responses" && Array.isArray(body.include) && body.include.includes("message.output_text.logprobs")) {
|
|
4204
|
+
fail(
|
|
4205
|
+
"message.output_text.logprobs",
|
|
4206
|
+
"Remove it from include or set reasoning effort to none on Sol or Luna."
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4022
4210
|
}
|
|
4023
4211
|
function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
|
|
4024
4212
|
const resolved = resolveThinkingOptions(options);
|
|
@@ -4034,11 +4222,15 @@ function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
|
|
|
4034
4222
|
};
|
|
4035
4223
|
}
|
|
4036
4224
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
4037
|
-
const oSeries = isOSeriesModel(model);
|
|
4038
|
-
const reasoningCapable = supportsReasoningEffort(model);
|
|
4039
4225
|
const wireEffort = resolveOpenAIChatReasoningEffort(model, resolved);
|
|
4040
4226
|
if (wireEffort) body.reasoning_effort = wireEffort;
|
|
4041
|
-
return {
|
|
4227
|
+
return {
|
|
4228
|
+
stripTemperature: stripsPortableSampling(
|
|
4229
|
+
resolveOpenAIModelDescriptor(model),
|
|
4230
|
+
"chat",
|
|
4231
|
+
wireEffort
|
|
4232
|
+
)
|
|
4233
|
+
};
|
|
4042
4234
|
};
|
|
4043
4235
|
var OPENAI_PROFILE = {
|
|
4044
4236
|
name: "openai",
|
|
@@ -4073,6 +4265,9 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
4073
4265
|
"OpenAI Chat Completions"
|
|
4074
4266
|
);
|
|
4075
4267
|
}
|
|
4268
|
+
validateFinalRequestBody(body) {
|
|
4269
|
+
validateOpenAIFinalRequestBody(body, "chat");
|
|
4270
|
+
}
|
|
4076
4271
|
computeCost(model, usage, _reportedCost, context) {
|
|
4077
4272
|
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
4078
4273
|
}
|
|
@@ -4345,12 +4540,12 @@ var OpenAIResponsesProvider = class {
|
|
|
4345
4540
|
// ---------------------------------------------------------------------------
|
|
4346
4541
|
buildRequestBody(messages, options, stream) {
|
|
4347
4542
|
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
4348
|
-
const
|
|
4349
|
-
const reasoningCapable =
|
|
4543
|
+
const descriptor = resolveOpenAIModelDescriptor(effectiveModel);
|
|
4544
|
+
const reasoningCapable = descriptor.reasoning;
|
|
4350
4545
|
const resolved = resolveThinkingOptions(options);
|
|
4351
4546
|
const { includeThoughts } = resolved;
|
|
4352
4547
|
const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
|
|
4353
|
-
const stripTemp =
|
|
4548
|
+
const stripTemp = stripsPortableSampling(descriptor, "responses", wireEffort);
|
|
4354
4549
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
4355
4550
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
4356
4551
|
const body = {
|
|
@@ -4399,6 +4594,7 @@ var OpenAIResponsesProvider = class {
|
|
|
4399
4594
|
if (options.providerOptions) {
|
|
4400
4595
|
Object.assign(body, options.providerOptions);
|
|
4401
4596
|
}
|
|
4597
|
+
validateOpenAIFinalRequestBody(body, "responses");
|
|
4402
4598
|
return body;
|
|
4403
4599
|
}
|
|
4404
4600
|
requestModel(body, fallback) {
|
|
@@ -4713,6 +4909,47 @@ function anthropicImageBlocks(parts, model) {
|
|
|
4713
4909
|
}
|
|
4714
4910
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
4715
4911
|
var ANTHROPIC_FILES_BETA = "files-api-2025-04-14";
|
|
4912
|
+
var ANTHROPIC_THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
|
|
4913
|
+
function isRecord(value) {
|
|
4914
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4915
|
+
}
|
|
4916
|
+
function isFinalThinkingAdaptive(capability, body) {
|
|
4917
|
+
const thinking = body.thinking;
|
|
4918
|
+
if (thinking === void 0) {
|
|
4919
|
+
return capability.thinking === "adaptive-default-on" || capability.thinking === "adaptive-always-on";
|
|
4920
|
+
}
|
|
4921
|
+
return isRecord(thinking) && thinking.type === "adaptive";
|
|
4922
|
+
}
|
|
4923
|
+
var NATIVE_CLAUDE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
4924
|
+
function isNativeClaudeEffort(value) {
|
|
4925
|
+
return NATIVE_CLAUDE_EFFORTS.includes(value);
|
|
4926
|
+
}
|
|
4927
|
+
function hasReplayedThinking(body) {
|
|
4928
|
+
return Array.isArray(body.messages) && body.messages.some(
|
|
4929
|
+
(message) => isRecord(message) && Array.isArray(message.content) && message.content.some(
|
|
4930
|
+
(block) => isRecord(block) && (block.type === "thinking" || block.type === "redacted_thinking")
|
|
4931
|
+
)
|
|
4932
|
+
);
|
|
4933
|
+
}
|
|
4934
|
+
function hasFinalFileImage(body) {
|
|
4935
|
+
return Array.isArray(body.messages) && body.messages.some(
|
|
4936
|
+
(message) => isRecord(message) && Array.isArray(message.content) && message.content.some(
|
|
4937
|
+
(block) => isRecord(block) && block.type === "image" && isRecord(block.source) && block.source.type === "file"
|
|
4938
|
+
)
|
|
4939
|
+
);
|
|
4940
|
+
}
|
|
4941
|
+
function normalizeThinkingDrops(value) {
|
|
4942
|
+
if (!Array.isArray(value)) return void 0;
|
|
4943
|
+
const reasons = {};
|
|
4944
|
+
let droppedBlocks = 0;
|
|
4945
|
+
for (const entry of value) {
|
|
4946
|
+
if (!isRecord(entry) || entry.type !== "thinking_dropped") continue;
|
|
4947
|
+
const reason = entry.reason === "prefix_binding_mismatch" || entry.reason === "model_binding_mismatch" || entry.reason === "organization_binding_mismatch" || entry.reason === "end_user_binding_mismatch" ? entry.reason : "other";
|
|
4948
|
+
reasons[reason] = (reasons[reason] ?? 0) + 1;
|
|
4949
|
+
droppedBlocks++;
|
|
4950
|
+
}
|
|
4951
|
+
return droppedBlocks > 0 ? { droppedBlocks, reasons } : void 0;
|
|
4952
|
+
}
|
|
4716
4953
|
function hasAnthropicProviderFileImage(messages) {
|
|
4717
4954
|
return messages.some(
|
|
4718
4955
|
(message) => Array.isArray(message.content) && message.content.some(
|
|
@@ -4721,11 +4958,51 @@ function hasAnthropicProviderFileImage(messages) {
|
|
|
4721
4958
|
);
|
|
4722
4959
|
}
|
|
4723
4960
|
var CLAUDE_CAPABILITIES = {
|
|
4961
|
+
// Thinking off is `between_tools` (progress updates between tool calls);
|
|
4962
|
+
// `disabled` is a provider 400. Forced tool choice is a 400.
|
|
4963
|
+
"claude-sonnet-5-5": {
|
|
4964
|
+
thinking: "adaptive-default-on",
|
|
4965
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
4966
|
+
manualBudget: false,
|
|
4967
|
+
stripTemperature: true,
|
|
4968
|
+
disableAt: "high",
|
|
4969
|
+
offShape: "between_tools",
|
|
4970
|
+
forcedToolChoice: "rejected",
|
|
4971
|
+
defaultOnlySampling: true,
|
|
4972
|
+
strictThinking: true,
|
|
4973
|
+
thinkingBinding: true
|
|
4974
|
+
},
|
|
4975
|
+
// Thinking off is `disabled`, only at effort high or below. Forced tool
|
|
4976
|
+
// choice is accepted (the provider skips thinking for that turn).
|
|
4977
|
+
"claude-haiku-5-5": {
|
|
4978
|
+
thinking: "adaptive-default-on",
|
|
4979
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
4980
|
+
manualBudget: false,
|
|
4981
|
+
stripTemperature: true,
|
|
4982
|
+
disableAt: "high",
|
|
4983
|
+
defaultOnlySampling: true,
|
|
4984
|
+
strictThinking: true,
|
|
4985
|
+
thinkingBinding: true
|
|
4986
|
+
},
|
|
4987
|
+
"claude-opus-5-5": {
|
|
4988
|
+
thinking: "adaptive-always-on",
|
|
4989
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
4990
|
+
manualBudget: false,
|
|
4991
|
+
stripTemperature: true,
|
|
4992
|
+
forcedToolChoice: "rejected",
|
|
4993
|
+
defaultOnlySampling: true,
|
|
4994
|
+
strictThinking: true,
|
|
4995
|
+
thinkingBinding: true
|
|
4996
|
+
},
|
|
4724
4997
|
"claude-fable-5-1": {
|
|
4725
4998
|
thinking: "adaptive-always-on",
|
|
4726
4999
|
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
4727
5000
|
manualBudget: false,
|
|
4728
|
-
stripTemperature: true
|
|
5001
|
+
stripTemperature: true,
|
|
5002
|
+
forcedToolChoice: "rejected",
|
|
5003
|
+
defaultOnlySampling: true,
|
|
5004
|
+
strictThinking: true,
|
|
5005
|
+
thinkingBinding: true
|
|
4729
5006
|
},
|
|
4730
5007
|
"claude-fable-5": {
|
|
4731
5008
|
thinking: "adaptive-always-on",
|
|
@@ -4819,6 +5096,16 @@ function resolveClaudeCapability(model) {
|
|
|
4819
5096
|
}
|
|
4820
5097
|
var DEFAULT_CACHE_READ_MULTIPLIER = 0.1;
|
|
4821
5098
|
var ANTHROPIC_RATES = {
|
|
5099
|
+
// Sonnet 5.5 cache reads are 0.05x ($0.10/MTok): the pricing table, its
|
|
5100
|
+
// footnote, the models overview, and the prompt-caching guide agree
|
|
5101
|
+
// (re-checked 2026-10-07).
|
|
5102
|
+
"claude-sonnet-5-5": { input: 2e-6, output: 1e-5, cacheReadMultiplier: 0.05 },
|
|
5103
|
+
"claude-haiku-5-5": {
|
|
5104
|
+
input: 1e-7,
|
|
5105
|
+
output: 5e-7,
|
|
5106
|
+
longPrompt: { aboveTokens: 1e5, card: { input: 5e-7, output: 25e-7 } }
|
|
5107
|
+
},
|
|
5108
|
+
"claude-opus-5-5": { input: 4e-6, output: 2e-5, cacheReadMultiplier: 0.05 },
|
|
4822
5109
|
"claude-fable-5-1": { input: 1e-5, output: 5e-5, cacheReadMultiplier: 0.025 },
|
|
4823
5110
|
"claude-fable-5": { input: 1e-5, output: 5e-5 },
|
|
4824
5111
|
// Limited availability (Glasswing). Priced from the public pricing table; no
|
|
@@ -4880,14 +5167,16 @@ function estimateAnthropicCost(model, usage) {
|
|
|
4880
5167
|
return void 0;
|
|
4881
5168
|
}
|
|
4882
5169
|
const cacheWriteTotal = safeTokenSum(cacheWrite5m, cacheWrite1h);
|
|
4883
|
-
|
|
5170
|
+
const promptTokens = cacheWriteTotal === void 0 ? void 0 : safeTokenSum(usage.inputTokens, cacheRead, cacheWriteTotal);
|
|
5171
|
+
if (cacheWriteTotal === void 0 || promptTokens === void 0) {
|
|
4884
5172
|
return void 0;
|
|
4885
5173
|
}
|
|
4886
5174
|
if (usage.aggregateCacheWriteTokens !== void 0 && (!isValidTokenCount(usage.aggregateCacheWriteTokens) || usage.aggregateCacheWriteTokens !== cacheWriteTotal)) {
|
|
4887
5175
|
return void 0;
|
|
4888
5176
|
}
|
|
4889
|
-
const
|
|
4890
|
-
const
|
|
5177
|
+
const card = rate.longPrompt && promptTokens > rate.longPrompt.aboveTokens ? rate.longPrompt.card : rate;
|
|
5178
|
+
const { input, output } = card;
|
|
5179
|
+
const cacheReadRate = input * (card.cacheReadMultiplier ?? DEFAULT_CACHE_READ_MULTIPLIER);
|
|
4891
5180
|
return usage.inputTokens * input + cacheRead * cacheReadRate + cacheWrite5m * input * 1.25 + cacheWrite1h * input * 2 + usage.outputTokens * output;
|
|
4892
5181
|
}
|
|
4893
5182
|
var THINKING_BUDGETS = {
|
|
@@ -4949,7 +5238,7 @@ function resolveClaudeThinking(model, resolved) {
|
|
|
4949
5238
|
};
|
|
4950
5239
|
}
|
|
4951
5240
|
return {
|
|
4952
|
-
thinking: { type: "disabled" },
|
|
5241
|
+
thinking: { type: capability.offShape ?? "disabled" },
|
|
4953
5242
|
outputConfig: activeEffort ? { effort: activeEffort } : void 0,
|
|
4954
5243
|
stripTemperature: true
|
|
4955
5244
|
};
|
|
@@ -5253,11 +5542,12 @@ var AnthropicProvider = class {
|
|
|
5253
5542
|
// chat - non-streaming completion
|
|
5254
5543
|
// ---------------------------------------------------------------------------
|
|
5255
5544
|
async chat(messages, options) {
|
|
5545
|
+
const body = this.buildRequestBody(messages, options, false);
|
|
5256
5546
|
const headers = this.buildHeaders(
|
|
5257
5547
|
await this.resolveKey(),
|
|
5258
|
-
hasAnthropicProviderFileImage(messages)
|
|
5548
|
+
hasAnthropicProviderFileImage(messages) || hasFinalFileImage(body),
|
|
5549
|
+
this.requiresBindingBeta(body)
|
|
5259
5550
|
);
|
|
5260
|
-
const body = this.buildRequestBody(messages, options, false);
|
|
5261
5551
|
const pricingContext = pricingContextFromBody(body);
|
|
5262
5552
|
const recorder = new CallTimingRecorder(options.requestLifecycle);
|
|
5263
5553
|
const governor = this.governorFor(pricingContext.model ?? options.model);
|
|
@@ -5295,11 +5585,12 @@ var AnthropicProvider = class {
|
|
|
5295
5585
|
// stream - SSE streaming completion
|
|
5296
5586
|
// ---------------------------------------------------------------------------
|
|
5297
5587
|
async *stream(messages, options) {
|
|
5588
|
+
const body = this.buildRequestBody(messages, options, true);
|
|
5298
5589
|
const headers = this.buildHeaders(
|
|
5299
5590
|
await this.resolveKey(),
|
|
5300
|
-
hasAnthropicProviderFileImage(messages)
|
|
5591
|
+
hasAnthropicProviderFileImage(messages) || hasFinalFileImage(body),
|
|
5592
|
+
this.requiresBindingBeta(body)
|
|
5301
5593
|
);
|
|
5302
|
-
const body = this.buildRequestBody(messages, options, true);
|
|
5303
5594
|
const pricingContext = pricingContextFromBody(body);
|
|
5304
5595
|
const recorder = new CallTimingRecorder(options.requestLifecycle);
|
|
5305
5596
|
const governor = this.governorFor(pricingContext.model ?? options.model);
|
|
@@ -5341,14 +5632,119 @@ var AnthropicProvider = class {
|
|
|
5341
5632
|
// ---------------------------------------------------------------------------
|
|
5342
5633
|
// Internal: request building
|
|
5343
5634
|
// ---------------------------------------------------------------------------
|
|
5344
|
-
buildHeaders(apiKey, requiresFilesBeta = false) {
|
|
5635
|
+
buildHeaders(apiKey, requiresFilesBeta = false, requiresBindingBeta = false) {
|
|
5636
|
+
const betas = [
|
|
5637
|
+
...requiresFilesBeta ? [ANTHROPIC_FILES_BETA] : [],
|
|
5638
|
+
...requiresBindingBeta ? [ANTHROPIC_THINKING_BINDING_BETA] : []
|
|
5639
|
+
];
|
|
5345
5640
|
return {
|
|
5346
5641
|
"Content-Type": "application/json",
|
|
5347
5642
|
"x-api-key": apiKey,
|
|
5348
5643
|
"anthropic-version": ANTHROPIC_API_VERSION,
|
|
5349
|
-
...
|
|
5644
|
+
...betas.length > 0 ? { "anthropic-beta": betas.join(",") } : {}
|
|
5350
5645
|
};
|
|
5351
5646
|
}
|
|
5647
|
+
/**
|
|
5648
|
+
* Whether the final body needs the binding beta header. Runs after
|
|
5649
|
+
* `validateAndBindFinalBody`, which has already injected `block_binding`
|
|
5650
|
+
* wherever it applies, so the header follows the final body rather than
|
|
5651
|
+
* re-deriving the injection rule.
|
|
5652
|
+
*/
|
|
5653
|
+
requiresBindingBeta(body) {
|
|
5654
|
+
const capability = resolveClaudeCapability(String(body.model));
|
|
5655
|
+
const thinking = body.thinking;
|
|
5656
|
+
return capability?.thinkingBinding === true && isRecord(thinking) && thinking.type === "adaptive" && thinking.block_binding !== void 0;
|
|
5657
|
+
}
|
|
5658
|
+
/**
|
|
5659
|
+
* Validate the final body (after `providerOptions`) against the effective
|
|
5660
|
+
* model's capability, then inject `block_binding` where it applies. Every
|
|
5661
|
+
* rule is gated on its own capability field and is a no-op when unset, so a
|
|
5662
|
+
* model only gets the guards its provider contract calls for.
|
|
5663
|
+
*/
|
|
5664
|
+
validateAndBindFinalBody(body) {
|
|
5665
|
+
const model = typeof body.model === "string" ? body.model : "";
|
|
5666
|
+
const capability = resolveClaudeCapability(model);
|
|
5667
|
+
if (!capability) return;
|
|
5668
|
+
const reject = (option, remediation) => {
|
|
5669
|
+
throw new UnsupportedModelOptionError({ provider: this.name, model, option, remediation });
|
|
5670
|
+
};
|
|
5671
|
+
if (capability.forcedToolChoice === "rejected") {
|
|
5672
|
+
const toolChoice = body.tool_choice;
|
|
5673
|
+
if (isRecord(toolChoice) && (toolChoice.type === "any" || toolChoice.type === "tool")) {
|
|
5674
|
+
reject("forced tool choice", "Use toolChoice: 'auto' or 'none' on this model.");
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
if (capability.strictThinking) {
|
|
5678
|
+
const offShape = capability.thinking === "adaptive-always-on" ? void 0 : capability.offShape ?? "disabled";
|
|
5679
|
+
const thinking = body.thinking;
|
|
5680
|
+
const outputConfig = body.output_config;
|
|
5681
|
+
const effort = isRecord(outputConfig) ? outputConfig.effort : void 0;
|
|
5682
|
+
if (thinking !== void 0 && offShape === void 0) {
|
|
5683
|
+
if (!isRecord(thinking) || thinking.type !== "adaptive" || thinking.budget_tokens !== void 0) {
|
|
5684
|
+
reject(
|
|
5685
|
+
"manual or disabled thinking",
|
|
5686
|
+
"Use thinking: { type: 'adaptive' } and output_config.effort."
|
|
5687
|
+
);
|
|
5688
|
+
}
|
|
5689
|
+
} else if (thinking !== void 0) {
|
|
5690
|
+
if (!isRecord(thinking) || thinking.type !== "adaptive" && thinking.type !== offShape) {
|
|
5691
|
+
reject(
|
|
5692
|
+
"thinking.type",
|
|
5693
|
+
`Use thinking: { type: 'adaptive' }, or { type: '${offShape}' } to turn thinking off.`
|
|
5694
|
+
);
|
|
5695
|
+
}
|
|
5696
|
+
const record = thinking;
|
|
5697
|
+
if (record.type === "adaptive" && record.budget_tokens !== void 0) {
|
|
5698
|
+
reject("thinking.budget_tokens", "Use output_config.effort instead of budget_tokens.");
|
|
5699
|
+
}
|
|
5700
|
+
if (record.type === offShape) {
|
|
5701
|
+
const extra = Object.keys(record).filter((key) => key !== "type");
|
|
5702
|
+
if (extra.length > 0) {
|
|
5703
|
+
reject(
|
|
5704
|
+
`thinking.${offShape}.${extra[0]}`,
|
|
5705
|
+
`Send thinking: { type: '${offShape}' } with no other fields.`
|
|
5706
|
+
);
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
if (outputConfig !== void 0 && (!isRecord(outputConfig) || effort !== void 0 && !isNativeClaudeEffort(effort))) {
|
|
5711
|
+
reject("output_config.effort", "Use low, medium, high, xhigh, or max effort.");
|
|
5712
|
+
}
|
|
5713
|
+
if (offShape !== void 0 && isRecord(thinking) && thinking.type === offShape && effort !== void 0 && capability.disableAt && !isEffortAtOrBelow(effort, capability.disableAt)) {
|
|
5714
|
+
reject(
|
|
5715
|
+
"output_config.effort",
|
|
5716
|
+
`Thinking can be turned off only at effort '${capability.disableAt}' or below; lower the effort or use adaptive thinking.`
|
|
5717
|
+
);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
if (capability.defaultOnlySampling) {
|
|
5721
|
+
if (body.temperature !== void 0 && body.temperature !== 1) {
|
|
5722
|
+
reject("temperature", "Omit temperature or use the default value 1.");
|
|
5723
|
+
}
|
|
5724
|
+
if (body.top_p !== void 0 && body.top_p !== 0.99) {
|
|
5725
|
+
reject("top_p", "Omit top_p or use the default value 0.99.");
|
|
5726
|
+
}
|
|
5727
|
+
if (body.temperature !== void 0 && body.top_p !== void 0) {
|
|
5728
|
+
reject("temperature and top_p", "Send at most one of temperature and top_p.");
|
|
5729
|
+
}
|
|
5730
|
+
if (body.top_k !== void 0) {
|
|
5731
|
+
reject("top_k", "Omit top_k on this model.");
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
if (capability.thinkingBinding === true && isFinalThinkingAdaptive(capability, body)) {
|
|
5735
|
+
const thinking = isRecord(body.thinking) ? body.thinking : { type: "adaptive" };
|
|
5736
|
+
const binding = thinking.block_binding;
|
|
5737
|
+
if (binding !== void 0 && (!isRecord(binding) || binding.prefix_mismatch_behavior !== "error" && binding.prefix_mismatch_behavior !== "drop_block")) {
|
|
5738
|
+
reject("thinking.block_binding", "Use prefix_mismatch_behavior: 'error' or 'drop_block'.");
|
|
5739
|
+
}
|
|
5740
|
+
if (hasReplayedThinking(body)) {
|
|
5741
|
+
body.thinking = {
|
|
5742
|
+
...thinking,
|
|
5743
|
+
block_binding: binding ?? { prefix_mismatch_behavior: "drop_block" }
|
|
5744
|
+
};
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5747
|
+
}
|
|
5352
5748
|
/** Extract a human-readable message from an API error response body. */
|
|
5353
5749
|
extractErrorMessage(body, status) {
|
|
5354
5750
|
try {
|
|
@@ -5425,6 +5821,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5425
5821
|
if (options.providerOptions) {
|
|
5426
5822
|
Object.assign(body, options.providerOptions);
|
|
5427
5823
|
}
|
|
5824
|
+
this.validateAndBindFinalBody(body);
|
|
5428
5825
|
return body;
|
|
5429
5826
|
}
|
|
5430
5827
|
/**
|
|
@@ -5581,6 +5978,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5581
5978
|
}
|
|
5582
5979
|
}
|
|
5583
5980
|
const normalized = json.usage ? normalizeAnthropicUsage(json.usage) : void 0;
|
|
5981
|
+
const reasoningContextReset = normalizeThinkingDrops(json.input_transformations);
|
|
5584
5982
|
const effectiveModel = typeof json.model === "string" ? json.model : pricingContext.model;
|
|
5585
5983
|
if (hasFallbackBoundary) {
|
|
5586
5984
|
throw new Error(
|
|
@@ -5595,7 +5993,8 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5595
5993
|
usage: normalized?.usage,
|
|
5596
5994
|
cost,
|
|
5597
5995
|
costProvenance: tableEstimate(cost),
|
|
5598
|
-
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
5996
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0,
|
|
5997
|
+
...reasoningContextReset ? { diagnostics: { reasoningContextReset } } : {}
|
|
5599
5998
|
};
|
|
5600
5999
|
}
|
|
5601
6000
|
// ---------------------------------------------------------------------------
|
|
@@ -5617,6 +6016,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5617
6016
|
let hasFallbackBoundary = false;
|
|
5618
6017
|
let hasFallbackIterationSignal = false;
|
|
5619
6018
|
let refused = false;
|
|
6019
|
+
let reasoningContextReset;
|
|
5620
6020
|
const finalizeUsage = () => {
|
|
5621
6021
|
const normalized = rawUsage ? normalizeAnthropicUsage(rawUsage) : void 0;
|
|
5622
6022
|
usage = normalized?.usage;
|
|
@@ -5629,7 +6029,8 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5629
6029
|
usage,
|
|
5630
6030
|
cost,
|
|
5631
6031
|
costProvenance: tableEstimate(cost),
|
|
5632
|
-
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
6032
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0,
|
|
6033
|
+
...reasoningContextReset ? { diagnostics: { reasoningContextReset } } : {}
|
|
5633
6034
|
};
|
|
5634
6035
|
};
|
|
5635
6036
|
try {
|
|
@@ -5722,6 +6123,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5722
6123
|
rawUsage = mergeAnthropicUsage(rawUsage, event.message.usage);
|
|
5723
6124
|
}
|
|
5724
6125
|
if (typeof event.message?.model === "string") effectiveModel = event.message.model;
|
|
6126
|
+
reasoningContextReset = normalizeThinkingDrops(event.message?.input_transformations);
|
|
5725
6127
|
const messageFallback = hasFallbackIteration(event.message?.usage?.iterations);
|
|
5726
6128
|
if (isAnthropicResponseModifier(
|
|
5727
6129
|
"inference_geo",
|
|
@@ -5738,6 +6140,10 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5738
6140
|
break;
|
|
5739
6141
|
}
|
|
5740
6142
|
case "message_delta": {
|
|
6143
|
+
const finalTransformations = event.input_transformations ?? event.delta?.input_transformations;
|
|
6144
|
+
if (finalTransformations !== void 0) {
|
|
6145
|
+
reasoningContextReset = normalizeThinkingDrops(finalTransformations);
|
|
6146
|
+
}
|
|
5741
6147
|
if (event.usage) {
|
|
5742
6148
|
rawUsage = mergeAnthropicUsage(rawUsage, event.usage);
|
|
5743
6149
|
finalizeUsage();
|
|
@@ -5966,7 +6372,31 @@ var GEMINI_PRICING = {
|
|
|
5966
6372
|
},
|
|
5967
6373
|
"gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
|
|
5968
6374
|
};
|
|
6375
|
+
var GEMINI_FLASH_SUCCESSOR_PRICING = {
|
|
6376
|
+
...GEMINI_PRICING,
|
|
6377
|
+
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 },
|
|
6378
|
+
"gemini-3.7-flash": {
|
|
6379
|
+
input: 15e-7,
|
|
6380
|
+
cached: 15e-8,
|
|
6381
|
+
output: 75e-7,
|
|
6382
|
+
audioInput: 15e-7
|
|
6383
|
+
},
|
|
6384
|
+
"gemini-3.8-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
6385
|
+
};
|
|
6386
|
+
var GEMINI_FLASH_SUCCESSOR_START_MS = Date.UTC(2027, 0, 1);
|
|
6387
|
+
function geminiRatesAtDispatch(at) {
|
|
6388
|
+
return at < GEMINI_FLASH_SUCCESSOR_START_MS ? GEMINI_PRICING : GEMINI_FLASH_SUCCESSOR_PRICING;
|
|
6389
|
+
}
|
|
5969
6390
|
var CANONICAL_GEMINI_BASE_URL = GEMINI_DEFAULT_BASE_URL;
|
|
6391
|
+
function geminiPricingTimingObserver(context, recorder) {
|
|
6392
|
+
return {
|
|
6393
|
+
...recorder.observer,
|
|
6394
|
+
onDispatch: (attempt, at) => {
|
|
6395
|
+
context.rates = geminiRatesAtDispatch(at);
|
|
6396
|
+
recorder.observer.onDispatch?.(attempt, at);
|
|
6397
|
+
}
|
|
6398
|
+
};
|
|
6399
|
+
}
|
|
5970
6400
|
function isValidTokenCount2(value) {
|
|
5971
6401
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
5972
6402
|
}
|
|
@@ -6047,8 +6477,8 @@ function geminiInteractionErrorStatus(code) {
|
|
|
6047
6477
|
function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
|
|
6048
6478
|
return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
|
|
6049
6479
|
}
|
|
6050
|
-
function estimateGeminiCost(model, usage) {
|
|
6051
|
-
const pricing =
|
|
6480
|
+
function estimateGeminiCost(model, usage, rates) {
|
|
6481
|
+
const pricing = rates && Object.hasOwn(rates, model) ? rates[model] : void 0;
|
|
6052
6482
|
const cached = usage.cachedTokens ?? 0;
|
|
6053
6483
|
if (!pricing || !isValidTokenCount2(usage.inputTokens) || !isValidTokenCount2(usage.outputTokens) || !isValidTokenCount2(cached) || cached > usage.inputTokens) {
|
|
6054
6484
|
return void 0;
|
|
@@ -6376,7 +6806,7 @@ var GeminiProvider = class {
|
|
|
6376
6806
|
{
|
|
6377
6807
|
governor: this.governorFor(pricingContext.model),
|
|
6378
6808
|
provider: this.name,
|
|
6379
|
-
timing: recorder
|
|
6809
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6380
6810
|
admission: options.dispatchAdmission
|
|
6381
6811
|
}
|
|
6382
6812
|
);
|
|
@@ -6419,7 +6849,7 @@ var GeminiProvider = class {
|
|
|
6419
6849
|
{
|
|
6420
6850
|
governor: this.governorFor(pricingContext.model),
|
|
6421
6851
|
provider: this.name,
|
|
6422
|
-
timing: recorder
|
|
6852
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6423
6853
|
admission: options.dispatchAdmission
|
|
6424
6854
|
}
|
|
6425
6855
|
);
|
|
@@ -6457,7 +6887,7 @@ var GeminiProvider = class {
|
|
|
6457
6887
|
{
|
|
6458
6888
|
governor: this.governorFor(pricingContext.model),
|
|
6459
6889
|
provider: this.name,
|
|
6460
|
-
timing: recorder
|
|
6890
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6461
6891
|
admission: options.dispatchAdmission
|
|
6462
6892
|
}
|
|
6463
6893
|
);
|
|
@@ -6493,7 +6923,7 @@ var GeminiProvider = class {
|
|
|
6493
6923
|
{
|
|
6494
6924
|
governor: this.governorFor(pricingContext.model),
|
|
6495
6925
|
provider: this.name,
|
|
6496
|
-
timing: recorder
|
|
6926
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6497
6927
|
admission: options.dispatchAdmission
|
|
6498
6928
|
}
|
|
6499
6929
|
);
|
|
@@ -6712,7 +7142,7 @@ var GeminiProvider = class {
|
|
|
6712
7142
|
)) {
|
|
6713
7143
|
return void 0;
|
|
6714
7144
|
}
|
|
6715
|
-
return estimateGeminiCost(effectiveModel, normalized.pricingUsage);
|
|
7145
|
+
return estimateGeminiCost(effectiveModel, normalized.pricingUsage, pricingContext.rates);
|
|
6716
7146
|
}
|
|
6717
7147
|
/**
|
|
6718
7148
|
* Whether every model-produced step is text. A non-text reply part (image,
|
|
@@ -7297,7 +7727,7 @@ var GeminiProvider = class {
|
|
|
7297
7727
|
pricingContext,
|
|
7298
7728
|
isDefinitiveStandardGeminiResponseTier(json.usageMetadata?.serviceTier),
|
|
7299
7729
|
json.usageMetadata?.serviceTier !== void 0 && !isDefinitiveStandardGeminiResponseTier(json.usageMetadata.serviceTier)
|
|
7300
|
-
) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
7730
|
+
) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage, pricingContext.rates) : void 0;
|
|
7301
7731
|
const rawParts = candidate?.content?.parts;
|
|
7302
7732
|
const providerMetadata = rawParts ? { geminiParts: rawParts } : void 0;
|
|
7303
7733
|
return {
|
|
@@ -7388,7 +7818,7 @@ var GeminiProvider = class {
|
|
|
7388
7818
|
pricingContext,
|
|
7389
7819
|
hasDefinitiveStandardResponseTier,
|
|
7390
7820
|
hasInvalidResponseTier
|
|
7391
|
-
) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage) : void 0;
|
|
7821
|
+
) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage, pricingContext.rates) : void 0;
|
|
7392
7822
|
yield {
|
|
7393
7823
|
type: "done",
|
|
7394
7824
|
usage: normalizedUsage?.usage,
|
|
@@ -9301,6 +9731,95 @@ function sessionHistoryForAsk(history, normalizedInput) {
|
|
|
9301
9731
|
return snapshot;
|
|
9302
9732
|
}
|
|
9303
9733
|
|
|
9734
|
+
// src/compaction.ts
|
|
9735
|
+
import { createHash } from "crypto";
|
|
9736
|
+
var SUMMARY_SYSTEM_PROMPT = "Summarize the following conversation concisely, preserving key facts, decisions, and context needed for continuing the conversation.";
|
|
9737
|
+
var SUMMARY_MAX_TOKENS = 1024;
|
|
9738
|
+
function buildSummaryPrompt(messages, previousSummary) {
|
|
9739
|
+
return [
|
|
9740
|
+
...previousSummary ? [`Previous conversation summary: ${previousSummary}`] : [],
|
|
9741
|
+
...messages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`)
|
|
9742
|
+
].join("\n");
|
|
9743
|
+
}
|
|
9744
|
+
function requestSummary(provider, options) {
|
|
9745
|
+
return provider.chat(
|
|
9746
|
+
[
|
|
9747
|
+
{ role: "system", content: SUMMARY_SYSTEM_PROMPT },
|
|
9748
|
+
{ role: "user", content: options.prompt }
|
|
9749
|
+
],
|
|
9750
|
+
{
|
|
9751
|
+
model: options.model,
|
|
9752
|
+
maxTokens: SUMMARY_MAX_TOKENS,
|
|
9753
|
+
...options.signal ? { signal: options.signal } : {}
|
|
9754
|
+
}
|
|
9755
|
+
);
|
|
9756
|
+
}
|
|
9757
|
+
function summaryContextMessage(summary) {
|
|
9758
|
+
return {
|
|
9759
|
+
role: "system",
|
|
9760
|
+
origin: "runtime",
|
|
9761
|
+
content: `Summary of earlier conversation:
|
|
9762
|
+
${summary}`
|
|
9763
|
+
};
|
|
9764
|
+
}
|
|
9765
|
+
function withoutAnthropicThinking(message) {
|
|
9766
|
+
const metadata = message.providerMetadata;
|
|
9767
|
+
if (!metadata || !Object.hasOwn(metadata, "anthropicThinkingBlocks")) return message;
|
|
9768
|
+
const retainedMetadata = { ...metadata };
|
|
9769
|
+
delete retainedMetadata.anthropicThinkingBlocks;
|
|
9770
|
+
return {
|
|
9771
|
+
...message,
|
|
9772
|
+
...Object.keys(retainedMetadata).length > 0 ? { providerMetadata: retainedMetadata } : { providerMetadata: void 0 }
|
|
9773
|
+
};
|
|
9774
|
+
}
|
|
9775
|
+
function anthropicThinkingBlockCount(message) {
|
|
9776
|
+
const blocks = message.providerMetadata?.anthropicThinkingBlocks;
|
|
9777
|
+
if (!Array.isArray(blocks)) return 0;
|
|
9778
|
+
return blocks.filter(
|
|
9779
|
+
(block) => block !== null && typeof block === "object" && (block.type === "thinking" || block.type === "redacted_thinking")
|
|
9780
|
+
).length;
|
|
9781
|
+
}
|
|
9782
|
+
function askSummaryMetaKey(agentName) {
|
|
9783
|
+
return `askSummary:${agentName}`;
|
|
9784
|
+
}
|
|
9785
|
+
function isCount(value) {
|
|
9786
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
9787
|
+
}
|
|
9788
|
+
function parseAskSummaryRecord(value) {
|
|
9789
|
+
if (!value || typeof value !== "object") return void 0;
|
|
9790
|
+
const record = value;
|
|
9791
|
+
if (typeof record.summary !== "string" || !isCount(record.coveredCount) || typeof record.prefixHash !== "string" || typeof record.summaryModelUri !== "string" || !isCount(record.invalidatedThinkingThrough) || record.sourceSummary !== void 0 && typeof record.sourceSummary !== "string") {
|
|
9792
|
+
return void 0;
|
|
9793
|
+
}
|
|
9794
|
+
return {
|
|
9795
|
+
summary: record.summary,
|
|
9796
|
+
coveredCount: record.coveredCount,
|
|
9797
|
+
prefixHash: record.prefixHash,
|
|
9798
|
+
...record.sourceSummary !== void 0 ? { sourceSummary: record.sourceSummary } : {},
|
|
9799
|
+
summaryModelUri: record.summaryModelUri,
|
|
9800
|
+
invalidatedThinkingThrough: record.invalidatedThinkingThrough
|
|
9801
|
+
};
|
|
9802
|
+
}
|
|
9803
|
+
function isPlainObject(value) {
|
|
9804
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9805
|
+
const proto = Object.getPrototypeOf(value);
|
|
9806
|
+
return proto === Object.prototype || proto === null;
|
|
9807
|
+
}
|
|
9808
|
+
function summaryPrefixHash(history, coveredCount) {
|
|
9809
|
+
let canonical;
|
|
9810
|
+
try {
|
|
9811
|
+
canonical = JSON.stringify(
|
|
9812
|
+
history.slice(0, coveredCount),
|
|
9813
|
+
(_key, value) => isPlainObject(value) ? Object.fromEntries(
|
|
9814
|
+
Object.keys(value).sort().map((key) => [key, value[key]])
|
|
9815
|
+
) : value
|
|
9816
|
+
);
|
|
9817
|
+
} catch {
|
|
9818
|
+
return void 0;
|
|
9819
|
+
}
|
|
9820
|
+
return createHash("sha256").update(canonical).digest("hex");
|
|
9821
|
+
}
|
|
9822
|
+
|
|
9304
9823
|
// src/partial-json.ts
|
|
9305
9824
|
var MAX_DEPTH = 256;
|
|
9306
9825
|
function parsePartialJson(input) {
|
|
@@ -10937,9 +11456,9 @@ var LEGACY_REDACTION_RULES = {
|
|
|
10937
11456
|
// which is user content or PII (field paths are schema-shape, preserved like
|
|
10938
11457
|
// `string_delta.path`). Passthrough, as an explicit reviewed decision.
|
|
10939
11458
|
schema_diagnostic: passthrough,
|
|
10940
|
-
// `provider_diagnostic` carries only provider
|
|
10941
|
-
//
|
|
10942
|
-
//
|
|
11459
|
+
// `provider_diagnostic` carries only provider/model metadata, effort levels,
|
|
11460
|
+
// and bounded dropped-thinking reason counts. Raw transformation paths and
|
|
11461
|
+
// signed thinking never enter this event. Passthrough is reviewed here.
|
|
10943
11462
|
provider_diagnostic: passthrough,
|
|
10944
11463
|
log: (e) => {
|
|
10945
11464
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
@@ -12944,7 +13463,14 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
12944
13463
|
* suppresses late branch events after bounded finalization. */
|
|
12945
13464
|
workflowLifecycleState;
|
|
12946
13465
|
signal;
|
|
12947
|
-
|
|
13466
|
+
/** Durable summary of messages removed by Session.history.maxMessages. */
|
|
13467
|
+
sessionSummary;
|
|
13468
|
+
/** Per-agent `maxContext` summaries, keyed by agent name. `null` records a
|
|
13469
|
+
* store lookup that found nothing. Mirrored to session metadata
|
|
13470
|
+
* (`askSummary:<agent>`) when this execution belongs to a session, so a
|
|
13471
|
+
* later execution can reuse the same exact-prefix boundary. Never the
|
|
13472
|
+
* durable `sessionSummary`. */
|
|
13473
|
+
askSummaries = /* @__PURE__ */ new Map();
|
|
12948
13474
|
/** Consecutive provider calls with promptCache on that wrote to the cache
|
|
12949
13475
|
* without reading from it (prefix changes per call), and consecutive calls
|
|
12950
13476
|
* with no cache activity at all (prefix below the model's minimum, which
|
|
@@ -13120,7 +13646,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
13120
13646
|
endEmitted: false
|
|
13121
13647
|
};
|
|
13122
13648
|
if (init.metadata?.summaryCache) {
|
|
13123
|
-
this.
|
|
13649
|
+
this.sessionSummary = init.metadata.summaryCache;
|
|
13124
13650
|
}
|
|
13125
13651
|
}
|
|
13126
13652
|
/**
|
|
@@ -13745,6 +14271,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
13745
14271
|
if (systemPrompt) {
|
|
13746
14272
|
messages.push({ role: "system", content: systemPrompt });
|
|
13747
14273
|
}
|
|
14274
|
+
const cachedSummaryMessage = this.sessionSummary ? summaryContextMessage(this.sessionSummary) : void 0;
|
|
13748
14275
|
const maxContext = agent2._config.maxContext;
|
|
13749
14276
|
if (maxContext && sessionHistory.length > 0) {
|
|
13750
14277
|
const reserveTokens = this.config.contextManagement?.reserveTokens ?? 2e3;
|
|
@@ -13769,22 +14296,44 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
13769
14296
|
}
|
|
13770
14297
|
});
|
|
13771
14298
|
}
|
|
13772
|
-
|
|
13773
|
-
|
|
14299
|
+
const summaryTokens = cachedSummaryMessage ? estimateMessagesTokens([cachedSummaryMessage]).tokens : 0;
|
|
14300
|
+
if (historyEstimate.tokens + summaryTokens > availableForHistory) {
|
|
14301
|
+
const projection = await this.summarizeHistory(
|
|
14302
|
+
agent2._name,
|
|
14303
|
+
modelUri,
|
|
13774
14304
|
provider,
|
|
13775
14305
|
model,
|
|
13776
14306
|
sessionHistory,
|
|
13777
14307
|
availableForHistory
|
|
13778
14308
|
);
|
|
13779
|
-
|
|
14309
|
+
if (this.budgetContext?.exceeded && this.budgetContext.policy !== "warn") {
|
|
14310
|
+
const { limit, totalCost: spent, policy } = this.budgetContext;
|
|
14311
|
+
throw new BudgetExceededError(limit, spent, policy);
|
|
14312
|
+
}
|
|
14313
|
+
if (projection.removedThinkingBlocks > 0) {
|
|
14314
|
+
this.emitEvent({
|
|
14315
|
+
type: "provider_diagnostic",
|
|
14316
|
+
agent: agent2._name,
|
|
14317
|
+
data: {
|
|
14318
|
+
kind: "reasoning_context_reset",
|
|
14319
|
+
...provider.name ? { provider: provider.name } : {},
|
|
14320
|
+
model: typeof providerOptions?.model === "string" ? providerOptions.model : model,
|
|
14321
|
+
droppedBlocks: projection.removedThinkingBlocks,
|
|
14322
|
+
reasons: { client_prefix_rewrite: projection.removedThinkingBlocks }
|
|
14323
|
+
}
|
|
14324
|
+
});
|
|
14325
|
+
}
|
|
14326
|
+
for (const msg of projection.messages) {
|
|
13780
14327
|
messages.push(msg);
|
|
13781
14328
|
}
|
|
13782
14329
|
} else {
|
|
14330
|
+
if (cachedSummaryMessage) messages.push(cachedSummaryMessage);
|
|
13783
14331
|
for (const msg of sessionHistory) {
|
|
13784
14332
|
messages.push(msg);
|
|
13785
14333
|
}
|
|
13786
14334
|
}
|
|
13787
14335
|
} else {
|
|
14336
|
+
if (cachedSummaryMessage) messages.push(cachedSummaryMessage);
|
|
13788
14337
|
for (const msg of sessionHistory) {
|
|
13789
14338
|
messages.push(msg);
|
|
13790
14339
|
}
|
|
@@ -13885,6 +14434,7 @@ ${appendedSchemaText}`;
|
|
|
13885
14434
|
}
|
|
13886
14435
|
const startTime = Date.now();
|
|
13887
14436
|
const askClock = currentAskClock();
|
|
14437
|
+
const pausedAtStartMs = askClock ? pausedAskClockMs(askClock, startTime) : 0;
|
|
13888
14438
|
const timingTotals = { turns: 0, queuedMs: 0, retryMs: 0, wireMs: 0 };
|
|
13889
14439
|
const currentMessages = [...messages];
|
|
13890
14440
|
let turns = 0;
|
|
@@ -13902,7 +14452,7 @@ ${appendedSchemaText}`;
|
|
|
13902
14452
|
this.currentSignal?.throwIfAborted();
|
|
13903
14453
|
const now = Date.now();
|
|
13904
14454
|
const elapsedMs = now - startTime;
|
|
13905
|
-
const pausedMs = askClock ? pausedAskClockMs(askClock, now) : 0;
|
|
14455
|
+
const pausedMs = askClock ? Math.max(0, pausedAskClockMs(askClock, now) - pausedAtStartMs) : 0;
|
|
13906
14456
|
const chargedMs = Math.max(0, elapsedMs - Math.min(pausedMs, Math.max(0, elapsedMs)));
|
|
13907
14457
|
if (chargedMs > timeoutMs) {
|
|
13908
14458
|
throw new TimeoutError(
|
|
@@ -14157,6 +14707,7 @@ ${appendedSchemaText}`;
|
|
|
14157
14707
|
usage: chunk.usage,
|
|
14158
14708
|
cost: chunk.cost,
|
|
14159
14709
|
providerMetadata: chunk.providerMetadata,
|
|
14710
|
+
diagnostics: chunk.diagnostics,
|
|
14160
14711
|
timing: chunk.timing
|
|
14161
14712
|
};
|
|
14162
14713
|
}
|
|
@@ -14274,6 +14825,34 @@ ${appendedSchemaText}`;
|
|
|
14274
14825
|
timingTotals.retryMs += response.timing.retryMs;
|
|
14275
14826
|
timingTotals.wireMs += response.timing.wireMs;
|
|
14276
14827
|
}
|
|
14828
|
+
const reasoningReset = response.diagnostics?.reasoningContextReset;
|
|
14829
|
+
if (reasoningReset && Number.isSafeInteger(reasoningReset.droppedBlocks) && reasoningReset.droppedBlocks > 0) {
|
|
14830
|
+
const reasons = {};
|
|
14831
|
+
for (const reason of [
|
|
14832
|
+
"prefix_binding_mismatch",
|
|
14833
|
+
"model_binding_mismatch",
|
|
14834
|
+
"organization_binding_mismatch",
|
|
14835
|
+
"end_user_binding_mismatch",
|
|
14836
|
+
"client_prefix_rewrite",
|
|
14837
|
+
"other"
|
|
14838
|
+
]) {
|
|
14839
|
+
const count = reasoningReset.reasons?.[reason];
|
|
14840
|
+
if (typeof count === "number" && Number.isSafeInteger(count) && count > 0) {
|
|
14841
|
+
reasons[reason] = count;
|
|
14842
|
+
}
|
|
14843
|
+
}
|
|
14844
|
+
this.emitEvent({
|
|
14845
|
+
type: "provider_diagnostic",
|
|
14846
|
+
agent: agent2._name,
|
|
14847
|
+
data: {
|
|
14848
|
+
kind: "reasoning_context_reset",
|
|
14849
|
+
...provider.name ? { provider: provider.name } : {},
|
|
14850
|
+
model: typeof providerOptions?.model === "string" ? providerOptions.model : model,
|
|
14851
|
+
droppedBlocks: reasoningReset.droppedBlocks,
|
|
14852
|
+
reasons
|
|
14853
|
+
}
|
|
14854
|
+
});
|
|
14855
|
+
}
|
|
14277
14856
|
this.emitEvent({
|
|
14278
14857
|
type: "agent_call_end",
|
|
14279
14858
|
agent: agent2._name,
|
|
@@ -15016,31 +15595,65 @@ ${appendedSchemaText}`;
|
|
|
15016
15595
|
}
|
|
15017
15596
|
return defs;
|
|
15018
15597
|
}
|
|
15598
|
+
/** Session id for session-scoped ask-summary persistence, when this
|
|
15599
|
+
* execution belongs to a session and has a store to persist into. */
|
|
15600
|
+
askSummarySessionId() {
|
|
15601
|
+
const sessionId = this.metadata?.sessionId;
|
|
15602
|
+
if (this.metadata?.sessionPersist === false) return void 0;
|
|
15603
|
+
return this.stateStore && typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
|
|
15604
|
+
}
|
|
15605
|
+
/** This agent's ask summary: the in-memory copy first, else the session's
|
|
15606
|
+
* persisted record (read once per execution). */
|
|
15607
|
+
async loadAskSummary(agentName) {
|
|
15608
|
+
if (this.askSummaries.has(agentName)) return this.askSummaries.get(agentName) ?? void 0;
|
|
15609
|
+
const sessionId = this.askSummarySessionId();
|
|
15610
|
+
const stored = sessionId ? parseAskSummaryRecord(
|
|
15611
|
+
await this.stateStore.getSessionMeta(sessionId, askSummaryMetaKey(agentName))
|
|
15612
|
+
) : void 0;
|
|
15613
|
+
this.askSummaries.set(agentName, stored ?? null);
|
|
15614
|
+
return stored;
|
|
15615
|
+
}
|
|
15616
|
+
async saveAskSummary(agentName, record) {
|
|
15617
|
+
this.askSummaries.set(agentName, record);
|
|
15618
|
+
const sessionId = this.askSummarySessionId();
|
|
15619
|
+
if (!sessionId) return;
|
|
15620
|
+
const key = askSummaryMetaKey(agentName);
|
|
15621
|
+
try {
|
|
15622
|
+
await this.stateStore.saveSessionMeta(sessionId, key, record);
|
|
15623
|
+
} catch (error) {
|
|
15624
|
+
this.emitEvent({
|
|
15625
|
+
type: "log",
|
|
15626
|
+
agent: agentName,
|
|
15627
|
+
data: {
|
|
15628
|
+
warning: `Could not persist ask summary '${key}' for session '${sessionId}'; later executions will regenerate it. ` + (error instanceof Error ? error.message : String(error))
|
|
15629
|
+
}
|
|
15630
|
+
});
|
|
15631
|
+
}
|
|
15632
|
+
}
|
|
15019
15633
|
/**
|
|
15020
|
-
*
|
|
15021
|
-
*
|
|
15634
|
+
* Project `history` into `availableTokens` for one agent's request: a summary
|
|
15635
|
+
* of an exact covered prefix followed by every later message. Reuses the
|
|
15636
|
+
* agent's cached boundary (in memory or persisted for the session) while the
|
|
15637
|
+
* covered prefix, durable session summary, and summary model are unchanged
|
|
15638
|
+
* and the whole tail fits; otherwise generates a new summary. Also reports how
|
|
15639
|
+
* many Anthropic thinking blocks the projection removed from carried turns.
|
|
15022
15640
|
*/
|
|
15023
|
-
async summarizeHistory(provider, model, history, availableTokens) {
|
|
15024
|
-
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
splitIdx2 = i;
|
|
15040
|
-
}
|
|
15041
|
-
splitIdx2 = nextUserTurn(history, splitIdx2) ?? history.length;
|
|
15042
|
-
if (splitIdx2 < history.length) {
|
|
15043
|
-
return [summaryMsg2, ...history.slice(splitIdx2)];
|
|
15641
|
+
async summarizeHistory(agentName, modelUri, provider, model, history, availableTokens) {
|
|
15642
|
+
const summaryModelUri = this.config.contextManagement?.summaryModel ?? modelUri;
|
|
15643
|
+
const cache = await this.loadAskSummary(agentName);
|
|
15644
|
+
if (cache && cache.sourceSummary === this.sessionSummary && cache.summaryModelUri === summaryModelUri && cache.coveredCount <= history.length && cache.prefixHash === summaryPrefixHash(history, cache.coveredCount)) {
|
|
15645
|
+
const summaryMsg = summaryContextMessage(cache.summary);
|
|
15646
|
+
const summaryTokens = estimateTokens(summarizeModelInput(summaryMsg.content)) + 4;
|
|
15647
|
+
const tail2 = history.slice(cache.coveredCount);
|
|
15648
|
+
const tailTokens = estimateMessagesTokens(tail2).tokens;
|
|
15649
|
+
if (summaryTokens + tailTokens <= availableTokens && (tail2.length === 0 || tail2[0].role === "user")) {
|
|
15650
|
+
let removedThinkingBlocks = 0;
|
|
15651
|
+
const projected = tail2.map((message, index) => {
|
|
15652
|
+
if (cache.coveredCount + index >= cache.invalidatedThinkingThrough) return message;
|
|
15653
|
+
removedThinkingBlocks += anthropicThinkingBlockCount(message);
|
|
15654
|
+
return withoutAnthropicThinking(message);
|
|
15655
|
+
});
|
|
15656
|
+
return { messages: [summaryMsg, ...projected], removedThinkingBlocks };
|
|
15044
15657
|
}
|
|
15045
15658
|
}
|
|
15046
15659
|
let recentTokens = 0;
|
|
@@ -15065,42 +15678,103 @@ ${this.summaryCache}`
|
|
|
15065
15678
|
} else {
|
|
15066
15679
|
splitIdx = history.length;
|
|
15067
15680
|
}
|
|
15068
|
-
if (splitIdx === 0)
|
|
15681
|
+
if (splitIdx === 0 && this.sessionSummary) splitIdx = history.length;
|
|
15682
|
+
if (splitIdx === 0) return { messages: history, removedThinkingBlocks: 0 };
|
|
15069
15683
|
const oldMessages = history.slice(0, splitIdx);
|
|
15070
|
-
const
|
|
15684
|
+
const configuredSummaryModelUri = this.config.contextManagement?.summaryModel;
|
|
15071
15685
|
let summaryProvider;
|
|
15072
15686
|
let summaryModel;
|
|
15073
|
-
if (
|
|
15074
|
-
const resolved = this.resolveProviderUri(
|
|
15687
|
+
if (configuredSummaryModelUri) {
|
|
15688
|
+
const resolved = this.resolveProviderUri(configuredSummaryModelUri);
|
|
15075
15689
|
summaryProvider = resolved.provider;
|
|
15076
15690
|
summaryModel = resolved.model;
|
|
15077
15691
|
} else {
|
|
15078
15692
|
summaryProvider = provider;
|
|
15079
15693
|
summaryModel = model;
|
|
15080
15694
|
}
|
|
15081
|
-
const
|
|
15082
|
-
const
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15695
|
+
const summaryPrompt = buildSummaryPrompt(oldMessages, this.sessionSummary);
|
|
15696
|
+
const summaryStart = Date.now();
|
|
15697
|
+
this.emitEvent({
|
|
15698
|
+
type: "agent_call_start",
|
|
15699
|
+
agent: agentName,
|
|
15700
|
+
model: summaryModelUri,
|
|
15701
|
+
turn: 0,
|
|
15702
|
+
data: {
|
|
15703
|
+
purpose: "summary",
|
|
15704
|
+
prompt: summaryPrompt,
|
|
15705
|
+
system: SUMMARY_SYSTEM_PROMPT,
|
|
15706
|
+
params: { maxTokens: SUMMARY_MAX_TOKENS },
|
|
15707
|
+
turn: 0
|
|
15708
|
+
}
|
|
15709
|
+
});
|
|
15710
|
+
let summaryResponse;
|
|
15711
|
+
try {
|
|
15712
|
+
this.currentSignal?.throwIfAborted();
|
|
15713
|
+
summaryResponse = await requestSummary(summaryProvider, {
|
|
15714
|
+
model: summaryModel,
|
|
15715
|
+
prompt: summaryPrompt,
|
|
15716
|
+
signal: this.currentSignal
|
|
15717
|
+
});
|
|
15718
|
+
} catch (error) {
|
|
15719
|
+
const providerError = error instanceof ProviderError ? error : void 0;
|
|
15720
|
+
this.emitEvent({
|
|
15721
|
+
type: "agent_call_end",
|
|
15722
|
+
agent: agentName,
|
|
15723
|
+
model: summaryModelUri,
|
|
15724
|
+
duration: Date.now() - summaryStart,
|
|
15725
|
+
...providerError?.timing ? { timing: providerError.timing } : {},
|
|
15726
|
+
data: {
|
|
15727
|
+
purpose: "summary",
|
|
15728
|
+
response: "",
|
|
15729
|
+
turn: 0,
|
|
15730
|
+
// Provider errors may echo the summarized history, so the event
|
|
15731
|
+
// uses a fixed projection even when trace redaction is disabled.
|
|
15732
|
+
error: "Context-management summary call failed",
|
|
15733
|
+
...providerError ? { status: providerError.status, retryable: providerError.retryable } : {}
|
|
15734
|
+
}
|
|
15735
|
+
});
|
|
15736
|
+
throw error;
|
|
15737
|
+
}
|
|
15738
|
+
if (isUsableCost2(summaryResponse.cost)) this._accumulateBudgetCost(summaryResponse.cost);
|
|
15739
|
+
this.emitEvent({
|
|
15740
|
+
type: "agent_call_end",
|
|
15741
|
+
agent: agentName,
|
|
15742
|
+
model: summaryModelUri,
|
|
15743
|
+
cost: summaryResponse.cost,
|
|
15744
|
+
...!isUsableCost2(summaryResponse.cost) ? { unpriced: true } : {},
|
|
15745
|
+
tokens: summaryResponse.usage ? {
|
|
15746
|
+
input: summaryResponse.usage.prompt_tokens,
|
|
15747
|
+
output: summaryResponse.usage.completion_tokens,
|
|
15748
|
+
reasoning: summaryResponse.usage.reasoning_tokens,
|
|
15749
|
+
cached: summaryResponse.usage.cached_tokens,
|
|
15750
|
+
cacheWrite: summaryResponse.usage.cache_write_tokens
|
|
15751
|
+
} : void 0,
|
|
15752
|
+
duration: Date.now() - summaryStart,
|
|
15753
|
+
...summaryResponse.timing ? { timing: summaryResponse.timing } : {},
|
|
15754
|
+
data: { purpose: "summary", response: summaryResponse.content, turn: 0 }
|
|
15755
|
+
});
|
|
15756
|
+
const prefixHash = summaryPrefixHash(history, splitIdx);
|
|
15757
|
+
if (prefixHash) {
|
|
15758
|
+
await this.saveAskSummary(agentName, {
|
|
15759
|
+
summary: summaryResponse.content,
|
|
15760
|
+
coveredCount: splitIdx,
|
|
15761
|
+
prefixHash,
|
|
15762
|
+
...this.sessionSummary !== void 0 ? { sourceSummary: this.sessionSummary } : {},
|
|
15763
|
+
summaryModelUri,
|
|
15764
|
+
invalidatedThinkingThrough: history.length
|
|
15765
|
+
});
|
|
15766
|
+
}
|
|
15767
|
+
const tail = history.slice(splitIdx);
|
|
15768
|
+
return {
|
|
15769
|
+
messages: [
|
|
15770
|
+
summaryContextMessage(summaryResponse.content),
|
|
15771
|
+
...tail.map(withoutAnthropicThinking)
|
|
15089
15772
|
],
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
if (sessionId && this.stateStore) {
|
|
15095
|
-
await this.stateStore.saveSessionMeta(sessionId, "summaryCache", this.summaryCache);
|
|
15096
|
-
}
|
|
15097
|
-
const summaryMsg = {
|
|
15098
|
-
role: "system",
|
|
15099
|
-
origin: "runtime",
|
|
15100
|
-
content: `Summary of earlier conversation:
|
|
15101
|
-
${summaryResponse.content}`
|
|
15773
|
+
removedThinkingBlocks: tail.reduce(
|
|
15774
|
+
(count, message) => count + anthropicThinkingBlockCount(message),
|
|
15775
|
+
0
|
|
15776
|
+
)
|
|
15102
15777
|
};
|
|
15103
|
-
return [summaryMsg, ...history.slice(splitIdx)];
|
|
15104
15778
|
}
|
|
15105
15779
|
// ── ctx.checkpoint() ────────────────────────────────────────────────
|
|
15106
15780
|
/**
|
|
@@ -17509,17 +18183,17 @@ var Session = class _Session {
|
|
|
17509
18183
|
throw new Error("SessionOptions.history.summaryModel is required when summarize is true");
|
|
17510
18184
|
}
|
|
17511
18185
|
const messagesToDrop = history.slice(0, history.length - maxMessages);
|
|
17512
|
-
const
|
|
17513
|
-
|
|
17514
|
-
|
|
17515
|
-
|
|
17516
|
-
|
|
18186
|
+
const summary = await this.runtime.summarizeMessages(
|
|
18187
|
+
messagesToDrop,
|
|
18188
|
+
summaryModel,
|
|
18189
|
+
cachedSummary ? { previousSummary: cachedSummary } : void 0
|
|
18190
|
+
);
|
|
17517
18191
|
await this.store.saveSessionMeta(this.sessionId, "summaryCache", summary);
|
|
17518
18192
|
cachedSummary = summary;
|
|
17519
18193
|
}
|
|
17520
18194
|
const trimmed = history.slice(-maxMessages);
|
|
17521
18195
|
history.length = 0;
|
|
17522
|
-
history.push(...trimmed);
|
|
18196
|
+
history.push(...trimmed.map(withoutAnthropicThinking));
|
|
17523
18197
|
}
|
|
17524
18198
|
const userMessage = { role: "user", content: preparedInput.content };
|
|
17525
18199
|
history.push(userMessage);
|
|
@@ -17529,7 +18203,10 @@ var Session = class _Session {
|
|
|
17529
18203
|
const metadata = {
|
|
17530
18204
|
sessionId: this.sessionId,
|
|
17531
18205
|
sessionHistory: history,
|
|
17532
|
-
...cachedSummary ? { summaryCache: cachedSummary } : {}
|
|
18206
|
+
...cachedSummary ? { summaryCache: cachedSummary } : {},
|
|
18207
|
+
// An opted-out session must leave nothing at rest, including the
|
|
18208
|
+
// agent-scoped ask-summary records the runtime would otherwise cache.
|
|
18209
|
+
...this.options.persist === false ? { sessionPersist: false } : {}
|
|
17533
18210
|
};
|
|
17534
18211
|
return { history, metadata };
|
|
17535
18212
|
}
|
|
@@ -20611,22 +21288,17 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
20611
21288
|
/**
|
|
20612
21289
|
* Summarize a list of chat messages into a concise summary string.
|
|
20613
21290
|
* Used by Session to summarize dropped messages when history.summarize is enabled.
|
|
21291
|
+
* `previousSummary` is folded in so a rolling summary never loses what it
|
|
21292
|
+
* alone still carries. Shares its prompt and provider call with the
|
|
21293
|
+
* `AgentConfig.maxContext` summarizer (see `compaction.ts`). It runs outside
|
|
21294
|
+
* any workflow execution, so it emits no ask-scoped events.
|
|
20614
21295
|
*/
|
|
20615
|
-
async summarizeMessages(messages, modelUri) {
|
|
21296
|
+
async summarizeMessages(messages, modelUri, options) {
|
|
20616
21297
|
const { provider, model } = this.resolveProvider(modelUri);
|
|
20617
|
-
const response = await provider
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20621
|
-
content: "Summarize the following conversation concisely, preserving key facts, decisions, and context needed for continuing the conversation."
|
|
20622
|
-
},
|
|
20623
|
-
{
|
|
20624
|
-
role: "user",
|
|
20625
|
-
content: messages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`).join("\n")
|
|
20626
|
-
}
|
|
20627
|
-
],
|
|
20628
|
-
{ model, maxTokens: 1024 }
|
|
20629
|
-
);
|
|
21298
|
+
const response = await requestSummary(provider, {
|
|
21299
|
+
model,
|
|
21300
|
+
prompt: buildSummaryPrompt(messages, options?.previousSummary)
|
|
21301
|
+
});
|
|
20630
21302
|
return response.content;
|
|
20631
21303
|
}
|
|
20632
21304
|
/** Get the state store (for testing and advanced use cases). */
|
|
@@ -22056,6 +22728,7 @@ export {
|
|
|
22056
22728
|
TranscriptionProviderRegistry,
|
|
22057
22729
|
UnsupportedEventSchemaVersionError,
|
|
22058
22730
|
UnsupportedModelInputError,
|
|
22731
|
+
UnsupportedModelOptionError,
|
|
22059
22732
|
UnsupportedTranscriptionInputError,
|
|
22060
22733
|
VLLM_PROFILE,
|
|
22061
22734
|
ValidationError,
|