@ai-sdk/anthropic 4.0.0-beta.3 → 4.0.0-beta.32

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.
@@ -23,11 +23,18 @@ import {
23
23
  FetchFunction,
24
24
  generateId,
25
25
  InferSchema,
26
+ isCustomReasoning,
27
+ mapReasoningToProviderBudget,
28
+ mapReasoningToProviderEffort,
26
29
  parseProviderOptions,
27
30
  ParseResult,
28
31
  postJsonToApi,
29
32
  Resolvable,
30
33
  resolve,
34
+ resolveProviderReference,
35
+ serializeModelOptions,
36
+ WORKFLOW_SERIALIZE,
37
+ WORKFLOW_DESERIALIZE,
31
38
  } from '@ai-sdk/provider-utils';
32
39
  import { anthropicFailedResponseHandler } from './anthropic-error';
33
40
  import { AnthropicMessageMetadata } from './anthropic-message-metadata';
@@ -42,6 +49,7 @@ import {
42
49
  } from './anthropic-messages-api';
43
50
  import {
44
51
  AnthropicMessagesModelId,
52
+ AnthropicLanguageModelOptions,
45
53
  anthropicLanguageModelOptions,
46
54
  } from './anthropic-messages-options';
47
55
  import { prepareTools } from './anthropic-prepare-tools';
@@ -115,7 +123,7 @@ function createCitationSource(
115
123
  type AnthropicMessagesConfig = {
116
124
  provider: string;
117
125
  baseURL: string;
118
- headers: Resolvable<Record<string, string | undefined>>;
126
+ headers?: Resolvable<Record<string, string | undefined>>;
119
127
  fetch?: FetchFunction;
120
128
  buildRequestUrl?: (baseURL: string, isStreaming: boolean) => string;
121
129
  transformRequestBody?: (
@@ -129,6 +137,12 @@ type AnthropicMessagesConfig = {
129
137
  * When false, the model will use JSON tool fallback for structured outputs.
130
138
  */
131
139
  supportsNativeStructuredOutput?: boolean;
140
+
141
+ /**
142
+ * When false, `strict` on tool definitions will be ignored and a warning emitted.
143
+ * Defaults to true.
144
+ */
145
+ supportsStrictTools?: boolean;
132
146
  };
133
147
 
134
148
  export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
@@ -139,6 +153,20 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
139
153
  private readonly config: AnthropicMessagesConfig;
140
154
  private readonly generateId: () => string;
141
155
 
156
+ static [WORKFLOW_SERIALIZE](model: AnthropicMessagesLanguageModel) {
157
+ return serializeModelOptions({
158
+ modelId: model.modelId,
159
+ config: model.config,
160
+ });
161
+ }
162
+
163
+ static [WORKFLOW_DESERIALIZE](options: {
164
+ modelId: AnthropicMessagesModelId;
165
+ config: AnthropicMessagesConfig;
166
+ }) {
167
+ return new AnthropicMessagesLanguageModel(options.modelId, options.config);
168
+ }
169
+
142
170
  constructor(
143
171
  modelId: AnthropicMessagesModelId,
144
172
  config: AnthropicMessagesConfig,
@@ -184,6 +212,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
184
212
  seed,
185
213
  tools,
186
214
  toolChoice,
215
+ reasoning,
187
216
  providerOptions,
188
217
  stream,
189
218
  }: LanguageModelV4CallOptions & {
@@ -263,13 +292,49 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
263
292
  const {
264
293
  maxOutputTokens: maxOutputTokensForModel,
265
294
  supportsStructuredOutput: modelSupportsStructuredOutput,
295
+ supportsAdaptiveThinking,
296
+ rejectsSamplingParameters,
297
+ supportsXhighEffort,
266
298
  isKnownModel,
267
299
  } = getModelCapabilities(this.modelId);
268
300
 
301
+ if (rejectsSamplingParameters) {
302
+ if (temperature != null) {
303
+ warnings.push({
304
+ type: 'unsupported',
305
+ feature: 'temperature',
306
+ details: `temperature is not supported by ${this.modelId} and will be ignored`,
307
+ });
308
+ temperature = undefined;
309
+ }
310
+ if (topK != null) {
311
+ warnings.push({
312
+ type: 'unsupported',
313
+ feature: 'topK',
314
+ details: `topK is not supported by ${this.modelId} and will be ignored`,
315
+ });
316
+ topK = undefined;
317
+ }
318
+ if (topP != null) {
319
+ warnings.push({
320
+ type: 'unsupported',
321
+ feature: 'topP',
322
+ details: `topP is not supported by ${this.modelId} and will be ignored`,
323
+ });
324
+ topP = undefined;
325
+ }
326
+ }
327
+
328
+ const isAnthropicModel = isKnownModel || this.modelId.startsWith('claude-');
329
+
269
330
  const supportsStructuredOutput =
270
331
  (this.config.supportsNativeStructuredOutput ?? true) &&
271
332
  modelSupportsStructuredOutput;
272
333
 
334
+ const supportsStrictTools =
335
+ (this.config.supportsStrictTools ?? true) &&
336
+ modelSupportsStructuredOutput;
337
+
273
338
  const structureOutputMode =
274
339
  anthropicOptions?.structuredOutputMode ?? 'auto';
275
340
  const useStructuredOutput =
@@ -326,6 +391,31 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
326
391
  toolNameMapping,
327
392
  });
328
393
 
394
+ /*
395
+ * Map top-level `reasoning` to Anthropic thinking/effort when provider
396
+ * options don't already specify them. Provider options always take precedence.
397
+ */
398
+ if (isCustomReasoning(reasoning) && anthropicOptions?.effort == null) {
399
+ const reasoningConfig = resolveAnthropicReasoningConfig({
400
+ reasoning,
401
+ supportsAdaptiveThinking,
402
+ supportsXhighEffort,
403
+ maxOutputTokensForModel,
404
+ warnings,
405
+ });
406
+ if (reasoningConfig != null) {
407
+ if (anthropicOptions.thinking == null) {
408
+ anthropicOptions.thinking = reasoningConfig.thinking;
409
+ }
410
+ if (
411
+ reasoningConfig.effort != null &&
412
+ anthropicOptions.thinking?.type !== 'disabled'
413
+ ) {
414
+ anthropicOptions.effort = reasoningConfig.effort;
415
+ }
416
+ }
417
+ }
418
+
329
419
  const thinkingType = anthropicOptions?.thinking?.type;
330
420
  const isThinking =
331
421
  thinkingType === 'enabled' || thinkingType === 'adaptive';
@@ -333,6 +423,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
333
423
  thinkingType === 'enabled'
334
424
  ? anthropicOptions?.thinking?.budgetTokens
335
425
  : undefined;
426
+ const thinkingDisplay =
427
+ thinkingType === 'adaptive'
428
+ ? anthropicOptions?.thinking?.display
429
+ : undefined;
336
430
 
337
431
  const maxTokens = maxOutputTokens ?? maxOutputTokensForModel;
338
432
 
@@ -352,9 +446,11 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
352
446
  thinking: {
353
447
  type: thinkingType,
354
448
  ...(thinkingBudget != null && { budget_tokens: thinkingBudget }),
449
+ ...(thinkingDisplay != null && { display: thinkingDisplay }),
355
450
  },
356
451
  }),
357
452
  ...((anthropicOptions?.effort ||
453
+ anthropicOptions?.taskBudget ||
358
454
  (useStructuredOutput &&
359
455
  responseFormat?.type === 'json' &&
360
456
  responseFormat.schema != null)) && {
@@ -362,6 +458,15 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
362
458
  ...(anthropicOptions?.effort && {
363
459
  effort: anthropicOptions.effort,
364
460
  }),
461
+ ...(anthropicOptions?.taskBudget && {
462
+ task_budget: {
463
+ type: anthropicOptions.taskBudget.type,
464
+ total: anthropicOptions.taskBudget.total,
465
+ ...(anthropicOptions.taskBudget.remaining != null && {
466
+ remaining: anthropicOptions.taskBudget.remaining,
467
+ }),
468
+ },
469
+ }),
365
470
  ...(useStructuredOutput &&
366
471
  responseFormat?.type === 'json' &&
367
472
  responseFormat.schema != null && {
@@ -375,9 +480,15 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
375
480
  ...(anthropicOptions?.speed && {
376
481
  speed: anthropicOptions.speed,
377
482
  }),
483
+ ...(anthropicOptions?.inferenceGeo && {
484
+ inference_geo: anthropicOptions.inferenceGeo,
485
+ }),
378
486
  ...(anthropicOptions?.cacheControl && {
379
487
  cache_control: anthropicOptions.cacheControl,
380
488
  }),
489
+ ...(anthropicOptions?.metadata?.userId != null && {
490
+ metadata: { user_id: anthropicOptions.metadata.userId },
491
+ }),
381
492
 
382
493
  // mcp servers:
383
494
  ...(anthropicOptions?.mcpServers &&
@@ -406,7 +517,13 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
406
517
  id: anthropicOptions.container.id,
407
518
  skills: anthropicOptions.container.skills.map(skill => ({
408
519
  type: skill.type,
409
- skill_id: skill.skillId,
520
+ skill_id:
521
+ skill.type === 'custom'
522
+ ? resolveProviderReference({
523
+ reference: skill.providerReference,
524
+ provider: 'anthropic',
525
+ })
526
+ : skill.skillId,
410
527
  version: skill.version,
411
528
  })),
412
529
  } satisfies AnthropicContainer)
@@ -522,8 +639,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
522
639
  // adjust max tokens to account for thinking:
523
640
  baseArgs.max_tokens = maxTokens + (thinkingBudget ?? 0);
524
641
  } else {
525
- // Only check temperature/topP mutual exclusivity when thinking is not enabled
526
- if (topP != null && temperature != null) {
642
+ // Only check temperature/topP mutual exclusivity for known Anthropic models
643
+ // when thinking is not enabled. Non-Anthropic models using the Anthropic-compatible
644
+ // API (e.g. Minimax) may require both parameters to be set.
645
+ if (isAnthropicModel && topP != null && temperature != null) {
527
646
  warnings.push({
528
647
  type: 'unsupported',
529
648
  feature: 'topP',
@@ -592,14 +711,16 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
592
711
  betas.add('effort-2025-11-24');
593
712
  }
594
713
 
714
+ if (anthropicOptions?.taskBudget) {
715
+ betas.add('task-budgets-2026-03-13');
716
+ }
717
+
595
718
  if (anthropicOptions?.speed === 'fast') {
596
719
  betas.add('fast-mode-2026-02-01');
597
720
  }
598
721
 
599
- // only when streaming: enable fine-grained tool streaming
600
- if (stream && (anthropicOptions?.toolStreaming ?? true)) {
601
- betas.add('fine-grained-tool-streaming-2025-05-14');
602
- }
722
+ const defaultEagerInputStreaming =
723
+ stream && (anthropicOptions?.toolStreaming ?? true);
603
724
 
604
725
  const {
605
726
  tools: anthropicTools,
@@ -614,6 +735,8 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
614
735
  disableParallelToolUse: true,
615
736
  cacheControlValidator,
616
737
  supportsStructuredOutput: false,
738
+ supportsStrictTools,
739
+ defaultEagerInputStreaming,
617
740
  }
618
741
  : {
619
742
  tools: tools ?? [],
@@ -621,6 +744,8 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
621
744
  disableParallelToolUse: anthropicOptions?.disableParallelToolUse,
622
745
  cacheControlValidator,
623
746
  supportsStructuredOutput,
747
+ supportsStrictTools,
748
+ defaultEagerInputStreaming,
624
749
  },
625
750
  );
626
751
 
@@ -656,7 +781,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
656
781
  headers: Record<string, string | undefined> | undefined;
657
782
  }) {
658
783
  return combineHeaders(
659
- await resolve(this.config.headers),
784
+ this.config.headers ? await resolve(this.config.headers) : undefined,
660
785
  headers,
661
786
  betas.size > 0 ? { 'anthropic-beta': Array.from(betas).join(',') } : {},
662
787
  );
@@ -665,9 +790,11 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
665
790
  private async getBetasFromHeaders(
666
791
  requestHeaders: Record<string, string | undefined> | undefined,
667
792
  ) {
668
- const configHeaders = await resolve(this.config.headers);
793
+ const configHeaders = this.config.headers
794
+ ? await resolve(this.config.headers)
795
+ : undefined;
669
796
 
670
- const configBetaHeader = configHeaders['anthropic-beta'] ?? '';
797
+ const configBetaHeader = configHeaders?.['anthropic-beta'] ?? '';
671
798
  const requestBetaHeader = requestHeaders?.['anthropic-beta'] ?? '';
672
799
 
673
800
  return new Set(
@@ -1221,6 +1348,8 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1221
1348
  async doStream(
1222
1349
  options: LanguageModelV4CallOptions,
1223
1350
  ): Promise<LanguageModelV4StreamResult> {
1351
+ 'use step';
1352
+
1224
1353
  const {
1225
1354
  args: body,
1226
1355
  warnings,
@@ -2259,18 +2388,33 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2259
2388
  * @see https://docs.claude.com/en/docs/about-claude/models/overview#model-comparison-table
2260
2389
  * @see https://platform.claude.com/docs/en/build-with-claude/structured-outputs
2261
2390
  */
2262
- function getModelCapabilities(modelId: string): {
2391
+ export function getModelCapabilities(modelId: string): {
2263
2392
  maxOutputTokens: number;
2264
2393
  supportsStructuredOutput: boolean;
2394
+ supportsAdaptiveThinking: boolean;
2395
+ rejectsSamplingParameters: boolean;
2396
+ supportsXhighEffort: boolean;
2265
2397
  isKnownModel: boolean;
2266
2398
  } {
2267
- if (
2399
+ if (modelId.includes('claude-opus-4-7')) {
2400
+ return {
2401
+ maxOutputTokens: 128000,
2402
+ supportsStructuredOutput: true,
2403
+ supportsAdaptiveThinking: true,
2404
+ rejectsSamplingParameters: true,
2405
+ supportsXhighEffort: true,
2406
+ isKnownModel: true,
2407
+ };
2408
+ } else if (
2268
2409
  modelId.includes('claude-sonnet-4-6') ||
2269
2410
  modelId.includes('claude-opus-4-6')
2270
2411
  ) {
2271
2412
  return {
2272
2413
  maxOutputTokens: 128000,
2273
2414
  supportsStructuredOutput: true,
2415
+ supportsAdaptiveThinking: true,
2416
+ rejectsSamplingParameters: false,
2417
+ supportsXhighEffort: false,
2274
2418
  isKnownModel: true,
2275
2419
  };
2276
2420
  } else if (
@@ -2281,36 +2425,54 @@ function getModelCapabilities(modelId: string): {
2281
2425
  return {
2282
2426
  maxOutputTokens: 64000,
2283
2427
  supportsStructuredOutput: true,
2428
+ supportsAdaptiveThinking: false,
2429
+ rejectsSamplingParameters: false,
2430
+ supportsXhighEffort: false,
2284
2431
  isKnownModel: true,
2285
2432
  };
2286
2433
  } else if (modelId.includes('claude-opus-4-1')) {
2287
2434
  return {
2288
2435
  maxOutputTokens: 32000,
2289
2436
  supportsStructuredOutput: true,
2437
+ supportsAdaptiveThinking: false,
2438
+ rejectsSamplingParameters: false,
2439
+ supportsXhighEffort: false,
2290
2440
  isKnownModel: true,
2291
2441
  };
2292
2442
  } else if (modelId.includes('claude-sonnet-4-')) {
2293
2443
  return {
2294
2444
  maxOutputTokens: 64000,
2295
2445
  supportsStructuredOutput: false,
2446
+ supportsAdaptiveThinking: false,
2447
+ rejectsSamplingParameters: false,
2448
+ supportsXhighEffort: false,
2296
2449
  isKnownModel: true,
2297
2450
  };
2298
2451
  } else if (modelId.includes('claude-opus-4-')) {
2299
2452
  return {
2300
2453
  maxOutputTokens: 32000,
2301
2454
  supportsStructuredOutput: false,
2455
+ supportsAdaptiveThinking: false,
2456
+ rejectsSamplingParameters: false,
2457
+ supportsXhighEffort: false,
2302
2458
  isKnownModel: true,
2303
2459
  };
2304
2460
  } else if (modelId.includes('claude-3-haiku')) {
2305
2461
  return {
2306
2462
  maxOutputTokens: 4096,
2307
2463
  supportsStructuredOutput: false,
2464
+ supportsAdaptiveThinking: false,
2465
+ rejectsSamplingParameters: false,
2466
+ supportsXhighEffort: false,
2308
2467
  isKnownModel: true,
2309
2468
  };
2310
2469
  } else {
2311
2470
  return {
2312
2471
  maxOutputTokens: 4096,
2313
2472
  supportsStructuredOutput: false,
2473
+ supportsAdaptiveThinking: false,
2474
+ rejectsSamplingParameters: false,
2475
+ supportsXhighEffort: false,
2314
2476
  isKnownModel: false,
2315
2477
  };
2316
2478
  }
@@ -2341,6 +2503,54 @@ function hasWebTool20260209WithoutCodeExecution(
2341
2503
  return hasWebTool20260209 && !hasCodeExecutionTool;
2342
2504
  }
2343
2505
 
2506
+ function resolveAnthropicReasoningConfig({
2507
+ reasoning,
2508
+ supportsAdaptiveThinking,
2509
+ supportsXhighEffort,
2510
+ maxOutputTokensForModel,
2511
+ warnings,
2512
+ }: {
2513
+ reasoning: LanguageModelV4CallOptions['reasoning'];
2514
+ supportsAdaptiveThinking: boolean;
2515
+ supportsXhighEffort: boolean;
2516
+ maxOutputTokensForModel: number;
2517
+ warnings: SharedV4Warning[];
2518
+ }): Pick<AnthropicLanguageModelOptions, 'thinking' | 'effort'> | undefined {
2519
+ if (!isCustomReasoning(reasoning)) {
2520
+ return undefined;
2521
+ }
2522
+
2523
+ if (reasoning === 'none') {
2524
+ return { thinking: { type: 'disabled' } };
2525
+ }
2526
+
2527
+ if (supportsAdaptiveThinking) {
2528
+ const effort = mapReasoningToProviderEffort({
2529
+ reasoning,
2530
+ effortMap: {
2531
+ minimal: 'low' as const,
2532
+ low: 'low' as const,
2533
+ medium: 'medium' as const,
2534
+ high: 'high' as const,
2535
+ xhigh: supportsXhighEffort ? ('xhigh' as const) : ('max' as const),
2536
+ },
2537
+ warnings,
2538
+ });
2539
+ return { thinking: { type: 'adaptive' }, effort };
2540
+ }
2541
+
2542
+ const budgetTokens = mapReasoningToProviderBudget({
2543
+ reasoning,
2544
+ maxOutputTokens: maxOutputTokensForModel,
2545
+ maxReasoningBudget: maxOutputTokensForModel,
2546
+ warnings,
2547
+ });
2548
+ if (budgetTokens == null) {
2549
+ return undefined;
2550
+ }
2551
+ return { thinking: { type: 'enabled', budgetTokens } };
2552
+ }
2553
+
2344
2554
  function mapAnthropicResponseContextManagement(
2345
2555
  contextManagement: AnthropicResponseContextManagement | null | undefined,
2346
2556
  ): AnthropicMessageMetadata['contextManagement'] | null {
@@ -17,6 +17,7 @@ export type AnthropicMessagesModelId =
17
17
  | 'claude-sonnet-4-5'
18
18
  | 'claude-sonnet-4-6'
19
19
  | 'claude-opus-4-6'
20
+ | 'claude-opus-4-7'
20
21
  | (string & {});
21
22
 
22
23
  /**
@@ -83,6 +84,12 @@ export const anthropicLanguageModelOptions = z.object({
83
84
  z.object({
84
85
  /** for Sonnet 4.6, Opus 4.6, and newer models */
85
86
  type: z.literal('adaptive'),
87
+ /**
88
+ * Controls whether thinking content is included in the response.
89
+ * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+).
90
+ * - `"summarized"`: Thinking content is returned. Required to see reasoning output.
91
+ */
92
+ display: z.enum(['omitted', 'summarized']).optional(),
86
93
  }),
87
94
  z.object({
88
95
  /** for models before Opus 4.6, except Sonnet 4.6 still supports it */
@@ -112,6 +119,23 @@ export const anthropicLanguageModelOptions = z.object({
112
119
  })
113
120
  .optional(),
114
121
 
122
+ /**
123
+ * Metadata to include with the request.
124
+ *
125
+ * See https://platform.claude.com/docs/en/api/messages/create for details.
126
+ */
127
+ metadata: z
128
+ .object({
129
+ /**
130
+ * An external identifier for the user associated with the request.
131
+ *
132
+ * Should be a UUID, hash value, or other opaque identifier.
133
+ * Must not contain PII (name, email, phone number, etc.).
134
+ */
135
+ userId: z.string().optional(),
136
+ })
137
+ .optional(),
138
+
115
139
  /**
116
140
  * MCP servers to be utilized in this request.
117
141
  */
@@ -142,21 +166,29 @@ export const anthropicLanguageModelOptions = z.object({
142
166
  id: z.string().optional(),
143
167
  skills: z
144
168
  .array(
145
- z.object({
146
- type: z.union([z.literal('anthropic'), z.literal('custom')]),
147
- skillId: z.string(),
148
- version: z.string().optional(),
149
- }),
169
+ z.discriminatedUnion('type', [
170
+ z.object({
171
+ type: z.literal('anthropic'),
172
+ skillId: z.string(),
173
+ version: z.string().optional(),
174
+ }),
175
+ z.object({
176
+ type: z.literal('custom'),
177
+ providerReference: z.record(z.string(), z.string()),
178
+ version: z.string().optional(),
179
+ }),
180
+ ]),
150
181
  )
151
182
  .optional(),
152
183
  })
153
184
  .optional(),
154
185
 
155
186
  /**
156
- * Whether to enable tool streaming (and structured output streaming).
157
- *
158
- * When set to false, the model will return all tool calls and results
159
- * at once after a delay.
187
+ * Whether to enable fine-grained (eager) streaming of tool call inputs
188
+ * and structured outputs for every function tool in the request. When
189
+ * true (the default), each function tool receives a default of
190
+ * `eager_input_streaming: true` unless it explicitly sets
191
+ * `providerOptions.anthropic.eagerInputStreaming`.
160
192
  *
161
193
  * @default true
162
194
  */
@@ -165,7 +197,22 @@ export const anthropicLanguageModelOptions = z.object({
165
197
  /**
166
198
  * @default 'high'
167
199
  */
168
- effort: z.enum(['low', 'medium', 'high', 'max']).optional(),
200
+ effort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
201
+
202
+ /**
203
+ * Task budget for agentic turns. Informs the model of the total token budget
204
+ * available for the current task, allowing it to prioritize work and wind down
205
+ * gracefully as the budget is consumed.
206
+ *
207
+ * Advisory only — does not enforce a hard token limit.
208
+ */
209
+ taskBudget: z
210
+ .object({
211
+ type: z.literal('tokens'),
212
+ total: z.number().int().min(20000),
213
+ remaining: z.number().int().min(0).optional(),
214
+ })
215
+ .optional(),
169
216
 
170
217
  /**
171
218
  * Enable fast mode for faster inference (2.5x faster output token speeds).
@@ -173,6 +220,16 @@ export const anthropicLanguageModelOptions = z.object({
173
220
  */
174
221
  speed: z.enum(['fast', 'standard']).optional(),
175
222
 
223
+ /**
224
+ * Controls where model inference runs for this request.
225
+ *
226
+ * - `"global"`: Inference may run in any available geography (default).
227
+ * - `"us"`: Inference runs only in US-based infrastructure.
228
+ *
229
+ * See https://platform.claude.com/docs/en/build-with-claude/data-residency
230
+ */
231
+ inferenceGeo: z.enum(['us', 'global']).optional(),
232
+
176
233
  /**
177
234
  * A set of beta features to enable.
178
235
  * Allow a provider to receive the full `betas` set if it needs it.
@@ -26,6 +26,8 @@ export async function prepareTools({
26
26
  disableParallelToolUse,
27
27
  cacheControlValidator,
28
28
  supportsStructuredOutput,
29
+ supportsStrictTools,
30
+ defaultEagerInputStreaming = false,
29
31
  }: {
30
32
  tools: LanguageModelV4CallOptions['tools'];
31
33
  toolChoice: LanguageModelV4CallOptions['toolChoice'] | undefined;
@@ -33,9 +35,20 @@ export async function prepareTools({
33
35
  cacheControlValidator?: CacheControlValidator;
34
36
 
35
37
  /**
36
- * Whether the model supports structured output.
38
+ * Whether the model supports native structured output response format.
37
39
  */
38
40
  supportsStructuredOutput: boolean;
41
+
42
+ /**
43
+ * Whether the model supports strict mode on tool definitions.
44
+ */
45
+ supportsStrictTools: boolean;
46
+
47
+ /**
48
+ * Default for `eager_input_streaming` on function tools that do not set
49
+ * it explicitly. Driven by the model-level `toolStreaming` option.
50
+ */
51
+ defaultEagerInputStreaming?: boolean;
39
52
  }): Promise<{
40
53
  tools: Array<AnthropicTool> | undefined;
41
54
  toolChoice: AnthropicToolChoice | undefined;
@@ -67,18 +80,28 @@ export async function prepareTools({
67
80
  const anthropicOptions = tool.providerOptions?.anthropic as
68
81
  | AnthropicToolOptions
69
82
  | undefined;
70
- // eager_input_streaming is only supported on custom (function) tools
71
- const eagerInputStreaming = anthropicOptions?.eagerInputStreaming;
83
+ // eager_input_streaming is only supported on custom (function) tools.
84
+ // Fall back to the model-level default when the tool doesn't set it.
85
+ const eagerInputStreaming =
86
+ anthropicOptions?.eagerInputStreaming ?? defaultEagerInputStreaming;
72
87
  const deferLoading = anthropicOptions?.deferLoading;
73
88
  const allowedCallers = anthropicOptions?.allowedCallers;
74
89
 
90
+ if (!supportsStrictTools && tool.strict != null) {
91
+ toolWarnings.push({
92
+ type: 'unsupported',
93
+ feature: 'strict',
94
+ details: `Tool '${tool.name}' has strict: ${tool.strict}, but strict mode is not supported by this provider. The strict property will be ignored.`,
95
+ });
96
+ }
97
+
75
98
  anthropicTools.push({
76
99
  name: tool.name,
77
100
  description: tool.description,
78
101
  input_schema: tool.inputSchema,
79
102
  cache_control: cacheControl,
80
103
  ...(eagerInputStreaming ? { eager_input_streaming: true } : {}),
81
- ...(supportsStructuredOutput === true && tool.strict != null
104
+ ...(supportsStrictTools === true && tool.strict != null
82
105
  ? { strict: tool.strict }
83
106
  : {}),
84
107
  ...(deferLoading != null ? { defer_loading: deferLoading } : {}),
@@ -308,7 +331,6 @@ export async function prepareTools({
308
331
  }
309
332
 
310
333
  case 'anthropic.tool_search_regex_20251119': {
311
- betas.add('advanced-tool-use-2025-11-20');
312
334
  anthropicTools.push({
313
335
  type: 'tool_search_tool_regex_20251119',
314
336
  name: 'tool_search_tool_regex',
@@ -317,7 +339,6 @@ export async function prepareTools({
317
339
  }
318
340
 
319
341
  case 'anthropic.tool_search_bm25_20251119': {
320
- betas.add('advanced-tool-use-2025-11-20');
321
342
  anthropicTools.push({
322
343
  type: 'tool_search_tool_bm25_20251119',
323
344
  name: 'tool_search_tool_bm25',