@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/esm/model.mjs
CHANGED
|
@@ -1,24 +1,44 @@
|
|
|
1
|
+
import { anthropicPricingFor, withAnthropicCacheWriteRates } from "./known-pricing.mjs";
|
|
2
|
+
import { acceptsTemperature, inferPreservedThinking, inferThinkingMode, noReasoningForm } from "./thinking-mode.mjs";
|
|
3
|
+
import { inferMaxOutputTokens } from "./known-output-limits.mjs";
|
|
1
4
|
import { inferVisionCapability } from "./known-vision-models.mjs";
|
|
2
|
-
import {
|
|
5
|
+
import { applyServerToolUsage } from "./utils/server-tool-usage.mjs";
|
|
6
|
+
import { applyAnthropicCacheUsage, requestCacheTtl } from "./utils/cache-usage.mjs";
|
|
3
7
|
import { mapStopReason } from "./utils/map-stop-reason.mjs";
|
|
4
8
|
import { toProviderBlocks } from "./utils/provider-blocks.mjs";
|
|
5
9
|
import { toAnthropicMessages } from "./utils/to-anthropic-messages.mjs";
|
|
6
10
|
import { toAnthropicTools } from "./utils/to-anthropic-tools.mjs";
|
|
7
11
|
import { wrapAnthropicError } from "./utils/wrap-anthropic-error.mjs";
|
|
8
12
|
import "./utils/index.mjs";
|
|
9
|
-
import { InvalidRequestError, safeJsonParse } from "@warlock.js/ai";
|
|
13
|
+
import { AIError, InvalidRequestError, computeCost, resolveStreamChunks, safeJsonParse, selectProviderTools } from "@warlock.js/ai";
|
|
10
14
|
import { log } from "@warlock.js/logger";
|
|
11
15
|
|
|
12
16
|
//#region ../../packages/ai-anthropic/src/model.ts
|
|
13
17
|
const LOG_MODULE = "ai.anthropic";
|
|
18
|
+
/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
|
|
19
|
+
const ANTHROPIC_WIRE_FORMAT = "anthropic";
|
|
14
20
|
/**
|
|
15
21
|
* Anthropic requires `max_tokens` on every request (unlike OpenAI,
|
|
16
|
-
* where it is optional). When neither the per-call option nor the
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
22
|
+
* where it is optional). When neither the per-call option nor the model
|
|
23
|
+
* config supplies one, the adapter picks a default large enough for agent
|
|
24
|
+
* work (see `resolveDefaultMaxTokens`):
|
|
25
|
+
*
|
|
26
|
+
* - streaming: the model's documented max output (`known-output-limits.ts`,
|
|
27
|
+
* or `config.maxOutputTokens`), else this value;
|
|
28
|
+
* - non-streaming: this value, capped by the model's max output. The
|
|
29
|
+
* installed `@anthropic-ai/sdk` (0.129.0, `calculateNonstreamingTimeout`)
|
|
30
|
+
* refuses a non-streaming request whose `max_tokens` implies more than
|
|
31
|
+
* ten minutes (above ~21,333 tokens) unless the client sets a `timeout`,
|
|
32
|
+
* so the non-streaming default stays below that.
|
|
33
|
+
*/
|
|
34
|
+
const DEFAULT_MAX_TOKENS = 16384;
|
|
35
|
+
/**
|
|
36
|
+
* Answer room kept on top of a thinking budget when the default
|
|
37
|
+
* `max_tokens` is sized: thinking counts toward `max_tokens` (installed
|
|
38
|
+
* SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`), and in budget
|
|
39
|
+
* mode `max_tokens` must exceed `budget_tokens`.
|
|
20
40
|
*/
|
|
21
|
-
const
|
|
41
|
+
const MIN_ANSWER_TOKENS = 4096;
|
|
22
42
|
/**
|
|
23
43
|
* Anthropic rejects an extended-thinking budget below 1024 tokens, so
|
|
24
44
|
* any resolved budget is floored at this minimum before it reaches the
|
|
@@ -37,11 +57,10 @@ const DEFAULT_THINKING_EFFORT = "medium";
|
|
|
37
57
|
*
|
|
38
58
|
* - Budget-mode models: sent as `thinking.budget_tokens` when the caller
|
|
39
59
|
* doesn't pass an explicit `reasoning.maxTokens`.
|
|
40
|
-
* - Both modes:
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* has always produced.
|
|
60
|
+
* - Both modes: when the caller sets no `maxTokens`, the default
|
|
61
|
+
* `max_tokens` keeps at least this budget plus `MIN_ANSWER_TOKENS`,
|
|
62
|
+
* because thinking counts toward `max_tokens` (installed SDK
|
|
63
|
+
* `ThinkingConfigParam` docs, `messages.d.ts:1105`).
|
|
45
64
|
*/
|
|
46
65
|
const EFFORT_THINKING_BUDGET = {
|
|
47
66
|
minimal: 1024,
|
|
@@ -67,7 +86,7 @@ const EFFORT_TO_OUTPUT_EFFORT = {
|
|
|
67
86
|
max: "max"
|
|
68
87
|
};
|
|
69
88
|
/** Content-block types whose input arrives as `input_json_delta` while streaming. */
|
|
70
|
-
const STREAMED_INPUT_BLOCK_TYPES = new Set([
|
|
89
|
+
const STREAMED_INPUT_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
71
90
|
"tool_use",
|
|
72
91
|
"server_tool_use",
|
|
73
92
|
"mcp_tool_use"
|
|
@@ -117,8 +136,10 @@ var AnthropicModel = class {
|
|
|
117
136
|
this.config = config;
|
|
118
137
|
this.name = config.name;
|
|
119
138
|
this.provider = provider;
|
|
120
|
-
this.pricing = config.pricing;
|
|
139
|
+
this.pricing = withAnthropicCacheWriteRates(config.pricing ?? anthropicPricingFor(config.name));
|
|
121
140
|
this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);
|
|
141
|
+
this.maxOutputTokens = config.maxOutputTokens ?? inferMaxOutputTokens(config.name);
|
|
142
|
+
this.preservedThinking = config.preservedThinking ?? (this.thinkingMode === "adaptive" && inferPreservedThinking(config.name));
|
|
122
143
|
this.capabilities = {
|
|
123
144
|
structuredOutput: config.structuredOutput ?? true,
|
|
124
145
|
vision: config.vision ?? inferVisionCapability(config.name),
|
|
@@ -134,23 +155,50 @@ var AnthropicModel = class {
|
|
|
134
155
|
* instance's `ModelConfig` defaults for this call only.
|
|
135
156
|
*/
|
|
136
157
|
async complete(messages, options) {
|
|
137
|
-
this.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
158
|
+
return this.send(this.buildLoggedRequest(messages, {
|
|
159
|
+
...options,
|
|
160
|
+
stream: false
|
|
161
|
+
}), { signal: options?.signal });
|
|
162
|
+
}
|
|
163
|
+
buildRequest(messages, options) {
|
|
164
|
+
const built = this.buildBody(messages, options);
|
|
165
|
+
const betas = [.../* @__PURE__ */ new Set([...resolveAnthropicOptions(options).betas ?? [], ...built.betas])];
|
|
166
|
+
const body = structuredClone({
|
|
167
|
+
...built.body,
|
|
168
|
+
stream: options?.stream ?? false
|
|
142
169
|
});
|
|
170
|
+
return {
|
|
171
|
+
endpoint: betas.length > 0 ? "beta.messages" : "messages",
|
|
172
|
+
body,
|
|
173
|
+
betas
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
buildLoggedRequest(messages, options) {
|
|
177
|
+
try {
|
|
178
|
+
return this.buildRequest(messages, options);
|
|
179
|
+
} catch (thrown) {
|
|
180
|
+
throw this.logAndWrap(thrown instanceof AIError ? thrown : new InvalidRequestError(`Could not build the Anthropic request: ${thrown instanceof Error ? thrown.message : String(thrown)}`, { cause: thrown }));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
send(request, options) {
|
|
184
|
+
const message = request.body.stream ? "Starting streaming call to messages.create" : "Starting call to messages.create";
|
|
185
|
+
this.logger.debug(LOG_MODULE, "request", message, {
|
|
186
|
+
model: request.body.model,
|
|
187
|
+
messageCount: Array.isArray(request.body.messages) ? request.body.messages.length : 0,
|
|
188
|
+
streaming: request.body.stream,
|
|
189
|
+
toolCount: Array.isArray(request.body.tools) ? request.body.tools.length : 0
|
|
190
|
+
});
|
|
191
|
+
return request.body.stream ? this.streamRequest(request, options) : this.completeRequest(request, options);
|
|
192
|
+
}
|
|
193
|
+
async completeRequest(request, options) {
|
|
143
194
|
let response;
|
|
144
195
|
try {
|
|
145
|
-
response = await this.
|
|
146
|
-
...this.buildBody(messages, options),
|
|
147
|
-
stream: false
|
|
148
|
-
}, options, "create");
|
|
196
|
+
response = await this.dispatch(request, options, "create");
|
|
149
197
|
} catch (thrown) {
|
|
150
198
|
throw this.logAndWrap(thrown);
|
|
151
199
|
}
|
|
152
200
|
const finishReason = mapStopReason(response.stop_reason);
|
|
153
|
-
const usage = this.extractUsage(response.usage);
|
|
201
|
+
const usage = this.extractUsage(response.usage, requestCacheTtl(request.body));
|
|
154
202
|
const toolCalls = this.extractToolCalls(response.content);
|
|
155
203
|
const blocks = toProviderBlocks(response.content);
|
|
156
204
|
this.logger.debug(LOG_MODULE, "response", "call to messages.create succeeded", {
|
|
@@ -177,11 +225,18 @@ var AnthropicModel = class {
|
|
|
177
225
|
* const tokens = await model.countTokens(messages, { tools });
|
|
178
226
|
*/
|
|
179
227
|
async countTokens(messages, options) {
|
|
180
|
-
const
|
|
228
|
+
const request = this.buildLoggedRequest(messages, {
|
|
229
|
+
...options,
|
|
230
|
+
stream: options?.stream === true
|
|
231
|
+
});
|
|
232
|
+
const body = request.body;
|
|
181
233
|
const countBody = {};
|
|
182
234
|
for (const key of COUNT_TOKENS_FIELDS) if (body[key] !== void 0) countBody[key] = body[key];
|
|
183
235
|
try {
|
|
184
|
-
return (await this.
|
|
236
|
+
return (await this.dispatch({
|
|
237
|
+
...request,
|
|
238
|
+
body: countBody
|
|
239
|
+
}, options, "countTokens")).input_tokens;
|
|
185
240
|
} catch (thrown) {
|
|
186
241
|
throw this.logAndWrap(thrown);
|
|
187
242
|
}
|
|
@@ -192,29 +247,38 @@ var AnthropicModel = class {
|
|
|
192
247
|
* arguments have fully accumulated, and a terminal `done` carrying the
|
|
193
248
|
* final finish reason + usage totals. Callers consume it with
|
|
194
249
|
* `for await`.
|
|
250
|
+
*
|
|
251
|
+
* With `options.streamChunks` it also yields `reasoning` for every
|
|
252
|
+
* non-empty `thinking_delta`, `tool-args` for every `input_json_delta`
|
|
253
|
+
* of a client `tool_use` block, and `usage` after `message_start` and
|
|
254
|
+
* each `message_delta` (whose counts are cumulative).
|
|
195
255
|
*/
|
|
196
256
|
async *stream(messages, options) {
|
|
197
|
-
this.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
257
|
+
yield* this.send(this.buildLoggedRequest(messages, {
|
|
258
|
+
...options,
|
|
259
|
+
stream: true
|
|
260
|
+
}), {
|
|
261
|
+
signal: options?.signal,
|
|
262
|
+
streamChunks: options?.streamChunks
|
|
202
263
|
});
|
|
264
|
+
}
|
|
265
|
+
async *streamRequest(request, options) {
|
|
203
266
|
let stream;
|
|
204
267
|
try {
|
|
205
|
-
stream = await this.
|
|
206
|
-
...this.buildBody(messages, options),
|
|
207
|
-
stream: true
|
|
208
|
-
}, options, "create");
|
|
268
|
+
stream = await this.dispatch(request, options, "create");
|
|
209
269
|
} catch (thrown) {
|
|
210
270
|
throw this.logAndWrap(thrown);
|
|
211
271
|
}
|
|
212
272
|
let rawStopReason = null;
|
|
273
|
+
let compaction = compactionIterationUsage(void 0);
|
|
213
274
|
const usage = {
|
|
214
275
|
input: 0,
|
|
215
276
|
output: 0,
|
|
216
|
-
total: 0
|
|
277
|
+
total: 0,
|
|
278
|
+
inputIncludesCache: false
|
|
217
279
|
};
|
|
280
|
+
const cacheTtl = requestCacheTtl(request.body);
|
|
281
|
+
const want = resolveStreamChunks(options);
|
|
218
282
|
const toolBlocks = /* @__PURE__ */ new Map();
|
|
219
283
|
const thinkingBlocks = /* @__PURE__ */ new Map();
|
|
220
284
|
const streamedBlocks = /* @__PURE__ */ new Map();
|
|
@@ -225,10 +289,15 @@ var AnthropicModel = class {
|
|
|
225
289
|
usage.input = event.message.usage.input_tokens ?? 0;
|
|
226
290
|
const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;
|
|
227
291
|
if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
292
|
+
applyServerToolUsage(usage, event.message.usage.server_tool_use);
|
|
293
|
+
applyAnthropicCacheUsage(usage, event.message.usage, cacheTtl);
|
|
294
|
+
if (want.usage) yield {
|
|
295
|
+
type: "usage",
|
|
296
|
+
usage: {
|
|
297
|
+
...usage,
|
|
298
|
+
total: usage.input + usage.output
|
|
299
|
+
}
|
|
300
|
+
};
|
|
232
301
|
continue;
|
|
233
302
|
}
|
|
234
303
|
if (event.type === "content_block_start") {
|
|
@@ -249,10 +318,22 @@ var AnthropicModel = class {
|
|
|
249
318
|
};
|
|
250
319
|
else if (event.delta.type === "input_json_delta") {
|
|
251
320
|
const accumulator = toolBlocks.get(event.index);
|
|
252
|
-
if (accumulator)
|
|
321
|
+
if (accumulator) {
|
|
322
|
+
accumulator.json += event.delta.partial_json;
|
|
323
|
+
if (want.toolArgs && event.delta.partial_json !== "") yield {
|
|
324
|
+
type: "tool-args",
|
|
325
|
+
callId: accumulator.id,
|
|
326
|
+
name: accumulator.name,
|
|
327
|
+
delta: event.delta.partial_json
|
|
328
|
+
};
|
|
329
|
+
}
|
|
253
330
|
} else if (event.delta.type === "thinking_delta") {
|
|
254
331
|
const thinking = thinkingBlocks.get(event.index);
|
|
255
332
|
if (thinking?.type === "thinking") thinking.thinking += event.delta.thinking;
|
|
333
|
+
if (want.reasoning && event.delta.thinking !== "") yield {
|
|
334
|
+
type: "reasoning",
|
|
335
|
+
delta: event.delta.thinking
|
|
336
|
+
};
|
|
256
337
|
} else if (event.delta.type === "signature_delta") {
|
|
257
338
|
const thinking = thinkingBlocks.get(event.index);
|
|
258
339
|
if (thinking?.type === "thinking") thinking.signature += event.delta.signature;
|
|
@@ -275,19 +356,34 @@ var AnthropicModel = class {
|
|
|
275
356
|
}
|
|
276
357
|
if (event.type === "message_delta") {
|
|
277
358
|
rawStopReason = event.delta.stop_reason ?? rawStopReason;
|
|
359
|
+
if (Array.isArray(event.usage.iterations)) compaction = compactionIterationUsage(event.usage);
|
|
360
|
+
applyServerToolUsage(usage, event.usage.server_tool_use);
|
|
278
361
|
usage.output = event.usage.output_tokens ?? usage.output;
|
|
279
362
|
const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
|
|
280
363
|
if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
364
|
+
applyAnthropicCacheUsage(usage, {
|
|
365
|
+
cache_read_input_tokens: event.usage.cache_read_input_tokens,
|
|
366
|
+
cache_creation_input_tokens: event.usage.cache_creation_input_tokens,
|
|
367
|
+
cache_creation: usage.cacheWrite1hTokens !== void 0 ? { ephemeral_1h_input_tokens: usage.cacheWrite1hTokens } : void 0
|
|
368
|
+
}, cacheTtl);
|
|
369
|
+
if (want.usage) yield {
|
|
370
|
+
type: "usage",
|
|
371
|
+
usage: {
|
|
372
|
+
...usage,
|
|
373
|
+
total: usage.input + usage.output
|
|
374
|
+
}
|
|
375
|
+
};
|
|
285
376
|
}
|
|
286
377
|
}
|
|
287
378
|
} catch (thrown) {
|
|
288
379
|
throw this.logAndWrap(thrown);
|
|
289
380
|
}
|
|
381
|
+
usage.input += compaction.input;
|
|
382
|
+
usage.output += compaction.output;
|
|
383
|
+
addCacheCounts(usage, compaction);
|
|
290
384
|
usage.total = usage.input + usage.output;
|
|
385
|
+
const cost = computeCost(usage, this.pricing);
|
|
386
|
+
if (cost) usage.cost = cost;
|
|
291
387
|
const finishReason = mapStopReason(rawStopReason);
|
|
292
388
|
const blocks = toProviderBlocks(this.finishStreamedBlocks(streamedBlocks));
|
|
293
389
|
this.logger.debug(LOG_MODULE, "response", "Streaming call to messages.create succeeded", {
|
|
@@ -341,38 +437,74 @@ var AnthropicModel = class {
|
|
|
341
437
|
* and the beta-only body fields); otherwise to `client.messages`. The
|
|
342
438
|
* cancellation signal rides in the request options, as before.
|
|
343
439
|
*/
|
|
344
|
-
|
|
345
|
-
const betas = resolveAnthropicOptions(options).betas;
|
|
440
|
+
dispatch(request, options, method) {
|
|
346
441
|
const requestOptions = options?.signal ? { signal: options.signal } : void 0;
|
|
347
|
-
const api =
|
|
348
|
-
return api[method].call(api,
|
|
349
|
-
...body,
|
|
350
|
-
betas
|
|
351
|
-
} : body, requestOptions);
|
|
442
|
+
const api = request.endpoint === "beta.messages" ? this.client.beta.messages : this.client.messages;
|
|
443
|
+
return api[method].call(api, request.endpoint === "beta.messages" ? {
|
|
444
|
+
...request.body,
|
|
445
|
+
betas: request.betas
|
|
446
|
+
} : request.body, requestOptions);
|
|
352
447
|
}
|
|
353
448
|
/**
|
|
354
|
-
* The full request body: the adapter-built
|
|
355
|
-
* `thinking.display` when thinking is sent on an adaptive
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
449
|
+
* The full request body and the beta flags it needs: the adapter-built
|
|
450
|
+
* params, then `thinking.display` when thinking is sent on an adaptive
|
|
451
|
+
* model, the typed `thinkingBlockBinding`, `contextManagement` and
|
|
452
|
+
* `serverCompaction` options, then `providerOptions.anthropic.body`
|
|
453
|
+
* merged last (caller fields win). The body may add fields the adapter
|
|
454
|
+
* does not build (`fallbacks`, …) but may not replace the conversation or
|
|
455
|
+
* the model — see {@link RESERVED_BODY_FIELDS}.
|
|
360
456
|
*/
|
|
361
457
|
buildBody(messages, options) {
|
|
362
458
|
const anthropic = resolveAnthropicOptions(options);
|
|
363
|
-
const
|
|
459
|
+
const betas = [];
|
|
460
|
+
const body = { ...this.buildParams(messages, options, { dropRetainedThinking: this.preservedThinking && anthropic.thinkingBlockBinding === void 0 }) };
|
|
364
461
|
if (anthropic.thinkingDisplay && this.thinkingMode === "adaptive" && body.thinking?.type === "adaptive") body.thinking = {
|
|
365
462
|
...body.thinking,
|
|
366
463
|
display: anthropic.thinkingDisplay
|
|
367
464
|
};
|
|
465
|
+
if (anthropic.thinkingBlockBinding) this.applyBlockBinding(body, anthropic.thinkingBlockBinding, betas);
|
|
466
|
+
const edits = buildContextEdits(anthropic);
|
|
467
|
+
if (edits.length > 0) {
|
|
468
|
+
if (anthropic.body?.context_management !== void 0) throw new InvalidRequestError("providerOptions.anthropic.contextManagement / serverCompaction cannot be combined with body.context_management: set the edits in one place.", { context: { fields: ["context_management"] } });
|
|
469
|
+
body.context_management = { edits };
|
|
470
|
+
if (edits.some((edit) => edit.type !== "compact_20260112")) betas.push(CONTEXT_MANAGEMENT_BETA);
|
|
471
|
+
if (edits.some((edit) => edit.type === "compact_20260112")) betas.push(COMPACTION_BETA);
|
|
472
|
+
}
|
|
368
473
|
const reserved = Object.keys(anthropic.body ?? {}).filter((key) => RESERVED_BODY_FIELDS.has(key));
|
|
369
474
|
if (reserved.length > 0) throw new 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 } });
|
|
370
475
|
return {
|
|
371
|
-
|
|
372
|
-
|
|
476
|
+
body: {
|
|
477
|
+
...body,
|
|
478
|
+
...anthropic.body
|
|
479
|
+
},
|
|
480
|
+
betas
|
|
373
481
|
};
|
|
374
482
|
}
|
|
375
483
|
/**
|
|
484
|
+
* Set `thinking.block_binding.prefix_mismatch_behavior`. It rides on
|
|
485
|
+
* adaptive or budget (`enabled`) thinking. A model that runs adaptive
|
|
486
|
+
* thinking when `thinking` is omitted (every preserved-thinking model) gets
|
|
487
|
+
* an explicit `{ type: "adaptive" }`, which is the same request. Any other
|
|
488
|
+
* shape (`between_tools`, `disabled`) takes no extra field, so the option
|
|
489
|
+
* is skipped with a debug log.
|
|
490
|
+
*/
|
|
491
|
+
applyBlockBinding(body, behavior, betas) {
|
|
492
|
+
const thinking = body.thinking;
|
|
493
|
+
const type = thinking?.type ?? (this.preservedThinking ? "adaptive" : void 0);
|
|
494
|
+
if (type !== "adaptive" && type !== "enabled") {
|
|
495
|
+
this.logger.debug(LOG_MODULE, "thinking-block-binding.ignored", "thinkingBlockBinding needs adaptive or budget thinking on this request; not sent", {
|
|
496
|
+
model: this.name,
|
|
497
|
+
thinking: thinking?.type ?? "none"
|
|
498
|
+
});
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
body.thinking = {
|
|
502
|
+
...thinking ?? { type },
|
|
503
|
+
block_binding: { prefix_mismatch_behavior: behavior }
|
|
504
|
+
};
|
|
505
|
+
betas.push(THINKING_BINDING_BETA);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
376
508
|
* Assemble the Anthropic request body shared by `complete()` and
|
|
377
509
|
* `stream()` (each adds its own `stream` literal so the SDK's create
|
|
378
510
|
* overload resolves to the right return type). Hoists the system
|
|
@@ -384,13 +516,13 @@ var AnthropicModel = class {
|
|
|
384
516
|
* rejects the two together, and always for adaptive-mode models, which
|
|
385
517
|
* reject any explicit temperature (see `acceptsTemperature`).
|
|
386
518
|
*/
|
|
387
|
-
buildParams(messages, options) {
|
|
388
|
-
const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
|
|
519
|
+
buildParams(messages, options, conversion) {
|
|
520
|
+
const { system, messages: anthropicMessages } = toAnthropicMessages(messages, conversion);
|
|
389
521
|
const thinking = this.buildThinking(options?.reasoning);
|
|
390
522
|
const temperature = options?.temperature ?? this.config.temperature;
|
|
391
523
|
const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
|
|
392
524
|
const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
|
|
393
|
-
const maxTokens = configuredMaxTokens ?? (
|
|
525
|
+
const maxTokens = configuredMaxTokens ?? this.resolveDefaultMaxTokens(options?.stream === true, thinking.reservedTokens);
|
|
394
526
|
const sendTemperature = temperature !== void 0 && !thinking.thinking && acceptsTemperature(this.thinkingMode);
|
|
395
527
|
if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
|
|
396
528
|
return {
|
|
@@ -400,12 +532,31 @@ var AnthropicModel = class {
|
|
|
400
532
|
...this.buildAutomaticCacheControl(options?.cacheControl),
|
|
401
533
|
...this.buildSystem(system, options?.cacheControl),
|
|
402
534
|
...sendTemperature ? { temperature } : {},
|
|
403
|
-
...this.buildTools(options
|
|
535
|
+
...this.buildTools(options, options?.cacheControl),
|
|
404
536
|
...this.buildOutputConfig(options?.responseSchema, thinking.effort),
|
|
405
537
|
...thinking.thinking ? { thinking: thinking.thinking } : {}
|
|
406
538
|
};
|
|
407
539
|
}
|
|
408
540
|
/**
|
|
541
|
+
* The `max_tokens` sent when neither the call nor the model config sets
|
|
542
|
+
* `maxTokens`:
|
|
543
|
+
*
|
|
544
|
+
* - streaming: the model's documented max output, or
|
|
545
|
+
* `DEFAULT_MAX_TOKENS` (16384) for an unknown id;
|
|
546
|
+
* - non-streaming: `DEFAULT_MAX_TOKENS`, capped by the model's max output
|
|
547
|
+
* (the SDK requires streaming for larger non-streaming requests unless
|
|
548
|
+
* the client sets a `timeout`; see `DEFAULT_MAX_TOKENS`).
|
|
549
|
+
*
|
|
550
|
+
* With thinking on, the value is raised to at least the thinking budget
|
|
551
|
+
* plus `MIN_ANSWER_TOKENS`, and never exceeds the model's max output.
|
|
552
|
+
*/
|
|
553
|
+
resolveDefaultMaxTokens(stream, reservedTokens) {
|
|
554
|
+
const limit = this.maxOutputTokens;
|
|
555
|
+
const base = stream ? limit ?? DEFAULT_MAX_TOKENS : limit === void 0 ? DEFAULT_MAX_TOKENS : Math.min(limit, DEFAULT_MAX_TOKENS);
|
|
556
|
+
const withThinking = reservedTokens === void 0 ? base : Math.max(base, reservedTokens + MIN_ANSWER_TOKENS);
|
|
557
|
+
return limit === void 0 ? withThinking : Math.min(withThinking, limit);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
409
560
|
* Spread-friendly `system` fragment. Returns an empty object when no
|
|
410
561
|
* system prompt was hoisted out of the messages.
|
|
411
562
|
*
|
|
@@ -468,12 +619,18 @@ var AnthropicModel = class {
|
|
|
468
619
|
* `messages.d.ts:1084`), so the budget is floored at that minimum.
|
|
469
620
|
*
|
|
470
621
|
* The neutral `effort: "none"` ("run without reasoning") is mapped per
|
|
471
|
-
* model:
|
|
472
|
-
* - Claude Sonnet 5.5
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
622
|
+
* model by `noReasoningForm` (see `thinking-mode.ts` for the sources):
|
|
623
|
+
* - Claude Sonnet 5.5: `thinking: { type: "between_tools" }`, its only
|
|
624
|
+
* thinking-off form. It is sent alone — no `output_config.effort` (the
|
|
625
|
+
* model default, `high`, is within the `high`-or-below range it
|
|
626
|
+
* requires) and no `display`, both of which the API rejects alongside it.
|
|
627
|
+
* - Claude Sonnet 5 and Claude Haiku 5.5: `thinking: { type: "disabled" }`
|
|
628
|
+
* with no effort and no `display`. Haiku 5.5 accepts `disabled` only at
|
|
629
|
+
* effort `high` or below; its default (`medium`) applies.
|
|
630
|
+
* - Claude Opus 5: adaptive thinking at `output_config.effort: "low"`.
|
|
631
|
+
* Opus 5 accepts `disabled`, but with it the model can write a tool call
|
|
632
|
+
* as plain text (the call never runs) and leak thinking tags; the lowest
|
|
633
|
+
* effort is the documented alternative.
|
|
477
634
|
* - Opus 5.5 and Fable 5/5.1: no `thinking` field. These models have no
|
|
478
635
|
* thinking-off form (`disabled` and `budget_tokens` are a 400 at every
|
|
479
636
|
* effort), so thinking still runs at the model's default effort. Pass
|
|
@@ -482,7 +639,7 @@ var AnthropicModel = class {
|
|
|
482
639
|
*/
|
|
483
640
|
buildThinking(reasoning) {
|
|
484
641
|
if (!this.capabilities.reasoning || !reasoning) return {};
|
|
485
|
-
if (reasoning.effort === "none") return
|
|
642
|
+
if (reasoning.effort === "none") return this.buildNoReasoning();
|
|
486
643
|
if (reasoning.maxTokens === void 0 && reasoning.effort === void 0) return {};
|
|
487
644
|
const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;
|
|
488
645
|
const budget = Math.max(MIN_THINKING_BUDGET, reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort]);
|
|
@@ -500,6 +657,22 @@ var AnthropicModel = class {
|
|
|
500
657
|
};
|
|
501
658
|
}
|
|
502
659
|
/**
|
|
660
|
+
* The thinking settings for `reasoning: { effort: "none" }`, per
|
|
661
|
+
* `noReasoningForm(this.name)` (documented on `buildThinking`).
|
|
662
|
+
*/
|
|
663
|
+
buildNoReasoning() {
|
|
664
|
+
switch (noReasoningForm(this.name)) {
|
|
665
|
+
case "between_tools": return { thinking: { type: "between_tools" } };
|
|
666
|
+
case "disabled": return { thinking: { type: "disabled" } };
|
|
667
|
+
case "lowest-effort": return {
|
|
668
|
+
thinking: { type: "adaptive" },
|
|
669
|
+
effort: EFFORT_TO_OUTPUT_EFFORT.low,
|
|
670
|
+
reservedTokens: EFFORT_THINKING_BUDGET.low
|
|
671
|
+
};
|
|
672
|
+
case "omit": return {};
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
503
676
|
* Spread-friendly tools fragment. Returns an empty object when no
|
|
504
677
|
* tools were supplied so the caller can unconditionally spread it.
|
|
505
678
|
*
|
|
@@ -512,9 +685,16 @@ var AnthropicModel = class {
|
|
|
512
685
|
* breakpoint there would pay the ~1.25x write surcharge every turn
|
|
513
686
|
* with no reads.
|
|
514
687
|
*/
|
|
515
|
-
buildTools(
|
|
688
|
+
buildTools(options, cacheControl) {
|
|
689
|
+
const { tools, definitions } = selectProviderTools(options, {
|
|
690
|
+
accepts: [ANTHROPIC_WIRE_FORMAT, this.provider],
|
|
691
|
+
module: LOG_MODULE,
|
|
692
|
+
model: this.name,
|
|
693
|
+
logger: this.logger
|
|
694
|
+
});
|
|
516
695
|
const mapped = toAnthropicTools(tools);
|
|
517
|
-
|
|
696
|
+
const providerDefined = definitions;
|
|
697
|
+
if (!mapped) return providerDefined.length > 0 ? { tools: providerDefined } : {};
|
|
518
698
|
if (this.config.promptCaching && mapped.length > 0) {
|
|
519
699
|
const last = mapped.length - 1;
|
|
520
700
|
const lastTool = mapped[last];
|
|
@@ -523,7 +703,7 @@ var AnthropicModel = class {
|
|
|
523
703
|
cache_control: this.cacheControl(cacheControl?.ttl)
|
|
524
704
|
};
|
|
525
705
|
}
|
|
526
|
-
return { tools: mapped };
|
|
706
|
+
return { tools: [...providerDefined, ...mapped] };
|
|
527
707
|
}
|
|
528
708
|
/**
|
|
529
709
|
* Spread-friendly `output_config` fragment carrying both the native
|
|
@@ -597,27 +777,35 @@ var AnthropicModel = class {
|
|
|
597
777
|
/**
|
|
598
778
|
* Normalize Anthropic's `usage` block into the neutral `Usage` shape.
|
|
599
779
|
* Anthropic reports `input_tokens` / `output_tokens` separately with
|
|
600
|
-
* no pre-summed total, so `total` is computed.
|
|
601
|
-
*
|
|
602
|
-
* `cacheWriteTokens`,
|
|
780
|
+
* no pre-summed total, so `total` is computed. `input_tokens` is the
|
|
781
|
+
* uncached remainder only — cache reads (`cachedTokens`) and writes
|
|
782
|
+
* (`cacheWriteTokens`, with the 1-hour share in `cacheWrite1hTokens`)
|
|
783
|
+
* come on top of it — so the usage is marked
|
|
784
|
+
* `inputIncludesCache: false`. Anthropic also reports
|
|
603
785
|
* `output_tokens_details.thinking_tokens` as the reasoning subset of
|
|
604
786
|
* its inclusive `output_tokens`; it is surfaced as `reasoningTokens`
|
|
605
|
-
* when present, without changing the billing total.
|
|
787
|
+
* when present, without changing the billing total. Server-side
|
|
788
|
+
* compaction iterations are added in (see {@link compactionIterationUsage}).
|
|
789
|
+
* `cost` is attached when the model is priced.
|
|
606
790
|
*/
|
|
607
|
-
extractUsage(raw) {
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
const cacheWrite = raw.cache_creation_input_tokens;
|
|
791
|
+
extractUsage(raw, cacheTtl) {
|
|
792
|
+
const compaction = compactionIterationUsage(raw);
|
|
793
|
+
const input = (raw.input_tokens ?? 0) + compaction.input;
|
|
794
|
+
const output = (raw.output_tokens ?? 0) + compaction.output;
|
|
612
795
|
const thinkingTokens = raw.output_tokens_details?.thinking_tokens;
|
|
613
|
-
|
|
796
|
+
const usage = {
|
|
614
797
|
input,
|
|
615
798
|
output,
|
|
616
799
|
total: input + output,
|
|
617
|
-
|
|
618
|
-
...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
|
|
800
|
+
inputIncludesCache: false,
|
|
619
801
|
...thinkingTokens !== null && thinkingTokens !== void 0 ? { reasoningTokens: thinkingTokens } : {}
|
|
620
802
|
};
|
|
803
|
+
applyServerToolUsage(usage, raw.server_tool_use);
|
|
804
|
+
applyAnthropicCacheUsage(usage, raw, cacheTtl);
|
|
805
|
+
addCacheCounts(usage, compaction);
|
|
806
|
+
const cost = computeCost(usage, this.pricing);
|
|
807
|
+
if (cost) usage.cost = cost;
|
|
808
|
+
return usage;
|
|
621
809
|
}
|
|
622
810
|
/**
|
|
623
811
|
* Wrap a thrown provider error into the typed `AIError` hierarchy and
|
|
@@ -640,7 +828,7 @@ var AnthropicModel = class {
|
|
|
640
828
|
* model call tools the agent never registered). `stream` is chosen per
|
|
641
829
|
* call by `complete()` / `stream()`.
|
|
642
830
|
*/
|
|
643
|
-
const RESERVED_BODY_FIELDS = new Set([
|
|
831
|
+
const RESERVED_BODY_FIELDS = /* @__PURE__ */ new Set([
|
|
644
832
|
"model",
|
|
645
833
|
"messages",
|
|
646
834
|
"system",
|
|
@@ -661,6 +849,119 @@ const COUNT_TOKENS_FIELDS = [
|
|
|
661
849
|
"context_management",
|
|
662
850
|
"mcp_servers"
|
|
663
851
|
];
|
|
852
|
+
/** Beta flag for typed context editing (Anthropic docs, "Context editing", read 2026-10-17). */
|
|
853
|
+
const CONTEXT_MANAGEMENT_BETA = "context-management-2025-06-27";
|
|
854
|
+
/** Beta flag for threshold compaction (Anthropic docs, "Compaction at a token threshold"). */
|
|
855
|
+
const COMPACTION_BETA = "compact-2026-01-12";
|
|
856
|
+
/** Beta flag for `thinking.block_binding` (Anthropic docs, "Preserved thinking"). */
|
|
857
|
+
const THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
|
|
858
|
+
/** The API rejects a compaction trigger below 50,000 input tokens. */
|
|
859
|
+
const MIN_COMPACTION_TRIGGER = 5e4;
|
|
860
|
+
/**
|
|
861
|
+
* Translate the typed `contextManagement` / `serverCompaction` options into
|
|
862
|
+
* `context_management.edits`. `clear_thinking_20251015` must come first
|
|
863
|
+
* when strategies are combined (Anthropic docs, "Context editing").
|
|
864
|
+
*/
|
|
865
|
+
function buildContextEdits(anthropic) {
|
|
866
|
+
const edits = [];
|
|
867
|
+
const { clearThinking, clearToolUses } = anthropic.contextManagement ?? {};
|
|
868
|
+
if (clearThinking) edits.push(clearThinkingEdit(clearThinking === true ? {} : clearThinking));
|
|
869
|
+
if (clearToolUses) edits.push(clearToolUsesEdit(clearToolUses === true ? {} : clearToolUses));
|
|
870
|
+
if (anthropic.serverCompaction) {
|
|
871
|
+
const compaction = anthropic.serverCompaction === true ? {} : anthropic.serverCompaction;
|
|
872
|
+
if (compaction.triggerTokens !== void 0) assertCount("serverCompaction.triggerTokens", compaction.triggerTokens, MIN_COMPACTION_TRIGGER);
|
|
873
|
+
edits.push({
|
|
874
|
+
type: "compact_20260112",
|
|
875
|
+
...compaction.triggerTokens !== void 0 ? { trigger: {
|
|
876
|
+
type: "input_tokens",
|
|
877
|
+
value: compaction.triggerTokens
|
|
878
|
+
} } : {},
|
|
879
|
+
...compaction.instructions ? { instructions: compaction.instructions } : {}
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
return edits;
|
|
883
|
+
}
|
|
884
|
+
function clearThinkingEdit(options) {
|
|
885
|
+
if (options.keep === void 0 || options.keep === "all") return {
|
|
886
|
+
type: "clear_thinking_20251015",
|
|
887
|
+
...options.keep ? { keep: "all" } : {}
|
|
888
|
+
};
|
|
889
|
+
assertCount("contextManagement.clearThinking.keep", options.keep, 1);
|
|
890
|
+
return {
|
|
891
|
+
type: "clear_thinking_20251015",
|
|
892
|
+
keep: {
|
|
893
|
+
type: "thinking_turns",
|
|
894
|
+
value: options.keep
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function clearToolUsesEdit(options) {
|
|
899
|
+
const edit = { type: "clear_tool_uses_20250919" };
|
|
900
|
+
if (options.trigger) {
|
|
901
|
+
const trigger = "inputTokens" in options.trigger ? {
|
|
902
|
+
type: "input_tokens",
|
|
903
|
+
value: options.trigger.inputTokens
|
|
904
|
+
} : {
|
|
905
|
+
type: "tool_uses",
|
|
906
|
+
value: options.trigger.toolUses
|
|
907
|
+
};
|
|
908
|
+
assertCount("contextManagement.clearToolUses.trigger", trigger.value, 1);
|
|
909
|
+
edit.trigger = trigger;
|
|
910
|
+
}
|
|
911
|
+
if (options.keep !== void 0) {
|
|
912
|
+
assertCount("contextManagement.clearToolUses.keep", options.keep, 0);
|
|
913
|
+
edit.keep = {
|
|
914
|
+
type: "tool_uses",
|
|
915
|
+
value: options.keep
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
if (options.clearAtLeast !== void 0) {
|
|
919
|
+
assertCount("contextManagement.clearToolUses.clearAtLeast", options.clearAtLeast, 1);
|
|
920
|
+
edit.clear_at_least = {
|
|
921
|
+
type: "input_tokens",
|
|
922
|
+
value: options.clearAtLeast
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
if (options.excludeTools !== void 0) edit.exclude_tools = [...options.excludeTools];
|
|
926
|
+
if (options.clearToolInputs !== void 0) edit.clear_tool_inputs = Array.isArray(options.clearToolInputs) ? [...options.clearToolInputs] : options.clearToolInputs;
|
|
927
|
+
return edit;
|
|
928
|
+
}
|
|
929
|
+
function assertCount(field, value, minimum) {
|
|
930
|
+
if (!Number.isSafeInteger(value) || value < minimum) throw new InvalidRequestError(`providerOptions.anthropic.${field} must be an integer of at least ${minimum}; got ${value}.`, { context: { field } });
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Tokens of the `compaction` entries in a beta response's `usage.iterations`.
|
|
934
|
+
* With server-side compaction the top-level `input_tokens` / `output_tokens`
|
|
935
|
+
* cover only the non-compaction iterations; the billed total is the sum
|
|
936
|
+
* over every iteration (Anthropic docs, "Compaction at a token threshold" →
|
|
937
|
+
* "Understanding usage", read 2026-10-17). Zeros when there are none.
|
|
938
|
+
*/
|
|
939
|
+
function compactionIterationUsage(raw) {
|
|
940
|
+
const totals = {
|
|
941
|
+
input: 0,
|
|
942
|
+
output: 0,
|
|
943
|
+
cacheRead: 0,
|
|
944
|
+
cacheWrite: 0
|
|
945
|
+
};
|
|
946
|
+
const iterations = raw?.iterations;
|
|
947
|
+
if (!Array.isArray(iterations)) return totals;
|
|
948
|
+
for (const entry of iterations) {
|
|
949
|
+
const iteration = entry;
|
|
950
|
+
if (iteration?.type !== "compaction") continue;
|
|
951
|
+
totals.input += numberOrZero(iteration.input_tokens);
|
|
952
|
+
totals.output += numberOrZero(iteration.output_tokens);
|
|
953
|
+
totals.cacheRead += numberOrZero(iteration.cache_read_input_tokens);
|
|
954
|
+
totals.cacheWrite += numberOrZero(iteration.cache_creation_input_tokens);
|
|
955
|
+
}
|
|
956
|
+
return totals;
|
|
957
|
+
}
|
|
958
|
+
function numberOrZero(value) {
|
|
959
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
960
|
+
}
|
|
961
|
+
function addCacheCounts(usage, extra) {
|
|
962
|
+
if (extra.cacheRead > 0) usage.cachedTokens = (usage.cachedTokens ?? 0) + extra.cacheRead;
|
|
963
|
+
if (extra.cacheWrite > 0) usage.cacheWriteTokens = (usage.cacheWriteTokens ?? 0) + extra.cacheWrite;
|
|
964
|
+
}
|
|
664
965
|
/** Read `providerOptions.anthropic`, tolerating its absence or a wrong shape. */
|
|
665
966
|
function resolveAnthropicOptions(options) {
|
|
666
967
|
const raw = options?.providerOptions?.anthropic;
|