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

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,6 +711,10 @@ 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
  }
@@ -614,6 +737,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
614
737
  disableParallelToolUse: true,
615
738
  cacheControlValidator,
616
739
  supportsStructuredOutput: false,
740
+ supportsStrictTools,
617
741
  }
618
742
  : {
619
743
  tools: tools ?? [],
@@ -621,6 +745,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
621
745
  disableParallelToolUse: anthropicOptions?.disableParallelToolUse,
622
746
  cacheControlValidator,
623
747
  supportsStructuredOutput,
748
+ supportsStrictTools,
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,11 +166,18 @@ 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
  })
@@ -165,7 +196,22 @@ export const anthropicLanguageModelOptions = z.object({
165
196
  /**
166
197
  * @default 'high'
167
198
  */
168
- effort: z.enum(['low', 'medium', 'high', 'max']).optional(),
199
+ effort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
200
+
201
+ /**
202
+ * Task budget for agentic turns. Informs the model of the total token budget
203
+ * available for the current task, allowing it to prioritize work and wind down
204
+ * gracefully as the budget is consumed.
205
+ *
206
+ * Advisory only — does not enforce a hard token limit.
207
+ */
208
+ taskBudget: z
209
+ .object({
210
+ type: z.literal('tokens'),
211
+ total: z.number().int().min(20000),
212
+ remaining: z.number().int().min(0).optional(),
213
+ })
214
+ .optional(),
169
215
 
170
216
  /**
171
217
  * Enable fast mode for faster inference (2.5x faster output token speeds).
@@ -173,6 +219,16 @@ export const anthropicLanguageModelOptions = z.object({
173
219
  */
174
220
  speed: z.enum(['fast', 'standard']).optional(),
175
221
 
222
+ /**
223
+ * Controls where model inference runs for this request.
224
+ *
225
+ * - `"global"`: Inference may run in any available geography (default).
226
+ * - `"us"`: Inference runs only in US-based infrastructure.
227
+ *
228
+ * See https://platform.claude.com/docs/en/build-with-claude/data-residency
229
+ */
230
+ inferenceGeo: z.enum(['us', 'global']).optional(),
231
+
176
232
  /**
177
233
  * A set of beta features to enable.
178
234
  * Allow a provider to receive the full `betas` set if it needs it.
@@ -26,6 +26,7 @@ export async function prepareTools({
26
26
  disableParallelToolUse,
27
27
  cacheControlValidator,
28
28
  supportsStructuredOutput,
29
+ supportsStrictTools,
29
30
  }: {
30
31
  tools: LanguageModelV4CallOptions['tools'];
31
32
  toolChoice: LanguageModelV4CallOptions['toolChoice'] | undefined;
@@ -33,9 +34,14 @@ export async function prepareTools({
33
34
  cacheControlValidator?: CacheControlValidator;
34
35
 
35
36
  /**
36
- * Whether the model supports structured output.
37
+ * Whether the model supports native structured output response format.
37
38
  */
38
39
  supportsStructuredOutput: boolean;
40
+
41
+ /**
42
+ * Whether the model supports strict mode on tool definitions.
43
+ */
44
+ supportsStrictTools: boolean;
39
45
  }): Promise<{
40
46
  tools: Array<AnthropicTool> | undefined;
41
47
  toolChoice: AnthropicToolChoice | undefined;
@@ -72,13 +78,21 @@ export async function prepareTools({
72
78
  const deferLoading = anthropicOptions?.deferLoading;
73
79
  const allowedCallers = anthropicOptions?.allowedCallers;
74
80
 
81
+ if (!supportsStrictTools && tool.strict != null) {
82
+ toolWarnings.push({
83
+ type: 'unsupported',
84
+ feature: 'strict',
85
+ details: `Tool '${tool.name}' has strict: ${tool.strict}, but strict mode is not supported by this provider. The strict property will be ignored.`,
86
+ });
87
+ }
88
+
75
89
  anthropicTools.push({
76
90
  name: tool.name,
77
91
  description: tool.description,
78
92
  input_schema: tool.inputSchema,
79
93
  cache_control: cacheControl,
80
94
  ...(eagerInputStreaming ? { eager_input_streaming: true } : {}),
81
- ...(supportsStructuredOutput === true && tool.strict != null
95
+ ...(supportsStrictTools === true && tool.strict != null
82
96
  ? { strict: tool.strict }
83
97
  : {}),
84
98
  ...(deferLoading != null ? { defer_loading: deferLoading } : {}),
@@ -308,7 +322,6 @@ export async function prepareTools({
308
322
  }
309
323
 
310
324
  case 'anthropic.tool_search_regex_20251119': {
311
- betas.add('advanced-tool-use-2025-11-20');
312
325
  anthropicTools.push({
313
326
  type: 'tool_search_tool_regex_20251119',
314
327
  name: 'tool_search_tool_regex',
@@ -317,7 +330,6 @@ export async function prepareTools({
317
330
  }
318
331
 
319
332
  case 'anthropic.tool_search_bm25_20251119': {
320
- betas.add('advanced-tool-use-2025-11-20');
321
333
  anthropicTools.push({
322
334
  type: 'tool_search_tool_bm25_20251119',
323
335
  name: 'tool_search_tool_bm25',
@@ -1,8 +1,10 @@
1
1
  import {
2
+ FilesV4,
2
3
  InvalidArgumentError,
3
4
  LanguageModelV4,
4
5
  NoSuchModelError,
5
6
  ProviderV4,
7
+ SkillsV4,
6
8
  } from '@ai-sdk/provider';
7
9
  import {
8
10
  FetchFunction,
@@ -12,10 +14,12 @@ import {
12
14
  withoutTrailingSlash,
13
15
  withUserAgentSuffix,
14
16
  } from '@ai-sdk/provider-utils';
15
- import { VERSION } from './version';
17
+ import { AnthropicFiles } from './anthropic-files';
16
18
  import { AnthropicMessagesLanguageModel } from './anthropic-messages-language-model';
17
19
  import { AnthropicMessagesModelId } from './anthropic-messages-options';
18
20
  import { anthropicTools } from './anthropic-tools';
21
+ import { AnthropicSkills } from './skills/anthropic-skills';
22
+ import { VERSION } from './version';
19
23
 
20
24
  export interface AnthropicProvider extends ProviderV4 {
21
25
  /**
@@ -37,6 +41,13 @@ export interface AnthropicProvider extends ProviderV4 {
37
41
  */
38
42
  textEmbeddingModel(modelId: string): never;
39
43
 
44
+ files(): FilesV4;
45
+
46
+ /**
47
+ * Returns a SkillsV4 interface for uploading skills to Anthropic.
48
+ */
49
+ skills(): SkillsV4;
50
+
40
51
  /**
41
52
  * Anthropic-specific computer use tool.
42
53
  */
@@ -143,6 +154,14 @@ export function createAnthropic(
143
154
  }),
144
155
  });
145
156
 
157
+ const createSkills = () =>
158
+ new AnthropicSkills({
159
+ provider: `${providerName.replace('.messages', '')}.skills`,
160
+ baseURL,
161
+ headers: getHeaders,
162
+ fetch: options.fetch,
163
+ });
164
+
146
165
  const provider = function (modelId: AnthropicMessagesModelId) {
147
166
  if (new.target) {
148
167
  throw new Error(
@@ -166,6 +185,16 @@ export function createAnthropic(
166
185
  throw new NoSuchModelError({ modelId, modelType: 'imageModel' });
167
186
  };
168
187
 
188
+ provider.files = () =>
189
+ new AnthropicFiles({
190
+ provider: providerName,
191
+ baseURL,
192
+ headers: getHeaders,
193
+ fetch: options.fetch,
194
+ });
195
+
196
+ provider.skills = createSkills;
197
+
169
198
  provider.tools = anthropicTools;
170
199
 
171
200
  return provider;