@ai-sdk/anthropic 3.0.81 → 3.0.82
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 +6 -0
- package/dist/index.d.mts +56 -31
- package/dist/index.d.ts +56 -31
- package/dist/index.js +114 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +114 -67
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.d.mts +1 -1
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal/index.js +113 -66
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +113 -66
- package/dist/internal/index.mjs.map +1 -1
- package/docs/05-anthropic.mdx +58 -0
- package/package.json +1 -1
- package/src/anthropic-message-metadata.ts +67 -54
- package/src/anthropic-messages-api.ts +48 -37
- package/src/anthropic-messages-language-model.ts +90 -73
- package/src/anthropic-messages-options.ts +29 -0
- package/src/convert-anthropic-messages-usage.ts +28 -20
package/docs/05-anthropic.mdx
CHANGED
|
@@ -293,6 +293,63 @@ const { text } = await generateText({
|
|
|
293
293
|
|
|
294
294
|
The `inferenceGeo` option accepts `'us'` (US-only infrastructure) or `'global'` (default, any available geography).
|
|
295
295
|
|
|
296
|
+
### Safety Classifiers and Fallbacks
|
|
297
|
+
|
|
298
|
+
Claude Fable 5 has safeguards that limit its performance in certain areas like cybersecurity, biology, and chemistry, and automated safety checks are run on every request.
|
|
299
|
+
|
|
300
|
+
When one of these checks blocks a request, the API does not answer it. Instead it returns a classifier block: a `200` response with a `refusal` stop reason and an optional `stop_details` object describing the category that triggered the block. The AI SDK surfaces this as a `content-filter` finish reason, with the details available on `providerMetadata.anthropic.stopDetails`.
|
|
301
|
+
|
|
302
|
+
A classifier block looks like this:
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"type": "message",
|
|
307
|
+
"model": "claude-fable-5",
|
|
308
|
+
"content": [],
|
|
309
|
+
"stop_reason": "refusal",
|
|
310
|
+
"stop_details": {
|
|
311
|
+
"type": "refusal",
|
|
312
|
+
"category": "cyber",
|
|
313
|
+
"explanation": "This request triggered restrictions on violative cyber content and was blocked under Anthropic's Usage Policy."
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Branch on the finish reason rather than on the presence of `stop_details` — the API may return a refusal with no details at all.
|
|
319
|
+
|
|
320
|
+
To avoid receiving a classifier block, pass the `fallbacks` option. When the primary model's classifiers block a turn, the API automatically retries it server-side on the next model in the chain and returns that model's answer. The required beta header is added for you.
|
|
321
|
+
|
|
322
|
+
```ts highlight="8-10"
|
|
323
|
+
import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
|
|
324
|
+
import { generateText } from 'ai';
|
|
325
|
+
|
|
326
|
+
const { text } = await generateText({
|
|
327
|
+
model: anthropic('claude-fable-5'),
|
|
328
|
+
prompt: 'Explain the history of cryptography.',
|
|
329
|
+
providerOptions: {
|
|
330
|
+
anthropic: {
|
|
331
|
+
fallbacks: [{ model: 'claude-fable-5' }],
|
|
332
|
+
} satisfies AnthropicLanguageModelOptions,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Each fallback entry requires a `model` and may additionally override `max_tokens`, `thinking`, `output_config`, and `speed` for that attempt only.
|
|
338
|
+
|
|
339
|
+
When a fallback serves the turn, it is recorded in the per-model usage breakdown (`usage.iterations`) as a `fallback_message` entry. The AI SDK exposes this breakdown on `providerMetadata.anthropic.iterations`, so you can detect whether a fallback ran:
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
// set up `result` with a `streamText` call passing `fallbacks` as shown
|
|
343
|
+
// above, then consume the stream
|
|
344
|
+
|
|
345
|
+
const { iterations } = (await result.providerMetadata)?.anthropic ?? {};
|
|
346
|
+
const servedByFallback = iterations?.some(
|
|
347
|
+
iteration => iteration.type === 'fallback_message',
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
console.log('Served by fallback:', servedByFallback);
|
|
351
|
+
```
|
|
352
|
+
|
|
296
353
|
### Reasoning
|
|
297
354
|
|
|
298
355
|
Anthropic models support extended thinking, where Claude shows its reasoning process before providing a final answer.
|
|
@@ -1502,6 +1559,7 @@ and the `mediaType` should be set to `'application/pdf'`.
|
|
|
1502
1559
|
|
|
1503
1560
|
| Model | Image Input | Object Generation | Tool Usage | Computer Use | Web Search | Tool Search | Compaction |
|
|
1504
1561
|
| ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
|
|
1562
|
+
| `claude-fable-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
1505
1563
|
| `claude-opus-4-8` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
1506
1564
|
| `claude-opus-4-7` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
1507
1565
|
| `claude-opus-4-6` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
package/package.json
CHANGED
|
@@ -8,66 +8,43 @@ import type { JSONObject } from '@ai-sdk/provider';
|
|
|
8
8
|
* - `compaction`: a context compaction step (billed at executor rates).
|
|
9
9
|
* - `message`: an executor sampling iteration (billed at executor rates).
|
|
10
10
|
* - `advisor_message`: an advisor sub-inference (billed at the advisor
|
|
11
|
-
* model's rates
|
|
12
|
-
* usage
|
|
13
|
-
*
|
|
11
|
+
* model's rates). Advisor token usage is NOT rolled into the top-level
|
|
12
|
+
* usage totals because it bills at a different rate; inspect this array
|
|
13
|
+
* directly for advisor billing.
|
|
14
|
+
* - `fallback_message`: a server-side fallback attempt that served the turn.
|
|
15
|
+
* Inspect this array for exact per-model attribution on a turn that fell
|
|
16
|
+
* back.
|
|
14
17
|
*/
|
|
15
|
-
export type AnthropicUsageIteration =
|
|
16
|
-
|
|
|
17
|
-
type: 'compaction' | 'message';
|
|
18
|
+
export type AnthropicUsageIteration = {
|
|
19
|
+
type: 'compaction' | 'message' | 'advisor_message' | 'fallback_message';
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Number of output tokens generated in this iteration.
|
|
26
|
-
*/
|
|
27
|
-
outputTokens: number;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Number of cache-creation input tokens consumed in this iteration.
|
|
31
|
-
*/
|
|
32
|
-
cacheCreationInputTokens?: number;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Number of cache-read input tokens consumed in this iteration.
|
|
36
|
-
*/
|
|
37
|
-
cacheReadInputTokens?: number;
|
|
38
|
-
}
|
|
39
|
-
| {
|
|
40
|
-
type: 'advisor_message';
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The advisor model that produced this iteration.
|
|
44
|
-
*/
|
|
45
|
-
model: string;
|
|
21
|
+
/**
|
|
22
|
+
* The model that produced this iteration. Populated for the per-model
|
|
23
|
+
* attribution cases (the fallback chain and advisor sub-inferences) and
|
|
24
|
+
* absent otherwise.
|
|
25
|
+
*/
|
|
26
|
+
model?: string;
|
|
46
27
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Number of input tokens consumed in this iteration.
|
|
30
|
+
*/
|
|
31
|
+
inputTokens: number;
|
|
51
32
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Number of output tokens generated in this iteration.
|
|
35
|
+
*/
|
|
36
|
+
outputTokens: number;
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*/
|
|
62
|
-
cacheCreationInputTokens?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Number of cache-creation input tokens consumed in this iteration.
|
|
40
|
+
*/
|
|
41
|
+
cacheCreationInputTokens?: number;
|
|
63
42
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
cacheReadInputTokens?: number;
|
|
70
|
-
};
|
|
43
|
+
/**
|
|
44
|
+
* Number of cache-read input tokens consumed in this iteration.
|
|
45
|
+
*/
|
|
46
|
+
cacheReadInputTokens?: number;
|
|
47
|
+
};
|
|
71
48
|
|
|
72
49
|
export interface AnthropicMessageMetadata {
|
|
73
50
|
usage: JSONObject;
|
|
@@ -76,6 +53,42 @@ export interface AnthropicMessageMetadata {
|
|
|
76
53
|
cacheCreationInputTokens: number | null;
|
|
77
54
|
stopSequence: string | null;
|
|
78
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Details about why the request stopped. Present only when the API returns
|
|
58
|
+
* a `refusal` stop reason together with a `stop_details` object (a
|
|
59
|
+
* classifier block or a model refusal).
|
|
60
|
+
*
|
|
61
|
+
* Branch on the finish reason (`content-filter`), not on this object: the
|
|
62
|
+
* API may return a refusal with no details at all, so this field can be
|
|
63
|
+
* absent even on a refusal and should not be relied upon being present.
|
|
64
|
+
*/
|
|
65
|
+
stopDetails?: {
|
|
66
|
+
/**
|
|
67
|
+
* The kind of stop detail. `'refusal'` for classifier blocks and model
|
|
68
|
+
* refusals.
|
|
69
|
+
*/
|
|
70
|
+
type: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The classifier category that triggered the block, e.g. `'cyber'` or
|
|
74
|
+
* `'bio'`. Absent for model refusals and other cases.
|
|
75
|
+
*/
|
|
76
|
+
category?: string;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Human-readable explanation of why the request was blocked. May be
|
|
80
|
+
* absent even on a refusal.
|
|
81
|
+
*/
|
|
82
|
+
explanation?: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The canonical id of a model to retry directly. Populated only when the
|
|
86
|
+
* request included fallbacks and the fallback attempt could not be made
|
|
87
|
+
* (e.g. the fallback model was rate limited or overloaded).
|
|
88
|
+
*/
|
|
89
|
+
recommendedModel?: string;
|
|
90
|
+
};
|
|
91
|
+
|
|
79
92
|
/**
|
|
80
93
|
* Usage breakdown by iteration when compaction is triggered.
|
|
81
94
|
*
|
|
@@ -601,6 +601,15 @@ export type AnthropicResponseContextManagement = {
|
|
|
601
601
|
applied_edits: AnthropicResponseContextManagementEdit[];
|
|
602
602
|
};
|
|
603
603
|
|
|
604
|
+
const anthropicStopDetailsSchema = z.object({
|
|
605
|
+
type: z.string(),
|
|
606
|
+
category: z.string().nullish(),
|
|
607
|
+
explanation: z.string().nullish(),
|
|
608
|
+
recommended_model: z.string().nullish(),
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
export type AnthropicStopDetails = z.infer<typeof anthropicStopDetailsSchema>;
|
|
612
|
+
|
|
604
613
|
// limited version of the schema, focussed on what is needed for the implementation
|
|
605
614
|
// this approach limits breakages when the API changes and increases efficiency
|
|
606
615
|
export const anthropicMessagesResponseSchema = lazySchema(() =>
|
|
@@ -901,10 +910,17 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
|
|
|
901
910
|
}),
|
|
902
911
|
]),
|
|
903
912
|
}),
|
|
913
|
+
// Server-side fallback marker. Parsed so the response validates, but
|
|
914
|
+
// dropped from the content output (the AI SDK has no model-hop
|
|
915
|
+
// primitive). The hop remains observable via usage.iterations.
|
|
916
|
+
z.object({
|
|
917
|
+
type: z.literal('fallback'),
|
|
918
|
+
}),
|
|
904
919
|
]),
|
|
905
920
|
),
|
|
906
921
|
stop_reason: z.string().nullish(),
|
|
907
922
|
stop_sequence: z.string().nullish(),
|
|
923
|
+
stop_details: anthropicStopDetailsSchema.nullish(),
|
|
908
924
|
usage: z.looseObject({
|
|
909
925
|
input_tokens: z.number(),
|
|
910
926
|
output_tokens: z.number(),
|
|
@@ -912,23 +928,19 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
|
|
|
912
928
|
cache_read_input_tokens: z.number().nullish(),
|
|
913
929
|
iterations: z
|
|
914
930
|
.array(
|
|
915
|
-
z.
|
|
916
|
-
z.
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
z.
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
cache_creation_input_tokens: z.number().nullish(),
|
|
929
|
-
cache_read_input_tokens: z.number().nullish(),
|
|
930
|
-
}),
|
|
931
|
-
]),
|
|
931
|
+
z.object({
|
|
932
|
+
type: z.union([
|
|
933
|
+
z.literal('compaction'),
|
|
934
|
+
z.literal('message'),
|
|
935
|
+
z.literal('advisor_message'),
|
|
936
|
+
z.literal('fallback_message'),
|
|
937
|
+
]),
|
|
938
|
+
model: z.string().nullish(),
|
|
939
|
+
input_tokens: z.number(),
|
|
940
|
+
output_tokens: z.number(),
|
|
941
|
+
cache_creation_input_tokens: z.number().nullish(),
|
|
942
|
+
cache_read_input_tokens: z.number().nullish(),
|
|
943
|
+
}),
|
|
932
944
|
)
|
|
933
945
|
.nullish(),
|
|
934
946
|
}),
|
|
@@ -1290,6 +1302,11 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
|
|
|
1290
1302
|
}),
|
|
1291
1303
|
]),
|
|
1292
1304
|
}),
|
|
1305
|
+
// Server-side fallback marker; dropped from content output (see the
|
|
1306
|
+
// response schema). The hop remains observable via usage.iterations.
|
|
1307
|
+
z.object({
|
|
1308
|
+
type: z.literal('fallback'),
|
|
1309
|
+
}),
|
|
1293
1310
|
]),
|
|
1294
1311
|
}),
|
|
1295
1312
|
z.object({
|
|
@@ -1362,6 +1379,7 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
|
|
|
1362
1379
|
delta: z.object({
|
|
1363
1380
|
stop_reason: z.string().nullish(),
|
|
1364
1381
|
stop_sequence: z.string().nullish(),
|
|
1382
|
+
stop_details: anthropicStopDetailsSchema.nullish(),
|
|
1365
1383
|
container: z
|
|
1366
1384
|
.object({
|
|
1367
1385
|
expires_at: z.string(),
|
|
@@ -1388,26 +1406,19 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
|
|
|
1388
1406
|
cache_read_input_tokens: z.number().nullish(),
|
|
1389
1407
|
iterations: z
|
|
1390
1408
|
.array(
|
|
1391
|
-
z.
|
|
1392
|
-
z.
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
z.
|
|
1403
|
-
|
|
1404
|
-
model: z.string(),
|
|
1405
|
-
input_tokens: z.number(),
|
|
1406
|
-
output_tokens: z.number(),
|
|
1407
|
-
cache_creation_input_tokens: z.number().nullish(),
|
|
1408
|
-
cache_read_input_tokens: z.number().nullish(),
|
|
1409
|
-
}),
|
|
1410
|
-
]),
|
|
1409
|
+
z.object({
|
|
1410
|
+
type: z.union([
|
|
1411
|
+
z.literal('compaction'),
|
|
1412
|
+
z.literal('message'),
|
|
1413
|
+
z.literal('advisor_message'),
|
|
1414
|
+
z.literal('fallback_message'),
|
|
1415
|
+
]),
|
|
1416
|
+
model: z.string().nullish(),
|
|
1417
|
+
input_tokens: z.number(),
|
|
1418
|
+
output_tokens: z.number(),
|
|
1419
|
+
cache_creation_input_tokens: z.number().nullish(),
|
|
1420
|
+
cache_read_input_tokens: z.number().nullish(),
|
|
1421
|
+
}),
|
|
1411
1422
|
)
|
|
1412
1423
|
.nullish(),
|
|
1413
1424
|
}),
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
type AnthropicContainer,
|
|
41
41
|
type AnthropicReasoningMetadata,
|
|
42
42
|
type AnthropicResponseContextManagement,
|
|
43
|
+
type AnthropicStopDetails,
|
|
43
44
|
type AnthropicTool,
|
|
44
45
|
type Citation,
|
|
45
46
|
} from './anthropic-messages-api';
|
|
@@ -438,6 +439,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
438
439
|
...(anthropicOptions?.inferenceGeo && {
|
|
439
440
|
inference_geo: anthropicOptions.inferenceGeo,
|
|
440
441
|
}),
|
|
442
|
+
...(anthropicOptions?.fallbacks &&
|
|
443
|
+
anthropicOptions.fallbacks.length > 0 && {
|
|
444
|
+
fallbacks: anthropicOptions.fallbacks,
|
|
445
|
+
}),
|
|
441
446
|
...(anthropicOptions?.cacheControl && {
|
|
442
447
|
cache_control: anthropicOptions.cacheControl,
|
|
443
448
|
}),
|
|
@@ -664,6 +669,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
664
669
|
betas.add('fast-mode-2026-02-01');
|
|
665
670
|
}
|
|
666
671
|
|
|
672
|
+
if (anthropicOptions?.fallbacks && anthropicOptions.fallbacks.length > 0) {
|
|
673
|
+
betas.add('server-side-fallback-2026-06-01');
|
|
674
|
+
}
|
|
675
|
+
|
|
667
676
|
const defaultEagerInputStreaming =
|
|
668
677
|
stream && (anthropicOptions?.toolStreaming ?? true);
|
|
669
678
|
|
|
@@ -1281,6 +1290,13 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
1281
1290
|
}
|
|
1282
1291
|
break;
|
|
1283
1292
|
}
|
|
1293
|
+
|
|
1294
|
+
// Server-side fallback marker: the AI SDK has no content primitive for
|
|
1295
|
+
// a model hop, so drop it. The hop is still observable via
|
|
1296
|
+
// usage.iterations.
|
|
1297
|
+
case 'fallback': {
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1284
1300
|
}
|
|
1285
1301
|
}
|
|
1286
1302
|
|
|
@@ -1303,48 +1319,35 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
1303
1319
|
},
|
|
1304
1320
|
warnings,
|
|
1305
1321
|
providerMetadata: (() => {
|
|
1322
|
+
const stopDetails = mapAnthropicStopDetails(response.stop_details);
|
|
1323
|
+
|
|
1306
1324
|
const anthropicMetadata = {
|
|
1307
1325
|
usage: response.usage as JSONObject,
|
|
1308
1326
|
cacheCreationInputTokens:
|
|
1309
1327
|
response.usage.cache_creation_input_tokens ?? null,
|
|
1310
1328
|
stopSequence: response.stop_sequence ?? null,
|
|
1329
|
+
...(stopDetails != null ? { stopDetails } : {}),
|
|
1311
1330
|
|
|
1312
1331
|
iterations: response.usage.iterations
|
|
1313
|
-
? response.usage.iterations.map(
|
|
1314
|
-
iter
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
: ({
|
|
1333
|
-
type: iter.type,
|
|
1334
|
-
inputTokens: iter.input_tokens,
|
|
1335
|
-
outputTokens: iter.output_tokens,
|
|
1336
|
-
...(iter.cache_creation_input_tokens
|
|
1337
|
-
? {
|
|
1338
|
-
cacheCreationInputTokens:
|
|
1339
|
-
iter.cache_creation_input_tokens,
|
|
1340
|
-
}
|
|
1341
|
-
: {}),
|
|
1342
|
-
...(iter.cache_read_input_tokens
|
|
1343
|
-
? {
|
|
1344
|
-
cacheReadInputTokens: iter.cache_read_input_tokens,
|
|
1345
|
-
}
|
|
1346
|
-
: {}),
|
|
1347
|
-
} satisfies AnthropicUsageIteration),
|
|
1332
|
+
? response.usage.iterations.map(
|
|
1333
|
+
iter =>
|
|
1334
|
+
({
|
|
1335
|
+
type: iter.type,
|
|
1336
|
+
...(iter.model != null ? { model: iter.model } : {}),
|
|
1337
|
+
inputTokens: iter.input_tokens,
|
|
1338
|
+
outputTokens: iter.output_tokens,
|
|
1339
|
+
...(iter.cache_creation_input_tokens
|
|
1340
|
+
? {
|
|
1341
|
+
cacheCreationInputTokens:
|
|
1342
|
+
iter.cache_creation_input_tokens,
|
|
1343
|
+
}
|
|
1344
|
+
: {}),
|
|
1345
|
+
...(iter.cache_read_input_tokens
|
|
1346
|
+
? {
|
|
1347
|
+
cacheReadInputTokens: iter.cache_read_input_tokens,
|
|
1348
|
+
}
|
|
1349
|
+
: {}),
|
|
1350
|
+
}) satisfies AnthropicUsageIteration,
|
|
1348
1351
|
)
|
|
1349
1352
|
: null,
|
|
1350
1353
|
container: response.container
|
|
@@ -1458,6 +1461,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
1458
1461
|
let rawUsage: JSONObject | undefined = undefined;
|
|
1459
1462
|
let cacheCreationInputTokens: number | null = null;
|
|
1460
1463
|
let stopSequence: string | null = null;
|
|
1464
|
+
let stopDetails: AnthropicMessageMetadata['stopDetails'] = undefined;
|
|
1461
1465
|
let container: AnthropicMessageMetadata['container'] | null = null;
|
|
1462
1466
|
let isJsonResponseFromTool = false;
|
|
1463
1467
|
|
|
@@ -1510,6 +1514,14 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
1510
1514
|
case 'content_block_start': {
|
|
1511
1515
|
const part = value.content_block;
|
|
1512
1516
|
const contentBlockType = part.type;
|
|
1517
|
+
|
|
1518
|
+
// Server-side fallback marker: the AI SDK has no content
|
|
1519
|
+
// primitive for a model hop, so drop it. The hop is still
|
|
1520
|
+
// observable via usage.iterations.
|
|
1521
|
+
if (contentBlockType === 'fallback') {
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1513
1525
|
blockType = contentBlockType;
|
|
1514
1526
|
|
|
1515
1527
|
switch (contentBlockType) {
|
|
@@ -2362,6 +2374,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
2362
2374
|
};
|
|
2363
2375
|
|
|
2364
2376
|
stopSequence = value.delta.stop_sequence ?? null;
|
|
2377
|
+
stopDetails = mapAnthropicStopDetails(value.delta.stop_details);
|
|
2365
2378
|
container =
|
|
2366
2379
|
value.delta.container != null
|
|
2367
2380
|
? {
|
|
@@ -2395,44 +2408,28 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
|
2395
2408
|
usage: (rawUsage as JSONObject) ?? null,
|
|
2396
2409
|
cacheCreationInputTokens,
|
|
2397
2410
|
stopSequence,
|
|
2411
|
+
...(stopDetails != null ? { stopDetails } : {}),
|
|
2398
2412
|
iterations: usage.iterations
|
|
2399
|
-
? usage.iterations.map(
|
|
2400
|
-
iter
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
: ({
|
|
2420
|
-
type: iter.type,
|
|
2421
|
-
inputTokens: iter.input_tokens,
|
|
2422
|
-
outputTokens: iter.output_tokens,
|
|
2423
|
-
...(iter.cache_creation_input_tokens
|
|
2424
|
-
? {
|
|
2425
|
-
cacheCreationInputTokens:
|
|
2426
|
-
iter.cache_creation_input_tokens,
|
|
2427
|
-
}
|
|
2428
|
-
: {}),
|
|
2429
|
-
...(iter.cache_read_input_tokens
|
|
2430
|
-
? {
|
|
2431
|
-
cacheReadInputTokens:
|
|
2432
|
-
iter.cache_read_input_tokens,
|
|
2433
|
-
}
|
|
2434
|
-
: {}),
|
|
2435
|
-
} satisfies AnthropicUsageIteration),
|
|
2413
|
+
? usage.iterations.map(
|
|
2414
|
+
iter =>
|
|
2415
|
+
({
|
|
2416
|
+
type: iter.type,
|
|
2417
|
+
...(iter.model != null ? { model: iter.model } : {}),
|
|
2418
|
+
inputTokens: iter.input_tokens,
|
|
2419
|
+
outputTokens: iter.output_tokens,
|
|
2420
|
+
...(iter.cache_creation_input_tokens
|
|
2421
|
+
? {
|
|
2422
|
+
cacheCreationInputTokens:
|
|
2423
|
+
iter.cache_creation_input_tokens,
|
|
2424
|
+
}
|
|
2425
|
+
: {}),
|
|
2426
|
+
...(iter.cache_read_input_tokens
|
|
2427
|
+
? {
|
|
2428
|
+
cacheReadInputTokens:
|
|
2429
|
+
iter.cache_read_input_tokens,
|
|
2430
|
+
}
|
|
2431
|
+
: {}),
|
|
2432
|
+
}) satisfies AnthropicUsageIteration,
|
|
2436
2433
|
)
|
|
2437
2434
|
: null,
|
|
2438
2435
|
container,
|
|
@@ -2530,7 +2527,8 @@ function getModelCapabilities(modelId: string): {
|
|
|
2530
2527
|
} {
|
|
2531
2528
|
if (
|
|
2532
2529
|
modelId.includes('claude-opus-4-8') ||
|
|
2533
|
-
modelId.includes('claude-opus-4-7')
|
|
2530
|
+
modelId.includes('claude-opus-4-7') ||
|
|
2531
|
+
modelId.includes('claude-fable-5')
|
|
2534
2532
|
) {
|
|
2535
2533
|
return {
|
|
2536
2534
|
maxOutputTokens: 128000,
|
|
@@ -2656,3 +2654,22 @@ function mapAnthropicResponseContextManagement(
|
|
|
2656
2654
|
}
|
|
2657
2655
|
: null;
|
|
2658
2656
|
}
|
|
2657
|
+
|
|
2658
|
+
function mapAnthropicStopDetails(
|
|
2659
|
+
stopDetails: AnthropicStopDetails | null | undefined,
|
|
2660
|
+
): AnthropicMessageMetadata['stopDetails'] | undefined {
|
|
2661
|
+
if (stopDetails == null) {
|
|
2662
|
+
return undefined;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
return {
|
|
2666
|
+
type: stopDetails.type,
|
|
2667
|
+
...(stopDetails.category != null ? { category: stopDetails.category } : {}),
|
|
2668
|
+
...(stopDetails.explanation != null
|
|
2669
|
+
? { explanation: stopDetails.explanation }
|
|
2670
|
+
: {}),
|
|
2671
|
+
...(stopDetails.recommended_model != null
|
|
2672
|
+
? { recommendedModel: stopDetails.recommended_model }
|
|
2673
|
+
: {}),
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
@@ -19,6 +19,7 @@ export type AnthropicMessagesModelId =
|
|
|
19
19
|
| 'claude-opus-4-6'
|
|
20
20
|
| 'claude-opus-4-7'
|
|
21
21
|
| 'claude-opus-4-8'
|
|
22
|
+
| 'claude-fable-5'
|
|
22
23
|
| (string & {});
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -224,6 +225,34 @@ export const anthropicLanguageModelOptions = z.object({
|
|
|
224
225
|
*/
|
|
225
226
|
inferenceGeo: z.enum(['us', 'global']).optional(),
|
|
226
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Server-side fallback chain.
|
|
230
|
+
*
|
|
231
|
+
* When the primary model's safety classifiers block a turn, the API
|
|
232
|
+
* automatically retries it on the next model in the chain, server-side. A
|
|
233
|
+
* `content-filter` finish reason means the entire chain refused.
|
|
234
|
+
*
|
|
235
|
+
* Each entry is merged into the request as a direct request to that entry's
|
|
236
|
+
* model, so it must be formatted accordingly: `model` is required, and an
|
|
237
|
+
* entry may additionally override `max_tokens`, `thinking`, `output_config`,
|
|
238
|
+
* and `speed` for that attempt only (`speed` additionally requires the speed
|
|
239
|
+
* beta). The value is passed through to the API as-is.
|
|
240
|
+
*
|
|
241
|
+
* The required `server-side-fallback-2026-06-01` beta is added automatically
|
|
242
|
+
* when this option is set.
|
|
243
|
+
*/
|
|
244
|
+
fallbacks: z
|
|
245
|
+
.array(
|
|
246
|
+
z.object({
|
|
247
|
+
model: z.string(),
|
|
248
|
+
max_tokens: z.number().int().optional(),
|
|
249
|
+
thinking: z.record(z.string(), z.unknown()).optional(),
|
|
250
|
+
output_config: z.record(z.string(), z.unknown()).optional(),
|
|
251
|
+
speed: z.enum(['fast', 'standard']).optional(),
|
|
252
|
+
}),
|
|
253
|
+
)
|
|
254
|
+
.optional(),
|
|
255
|
+
|
|
227
256
|
/**
|
|
228
257
|
* A set of beta features to enable.
|
|
229
258
|
* Allow a provider to receive the full `betas` set if it needs it.
|