@warlock.js/ai-anthropic 5.30.0 → 5.32.0
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/CHANGELOG.md +58 -6
- package/cjs/index.cjs +847 -138
- package/cjs/index.cjs.map +1 -1
- package/esm/config.type.d.mts +117 -8
- package/esm/config.type.d.mts.map +1 -1
- package/esm/index.d.mts +9 -3
- package/esm/index.mjs +7 -1
- package/esm/known-output-limits.mjs +46 -0
- package/esm/known-output-limits.mjs.map +1 -0
- package/esm/known-pricing.d.mts +63 -0
- package/esm/known-pricing.d.mts.map +1 -0
- package/esm/known-pricing.mjs +172 -0
- package/esm/known-pricing.mjs.map +1 -0
- package/esm/model.d.mts +90 -20
- package/esm/model.d.mts.map +1 -1
- package/esm/model.mjs +389 -88
- package/esm/model.mjs.map +1 -1
- package/esm/request.type.d.mts +23 -0
- package/esm/request.type.d.mts.map +1 -0
- package/esm/sdk.d.mts +1 -3
- package/esm/sdk.d.mts.map +1 -1
- package/esm/thinking-mode.d.mts +14 -2
- package/esm/thinking-mode.d.mts.map +1 -1
- package/esm/thinking-mode.mjs +102 -2
- package/esm/thinking-mode.mjs.map +1 -1
- package/esm/utils/cache-usage.mjs +62 -0
- package/esm/utils/cache-usage.mjs.map +1 -0
- package/esm/utils/index.d.mts +6 -0
- package/esm/utils/index.mjs +1 -1
- package/esm/utils/map-stop-reason.d.mts +1 -0
- package/esm/utils/provider-blocks.d.mts +37 -0
- package/esm/utils/provider-blocks.d.mts.map +1 -0
- package/esm/utils/provider-blocks.mjs +3 -3
- package/esm/utils/provider-blocks.mjs.map +1 -1
- package/esm/utils/server-tool-usage.mjs +17 -0
- package/esm/utils/server-tool-usage.mjs.map +1 -0
- package/esm/utils/to-anthropic-messages.d.mts +69 -0
- package/esm/utils/to-anthropic-messages.d.mts.map +1 -0
- package/esm/utils/to-anthropic-messages.mjs +12 -4
- package/esm/utils/to-anthropic-messages.mjs.map +1 -1
- package/esm/utils/to-anthropic-tools.d.mts +17 -0
- package/esm/utils/to-anthropic-tools.d.mts.map +1 -0
- package/esm/utils/wrap-anthropic-error.d.mts +1 -0
- package/esm/utils/wrap-anthropic-error.mjs +6 -1
- package/esm/utils/wrap-anthropic-error.mjs.map +1 -1
- package/llms-full.txt +248 -32
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/README.md +1 -1
- package/skills/setup-anthropic/SKILL.md +248 -32
package/cjs/index.cjs
CHANGED
|
@@ -20,7 +20,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
20
20
|
}
|
|
21
21
|
return to;
|
|
22
22
|
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
24
24
|
value: mod,
|
|
25
25
|
enumerable: true
|
|
26
26
|
}) : target, mod));
|
|
@@ -31,59 +31,174 @@ _anthropic_ai_sdk = __toESM(_anthropic_ai_sdk, 1);
|
|
|
31
31
|
let _warlock_js_ai = require("@warlock.js/ai");
|
|
32
32
|
let _warlock_js_logger = require("@warlock.js/logger");
|
|
33
33
|
|
|
34
|
-
//#region ../../packages/ai-anthropic/src/known-
|
|
34
|
+
//#region ../../packages/ai-anthropic/src/known-pricing.ts
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Anthropic's prompt-cache write multipliers, relative to the base input
|
|
37
|
+
* rate: a 5-minute write costs 1.25x, a 1-hour write 2x. They hold for
|
|
38
|
+
* every Claude model on Anthropic's price list.
|
|
38
39
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
* Source: https://platform.claude.com/docs/en/about-claude/pricing
|
|
41
|
+
* (fetched 2026-10-17).
|
|
42
|
+
*/
|
|
43
|
+
const ANTHROPIC_CACHE_WRITE_MULTIPLIER = 1.25;
|
|
44
|
+
const ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER = 2;
|
|
45
|
+
/**
|
|
46
|
+
* Built-in Claude API list prices, USD per 1,000,000 tokens, keyed by
|
|
47
|
+
* model id. `cachedInput` is the cache-hit rate, `cacheWrite` the
|
|
48
|
+
* 5-minute write rate and `cacheWrite1h` the 1-hour write rate.
|
|
45
49
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* high-resolution vision; Claude Mythos 5.1 has "same capabilities" as
|
|
50
|
-
* Fable 5.1. The tier-5 prefixes cover every 5.x release
|
|
51
|
-
* (`claude-opus-5`, `claude-opus-5-5`, `claude-sonnet-5-5`,
|
|
52
|
-
* `claude-fable-5-1`, …). Opus/Sonnet 4.6–4.8 are already covered by the
|
|
53
|
-
* tier-4 prefixes.
|
|
50
|
+
* Source: https://platform.claude.com/docs/en/about-claude/pricing,
|
|
51
|
+
* "Model pricing" table, fetched 2026-10-17. Standard (global) routing,
|
|
52
|
+
* non-batch. Not covered, so pass `pricing` yourself when you need them:
|
|
54
53
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
54
|
+
* - Claude Haiku 5.5 — priced by prompt length (higher rates above
|
|
55
|
+
* 100,000 prompt tokens), which a flat `ModelPricing` cannot express.
|
|
56
|
+
* - US-only inference (`inference_geo: "us"`, 1.1x), fast mode, the Batch
|
|
57
|
+
* API, and Claude on Bedrock / Vertex AI (partner-priced).
|
|
58
|
+
* - Limited-availability models (Mythos) and retired models.
|
|
59
|
+
*
|
|
60
|
+
* Dated snapshot ids (`claude-haiku-4-5-20251001`) resolve to their alias
|
|
61
|
+
* entry through {@link anthropicPricingFor}.
|
|
59
62
|
*/
|
|
60
|
-
const
|
|
61
|
-
"claude-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"claude-fable-5"
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
const ANTHROPIC_MODEL_PRICING = {
|
|
64
|
+
"claude-fable-5-1": {
|
|
65
|
+
input: 10,
|
|
66
|
+
output: 50,
|
|
67
|
+
cachedInput: .25,
|
|
68
|
+
cacheWrite: 12.5,
|
|
69
|
+
cacheWrite1h: 20
|
|
70
|
+
},
|
|
71
|
+
"claude-fable-5": {
|
|
72
|
+
input: 10,
|
|
73
|
+
output: 50,
|
|
74
|
+
cachedInput: 1,
|
|
75
|
+
cacheWrite: 12.5,
|
|
76
|
+
cacheWrite1h: 20
|
|
77
|
+
},
|
|
78
|
+
"claude-opus-5-5": {
|
|
79
|
+
input: 4,
|
|
80
|
+
output: 20,
|
|
81
|
+
cachedInput: .2,
|
|
82
|
+
cacheWrite: 5,
|
|
83
|
+
cacheWrite1h: 8
|
|
84
|
+
},
|
|
85
|
+
"claude-opus-5": {
|
|
86
|
+
input: 5,
|
|
87
|
+
output: 25,
|
|
88
|
+
cachedInput: .5,
|
|
89
|
+
cacheWrite: 6.25,
|
|
90
|
+
cacheWrite1h: 10
|
|
91
|
+
},
|
|
92
|
+
"claude-opus-4-8": {
|
|
93
|
+
input: 5,
|
|
94
|
+
output: 25,
|
|
95
|
+
cachedInput: .5,
|
|
96
|
+
cacheWrite: 6.25,
|
|
97
|
+
cacheWrite1h: 10
|
|
98
|
+
},
|
|
99
|
+
"claude-opus-4-7": {
|
|
100
|
+
input: 5,
|
|
101
|
+
output: 25,
|
|
102
|
+
cachedInput: .5,
|
|
103
|
+
cacheWrite: 6.25,
|
|
104
|
+
cacheWrite1h: 10
|
|
105
|
+
},
|
|
106
|
+
"claude-opus-4-6": {
|
|
107
|
+
input: 5,
|
|
108
|
+
output: 25,
|
|
109
|
+
cachedInput: .5,
|
|
110
|
+
cacheWrite: 6.25,
|
|
111
|
+
cacheWrite1h: 10
|
|
112
|
+
},
|
|
113
|
+
"claude-opus-4-5": {
|
|
114
|
+
input: 5,
|
|
115
|
+
output: 25,
|
|
116
|
+
cachedInput: .5,
|
|
117
|
+
cacheWrite: 6.25,
|
|
118
|
+
cacheWrite1h: 10
|
|
119
|
+
},
|
|
120
|
+
"claude-sonnet-5-5": {
|
|
121
|
+
input: 2,
|
|
122
|
+
output: 10,
|
|
123
|
+
cachedInput: .1,
|
|
124
|
+
cacheWrite: 2.5,
|
|
125
|
+
cacheWrite1h: 4
|
|
126
|
+
},
|
|
127
|
+
"claude-sonnet-5": {
|
|
128
|
+
input: 2,
|
|
129
|
+
output: 10,
|
|
130
|
+
cachedInput: .2,
|
|
131
|
+
cacheWrite: 2.5,
|
|
132
|
+
cacheWrite1h: 4
|
|
133
|
+
},
|
|
134
|
+
"claude-sonnet-4-6": {
|
|
135
|
+
input: 3,
|
|
136
|
+
output: 15,
|
|
137
|
+
cachedInput: .3,
|
|
138
|
+
cacheWrite: 3.75,
|
|
139
|
+
cacheWrite1h: 6
|
|
140
|
+
},
|
|
141
|
+
"claude-sonnet-4-5": {
|
|
142
|
+
input: 3,
|
|
143
|
+
output: 15,
|
|
144
|
+
cachedInput: .3,
|
|
145
|
+
cacheWrite: 3.75,
|
|
146
|
+
cacheWrite1h: 6
|
|
147
|
+
},
|
|
148
|
+
"claude-haiku-4-5": {
|
|
149
|
+
input: 1,
|
|
150
|
+
output: 5,
|
|
151
|
+
cachedInput: .1,
|
|
152
|
+
cacheWrite: 1.25,
|
|
153
|
+
cacheWrite1h: 2
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
/** A trailing `-YYYYMMDD` snapshot date on a Claude model id. */
|
|
157
|
+
const SNAPSHOT_SUFFIX = /-\d{8}$/;
|
|
71
158
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
159
|
+
* Look up the built-in list price for a Claude model id. Accepts the
|
|
160
|
+
* alias (`claude-sonnet-4-5`) or a dated snapshot
|
|
161
|
+
* (`claude-sonnet-4-5-20250929`). Returns `undefined` for any id not in
|
|
162
|
+
* {@link ANTHROPIC_MODEL_PRICING}, so cost stays unknown rather than wrong.
|
|
76
163
|
*
|
|
77
164
|
* @example
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* inferVisionCapability("claude-2.1"); // → false
|
|
81
|
-
* inferVisionCapability("custom-proxy-llm"); // → false
|
|
165
|
+
* anthropicPricingFor("claude-haiku-4-5-20251001"); // { input: 1, output: 5, … }
|
|
166
|
+
* anthropicPricingFor("claude-haiku-5-5"); // undefined (prompt-length tiers)
|
|
82
167
|
*/
|
|
83
|
-
function
|
|
84
|
-
const
|
|
85
|
-
|
|
168
|
+
function anthropicPricingFor(modelName) {
|
|
169
|
+
const exact = ANTHROPIC_MODEL_PRICING[modelName];
|
|
170
|
+
if (exact) return exact;
|
|
171
|
+
return ANTHROPIC_MODEL_PRICING[modelName.replace(SNAPSHOT_SUFFIX, "")];
|
|
86
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Fill the cache-write rates Anthropic derives from the input rate
|
|
175
|
+
* (5-minute writes 1.25x, 1-hour writes 2x) into a pricing entry that
|
|
176
|
+
* omits them, so a caller who supplies only `{ input, output }` is no
|
|
177
|
+
* longer billed nothing for cache writes. Explicit rates always win; the
|
|
178
|
+
* cache-read rate is left alone because it differs per model.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* withAnthropicCacheWriteRates({ input: 3, output: 15 });
|
|
182
|
+
* // { input: 3, output: 15, cacheWrite: 3.75, cacheWrite1h: 6 }
|
|
183
|
+
*/
|
|
184
|
+
function withAnthropicCacheWriteRates(pricing) {
|
|
185
|
+
if (!pricing) return;
|
|
186
|
+
const cacheWrite = pricing.cacheWrite ?? pricing.cachedOutput ?? pricing.input * 1.25;
|
|
187
|
+
const cacheWrite1h = pricing.cacheWrite1h ?? pricing.input * 2;
|
|
188
|
+
if (pricing.cacheWrite === cacheWrite && pricing.cacheWrite1h === cacheWrite1h) return pricing;
|
|
189
|
+
return {
|
|
190
|
+
...pricing,
|
|
191
|
+
cacheWrite,
|
|
192
|
+
cacheWrite1h
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/** USD per request, verified 2026-10-18. Tokens are additional.
|
|
196
|
+
* https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool#usage-and-pricing
|
|
197
|
+
* https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool#usage-and-pricing */
|
|
198
|
+
const ANTHROPIC_SERVER_TOOL_PRICING = {
|
|
199
|
+
webSearch: .01,
|
|
200
|
+
webFetch: 0
|
|
201
|
+
};
|
|
87
202
|
|
|
88
203
|
//#endregion
|
|
89
204
|
//#region ../../packages/ai-anthropic/src/thinking-mode.ts
|
|
@@ -121,6 +236,11 @@ const BUDGET_RELEASE_ROOTS = [
|
|
|
121
236
|
* Dates (`-20250514`) and tags (`-latest`, `-v1`) are not minor versions.
|
|
122
237
|
*/
|
|
123
238
|
const NEXT_MINOR_VERSION = /^-\d{1,2}(?!\d)/;
|
|
239
|
+
/**
|
|
240
|
+
* Whether a lower-cased model id is the release `root` itself, or a dated /
|
|
241
|
+
* tagged / `@` snapshot of it — but not a later minor version of the same
|
|
242
|
+
* line (`claude-opus-4` matches `claude-opus-4-20250514`, not `claude-opus-4-7`).
|
|
243
|
+
*/
|
|
124
244
|
function matchesReleaseRoot(name, root) {
|
|
125
245
|
if (!name.startsWith(root)) return false;
|
|
126
246
|
const rest = name.slice(root.length);
|
|
@@ -185,9 +305,276 @@ const BETWEEN_TOOLS_RELEASE_ROOTS = ["claude-sonnet-5-5"];
|
|
|
185
305
|
* acceptsBetweenTools("claude-opus-5-5"); // → false
|
|
186
306
|
*/
|
|
187
307
|
function acceptsBetweenTools(modelName) {
|
|
308
|
+
return matchesAnyRelease(modelName, BETWEEN_TOOLS_RELEASE_ROOTS);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Whether the model id — lower-cased, with any platform prefix
|
|
312
|
+
* (`anthropic.`, `us.anthropic.` on Bedrock) removed — is one of these
|
|
313
|
+
* releases, dated or tagged, but not a later minor release.
|
|
314
|
+
*/
|
|
315
|
+
function matchesAnyRelease(modelName, roots) {
|
|
316
|
+
const normalized = modelName.toLowerCase();
|
|
317
|
+
const id = normalized.slice(normalized.lastIndexOf(".") + 1);
|
|
318
|
+
return roots.some((root) => matchesReleaseRoot(id, root));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Releases that accept `thinking: { type: "disabled" }` and run reliably
|
|
322
|
+
* with it. Source: claude-api ref, "Thinking & Effort" per-model table
|
|
323
|
+
* (cached 2026-10-06): Claude Sonnet 5 accepts `disabled`; Claude Haiku 5.5
|
|
324
|
+
* accepts it at effort `low`, `medium` and `high` (a 400 at `xhigh` /
|
|
325
|
+
* `max`). No effort is sent alongside it, so Haiku 5.5 runs at its default,
|
|
326
|
+
* `medium`, which is inside the accepted range.
|
|
327
|
+
*/
|
|
328
|
+
const DISABLED_THINKING_RELEASE_ROOTS = ["claude-sonnet-5", "claude-haiku-5-5"];
|
|
329
|
+
/**
|
|
330
|
+
* Releases that accept `disabled` but are kept on adaptive thinking at the
|
|
331
|
+
* lowest effort instead. Claude Opus 5 accepts `disabled` at effort `high`
|
|
332
|
+
* or below, but the claude-api ref ("Two failure modes when thinking is
|
|
333
|
+
* disabled", cached 2026-10-06) documents that with thinking disabled it
|
|
334
|
+
* occasionally writes a tool call into its visible text instead of a
|
|
335
|
+
* `tool_use` block — the call never runs and no error is raised — and can
|
|
336
|
+
* leak `<thinking>` tags into the response. The ref's primary
|
|
337
|
+
* recommendation is to keep thinking on and lower `effort`, so the adapter
|
|
338
|
+
* does exactly that.
|
|
339
|
+
*/
|
|
340
|
+
const LOWEST_EFFORT_RELEASE_ROOTS = ["claude-opus-5"];
|
|
341
|
+
/**
|
|
342
|
+
* The wire form for `reasoning: { effort: "none" }` on this model:
|
|
343
|
+
*
|
|
344
|
+
* | Model | Form |
|
|
345
|
+
* |----------------------------------------|-------------------|
|
|
346
|
+
* | Claude Sonnet 5.5 | `between_tools` |
|
|
347
|
+
* | Claude Sonnet 5, Claude Haiku 5.5 | `disabled` |
|
|
348
|
+
* | Claude Opus 5 | `lowest-effort` |
|
|
349
|
+
* | Opus 5.5, Fable 5/5.1, everything else | `omit` |
|
|
350
|
+
*
|
|
351
|
+
* Opus 5.5 and Fable 5/5.1 have no thinking-off form (`disabled` and
|
|
352
|
+
* `budget_tokens` are a 400 at every effort), so nothing is sent and the
|
|
353
|
+
* model runs at its default effort; older models run without thinking when
|
|
354
|
+
* the field is absent.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* noReasoningForm("claude-sonnet-5"); // → "disabled"
|
|
358
|
+
* noReasoningForm("claude-opus-5"); // → "lowest-effort"
|
|
359
|
+
* noReasoningForm("claude-opus-5-5"); // → "omit"
|
|
360
|
+
*/
|
|
361
|
+
function noReasoningForm(modelName) {
|
|
362
|
+
if (acceptsBetweenTools(modelName)) return "between_tools";
|
|
363
|
+
if (matchesAnyRelease(modelName, DISABLED_THINKING_RELEASE_ROOTS)) return "disabled";
|
|
364
|
+
if (matchesAnyRelease(modelName, LOWEST_EFFORT_RELEASE_ROOTS)) return "lowest-effort";
|
|
365
|
+
return "omit";
|
|
366
|
+
}
|
|
367
|
+
const PREFIX_CHECK_RELEASE_ROOTS = [
|
|
368
|
+
"claude-fable-5-1",
|
|
369
|
+
"claude-opus-5-5",
|
|
370
|
+
"claude-sonnet-5-5",
|
|
371
|
+
"claude-haiku-5-5"
|
|
372
|
+
];
|
|
373
|
+
/** Releases documented not to run the conversation check. */
|
|
374
|
+
const NO_PREFIX_CHECK_RELEASE_ROOTS = [
|
|
375
|
+
"claude-mythos-5-1",
|
|
376
|
+
"claude-mythos-5",
|
|
377
|
+
"claude-mythos-preview",
|
|
378
|
+
"claude-fable-5",
|
|
379
|
+
"claude-opus-5",
|
|
380
|
+
"claude-sonnet-5"
|
|
381
|
+
];
|
|
382
|
+
/** Families released before Claude Fable 5.1 in their entirety. */
|
|
383
|
+
const NO_PREFIX_CHECK_FAMILY_PREFIXES = [
|
|
384
|
+
...BUDGET_FAMILY_PREFIXES,
|
|
385
|
+
"claude-opus-4",
|
|
386
|
+
"claude-sonnet-4"
|
|
387
|
+
];
|
|
388
|
+
/**
|
|
389
|
+
* Whether this model binds a replayed thinking block to the exact
|
|
390
|
+
* conversation before it (preserved thinking). Unknown and future ids are
|
|
391
|
+
* assumed to run the check — the safe direction: reasoning a kept turn
|
|
392
|
+
* can't use is left out instead of risking a 400. Matches the id with or
|
|
393
|
+
* without a platform prefix and with dated or tagged suffixes.
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* inferPreservedThinking("claude-opus-5-5"); // → true
|
|
397
|
+
* inferPreservedThinking("claude-opus-5"); // → false
|
|
398
|
+
* inferPreservedThinking("claude-opus-4-8"); // → false
|
|
399
|
+
*/
|
|
400
|
+
function inferPreservedThinking(modelName) {
|
|
188
401
|
const normalized = modelName.toLowerCase();
|
|
189
402
|
const id = normalized.slice(normalized.lastIndexOf(".") + 1);
|
|
190
|
-
|
|
403
|
+
if (PREFIX_CHECK_RELEASE_ROOTS.some((root) => matchesReleaseRoot(id, root))) return true;
|
|
404
|
+
if (NO_PREFIX_CHECK_RELEASE_ROOTS.some((root) => matchesReleaseRoot(id, root)) || NO_PREFIX_CHECK_FAMILY_PREFIXES.some((prefix) => id.startsWith(prefix))) return false;
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region ../../packages/ai-anthropic/src/known-output-limits.ts
|
|
410
|
+
/** Family prefixes whose every release has a 128K output limit. */
|
|
411
|
+
const FAMILY_128K_PREFIXES = [
|
|
412
|
+
"claude-fable-5",
|
|
413
|
+
"claude-mythos-5",
|
|
414
|
+
"claude-opus-5",
|
|
415
|
+
"claude-sonnet-5",
|
|
416
|
+
"claude-haiku-5"
|
|
417
|
+
];
|
|
418
|
+
/** Individual releases, matched as a release root (dated / `@` variants included). */
|
|
419
|
+
const RELEASE_OUTPUT_LIMITS = [
|
|
420
|
+
["claude-opus-4-8", 128e3],
|
|
421
|
+
["claude-opus-4-7", 128e3],
|
|
422
|
+
["claude-opus-4-6", 128e3],
|
|
423
|
+
["claude-sonnet-4-6", 128e3],
|
|
424
|
+
["claude-opus-4-5", 64e3],
|
|
425
|
+
["claude-sonnet-4-5", 64e3],
|
|
426
|
+
["claude-haiku-4-5", 64e3]
|
|
427
|
+
];
|
|
428
|
+
/**
|
|
429
|
+
* The documented maximum output tokens of a Claude model, or `undefined`
|
|
430
|
+
* when the id is not a known Claude model. Platform prefixes before
|
|
431
|
+
* `claude-` (`anthropic.`, `us.anthropic.`) and dated or `@` snapshot
|
|
432
|
+
* suffixes are accepted, so the Bedrock / Google Cloud ids of a known
|
|
433
|
+
* model resolve too. Override per model with
|
|
434
|
+
* `anthropic.model({ name, maxOutputTokens })`.
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* inferMaxOutputTokens("claude-opus-5-5"); // → 128000
|
|
438
|
+
* inferMaxOutputTokens("claude-haiku-4-5-20251001"); // → 64000
|
|
439
|
+
* inferMaxOutputTokens("my-gateway/model"); // → undefined
|
|
440
|
+
*/
|
|
441
|
+
function inferMaxOutputTokens(modelName) {
|
|
442
|
+
const lowered = modelName.toLowerCase();
|
|
443
|
+
const start = lowered.indexOf("claude-");
|
|
444
|
+
if (start === -1) return;
|
|
445
|
+
const normalized = lowered.slice(start);
|
|
446
|
+
if (FAMILY_128K_PREFIXES.some((prefix) => normalized.startsWith(prefix))) return 128e3;
|
|
447
|
+
for (const [root, limit] of RELEASE_OUTPUT_LIMITS) if (matchesReleaseRoot(normalized, root)) return limit;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region ../../packages/ai-anthropic/src/known-vision-models.ts
|
|
452
|
+
/**
|
|
453
|
+
* Model-name prefixes for Claude families that accept image input
|
|
454
|
+
* (vision) on the Messages API.
|
|
455
|
+
*
|
|
456
|
+
* Every Claude 3, Claude 3.5/3.7, Claude 4 and Claude 5 family model is
|
|
457
|
+
* multimodal, so the list covers both the dotted legacy naming
|
|
458
|
+
* (`claude-3-haiku-...`, `claude-3-5-sonnet-...`) and the current
|
|
459
|
+
* `claude-<tier>-4-*` naming (`claude-opus-4-7`, `claude-sonnet-4-6`,
|
|
460
|
+
* `claude-haiku-4-5`). Pre-3 families (`claude-2`, `claude-instant`)
|
|
461
|
+
* are text-only and intentionally absent.
|
|
462
|
+
*
|
|
463
|
+
* The Claude 5 series is multimodal too (claude-api ref, cached
|
|
464
|
+
* 2026-09-25): its vision examples use `claude-opus-5-5`; the models list
|
|
465
|
+
* credits Claude Fable 5.1 with vision and Claude Sonnet 5 with
|
|
466
|
+
* high-resolution vision; Claude Mythos 5.1 has "same capabilities" as
|
|
467
|
+
* Fable 5.1. The tier-5 prefixes cover every 5.x release
|
|
468
|
+
* (`claude-opus-5`, `claude-opus-5-5`, `claude-sonnet-5-5`,
|
|
469
|
+
* `claude-fable-5-1`, …). Opus/Sonnet 4.6–4.8 are already covered by the
|
|
470
|
+
* tier-4 prefixes.
|
|
471
|
+
*
|
|
472
|
+
* Matched as a prefix so dated variants (`claude-opus-4-20250514`) are
|
|
473
|
+
* covered without listing every release tag. Devs can always override
|
|
474
|
+
* per-model via `anthropic.model({ name, vision: true | false })` —
|
|
475
|
+
* explicit config wins over inference in either direction.
|
|
476
|
+
*/
|
|
477
|
+
const VISION_CAPABLE_PREFIXES = [
|
|
478
|
+
"claude-3",
|
|
479
|
+
"claude-4",
|
|
480
|
+
"claude-opus-4",
|
|
481
|
+
"claude-sonnet-4",
|
|
482
|
+
"claude-haiku-4",
|
|
483
|
+
"claude-opus-5",
|
|
484
|
+
"claude-sonnet-5",
|
|
485
|
+
"claude-fable-5",
|
|
486
|
+
"claude-mythos-5"
|
|
487
|
+
];
|
|
488
|
+
/**
|
|
489
|
+
* Infer whether a given Claude model name supports vision based on the
|
|
490
|
+
* known-prefix list. Unknown models default to `false` so that passing
|
|
491
|
+
* an image attachment to an unsupported model surfaces a clear,
|
|
492
|
+
* agent-side capability error instead of an opaque Anthropic 400.
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* inferVisionCapability("claude-sonnet-4-6"); // → true
|
|
496
|
+
* inferVisionCapability("claude-3-5-sonnet-latest"); // → true
|
|
497
|
+
* inferVisionCapability("claude-2.1"); // → false
|
|
498
|
+
* inferVisionCapability("custom-proxy-llm"); // → false
|
|
499
|
+
*/
|
|
500
|
+
function inferVisionCapability(modelName) {
|
|
501
|
+
const normalized = modelName.toLowerCase();
|
|
502
|
+
return VISION_CAPABLE_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region ../../packages/ai-anthropic/src/utils/server-tool-usage.ts
|
|
507
|
+
/** Installed @anthropic-ai/sdk 0.129.0 ServerToolUsage and MessageDeltaUsage use the same cumulative counters.
|
|
508
|
+
* https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool
|
|
509
|
+
* https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool
|
|
510
|
+
* No code-execution request counter is reported: never count content blocks instead. */
|
|
511
|
+
function applyServerToolUsage(usage, raw) {
|
|
512
|
+
if (!raw) return;
|
|
513
|
+
usage.serverToolUse = {
|
|
514
|
+
...usage.serverToolUse,
|
|
515
|
+
...raw.web_search_requests !== void 0 ? { webSearchRequests: raw.web_search_requests } : {},
|
|
516
|
+
...raw.web_fetch_requests !== void 0 ? { webFetchRequests: raw.web_fetch_requests } : {}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region ../../packages/ai-anthropic/src/utils/cache-usage.ts
|
|
522
|
+
/**
|
|
523
|
+
* Copy Anthropic's cache counters onto a neutral `Usage`.
|
|
524
|
+
*
|
|
525
|
+
* - `cache_read_input_tokens` → `cachedTokens`
|
|
526
|
+
* - `cache_creation_input_tokens` → `cacheWriteTokens`
|
|
527
|
+
* - `cache_creation.ephemeral_1h_input_tokens` → `cacheWrite1hTokens`
|
|
528
|
+
* (the 1-hour writes, billed at 2x input instead of 1.25x)
|
|
529
|
+
*
|
|
530
|
+
* When Anthropic omits the per-lifetime breakdown, `requestTtl` decides:
|
|
531
|
+
* a request whose every cache marker asked for `"1h"` wrote only 1-hour
|
|
532
|
+
* entries. Counters are set only when positive, so an absent channel
|
|
533
|
+
* stays undefined. Anthropic's `input_tokens` excludes all of these, so
|
|
534
|
+
* the caller marks the usage `inputIncludesCache: false`.
|
|
535
|
+
*/
|
|
536
|
+
function applyAnthropicCacheUsage(usage, raw, requestTtl) {
|
|
537
|
+
const cacheRead = raw.cache_read_input_tokens;
|
|
538
|
+
if (typeof cacheRead === "number" && cacheRead > 0) usage.cachedTokens = cacheRead;
|
|
539
|
+
const cacheWrite = raw.cache_creation_input_tokens;
|
|
540
|
+
if (typeof cacheWrite !== "number" || cacheWrite <= 0) return;
|
|
541
|
+
usage.cacheWriteTokens = cacheWrite;
|
|
542
|
+
const reported1h = raw.cache_creation?.ephemeral_1h_input_tokens;
|
|
543
|
+
const writes1h = typeof reported1h === "number" ? reported1h : requestTtl === "1h" ? cacheWrite : 0;
|
|
544
|
+
if (writes1h > 0) usage.cacheWrite1hTokens = Math.min(writes1h, cacheWrite);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Scan a request body (top level, system, tools, messages) for
|
|
548
|
+
* `cache_control` markers and report the lifetime they request. Used only
|
|
549
|
+
* as a fallback when the response lacks Anthropic's per-lifetime
|
|
550
|
+
* cache-write breakdown.
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* requestCacheTtl({ cache_control: { type: "ephemeral", ttl: "1h" } }); // "1h"
|
|
554
|
+
*/
|
|
555
|
+
function requestCacheTtl(body) {
|
|
556
|
+
let found;
|
|
557
|
+
const visit = (value, depth) => {
|
|
558
|
+
if (depth > 8 || typeof value !== "object" || value === null) return;
|
|
559
|
+
if (Array.isArray(value)) {
|
|
560
|
+
for (const item of value) visit(item, depth + 1);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const record = value;
|
|
564
|
+
const marker = record.cache_control;
|
|
565
|
+
if (typeof marker === "object" && marker !== null) {
|
|
566
|
+
const ttl = marker.ttl === "1h" ? "1h" : "5m";
|
|
567
|
+
found = found === void 0 || found === ttl ? ttl : "mixed";
|
|
568
|
+
}
|
|
569
|
+
for (const key of [
|
|
570
|
+
"system",
|
|
571
|
+
"tools",
|
|
572
|
+
"messages",
|
|
573
|
+
"content"
|
|
574
|
+
]) if (key in record) visit(record[key], depth + 1);
|
|
575
|
+
};
|
|
576
|
+
visit(body, 0);
|
|
577
|
+
return found;
|
|
191
578
|
}
|
|
192
579
|
|
|
193
580
|
//#endregion
|
|
@@ -229,9 +616,9 @@ function mapStopReason(raw) {
|
|
|
229
616
|
/** The `ProviderContentBlock.provider` value this adapter writes and replays. */
|
|
230
617
|
const ANTHROPIC_BLOCK_FORMAT = "anthropic";
|
|
231
618
|
/** Block types the neutral `content` + `toolCalls` fields already carry losslessly. */
|
|
232
|
-
const NEUTRAL_BLOCK_TYPES = new Set(["text", "tool_use"]);
|
|
619
|
+
const NEUTRAL_BLOCK_TYPES = /* @__PURE__ */ new Set(["text", "tool_use"]);
|
|
233
620
|
/** Block types that carry a model-requested call whose id must match `toolCalls`. */
|
|
234
|
-
const TOOL_USE_BLOCK_TYPES = new Set(["tool_use"]);
|
|
621
|
+
const TOOL_USE_BLOCK_TYPES = /* @__PURE__ */ new Set(["tool_use"]);
|
|
235
622
|
/**
|
|
236
623
|
* Assistant-turn block types that may be sent back verbatim. Server-tool
|
|
237
624
|
* results all end in `_tool_result` (`web_search_tool_result`,
|
|
@@ -239,7 +626,7 @@ const TOOL_USE_BLOCK_TYPES = new Set(["tool_use"]);
|
|
|
239
626
|
* else (an unknown or user-side type such as `image`, `document` or a
|
|
240
627
|
* client `tool_result`) is not replayed.
|
|
241
628
|
*/
|
|
242
|
-
const REPLAYABLE_BLOCK_TYPES = new Set([
|
|
629
|
+
const REPLAYABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
243
630
|
"text",
|
|
244
631
|
"thinking",
|
|
245
632
|
"redacted_thinking",
|
|
@@ -333,6 +720,9 @@ function isReplayable(block) {
|
|
|
333
720
|
* provider, or blocks whose `tool_use` ids no longer match `toolCalls`,
|
|
334
721
|
* are ignored and the turn is rebuilt from `content` + `toolCalls`.
|
|
335
722
|
*
|
|
723
|
+
* With `dropRetainedThinking`, a turn marked `compaction: "retained"` is
|
|
724
|
+
* replayed without its thinking blocks (see {@link ToAnthropicMessagesOptions}).
|
|
725
|
+
*
|
|
336
726
|
* Other consecutive same-role turns are left as-is — the Messages API
|
|
337
727
|
* merges them server-side.
|
|
338
728
|
*
|
|
@@ -344,7 +734,7 @@ function isReplayable(block) {
|
|
|
344
734
|
* // system === "Be concise."
|
|
345
735
|
* // messages === [{ role: "user", content: "Hi" }]
|
|
346
736
|
*/
|
|
347
|
-
function toAnthropicMessages(messages) {
|
|
737
|
+
function toAnthropicMessages(messages, options = {}) {
|
|
348
738
|
const systemParts = [];
|
|
349
739
|
const mapped = [];
|
|
350
740
|
for (const message of messages) {
|
|
@@ -366,18 +756,20 @@ function toAnthropicMessages(messages) {
|
|
|
366
756
|
});
|
|
367
757
|
continue;
|
|
368
758
|
}
|
|
759
|
+
const dropThinking = options.dropRetainedThinking === true && message.compaction === "retained";
|
|
369
760
|
if (message.role === "assistant") {
|
|
370
761
|
const replayed = fromProviderBlocks(message.blocks, message.toolCalls);
|
|
371
|
-
|
|
762
|
+
const content = dropThinking ? replayed?.filter((block) => !isThinkingBlock(block)) : replayed;
|
|
763
|
+
if (content && content.length > 0) {
|
|
372
764
|
mapped.push({
|
|
373
765
|
role: "assistant",
|
|
374
|
-
content
|
|
766
|
+
content
|
|
375
767
|
});
|
|
376
768
|
continue;
|
|
377
769
|
}
|
|
378
770
|
}
|
|
379
771
|
if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
|
|
380
|
-
const blocks = extractThinkingBlocks(message.toolCalls);
|
|
772
|
+
const blocks = dropThinking ? [] : extractThinkingBlocks(message.toolCalls);
|
|
381
773
|
const text = stringifyContent(message.content);
|
|
382
774
|
if (text) blocks.push({
|
|
383
775
|
type: "text",
|
|
@@ -412,6 +804,9 @@ function toAnthropicMessages(messages) {
|
|
|
412
804
|
messages: mapped
|
|
413
805
|
};
|
|
414
806
|
}
|
|
807
|
+
function isThinkingBlock(block) {
|
|
808
|
+
return block.type === "thinking" || block.type === "redacted_thinking";
|
|
809
|
+
}
|
|
415
810
|
/**
|
|
416
811
|
* Recover signed extended-thinking blocks stored by this adapter on a tool
|
|
417
812
|
* request. Anthropic requires these blocks to be replayed before the
|
|
@@ -583,6 +978,11 @@ function wrapAnthropicError(thrown) {
|
|
|
583
978
|
context,
|
|
584
979
|
retryAfter: parseRetryAfter(shape.headers)
|
|
585
980
|
});
|
|
981
|
+
if (shape.type === "overloaded_error" || shape.status === 529) return new _warlock_js_ai.ProviderOverloadedError(message, {
|
|
982
|
+
cause: thrown,
|
|
983
|
+
context,
|
|
984
|
+
retryAfter: parseRetryAfter(shape.headers)
|
|
985
|
+
});
|
|
586
986
|
if (shape.type === "invalid_request_error" || isClientStatus(shape.status)) {
|
|
587
987
|
if (/prompt is too long/i.test(message)) return new _warlock_js_ai.ContextLengthExceededError(message, {
|
|
588
988
|
cause: thrown,
|
|
@@ -688,14 +1088,30 @@ function parseRetryAfter(headers) {
|
|
|
688
1088
|
//#endregion
|
|
689
1089
|
//#region ../../packages/ai-anthropic/src/model.ts
|
|
690
1090
|
const LOG_MODULE = "ai.anthropic";
|
|
1091
|
+
/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
|
|
1092
|
+
const ANTHROPIC_WIRE_FORMAT = "anthropic";
|
|
691
1093
|
/**
|
|
692
1094
|
* Anthropic requires `max_tokens` on every request (unlike OpenAI,
|
|
693
|
-
* where it is optional). When neither the per-call option nor the
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
1095
|
+
* where it is optional). When neither the per-call option nor the model
|
|
1096
|
+
* config supplies one, the adapter picks a default large enough for agent
|
|
1097
|
+
* work (see `resolveDefaultMaxTokens`):
|
|
1098
|
+
*
|
|
1099
|
+
* - streaming: the model's documented max output (`known-output-limits.ts`,
|
|
1100
|
+
* or `config.maxOutputTokens`), else this value;
|
|
1101
|
+
* - non-streaming: this value, capped by the model's max output. The
|
|
1102
|
+
* installed `@anthropic-ai/sdk` (0.129.0, `calculateNonstreamingTimeout`)
|
|
1103
|
+
* refuses a non-streaming request whose `max_tokens` implies more than
|
|
1104
|
+
* ten minutes (above ~21,333 tokens) unless the client sets a `timeout`,
|
|
1105
|
+
* so the non-streaming default stays below that.
|
|
1106
|
+
*/
|
|
1107
|
+
const DEFAULT_MAX_TOKENS = 16384;
|
|
1108
|
+
/**
|
|
1109
|
+
* Answer room kept on top of a thinking budget when the default
|
|
1110
|
+
* `max_tokens` is sized: thinking counts toward `max_tokens` (installed
|
|
1111
|
+
* SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`), and in budget
|
|
1112
|
+
* mode `max_tokens` must exceed `budget_tokens`.
|
|
697
1113
|
*/
|
|
698
|
-
const
|
|
1114
|
+
const MIN_ANSWER_TOKENS = 4096;
|
|
699
1115
|
/**
|
|
700
1116
|
* Anthropic rejects an extended-thinking budget below 1024 tokens, so
|
|
701
1117
|
* any resolved budget is floored at this minimum before it reaches the
|
|
@@ -714,11 +1130,10 @@ const DEFAULT_THINKING_EFFORT = "medium";
|
|
|
714
1130
|
*
|
|
715
1131
|
* - Budget-mode models: sent as `thinking.budget_tokens` when the caller
|
|
716
1132
|
* doesn't pass an explicit `reasoning.maxTokens`.
|
|
717
|
-
* - Both modes:
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
* has always produced.
|
|
1133
|
+
* - Both modes: when the caller sets no `maxTokens`, the default
|
|
1134
|
+
* `max_tokens` keeps at least this budget plus `MIN_ANSWER_TOKENS`,
|
|
1135
|
+
* because thinking counts toward `max_tokens` (installed SDK
|
|
1136
|
+
* `ThinkingConfigParam` docs, `messages.d.ts:1105`).
|
|
722
1137
|
*/
|
|
723
1138
|
const EFFORT_THINKING_BUDGET = {
|
|
724
1139
|
minimal: 1024,
|
|
@@ -744,7 +1159,7 @@ const EFFORT_TO_OUTPUT_EFFORT = {
|
|
|
744
1159
|
max: "max"
|
|
745
1160
|
};
|
|
746
1161
|
/** Content-block types whose input arrives as `input_json_delta` while streaming. */
|
|
747
|
-
const STREAMED_INPUT_BLOCK_TYPES = new Set([
|
|
1162
|
+
const STREAMED_INPUT_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
748
1163
|
"tool_use",
|
|
749
1164
|
"server_tool_use",
|
|
750
1165
|
"mcp_tool_use"
|
|
@@ -794,8 +1209,10 @@ var AnthropicModel = class {
|
|
|
794
1209
|
this.config = config;
|
|
795
1210
|
this.name = config.name;
|
|
796
1211
|
this.provider = provider;
|
|
797
|
-
this.pricing = config.pricing;
|
|
1212
|
+
this.pricing = withAnthropicCacheWriteRates(config.pricing ?? anthropicPricingFor(config.name));
|
|
798
1213
|
this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);
|
|
1214
|
+
this.maxOutputTokens = config.maxOutputTokens ?? inferMaxOutputTokens(config.name);
|
|
1215
|
+
this.preservedThinking = config.preservedThinking ?? (this.thinkingMode === "adaptive" && inferPreservedThinking(config.name));
|
|
799
1216
|
this.capabilities = {
|
|
800
1217
|
structuredOutput: config.structuredOutput ?? true,
|
|
801
1218
|
vision: config.vision ?? inferVisionCapability(config.name),
|
|
@@ -811,23 +1228,50 @@ var AnthropicModel = class {
|
|
|
811
1228
|
* instance's `ModelConfig` defaults for this call only.
|
|
812
1229
|
*/
|
|
813
1230
|
async complete(messages, options) {
|
|
814
|
-
this.
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1231
|
+
return this.send(this.buildLoggedRequest(messages, {
|
|
1232
|
+
...options,
|
|
1233
|
+
stream: false
|
|
1234
|
+
}), { signal: options?.signal });
|
|
1235
|
+
}
|
|
1236
|
+
buildRequest(messages, options) {
|
|
1237
|
+
const built = this.buildBody(messages, options);
|
|
1238
|
+
const betas = [.../* @__PURE__ */ new Set([...resolveAnthropicOptions(options).betas ?? [], ...built.betas])];
|
|
1239
|
+
const body = structuredClone({
|
|
1240
|
+
...built.body,
|
|
1241
|
+
stream: options?.stream ?? false
|
|
1242
|
+
});
|
|
1243
|
+
return {
|
|
1244
|
+
endpoint: betas.length > 0 ? "beta.messages" : "messages",
|
|
1245
|
+
body,
|
|
1246
|
+
betas
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
buildLoggedRequest(messages, options) {
|
|
1250
|
+
try {
|
|
1251
|
+
return this.buildRequest(messages, options);
|
|
1252
|
+
} catch (thrown) {
|
|
1253
|
+
throw this.logAndWrap(thrown instanceof _warlock_js_ai.AIError ? thrown : new _warlock_js_ai.InvalidRequestError(`Could not build the Anthropic request: ${thrown instanceof Error ? thrown.message : String(thrown)}`, { cause: thrown }));
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
send(request, options) {
|
|
1257
|
+
const message = request.body.stream ? "Starting streaming call to messages.create" : "Starting call to messages.create";
|
|
1258
|
+
this.logger.debug(LOG_MODULE, "request", message, {
|
|
1259
|
+
model: request.body.model,
|
|
1260
|
+
messageCount: Array.isArray(request.body.messages) ? request.body.messages.length : 0,
|
|
1261
|
+
streaming: request.body.stream,
|
|
1262
|
+
toolCount: Array.isArray(request.body.tools) ? request.body.tools.length : 0
|
|
819
1263
|
});
|
|
1264
|
+
return request.body.stream ? this.streamRequest(request, options) : this.completeRequest(request, options);
|
|
1265
|
+
}
|
|
1266
|
+
async completeRequest(request, options) {
|
|
820
1267
|
let response;
|
|
821
1268
|
try {
|
|
822
|
-
response = await this.
|
|
823
|
-
...this.buildBody(messages, options),
|
|
824
|
-
stream: false
|
|
825
|
-
}, options, "create");
|
|
1269
|
+
response = await this.dispatch(request, options, "create");
|
|
826
1270
|
} catch (thrown) {
|
|
827
1271
|
throw this.logAndWrap(thrown);
|
|
828
1272
|
}
|
|
829
1273
|
const finishReason = mapStopReason(response.stop_reason);
|
|
830
|
-
const usage = this.extractUsage(response.usage);
|
|
1274
|
+
const usage = this.extractUsage(response.usage, requestCacheTtl(request.body));
|
|
831
1275
|
const toolCalls = this.extractToolCalls(response.content);
|
|
832
1276
|
const blocks = toProviderBlocks(response.content);
|
|
833
1277
|
this.logger.debug(LOG_MODULE, "response", "call to messages.create succeeded", {
|
|
@@ -854,11 +1298,18 @@ var AnthropicModel = class {
|
|
|
854
1298
|
* const tokens = await model.countTokens(messages, { tools });
|
|
855
1299
|
*/
|
|
856
1300
|
async countTokens(messages, options) {
|
|
857
|
-
const
|
|
1301
|
+
const request = this.buildLoggedRequest(messages, {
|
|
1302
|
+
...options,
|
|
1303
|
+
stream: options?.stream === true
|
|
1304
|
+
});
|
|
1305
|
+
const body = request.body;
|
|
858
1306
|
const countBody = {};
|
|
859
1307
|
for (const key of COUNT_TOKENS_FIELDS) if (body[key] !== void 0) countBody[key] = body[key];
|
|
860
1308
|
try {
|
|
861
|
-
return (await this.
|
|
1309
|
+
return (await this.dispatch({
|
|
1310
|
+
...request,
|
|
1311
|
+
body: countBody
|
|
1312
|
+
}, options, "countTokens")).input_tokens;
|
|
862
1313
|
} catch (thrown) {
|
|
863
1314
|
throw this.logAndWrap(thrown);
|
|
864
1315
|
}
|
|
@@ -869,29 +1320,38 @@ var AnthropicModel = class {
|
|
|
869
1320
|
* arguments have fully accumulated, and a terminal `done` carrying the
|
|
870
1321
|
* final finish reason + usage totals. Callers consume it with
|
|
871
1322
|
* `for await`.
|
|
1323
|
+
*
|
|
1324
|
+
* With `options.streamChunks` it also yields `reasoning` for every
|
|
1325
|
+
* non-empty `thinking_delta`, `tool-args` for every `input_json_delta`
|
|
1326
|
+
* of a client `tool_use` block, and `usage` after `message_start` and
|
|
1327
|
+
* each `message_delta` (whose counts are cumulative).
|
|
872
1328
|
*/
|
|
873
1329
|
async *stream(messages, options) {
|
|
874
|
-
this.
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1330
|
+
yield* this.send(this.buildLoggedRequest(messages, {
|
|
1331
|
+
...options,
|
|
1332
|
+
stream: true
|
|
1333
|
+
}), {
|
|
1334
|
+
signal: options?.signal,
|
|
1335
|
+
streamChunks: options?.streamChunks
|
|
879
1336
|
});
|
|
1337
|
+
}
|
|
1338
|
+
async *streamRequest(request, options) {
|
|
880
1339
|
let stream;
|
|
881
1340
|
try {
|
|
882
|
-
stream = await this.
|
|
883
|
-
...this.buildBody(messages, options),
|
|
884
|
-
stream: true
|
|
885
|
-
}, options, "create");
|
|
1341
|
+
stream = await this.dispatch(request, options, "create");
|
|
886
1342
|
} catch (thrown) {
|
|
887
1343
|
throw this.logAndWrap(thrown);
|
|
888
1344
|
}
|
|
889
1345
|
let rawStopReason = null;
|
|
1346
|
+
let compaction = compactionIterationUsage(void 0);
|
|
890
1347
|
const usage = {
|
|
891
1348
|
input: 0,
|
|
892
1349
|
output: 0,
|
|
893
|
-
total: 0
|
|
1350
|
+
total: 0,
|
|
1351
|
+
inputIncludesCache: false
|
|
894
1352
|
};
|
|
1353
|
+
const cacheTtl = requestCacheTtl(request.body);
|
|
1354
|
+
const want = (0, _warlock_js_ai.resolveStreamChunks)(options);
|
|
895
1355
|
const toolBlocks = /* @__PURE__ */ new Map();
|
|
896
1356
|
const thinkingBlocks = /* @__PURE__ */ new Map();
|
|
897
1357
|
const streamedBlocks = /* @__PURE__ */ new Map();
|
|
@@ -902,10 +1362,15 @@ var AnthropicModel = class {
|
|
|
902
1362
|
usage.input = event.message.usage.input_tokens ?? 0;
|
|
903
1363
|
const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;
|
|
904
1364
|
if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1365
|
+
applyServerToolUsage(usage, event.message.usage.server_tool_use);
|
|
1366
|
+
applyAnthropicCacheUsage(usage, event.message.usage, cacheTtl);
|
|
1367
|
+
if (want.usage) yield {
|
|
1368
|
+
type: "usage",
|
|
1369
|
+
usage: {
|
|
1370
|
+
...usage,
|
|
1371
|
+
total: usage.input + usage.output
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
909
1374
|
continue;
|
|
910
1375
|
}
|
|
911
1376
|
if (event.type === "content_block_start") {
|
|
@@ -926,10 +1391,22 @@ var AnthropicModel = class {
|
|
|
926
1391
|
};
|
|
927
1392
|
else if (event.delta.type === "input_json_delta") {
|
|
928
1393
|
const accumulator = toolBlocks.get(event.index);
|
|
929
|
-
if (accumulator)
|
|
1394
|
+
if (accumulator) {
|
|
1395
|
+
accumulator.json += event.delta.partial_json;
|
|
1396
|
+
if (want.toolArgs && event.delta.partial_json !== "") yield {
|
|
1397
|
+
type: "tool-args",
|
|
1398
|
+
callId: accumulator.id,
|
|
1399
|
+
name: accumulator.name,
|
|
1400
|
+
delta: event.delta.partial_json
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
930
1403
|
} else if (event.delta.type === "thinking_delta") {
|
|
931
1404
|
const thinking = thinkingBlocks.get(event.index);
|
|
932
1405
|
if (thinking?.type === "thinking") thinking.thinking += event.delta.thinking;
|
|
1406
|
+
if (want.reasoning && event.delta.thinking !== "") yield {
|
|
1407
|
+
type: "reasoning",
|
|
1408
|
+
delta: event.delta.thinking
|
|
1409
|
+
};
|
|
933
1410
|
} else if (event.delta.type === "signature_delta") {
|
|
934
1411
|
const thinking = thinkingBlocks.get(event.index);
|
|
935
1412
|
if (thinking?.type === "thinking") thinking.signature += event.delta.signature;
|
|
@@ -952,19 +1429,34 @@ var AnthropicModel = class {
|
|
|
952
1429
|
}
|
|
953
1430
|
if (event.type === "message_delta") {
|
|
954
1431
|
rawStopReason = event.delta.stop_reason ?? rawStopReason;
|
|
1432
|
+
if (Array.isArray(event.usage.iterations)) compaction = compactionIterationUsage(event.usage);
|
|
1433
|
+
applyServerToolUsage(usage, event.usage.server_tool_use);
|
|
955
1434
|
usage.output = event.usage.output_tokens ?? usage.output;
|
|
956
1435
|
const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
|
|
957
1436
|
if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1437
|
+
applyAnthropicCacheUsage(usage, {
|
|
1438
|
+
cache_read_input_tokens: event.usage.cache_read_input_tokens,
|
|
1439
|
+
cache_creation_input_tokens: event.usage.cache_creation_input_tokens,
|
|
1440
|
+
cache_creation: usage.cacheWrite1hTokens !== void 0 ? { ephemeral_1h_input_tokens: usage.cacheWrite1hTokens } : void 0
|
|
1441
|
+
}, cacheTtl);
|
|
1442
|
+
if (want.usage) yield {
|
|
1443
|
+
type: "usage",
|
|
1444
|
+
usage: {
|
|
1445
|
+
...usage,
|
|
1446
|
+
total: usage.input + usage.output
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
962
1449
|
}
|
|
963
1450
|
}
|
|
964
1451
|
} catch (thrown) {
|
|
965
1452
|
throw this.logAndWrap(thrown);
|
|
966
1453
|
}
|
|
1454
|
+
usage.input += compaction.input;
|
|
1455
|
+
usage.output += compaction.output;
|
|
1456
|
+
addCacheCounts(usage, compaction);
|
|
967
1457
|
usage.total = usage.input + usage.output;
|
|
1458
|
+
const cost = (0, _warlock_js_ai.computeCost)(usage, this.pricing);
|
|
1459
|
+
if (cost) usage.cost = cost;
|
|
968
1460
|
const finishReason = mapStopReason(rawStopReason);
|
|
969
1461
|
const blocks = toProviderBlocks(this.finishStreamedBlocks(streamedBlocks));
|
|
970
1462
|
this.logger.debug(LOG_MODULE, "response", "Streaming call to messages.create succeeded", {
|
|
@@ -1018,38 +1510,74 @@ var AnthropicModel = class {
|
|
|
1018
1510
|
* and the beta-only body fields); otherwise to `client.messages`. The
|
|
1019
1511
|
* cancellation signal rides in the request options, as before.
|
|
1020
1512
|
*/
|
|
1021
|
-
|
|
1022
|
-
const betas = resolveAnthropicOptions(options).betas;
|
|
1513
|
+
dispatch(request, options, method) {
|
|
1023
1514
|
const requestOptions = options?.signal ? { signal: options.signal } : void 0;
|
|
1024
|
-
const api =
|
|
1025
|
-
return api[method].call(api,
|
|
1026
|
-
...body,
|
|
1027
|
-
betas
|
|
1028
|
-
} : body, requestOptions);
|
|
1515
|
+
const api = request.endpoint === "beta.messages" ? this.client.beta.messages : this.client.messages;
|
|
1516
|
+
return api[method].call(api, request.endpoint === "beta.messages" ? {
|
|
1517
|
+
...request.body,
|
|
1518
|
+
betas: request.betas
|
|
1519
|
+
} : request.body, requestOptions);
|
|
1029
1520
|
}
|
|
1030
1521
|
/**
|
|
1031
|
-
* The full request body: the adapter-built
|
|
1032
|
-
* `thinking.display` when thinking is sent on an adaptive
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1036
|
-
*
|
|
1522
|
+
* The full request body and the beta flags it needs: the adapter-built
|
|
1523
|
+
* params, then `thinking.display` when thinking is sent on an adaptive
|
|
1524
|
+
* model, the typed `thinkingBlockBinding`, `contextManagement` and
|
|
1525
|
+
* `serverCompaction` options, then `providerOptions.anthropic.body`
|
|
1526
|
+
* merged last (caller fields win). The body may add fields the adapter
|
|
1527
|
+
* does not build (`fallbacks`, …) but may not replace the conversation or
|
|
1528
|
+
* the model — see {@link RESERVED_BODY_FIELDS}.
|
|
1037
1529
|
*/
|
|
1038
1530
|
buildBody(messages, options) {
|
|
1039
1531
|
const anthropic = resolveAnthropicOptions(options);
|
|
1040
|
-
const
|
|
1532
|
+
const betas = [];
|
|
1533
|
+
const body = { ...this.buildParams(messages, options, { dropRetainedThinking: this.preservedThinking && anthropic.thinkingBlockBinding === void 0 }) };
|
|
1041
1534
|
if (anthropic.thinkingDisplay && this.thinkingMode === "adaptive" && body.thinking?.type === "adaptive") body.thinking = {
|
|
1042
1535
|
...body.thinking,
|
|
1043
1536
|
display: anthropic.thinkingDisplay
|
|
1044
1537
|
};
|
|
1538
|
+
if (anthropic.thinkingBlockBinding) this.applyBlockBinding(body, anthropic.thinkingBlockBinding, betas);
|
|
1539
|
+
const edits = buildContextEdits(anthropic);
|
|
1540
|
+
if (edits.length > 0) {
|
|
1541
|
+
if (anthropic.body?.context_management !== void 0) throw new _warlock_js_ai.InvalidRequestError("providerOptions.anthropic.contextManagement / serverCompaction cannot be combined with body.context_management: set the edits in one place.", { context: { fields: ["context_management"] } });
|
|
1542
|
+
body.context_management = { edits };
|
|
1543
|
+
if (edits.some((edit) => edit.type !== "compact_20260112")) betas.push(CONTEXT_MANAGEMENT_BETA);
|
|
1544
|
+
if (edits.some((edit) => edit.type === "compact_20260112")) betas.push(COMPACTION_BETA);
|
|
1545
|
+
}
|
|
1045
1546
|
const reserved = Object.keys(anthropic.body ?? {}).filter((key) => RESERVED_BODY_FIELDS.has(key));
|
|
1046
1547
|
if (reserved.length > 0) throw new _warlock_js_ai.InvalidRequestError(`providerOptions.anthropic.body cannot set ${reserved.map((key) => `"${key}"`).join(", ")}: the adapter builds these from the call's messages, tools and model. Use the agent/model options instead.`, { context: { fields: reserved } });
|
|
1047
1548
|
return {
|
|
1048
|
-
|
|
1049
|
-
|
|
1549
|
+
body: {
|
|
1550
|
+
...body,
|
|
1551
|
+
...anthropic.body
|
|
1552
|
+
},
|
|
1553
|
+
betas
|
|
1050
1554
|
};
|
|
1051
1555
|
}
|
|
1052
1556
|
/**
|
|
1557
|
+
* Set `thinking.block_binding.prefix_mismatch_behavior`. It rides on
|
|
1558
|
+
* adaptive or budget (`enabled`) thinking. A model that runs adaptive
|
|
1559
|
+
* thinking when `thinking` is omitted (every preserved-thinking model) gets
|
|
1560
|
+
* an explicit `{ type: "adaptive" }`, which is the same request. Any other
|
|
1561
|
+
* shape (`between_tools`, `disabled`) takes no extra field, so the option
|
|
1562
|
+
* is skipped with a debug log.
|
|
1563
|
+
*/
|
|
1564
|
+
applyBlockBinding(body, behavior, betas) {
|
|
1565
|
+
const thinking = body.thinking;
|
|
1566
|
+
const type = thinking?.type ?? (this.preservedThinking ? "adaptive" : void 0);
|
|
1567
|
+
if (type !== "adaptive" && type !== "enabled") {
|
|
1568
|
+
this.logger.debug(LOG_MODULE, "thinking-block-binding.ignored", "thinkingBlockBinding needs adaptive or budget thinking on this request; not sent", {
|
|
1569
|
+
model: this.name,
|
|
1570
|
+
thinking: thinking?.type ?? "none"
|
|
1571
|
+
});
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
body.thinking = {
|
|
1575
|
+
...thinking ?? { type },
|
|
1576
|
+
block_binding: { prefix_mismatch_behavior: behavior }
|
|
1577
|
+
};
|
|
1578
|
+
betas.push(THINKING_BINDING_BETA);
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1053
1581
|
* Assemble the Anthropic request body shared by `complete()` and
|
|
1054
1582
|
* `stream()` (each adds its own `stream` literal so the SDK's create
|
|
1055
1583
|
* overload resolves to the right return type). Hoists the system
|
|
@@ -1061,13 +1589,13 @@ var AnthropicModel = class {
|
|
|
1061
1589
|
* rejects the two together, and always for adaptive-mode models, which
|
|
1062
1590
|
* reject any explicit temperature (see `acceptsTemperature`).
|
|
1063
1591
|
*/
|
|
1064
|
-
buildParams(messages, options) {
|
|
1065
|
-
const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
|
|
1592
|
+
buildParams(messages, options, conversion) {
|
|
1593
|
+
const { system, messages: anthropicMessages } = toAnthropicMessages(messages, conversion);
|
|
1066
1594
|
const thinking = this.buildThinking(options?.reasoning);
|
|
1067
1595
|
const temperature = options?.temperature ?? this.config.temperature;
|
|
1068
1596
|
const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
|
|
1069
1597
|
const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
|
|
1070
|
-
const maxTokens = configuredMaxTokens ?? (
|
|
1598
|
+
const maxTokens = configuredMaxTokens ?? this.resolveDefaultMaxTokens(options?.stream === true, thinking.reservedTokens);
|
|
1071
1599
|
const sendTemperature = temperature !== void 0 && !thinking.thinking && acceptsTemperature(this.thinkingMode);
|
|
1072
1600
|
if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
|
|
1073
1601
|
return {
|
|
@@ -1077,12 +1605,31 @@ var AnthropicModel = class {
|
|
|
1077
1605
|
...this.buildAutomaticCacheControl(options?.cacheControl),
|
|
1078
1606
|
...this.buildSystem(system, options?.cacheControl),
|
|
1079
1607
|
...sendTemperature ? { temperature } : {},
|
|
1080
|
-
...this.buildTools(options
|
|
1608
|
+
...this.buildTools(options, options?.cacheControl),
|
|
1081
1609
|
...this.buildOutputConfig(options?.responseSchema, thinking.effort),
|
|
1082
1610
|
...thinking.thinking ? { thinking: thinking.thinking } : {}
|
|
1083
1611
|
};
|
|
1084
1612
|
}
|
|
1085
1613
|
/**
|
|
1614
|
+
* The `max_tokens` sent when neither the call nor the model config sets
|
|
1615
|
+
* `maxTokens`:
|
|
1616
|
+
*
|
|
1617
|
+
* - streaming: the model's documented max output, or
|
|
1618
|
+
* `DEFAULT_MAX_TOKENS` (16384) for an unknown id;
|
|
1619
|
+
* - non-streaming: `DEFAULT_MAX_TOKENS`, capped by the model's max output
|
|
1620
|
+
* (the SDK requires streaming for larger non-streaming requests unless
|
|
1621
|
+
* the client sets a `timeout`; see `DEFAULT_MAX_TOKENS`).
|
|
1622
|
+
*
|
|
1623
|
+
* With thinking on, the value is raised to at least the thinking budget
|
|
1624
|
+
* plus `MIN_ANSWER_TOKENS`, and never exceeds the model's max output.
|
|
1625
|
+
*/
|
|
1626
|
+
resolveDefaultMaxTokens(stream, reservedTokens) {
|
|
1627
|
+
const limit = this.maxOutputTokens;
|
|
1628
|
+
const base = stream ? limit ?? DEFAULT_MAX_TOKENS : limit === void 0 ? DEFAULT_MAX_TOKENS : Math.min(limit, DEFAULT_MAX_TOKENS);
|
|
1629
|
+
const withThinking = reservedTokens === void 0 ? base : Math.max(base, reservedTokens + MIN_ANSWER_TOKENS);
|
|
1630
|
+
return limit === void 0 ? withThinking : Math.min(withThinking, limit);
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1086
1633
|
* Spread-friendly `system` fragment. Returns an empty object when no
|
|
1087
1634
|
* system prompt was hoisted out of the messages.
|
|
1088
1635
|
*
|
|
@@ -1145,12 +1692,18 @@ var AnthropicModel = class {
|
|
|
1145
1692
|
* `messages.d.ts:1084`), so the budget is floored at that minimum.
|
|
1146
1693
|
*
|
|
1147
1694
|
* The neutral `effort: "none"` ("run without reasoning") is mapped per
|
|
1148
|
-
* model:
|
|
1149
|
-
* - Claude Sonnet 5.5
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1695
|
+
* model by `noReasoningForm` (see `thinking-mode.ts` for the sources):
|
|
1696
|
+
* - Claude Sonnet 5.5: `thinking: { type: "between_tools" }`, its only
|
|
1697
|
+
* thinking-off form. It is sent alone — no `output_config.effort` (the
|
|
1698
|
+
* model default, `high`, is within the `high`-or-below range it
|
|
1699
|
+
* requires) and no `display`, both of which the API rejects alongside it.
|
|
1700
|
+
* - Claude Sonnet 5 and Claude Haiku 5.5: `thinking: { type: "disabled" }`
|
|
1701
|
+
* with no effort and no `display`. Haiku 5.5 accepts `disabled` only at
|
|
1702
|
+
* effort `high` or below; its default (`medium`) applies.
|
|
1703
|
+
* - Claude Opus 5: adaptive thinking at `output_config.effort: "low"`.
|
|
1704
|
+
* Opus 5 accepts `disabled`, but with it the model can write a tool call
|
|
1705
|
+
* as plain text (the call never runs) and leak thinking tags; the lowest
|
|
1706
|
+
* effort is the documented alternative.
|
|
1154
1707
|
* - Opus 5.5 and Fable 5/5.1: no `thinking` field. These models have no
|
|
1155
1708
|
* thinking-off form (`disabled` and `budget_tokens` are a 400 at every
|
|
1156
1709
|
* effort), so thinking still runs at the model's default effort. Pass
|
|
@@ -1159,7 +1712,7 @@ var AnthropicModel = class {
|
|
|
1159
1712
|
*/
|
|
1160
1713
|
buildThinking(reasoning) {
|
|
1161
1714
|
if (!this.capabilities.reasoning || !reasoning) return {};
|
|
1162
|
-
if (reasoning.effort === "none") return
|
|
1715
|
+
if (reasoning.effort === "none") return this.buildNoReasoning();
|
|
1163
1716
|
if (reasoning.maxTokens === void 0 && reasoning.effort === void 0) return {};
|
|
1164
1717
|
const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;
|
|
1165
1718
|
const budget = Math.max(MIN_THINKING_BUDGET, reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort]);
|
|
@@ -1177,6 +1730,22 @@ var AnthropicModel = class {
|
|
|
1177
1730
|
};
|
|
1178
1731
|
}
|
|
1179
1732
|
/**
|
|
1733
|
+
* The thinking settings for `reasoning: { effort: "none" }`, per
|
|
1734
|
+
* `noReasoningForm(this.name)` (documented on `buildThinking`).
|
|
1735
|
+
*/
|
|
1736
|
+
buildNoReasoning() {
|
|
1737
|
+
switch (noReasoningForm(this.name)) {
|
|
1738
|
+
case "between_tools": return { thinking: { type: "between_tools" } };
|
|
1739
|
+
case "disabled": return { thinking: { type: "disabled" } };
|
|
1740
|
+
case "lowest-effort": return {
|
|
1741
|
+
thinking: { type: "adaptive" },
|
|
1742
|
+
effort: EFFORT_TO_OUTPUT_EFFORT.low,
|
|
1743
|
+
reservedTokens: EFFORT_THINKING_BUDGET.low
|
|
1744
|
+
};
|
|
1745
|
+
case "omit": return {};
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1180
1749
|
* Spread-friendly tools fragment. Returns an empty object when no
|
|
1181
1750
|
* tools were supplied so the caller can unconditionally spread it.
|
|
1182
1751
|
*
|
|
@@ -1189,9 +1758,16 @@ var AnthropicModel = class {
|
|
|
1189
1758
|
* breakpoint there would pay the ~1.25x write surcharge every turn
|
|
1190
1759
|
* with no reads.
|
|
1191
1760
|
*/
|
|
1192
|
-
buildTools(
|
|
1761
|
+
buildTools(options, cacheControl) {
|
|
1762
|
+
const { tools, definitions } = (0, _warlock_js_ai.selectProviderTools)(options, {
|
|
1763
|
+
accepts: [ANTHROPIC_WIRE_FORMAT, this.provider],
|
|
1764
|
+
module: LOG_MODULE,
|
|
1765
|
+
model: this.name,
|
|
1766
|
+
logger: this.logger
|
|
1767
|
+
});
|
|
1193
1768
|
const mapped = toAnthropicTools(tools);
|
|
1194
|
-
|
|
1769
|
+
const providerDefined = definitions;
|
|
1770
|
+
if (!mapped) return providerDefined.length > 0 ? { tools: providerDefined } : {};
|
|
1195
1771
|
if (this.config.promptCaching && mapped.length > 0) {
|
|
1196
1772
|
const last = mapped.length - 1;
|
|
1197
1773
|
const lastTool = mapped[last];
|
|
@@ -1200,7 +1776,7 @@ var AnthropicModel = class {
|
|
|
1200
1776
|
cache_control: this.cacheControl(cacheControl?.ttl)
|
|
1201
1777
|
};
|
|
1202
1778
|
}
|
|
1203
|
-
return { tools: mapped };
|
|
1779
|
+
return { tools: [...providerDefined, ...mapped] };
|
|
1204
1780
|
}
|
|
1205
1781
|
/**
|
|
1206
1782
|
* Spread-friendly `output_config` fragment carrying both the native
|
|
@@ -1274,27 +1850,35 @@ var AnthropicModel = class {
|
|
|
1274
1850
|
/**
|
|
1275
1851
|
* Normalize Anthropic's `usage` block into the neutral `Usage` shape.
|
|
1276
1852
|
* Anthropic reports `input_tokens` / `output_tokens` separately with
|
|
1277
|
-
* no pre-summed total, so `total` is computed.
|
|
1278
|
-
*
|
|
1279
|
-
* `cacheWriteTokens`,
|
|
1853
|
+
* no pre-summed total, so `total` is computed. `input_tokens` is the
|
|
1854
|
+
* uncached remainder only — cache reads (`cachedTokens`) and writes
|
|
1855
|
+
* (`cacheWriteTokens`, with the 1-hour share in `cacheWrite1hTokens`)
|
|
1856
|
+
* come on top of it — so the usage is marked
|
|
1857
|
+
* `inputIncludesCache: false`. Anthropic also reports
|
|
1280
1858
|
* `output_tokens_details.thinking_tokens` as the reasoning subset of
|
|
1281
1859
|
* its inclusive `output_tokens`; it is surfaced as `reasoningTokens`
|
|
1282
|
-
* when present, without changing the billing total.
|
|
1860
|
+
* when present, without changing the billing total. Server-side
|
|
1861
|
+
* compaction iterations are added in (see {@link compactionIterationUsage}).
|
|
1862
|
+
* `cost` is attached when the model is priced.
|
|
1283
1863
|
*/
|
|
1284
|
-
extractUsage(raw) {
|
|
1285
|
-
const
|
|
1286
|
-
const
|
|
1287
|
-
const
|
|
1288
|
-
const cacheWrite = raw.cache_creation_input_tokens;
|
|
1864
|
+
extractUsage(raw, cacheTtl) {
|
|
1865
|
+
const compaction = compactionIterationUsage(raw);
|
|
1866
|
+
const input = (raw.input_tokens ?? 0) + compaction.input;
|
|
1867
|
+
const output = (raw.output_tokens ?? 0) + compaction.output;
|
|
1289
1868
|
const thinkingTokens = raw.output_tokens_details?.thinking_tokens;
|
|
1290
|
-
|
|
1869
|
+
const usage = {
|
|
1291
1870
|
input,
|
|
1292
1871
|
output,
|
|
1293
1872
|
total: input + output,
|
|
1294
|
-
|
|
1295
|
-
...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
|
|
1873
|
+
inputIncludesCache: false,
|
|
1296
1874
|
...thinkingTokens !== null && thinkingTokens !== void 0 ? { reasoningTokens: thinkingTokens } : {}
|
|
1297
1875
|
};
|
|
1876
|
+
applyServerToolUsage(usage, raw.server_tool_use);
|
|
1877
|
+
applyAnthropicCacheUsage(usage, raw, cacheTtl);
|
|
1878
|
+
addCacheCounts(usage, compaction);
|
|
1879
|
+
const cost = (0, _warlock_js_ai.computeCost)(usage, this.pricing);
|
|
1880
|
+
if (cost) usage.cost = cost;
|
|
1881
|
+
return usage;
|
|
1298
1882
|
}
|
|
1299
1883
|
/**
|
|
1300
1884
|
* Wrap a thrown provider error into the typed `AIError` hierarchy and
|
|
@@ -1317,7 +1901,7 @@ var AnthropicModel = class {
|
|
|
1317
1901
|
* model call tools the agent never registered). `stream` is chosen per
|
|
1318
1902
|
* call by `complete()` / `stream()`.
|
|
1319
1903
|
*/
|
|
1320
|
-
const RESERVED_BODY_FIELDS = new Set([
|
|
1904
|
+
const RESERVED_BODY_FIELDS = /* @__PURE__ */ new Set([
|
|
1321
1905
|
"model",
|
|
1322
1906
|
"messages",
|
|
1323
1907
|
"system",
|
|
@@ -1338,6 +1922,119 @@ const COUNT_TOKENS_FIELDS = [
|
|
|
1338
1922
|
"context_management",
|
|
1339
1923
|
"mcp_servers"
|
|
1340
1924
|
];
|
|
1925
|
+
/** Beta flag for typed context editing (Anthropic docs, "Context editing", read 2026-10-17). */
|
|
1926
|
+
const CONTEXT_MANAGEMENT_BETA = "context-management-2025-06-27";
|
|
1927
|
+
/** Beta flag for threshold compaction (Anthropic docs, "Compaction at a token threshold"). */
|
|
1928
|
+
const COMPACTION_BETA = "compact-2026-01-12";
|
|
1929
|
+
/** Beta flag for `thinking.block_binding` (Anthropic docs, "Preserved thinking"). */
|
|
1930
|
+
const THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
|
|
1931
|
+
/** The API rejects a compaction trigger below 50,000 input tokens. */
|
|
1932
|
+
const MIN_COMPACTION_TRIGGER = 5e4;
|
|
1933
|
+
/**
|
|
1934
|
+
* Translate the typed `contextManagement` / `serverCompaction` options into
|
|
1935
|
+
* `context_management.edits`. `clear_thinking_20251015` must come first
|
|
1936
|
+
* when strategies are combined (Anthropic docs, "Context editing").
|
|
1937
|
+
*/
|
|
1938
|
+
function buildContextEdits(anthropic) {
|
|
1939
|
+
const edits = [];
|
|
1940
|
+
const { clearThinking, clearToolUses } = anthropic.contextManagement ?? {};
|
|
1941
|
+
if (clearThinking) edits.push(clearThinkingEdit(clearThinking === true ? {} : clearThinking));
|
|
1942
|
+
if (clearToolUses) edits.push(clearToolUsesEdit(clearToolUses === true ? {} : clearToolUses));
|
|
1943
|
+
if (anthropic.serverCompaction) {
|
|
1944
|
+
const compaction = anthropic.serverCompaction === true ? {} : anthropic.serverCompaction;
|
|
1945
|
+
if (compaction.triggerTokens !== void 0) assertCount("serverCompaction.triggerTokens", compaction.triggerTokens, MIN_COMPACTION_TRIGGER);
|
|
1946
|
+
edits.push({
|
|
1947
|
+
type: "compact_20260112",
|
|
1948
|
+
...compaction.triggerTokens !== void 0 ? { trigger: {
|
|
1949
|
+
type: "input_tokens",
|
|
1950
|
+
value: compaction.triggerTokens
|
|
1951
|
+
} } : {},
|
|
1952
|
+
...compaction.instructions ? { instructions: compaction.instructions } : {}
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
return edits;
|
|
1956
|
+
}
|
|
1957
|
+
function clearThinkingEdit(options) {
|
|
1958
|
+
if (options.keep === void 0 || options.keep === "all") return {
|
|
1959
|
+
type: "clear_thinking_20251015",
|
|
1960
|
+
...options.keep ? { keep: "all" } : {}
|
|
1961
|
+
};
|
|
1962
|
+
assertCount("contextManagement.clearThinking.keep", options.keep, 1);
|
|
1963
|
+
return {
|
|
1964
|
+
type: "clear_thinking_20251015",
|
|
1965
|
+
keep: {
|
|
1966
|
+
type: "thinking_turns",
|
|
1967
|
+
value: options.keep
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
function clearToolUsesEdit(options) {
|
|
1972
|
+
const edit = { type: "clear_tool_uses_20250919" };
|
|
1973
|
+
if (options.trigger) {
|
|
1974
|
+
const trigger = "inputTokens" in options.trigger ? {
|
|
1975
|
+
type: "input_tokens",
|
|
1976
|
+
value: options.trigger.inputTokens
|
|
1977
|
+
} : {
|
|
1978
|
+
type: "tool_uses",
|
|
1979
|
+
value: options.trigger.toolUses
|
|
1980
|
+
};
|
|
1981
|
+
assertCount("contextManagement.clearToolUses.trigger", trigger.value, 1);
|
|
1982
|
+
edit.trigger = trigger;
|
|
1983
|
+
}
|
|
1984
|
+
if (options.keep !== void 0) {
|
|
1985
|
+
assertCount("contextManagement.clearToolUses.keep", options.keep, 0);
|
|
1986
|
+
edit.keep = {
|
|
1987
|
+
type: "tool_uses",
|
|
1988
|
+
value: options.keep
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
if (options.clearAtLeast !== void 0) {
|
|
1992
|
+
assertCount("contextManagement.clearToolUses.clearAtLeast", options.clearAtLeast, 1);
|
|
1993
|
+
edit.clear_at_least = {
|
|
1994
|
+
type: "input_tokens",
|
|
1995
|
+
value: options.clearAtLeast
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
if (options.excludeTools !== void 0) edit.exclude_tools = [...options.excludeTools];
|
|
1999
|
+
if (options.clearToolInputs !== void 0) edit.clear_tool_inputs = Array.isArray(options.clearToolInputs) ? [...options.clearToolInputs] : options.clearToolInputs;
|
|
2000
|
+
return edit;
|
|
2001
|
+
}
|
|
2002
|
+
function assertCount(field, value, minimum) {
|
|
2003
|
+
if (!Number.isSafeInteger(value) || value < minimum) throw new _warlock_js_ai.InvalidRequestError(`providerOptions.anthropic.${field} must be an integer of at least ${minimum}; got ${value}.`, { context: { field } });
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Tokens of the `compaction` entries in a beta response's `usage.iterations`.
|
|
2007
|
+
* With server-side compaction the top-level `input_tokens` / `output_tokens`
|
|
2008
|
+
* cover only the non-compaction iterations; the billed total is the sum
|
|
2009
|
+
* over every iteration (Anthropic docs, "Compaction at a token threshold" →
|
|
2010
|
+
* "Understanding usage", read 2026-10-17). Zeros when there are none.
|
|
2011
|
+
*/
|
|
2012
|
+
function compactionIterationUsage(raw) {
|
|
2013
|
+
const totals = {
|
|
2014
|
+
input: 0,
|
|
2015
|
+
output: 0,
|
|
2016
|
+
cacheRead: 0,
|
|
2017
|
+
cacheWrite: 0
|
|
2018
|
+
};
|
|
2019
|
+
const iterations = raw?.iterations;
|
|
2020
|
+
if (!Array.isArray(iterations)) return totals;
|
|
2021
|
+
for (const entry of iterations) {
|
|
2022
|
+
const iteration = entry;
|
|
2023
|
+
if (iteration?.type !== "compaction") continue;
|
|
2024
|
+
totals.input += numberOrZero(iteration.input_tokens);
|
|
2025
|
+
totals.output += numberOrZero(iteration.output_tokens);
|
|
2026
|
+
totals.cacheRead += numberOrZero(iteration.cache_read_input_tokens);
|
|
2027
|
+
totals.cacheWrite += numberOrZero(iteration.cache_creation_input_tokens);
|
|
2028
|
+
}
|
|
2029
|
+
return totals;
|
|
2030
|
+
}
|
|
2031
|
+
function numberOrZero(value) {
|
|
2032
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2033
|
+
}
|
|
2034
|
+
function addCacheCounts(usage, extra) {
|
|
2035
|
+
if (extra.cacheRead > 0) usage.cachedTokens = (usage.cachedTokens ?? 0) + extra.cacheRead;
|
|
2036
|
+
if (extra.cacheWrite > 0) usage.cacheWriteTokens = (usage.cacheWriteTokens ?? 0) + extra.cacheWrite;
|
|
2037
|
+
}
|
|
1341
2038
|
/** Read `providerOptions.anthropic`, tolerating its absence or a wrong shape. */
|
|
1342
2039
|
function resolveAnthropicOptions(options) {
|
|
1343
2040
|
const raw = options?.providerOptions?.anthropic;
|
|
@@ -1422,6 +2119,18 @@ var AnthropicSDK = class {
|
|
|
1422
2119
|
};
|
|
1423
2120
|
|
|
1424
2121
|
//#endregion
|
|
2122
|
+
exports.ANTHROPIC_BLOCK_FORMAT = ANTHROPIC_BLOCK_FORMAT;
|
|
2123
|
+
exports.ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER = ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER;
|
|
2124
|
+
exports.ANTHROPIC_CACHE_WRITE_MULTIPLIER = ANTHROPIC_CACHE_WRITE_MULTIPLIER;
|
|
2125
|
+
exports.ANTHROPIC_MODEL_PRICING = ANTHROPIC_MODEL_PRICING;
|
|
2126
|
+
exports.ANTHROPIC_SERVER_TOOL_PRICING = ANTHROPIC_SERVER_TOOL_PRICING;
|
|
1425
2127
|
exports.AnthropicModel = AnthropicModel;
|
|
1426
2128
|
exports.AnthropicSDK = AnthropicSDK;
|
|
2129
|
+
exports.anthropicPricingFor = anthropicPricingFor;
|
|
2130
|
+
exports.fromProviderBlocks = fromProviderBlocks;
|
|
2131
|
+
exports.inferPreservedThinking = inferPreservedThinking;
|
|
2132
|
+
exports.toAnthropicMessages = toAnthropicMessages;
|
|
2133
|
+
exports.toAnthropicTools = toAnthropicTools;
|
|
2134
|
+
exports.toProviderBlocks = toProviderBlocks;
|
|
2135
|
+
exports.withAnthropicCacheWriteRates = withAnthropicCacheWriteRates;
|
|
1427
2136
|
//# sourceMappingURL=index.cjs.map
|