@ai-sdk/anthropic 3.0.115 → 3.0.116

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.
@@ -189,9 +189,14 @@ const result = streamText({
189
189
  });
190
190
  ```
191
191
 
192
+ For `claude-fable-5-1`, the default `"auto"` mode uses native structured
193
+ outputs through `output_config.format`. Fable 5.1 rejects forced tool use, so
194
+ do not use `"jsonTool"` or a required or named tool choice to implement
195
+ structured output with this model.
196
+
192
197
  ### Effort
193
198
 
194
- Anthropic introduced an `effort` option with `claude-opus-4-5` that affects thinking, text responses, and function calls. Effort defaults to `high` and you can set it to `medium` or `low` to save tokens and to lower time-to-last-token latency (TTLT). `claude-opus-4-7`, `claude-opus-5`, and `claude-sonnet-5` additionally support `xhigh` for maximum reasoning effort.
199
+ Anthropic introduced an `effort` option with `claude-opus-4-5` that affects thinking, text responses, and function calls. Effort defaults to `high` and you can set it to `medium` or `low` to save tokens and to lower time-to-last-token latency (TTLT). `claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5`, `claude-fable-5`, `claude-fable-5-1`, and `claude-sonnet-5` additionally support `xhigh` for maximum reasoning effort.
195
200
 
196
201
  On `claude-opus-5`, thinking can only be disabled at effort levels up to and including `high`. When you combine `thinking: { type: 'disabled' }` with `effort: 'xhigh'` or `effort: 'max'`, the AI SDK lowers the effort to `high` and emits a warning instead of sending a request that the API would reject.
197
202
 
@@ -439,6 +444,55 @@ console.log(text);
439
444
  progress during thinking.
440
445
  </Note>
441
446
 
447
+ ##### Thinking Updates
448
+
449
+ Use `display: 'updates'` with `claude-fable-5-1` to stream thinking summaries
450
+ between tool calls:
451
+
452
+ ```ts highlight="12-16"
453
+ import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
454
+ import { streamText, tool } from 'ai';
455
+ import { z } from 'zod';
456
+
457
+ const result = streamText({
458
+ model: anthropic('claude-fable-5-1'),
459
+ tools: {
460
+ weather: tool({
461
+ inputSchema: z.object({ city: z.string() }),
462
+ }),
463
+ },
464
+ providerOptions: {
465
+ anthropic: {
466
+ thinking: { type: 'adaptive', display: 'updates' },
467
+ } satisfies AnthropicLanguageModelOptions,
468
+ },
469
+ prompt: 'Compare the weather in San Francisco and New York.',
470
+ });
471
+ ```
472
+
473
+ ##### Thinking Block Binding
474
+
475
+ Fable 5.1 can recover from a thinking-block prefix mismatch by dropping the
476
+ mismatched block. Set `blockBinding.prefixMismatchBehavior` to `drop_block`.
477
+ You can provide block binding by itself to preserve the model's default
478
+ thinking mode, or combine it with adaptive thinking.
479
+
480
+ ```ts highlight="7-11"
481
+ const { text } = await generateText({
482
+ model: anthropic('claude-fable-5-1'),
483
+ prompt: 'Continue from this conversation.',
484
+ providerOptions: {
485
+ anthropic: {
486
+ thinking: {
487
+ blockBinding: {
488
+ prefixMismatchBehavior: 'drop_block',
489
+ },
490
+ },
491
+ } satisfies AnthropicLanguageModelOptions,
492
+ },
493
+ });
494
+ ```
495
+
442
496
  #### Budget-Based Thinking
443
497
 
444
498
  For earlier models (`claude-opus-4-20250514`, `claude-sonnet-4-20250514`, `claude-sonnet-4-5-20250929`),
@@ -1203,6 +1257,59 @@ const result = await generateText({
1203
1257
 
1204
1258
  This sends `tool_reference` blocks to Anthropic, which loads the corresponding deferred tool schemas into Claude's context.
1205
1259
 
1260
+ ### Mid-Conversation System Controls
1261
+
1262
+ With `claude-fable-5-1`, a mid-conversation system message can be cleared
1263
+ before the next user message with `clearAt: 'next_user_message'`, or set the
1264
+ effort for the next turn. The provider adds the required
1265
+ `mid-conversation-system-clear-at-2026-08-21` and
1266
+ `mid-conversation-effort-2026-08-01` beta headers automatically.
1267
+
1268
+ ```ts highlight="17-25"
1269
+ import { anthropic } from '@ai-sdk/anthropic';
1270
+ import { generateText } from 'ai';
1271
+
1272
+ const result = await generateText({
1273
+ model: anthropic('claude-fable-5-1'),
1274
+ allowSystemInMessages: true,
1275
+ messages: [
1276
+ {
1277
+ role: 'user',
1278
+ content: 'Draft a migration plan.',
1279
+ },
1280
+ {
1281
+ role: 'assistant',
1282
+ content: 'First, inventory the public API.',
1283
+ },
1284
+ {
1285
+ role: 'system',
1286
+ content: 'For the next turn, verify every compatibility claim.',
1287
+ providerOptions: {
1288
+ anthropic: {
1289
+ clearAt: 'next_user_message',
1290
+ effort: 'high',
1291
+ },
1292
+ },
1293
+ },
1294
+ {
1295
+ role: 'user',
1296
+ content: 'Complete the plan.',
1297
+ },
1298
+ ],
1299
+ });
1300
+ ```
1301
+
1302
+ An effort-only system message can use an empty content array at the API level;
1303
+ in an AI SDK prompt, set `content: ''`. The provider serializes it as
1304
+ `content: []`.
1305
+
1306
+ <Note>
1307
+ `clearAt` and per-message `effort` apply only to mid-conversation system
1308
+ messages. When used on the initial system message, the provider ignores them
1309
+ and emits a warning. Use the top-level `effort` provider option to configure
1310
+ effort for the full request.
1311
+ </Note>
1312
+
1206
1313
  ### Mid-Conversation Tool Changes
1207
1314
 
1208
1315
  With `claude-opus-4-8`, you can add or remove tools between turns of a conversation without invalidating the prompt cache. Attach `toolChanges` to a system message that appears mid-conversation (right before an assistant message or at the end of the messages). The required `mid-conversation-tool-changes-2026-07-01` beta header is added automatically.
@@ -1640,6 +1747,7 @@ and the `mediaType` should be set to `'application/pdf'`.
1640
1747
 
1641
1748
  | Model | Image Input | Object Generation | Tool Usage | Computer Use | Web Search | Tool Search | Compaction |
1642
1749
  | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
1750
+ | `claude-fable-5-1` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1643
1751
  | `claude-opus-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1644
1752
  | `claude-sonnet-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1645
1753
  | `claude-fable-5` | <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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/anthropic",
3
- "version": "3.0.115",
3
+ "version": "3.0.116",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -24,6 +24,10 @@ export type AnthropicCacheControl = {
24
24
  export interface AnthropicSystemMessage {
25
25
  role: 'system';
26
26
  content: Array<AnthropicTextContent | AnthropicToolChangeContent>;
27
+ clear_at?: 'next_user_message';
28
+ output_config?: {
29
+ effort: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
30
+ };
27
31
  }
28
32
 
29
33
  /**
@@ -418,13 +418,17 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
418
418
  toolNameMapping,
419
419
  });
420
420
 
421
+ const thinking = anthropicOptions?.thinking;
422
+ const thinkingType =
423
+ thinking != null && 'type' in thinking ? thinking.type : undefined;
424
+
421
425
  // Newer models only allow disabling thinking at effort levels up to and
422
426
  // including `high`; at `xhigh` and `max` the API returns a 400. Lower
423
427
  // the effort to `high` to preserve the explicit request to run without
424
428
  // thinking.
425
429
  if (
426
430
  rejectsThinkingDisabledAboveHighEffort &&
427
- anthropicOptions?.thinking?.type === 'disabled' &&
431
+ thinkingType === 'disabled' &&
428
432
  (anthropicOptions.effort === 'xhigh' || anthropicOptions.effort === 'max')
429
433
  ) {
430
434
  warnings.push({
@@ -437,7 +441,6 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
437
441
  anthropicOptions.effort = 'high';
438
442
  }
439
443
 
440
- const thinkingType = anthropicOptions?.thinking?.type;
441
444
  const isThinking =
442
445
  thinkingType === 'enabled' || thinkingType === 'adaptive';
443
446
  // `disabled` must still be forwarded to the API: some models (e.g. Sonnet 5)
@@ -445,13 +448,20 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
445
448
  // consume the max_tokens budget.
446
449
  const sendThinking = isThinking || thinkingType === 'disabled';
447
450
  let thinkingBudget =
448
- thinkingType === 'enabled'
449
- ? anthropicOptions?.thinking?.budgetTokens
451
+ thinkingType === 'enabled' &&
452
+ thinking != null &&
453
+ 'budgetTokens' in thinking
454
+ ? thinking.budgetTokens
450
455
  : undefined;
451
456
  const thinkingDisplay =
452
- thinkingType === 'adaptive'
453
- ? anthropicOptions?.thinking?.display
457
+ thinkingType === 'adaptive' && thinking != null && 'display' in thinking
458
+ ? thinking.display
459
+ : undefined;
460
+ const thinkingBlockBinding =
461
+ thinking != null && 'blockBinding' in thinking
462
+ ? thinking.blockBinding
454
463
  : undefined;
464
+ const sendThinkingConfig = sendThinking || thinkingBlockBinding != null;
455
465
 
456
466
  const maxTokens = maxOutputTokens ?? maxOutputTokensForModel;
457
467
 
@@ -467,11 +477,17 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
467
477
  stop_sequences: stopSequences,
468
478
 
469
479
  // provider specific settings:
470
- ...(sendThinking && {
480
+ ...(sendThinkingConfig && {
471
481
  thinking: {
472
- type: thinkingType,
482
+ ...(thinkingType != null && { type: thinkingType }),
473
483
  ...(thinkingBudget != null && { budget_tokens: thinkingBudget }),
474
484
  ...(thinkingDisplay != null && { display: thinkingDisplay }),
485
+ ...(thinkingBlockBinding != null && {
486
+ block_binding: {
487
+ prefix_mismatch_behavior:
488
+ thinkingBlockBinding.prefixMismatchBehavior,
489
+ },
490
+ }),
475
491
  },
476
492
  }),
477
493
  ...((anthropicOptions?.effort ||
@@ -735,6 +751,14 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
735
751
  betas.add('task-budgets-2026-03-13');
736
752
  }
737
753
 
754
+ if (thinkingDisplay === 'updates') {
755
+ betas.add('thinking-display-updates-2026-08-18');
756
+ }
757
+
758
+ if (thinkingBlockBinding != null) {
759
+ betas.add('thinking-binding-controls-2026-08-01');
760
+ }
761
+
738
762
  if (anthropicOptions?.speed === 'fast') {
739
763
  betas.add('fast-mode-2026-02-01');
740
764
  }
@@ -21,6 +21,7 @@ export type AnthropicMessagesModelId =
21
21
  | 'claude-opus-4-8'
22
22
  | 'claude-opus-5'
23
23
  | 'claude-fable-5'
24
+ | 'claude-fable-5-1'
24
25
  | 'claude-sonnet-5'
25
26
  | (string & {});
26
27
 
@@ -64,6 +65,23 @@ export type AnthropicFilePartProviderOptions = z.infer<
64
65
  * Anthropic provider options for system messages.
65
66
  */
66
67
  export const anthropicSystemMessageProviderOptions = z.object({
68
+ /**
69
+ * Clears this mid-conversation system message after the current turn.
70
+ *
71
+ * Requires the `mid-conversation-system-clear-at-2026-08-21` beta,
72
+ * which is added automatically.
73
+ */
74
+ clearAt: z.literal('next_user_message').optional(),
75
+
76
+ /**
77
+ * Overrides the effort level for the turn following this
78
+ * mid-conversation system message.
79
+ *
80
+ * Requires the `mid-conversation-effort-2026-08-01` beta,
81
+ * which is added automatically.
82
+ */
83
+ effort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
84
+
67
85
  /**
68
86
  * Mid-conversation tool changes. Adds or removes tools from the
69
87
  * conversation's tool set between turns without invalidating the prompt
@@ -98,6 +116,10 @@ export type AnthropicSystemMessageProviderOptions = z.infer<
98
116
  typeof anthropicSystemMessageProviderOptions
99
117
  >;
100
118
 
119
+ const anthropicThinkingBlockBinding = z.object({
120
+ prefixMismatchBehavior: z.literal('drop_block'),
121
+ });
122
+
101
123
  export const anthropicLanguageModelOptions = z.object({
102
124
  /**
103
125
  * Whether to send reasoning to the model.
@@ -122,24 +144,41 @@ export const anthropicLanguageModelOptions = z.object({
122
144
  * Requires a minimum budget of 1,024 tokens and counts towards the `max_tokens` limit.
123
145
  */
124
146
  thinking: z
125
- .discriminatedUnion('type', [
126
- z.object({
127
- /** for Sonnet 4.6, Opus 4.6, and newer models */
128
- type: z.literal('adaptive'),
129
- /**
130
- * Controls whether thinking content is included in the response.
131
- * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+).
132
- * - `"summarized"`: Thinking content is returned. Required to see reasoning output.
133
- */
134
- display: z.enum(['omitted', 'summarized']).optional(),
135
- }),
136
- z.object({
137
- /** for models before Opus 4.6, except Sonnet 4.6 still supports it */
138
- type: z.literal('enabled'),
139
- budgetTokens: z.number().optional(),
140
- }),
147
+ .union([
148
+ z.discriminatedUnion('type', [
149
+ z.object({
150
+ /** for Sonnet 4.6, Opus 4.6, and newer models */
151
+ type: z.literal('adaptive'),
152
+ /**
153
+ * Controls whether thinking content is included in the response.
154
+ * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+).
155
+ * - `"summarized"`: Thinking content is returned. Required to see reasoning output.
156
+ * - `"updates"`: Thinking updates are returned between tool calls.
157
+ */
158
+ display: z.enum(['omitted', 'summarized', 'updates']).optional(),
159
+ /**
160
+ * Controls how thinking blocks are bound to an assistant prefix.
161
+ *
162
+ * Requires the `thinking-binding-controls-2026-08-01` beta,
163
+ * which is added automatically.
164
+ */
165
+ blockBinding: anthropicThinkingBlockBinding.optional(),
166
+ }),
167
+ z.object({
168
+ /** for models before Opus 4.6, except Sonnet 4.6 still supports it */
169
+ type: z.literal('enabled'),
170
+ budgetTokens: z.number().optional(),
171
+ }),
172
+ z.object({
173
+ type: z.literal('disabled'),
174
+ }),
175
+ ]),
176
+ /**
177
+ * Configures prefix mismatch recovery without changing the model's
178
+ * default thinking mode.
179
+ */
141
180
  z.object({
142
- type: z.literal('disabled'),
181
+ blockBinding: anthropicThinkingBlockBinding,
143
182
  }),
144
183
  ])
145
184
  .optional(),
@@ -166,8 +166,9 @@ export async function convertToAnthropicMessagesPrompt({
166
166
 
167
167
  switch (type) {
168
168
  case 'system': {
169
- const content: AnthropicSystemMessage['content'] = [];
169
+ const systemMessages: AnthropicSystemMessage[] = [];
170
170
  let toolChangeCount = 0;
171
+ let hasMidConversationOptions = false;
171
172
 
172
173
  for (const { content: text, providerOptions } of block.messages) {
173
174
  const systemMessageOptions = await parseProviderOptions({
@@ -176,10 +177,16 @@ export async function convertToAnthropicMessagesPrompt({
176
177
  schema: anthropicSystemMessageProviderOptions,
177
178
  });
178
179
  const toolChanges = systemMessageOptions?.toolChanges ?? [];
179
-
180
- // A system message that only carries tool changes may have empty
181
- // text; do not emit an empty text block for it.
182
- if (text !== '' || toolChanges.length === 0) {
180
+ const clearAt = systemMessageOptions?.clearAt;
181
+ const effort = systemMessageOptions?.effort;
182
+ const content: AnthropicSystemMessage['content'] = [];
183
+
184
+ // A system message that only carries message-level options or tool
185
+ // changes may have empty text; do not emit an empty text block for it.
186
+ if (
187
+ text !== '' ||
188
+ (toolChanges.length === 0 && clearAt == null && effort == null)
189
+ ) {
183
190
  content.push({
184
191
  type: 'text' as const,
185
192
  text,
@@ -200,14 +207,29 @@ export async function convertToAnthropicMessagesPrompt({
200
207
  },
201
208
  } satisfies AnthropicToolChangeContent);
202
209
  }
210
+
211
+ hasMidConversationOptions ||= clearAt != null || effort != null;
212
+
213
+ systemMessages.push({
214
+ role: 'system',
215
+ content,
216
+ ...(clearAt != null && { clear_at: clearAt }),
217
+ ...(effort != null && { output_config: { effort } }),
218
+ });
203
219
  }
204
220
 
205
221
  // The first block becomes the top-level system prompt. Later system
206
222
  // blocks are sent as inline system messages — always when they carry
207
- // tool changes (which are only valid mid-conversation), and otherwise
208
- // only when a top-level system prompt already exists (preserving the
209
- // existing hoisting behavior for plain text).
210
- if (i === 0 || (system == null && toolChangeCount === 0)) {
223
+ // tool changes or per-turn options (which are only valid
224
+ // mid-conversation), and otherwise only when a top-level system prompt
225
+ // already exists (preserving the existing hoisting behavior for plain
226
+ // text).
227
+ if (
228
+ i === 0 ||
229
+ (system == null &&
230
+ toolChangeCount === 0 &&
231
+ !hasMidConversationOptions)
232
+ ) {
211
233
  if (toolChangeCount > 0) {
212
234
  warnings.push({
213
235
  type: 'other',
@@ -217,15 +239,32 @@ export async function convertToAnthropicMessagesPrompt({
217
239
  'The tool changes have been ignored.',
218
240
  });
219
241
  }
220
- system = content.filter(
221
- (part): part is AnthropicTextContent => part.type === 'text',
242
+ if (hasMidConversationOptions) {
243
+ warnings.push({
244
+ type: 'other',
245
+ message:
246
+ 'clearAt and effort on the initial system message are not supported by Anthropic. ' +
247
+ 'Configure these options on a mid-conversation system message instead. ' +
248
+ 'The options have been ignored.',
249
+ });
250
+ }
251
+ system = systemMessages.flatMap(message =>
252
+ message.content.filter(
253
+ (part): part is AnthropicTextContent => part.type === 'text',
254
+ ),
222
255
  );
223
256
  } else {
224
- messages.push({ role: 'system', content });
257
+ messages.push(...systemMessages);
225
258
  betas.add('mid-conversation-system-2026-04-07');
226
259
  if (toolChangeCount > 0) {
227
260
  betas.add('mid-conversation-tool-changes-2026-07-01');
228
261
  }
262
+ if (systemMessages.some(message => message.clear_at != null)) {
263
+ betas.add('mid-conversation-system-clear-at-2026-08-21');
264
+ }
265
+ if (systemMessages.some(message => message.output_config != null)) {
266
+ betas.add('mid-conversation-effort-2026-08-01');
267
+ }
229
268
  }
230
269
 
231
270
  break;