@ai-sdk/anthropic 4.0.0-beta.5 → 4.0.0-beta.67

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +500 -4
  2. package/README.md +2 -0
  3. package/dist/index.d.ts +265 -68
  4. package/dist/index.js +2636 -1427
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +234 -62
  7. package/dist/internal/index.js +2605 -1413
  8. package/dist/internal/index.js.map +1 -1
  9. package/docs/05-anthropic.mdx +303 -20
  10. package/package.json +16 -17
  11. package/src/{anthropic-messages-api.ts → anthropic-api.ts} +158 -17
  12. package/src/anthropic-error.ts +1 -1
  13. package/src/anthropic-files.ts +95 -0
  14. package/src/{anthropic-messages-options.ts → anthropic-language-model-options.ts} +104 -11
  15. package/src/{anthropic-messages-language-model.ts → anthropic-language-model.ts} +494 -96
  16. package/src/anthropic-message-metadata.ts +69 -9
  17. package/src/anthropic-prepare-tools.ts +31 -7
  18. package/src/anthropic-provider.ts +42 -13
  19. package/src/anthropic-tools.ts +31 -0
  20. package/src/convert-anthropic-usage.ts +109 -0
  21. package/src/{convert-to-anthropic-messages-prompt.ts → convert-to-anthropic-prompt.ts} +376 -198
  22. package/src/forward-anthropic-container-id-from-last-step.ts +2 -2
  23. package/src/get-cache-control.ts +5 -2
  24. package/src/index.ts +1 -1
  25. package/src/internal/index.ts +13 -2
  26. package/src/map-anthropic-stop-reason.ts +1 -1
  27. package/src/sanitize-json-schema.ts +203 -0
  28. package/src/skills/anthropic-skills-api.ts +44 -0
  29. package/src/skills/anthropic-skills.ts +132 -0
  30. package/src/tool/advisor_20260301.ts +128 -0
  31. package/src/tool/bash_20241022.ts +84 -13
  32. package/src/tool/bash_20250124.ts +84 -13
  33. package/src/tool/code-execution_20250522.ts +2 -2
  34. package/src/tool/code-execution_20250825.ts +2 -2
  35. package/src/tool/code-execution_20260120.ts +2 -2
  36. package/src/tool/computer_20241022.ts +2 -2
  37. package/src/tool/computer_20250124.ts +2 -2
  38. package/src/tool/computer_20251124.ts +2 -2
  39. package/src/tool/memory_20250818.ts +2 -2
  40. package/src/tool/text-editor_20241022.ts +2 -2
  41. package/src/tool/text-editor_20250124.ts +2 -2
  42. package/src/tool/text-editor_20250429.ts +2 -2
  43. package/src/tool/text-editor_20250728.ts +6 -3
  44. package/src/tool/tool-search-bm25_20251119.ts +2 -2
  45. package/src/tool/tool-search-regex_20251119.ts +2 -2
  46. package/src/tool/web-fetch-20250910.ts +2 -2
  47. package/src/tool/web-fetch-20260209.ts +2 -2
  48. package/src/tool/web-search_20250305.ts +2 -2
  49. package/src/tool/web-search_20260209.ts +2 -2
  50. package/dist/index.d.mts +0 -1090
  51. package/dist/index.mjs +0 -5244
  52. package/dist/index.mjs.map +0 -1
  53. package/dist/internal/index.d.mts +0 -969
  54. package/dist/internal/index.mjs +0 -5136
  55. package/dist/internal/index.mjs.map +0 -1
  56. package/src/convert-anthropic-messages-usage.ts +0 -73
@@ -1,57 +1,70 @@
1
1
  import {
2
2
  APICallError,
3
- JSONObject,
4
- LanguageModelV4,
5
- LanguageModelV4CallOptions,
6
- LanguageModelV4Content,
7
- LanguageModelV4FinishReason,
8
- LanguageModelV4FunctionTool,
9
- LanguageModelV4GenerateResult,
10
- LanguageModelV4Prompt,
11
- LanguageModelV4Source,
12
- LanguageModelV4StreamPart,
13
- LanguageModelV4StreamResult,
14
- LanguageModelV4ToolCall,
15
- SharedV4ProviderMetadata,
16
- SharedV4Warning,
3
+ type JSONObject,
4
+ type LanguageModelV4,
5
+ type LanguageModelV4CallOptions,
6
+ type LanguageModelV4Content,
7
+ type LanguageModelV4FinishReason,
8
+ type LanguageModelV4FunctionTool,
9
+ type LanguageModelV4GenerateResult,
10
+ type LanguageModelV4Prompt,
11
+ type LanguageModelV4Source,
12
+ type LanguageModelV4StreamPart,
13
+ type LanguageModelV4StreamResult,
14
+ type LanguageModelV4ToolCall,
15
+ type SharedV4ProviderMetadata,
16
+ type SharedV4Warning,
17
17
  } from '@ai-sdk/provider';
18
18
  import {
19
19
  combineHeaders,
20
20
  createEventSourceResponseHandler,
21
21
  createJsonResponseHandler,
22
22
  createToolNameMapping,
23
- FetchFunction,
24
23
  generateId,
25
- InferSchema,
24
+ isCustomReasoning,
25
+ mapReasoningToProviderBudget,
26
+ mapReasoningToProviderEffort,
26
27
  parseProviderOptions,
27
- ParseResult,
28
28
  postJsonToApi,
29
- Resolvable,
30
29
  resolve,
30
+ resolveProviderReference,
31
+ serializeModelOptions,
32
+ WORKFLOW_SERIALIZE,
33
+ WORKFLOW_DESERIALIZE,
34
+ type FetchFunction,
35
+ type InferSchema,
36
+ type ParseResult,
37
+ type Resolvable,
31
38
  } from '@ai-sdk/provider-utils';
32
39
  import { anthropicFailedResponseHandler } from './anthropic-error';
33
- import { AnthropicMessageMetadata } from './anthropic-message-metadata';
40
+ import type {
41
+ AnthropicMessageMetadata,
42
+ AnthropicUsageIteration,
43
+ } from './anthropic-message-metadata';
34
44
  import {
35
- AnthropicContainer,
36
- anthropicMessagesChunkSchema,
37
- anthropicMessagesResponseSchema,
38
- AnthropicReasoningMetadata,
39
- AnthropicResponseContextManagement,
40
- AnthropicTool,
41
- Citation,
42
- } from './anthropic-messages-api';
45
+ anthropicChunkSchema,
46
+ anthropicResponseSchema,
47
+ type AnthropicContainer,
48
+ type AnthropicReasoningMetadata,
49
+ type AnthropicResponseContextManagement,
50
+ type AnthropicStopDetails,
51
+ type AnthropicTool,
52
+ type Citation,
53
+ } from './anthropic-api';
43
54
  import {
44
- AnthropicMessagesModelId,
45
55
  anthropicLanguageModelOptions,
46
- } from './anthropic-messages-options';
56
+ type AnthropicModelId,
57
+ type AnthropicLanguageModelOptions,
58
+ } from './anthropic-language-model-options';
47
59
  import { prepareTools } from './anthropic-prepare-tools';
48
60
  import {
49
- AnthropicMessagesUsage,
50
- convertAnthropicMessagesUsage,
51
- } from './convert-anthropic-messages-usage';
52
- import { convertToAnthropicMessagesPrompt } from './convert-to-anthropic-messages-prompt';
61
+ convertAnthropicUsage,
62
+ type AnthropicUsage,
63
+ } from './convert-anthropic-usage';
64
+ import { convertToAnthropicPrompt } from './convert-to-anthropic-prompt';
53
65
  import { CacheControlValidator } from './get-cache-control';
54
66
  import { mapAnthropicStopReason } from './map-anthropic-stop-reason';
67
+ import { sanitizeJsonSchema } from './sanitize-json-schema';
55
68
 
56
69
  function createCitationSource(
57
70
  citation: Citation,
@@ -112,10 +125,10 @@ function createCitationSource(
112
125
  };
113
126
  }
114
127
 
115
- type AnthropicMessagesConfig = {
128
+ type AnthropicLanguageModelConfig = {
116
129
  provider: string;
117
130
  baseURL: string;
118
- headers: Resolvable<Record<string, string | undefined>>;
131
+ headers?: Resolvable<Record<string, string | undefined>>;
119
132
  fetch?: FetchFunction;
120
133
  buildRequestUrl?: (baseURL: string, isStreaming: boolean) => string;
121
134
  transformRequestBody?: (
@@ -137,18 +150,29 @@ type AnthropicMessagesConfig = {
137
150
  supportsStrictTools?: boolean;
138
151
  };
139
152
 
140
- export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
153
+ export class AnthropicLanguageModel implements LanguageModelV4 {
141
154
  readonly specificationVersion = 'v4';
142
155
 
143
- readonly modelId: AnthropicMessagesModelId;
156
+ readonly modelId: AnthropicModelId;
144
157
 
145
- private readonly config: AnthropicMessagesConfig;
158
+ private readonly config: AnthropicLanguageModelConfig;
146
159
  private readonly generateId: () => string;
147
160
 
148
- constructor(
149
- modelId: AnthropicMessagesModelId,
150
- config: AnthropicMessagesConfig,
151
- ) {
161
+ static [WORKFLOW_SERIALIZE](model: AnthropicLanguageModel) {
162
+ return serializeModelOptions({
163
+ modelId: model.modelId,
164
+ config: model.config,
165
+ });
166
+ }
167
+
168
+ static [WORKFLOW_DESERIALIZE](options: {
169
+ modelId: AnthropicModelId;
170
+ config: AnthropicLanguageModelConfig;
171
+ }) {
172
+ return new AnthropicLanguageModel(options.modelId, options.config);
173
+ }
174
+
175
+ constructor(modelId: AnthropicModelId, config: AnthropicLanguageModelConfig) {
152
176
  this.modelId = modelId;
153
177
  this.config = config;
154
178
  this.generateId = config.generateId ?? generateId;
@@ -190,6 +214,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
190
214
  seed,
191
215
  tools,
192
216
  toolChoice,
217
+ reasoning,
193
218
  providerOptions,
194
219
  stream,
195
220
  }: LanguageModelV4CallOptions & {
@@ -269,9 +294,41 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
269
294
  const {
270
295
  maxOutputTokens: maxOutputTokensForModel,
271
296
  supportsStructuredOutput: modelSupportsStructuredOutput,
297
+ supportsAdaptiveThinking,
298
+ rejectsSamplingParameters,
299
+ supportsXhighEffort,
272
300
  isKnownModel,
273
301
  } = getModelCapabilities(this.modelId);
274
302
 
303
+ if (rejectsSamplingParameters) {
304
+ if (temperature != null) {
305
+ warnings.push({
306
+ type: 'unsupported',
307
+ feature: 'temperature',
308
+ details: `temperature is not supported by ${this.modelId} and will be ignored`,
309
+ });
310
+ temperature = undefined;
311
+ }
312
+ if (topK != null) {
313
+ warnings.push({
314
+ type: 'unsupported',
315
+ feature: 'topK',
316
+ details: `topK is not supported by ${this.modelId} and will be ignored`,
317
+ });
318
+ topK = undefined;
319
+ }
320
+ if (topP != null) {
321
+ warnings.push({
322
+ type: 'unsupported',
323
+ feature: 'topP',
324
+ details: `topP is not supported by ${this.modelId} and will be ignored`,
325
+ });
326
+ topP = undefined;
327
+ }
328
+ }
329
+
330
+ const isAnthropicModel = isKnownModel || this.modelId.startsWith('claude-');
331
+
275
332
  const supportsStructuredOutput =
276
333
  (this.config.supportsNativeStructuredOutput ?? true) &&
277
334
  modelSupportsStructuredOutput;
@@ -324,17 +381,42 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
324
381
  'anthropic.web_fetch_20260209': 'web_fetch',
325
382
  'anthropic.tool_search_regex_20251119': 'tool_search_tool_regex',
326
383
  'anthropic.tool_search_bm25_20251119': 'tool_search_tool_bm25',
384
+ 'anthropic.advisor_20260301': 'advisor',
327
385
  },
328
386
  });
329
387
 
330
- const { prompt: messagesPrompt, betas } =
331
- await convertToAnthropicMessagesPrompt({
332
- prompt,
333
- sendReasoning: anthropicOptions?.sendReasoning ?? true,
388
+ const { prompt: messagesPrompt, betas } = await convertToAnthropicPrompt({
389
+ prompt,
390
+ sendReasoning: anthropicOptions?.sendReasoning ?? true,
391
+ warnings,
392
+ cacheControlValidator,
393
+ toolNameMapping,
394
+ });
395
+
396
+ /*
397
+ * Map top-level `reasoning` to Anthropic thinking/effort when provider
398
+ * options don't already specify them. Provider options always take precedence.
399
+ */
400
+ if (isCustomReasoning(reasoning) && anthropicOptions?.effort == null) {
401
+ const reasoningConfig = resolveAnthropicReasoningConfig({
402
+ reasoning,
403
+ supportsAdaptiveThinking,
404
+ supportsXhighEffort,
405
+ maxOutputTokensForModel,
334
406
  warnings,
335
- cacheControlValidator,
336
- toolNameMapping,
337
407
  });
408
+ if (reasoningConfig != null) {
409
+ if (anthropicOptions.thinking == null) {
410
+ anthropicOptions.thinking = reasoningConfig.thinking;
411
+ }
412
+ if (
413
+ reasoningConfig.effort != null &&
414
+ anthropicOptions.thinking?.type !== 'disabled'
415
+ ) {
416
+ anthropicOptions.effort = reasoningConfig.effort;
417
+ }
418
+ }
419
+ }
338
420
 
339
421
  const thinkingType = anthropicOptions?.thinking?.type;
340
422
  const isThinking =
@@ -343,6 +425,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
343
425
  thinkingType === 'enabled'
344
426
  ? anthropicOptions?.thinking?.budgetTokens
345
427
  : undefined;
428
+ const thinkingDisplay =
429
+ thinkingType === 'adaptive'
430
+ ? anthropicOptions?.thinking?.display
431
+ : undefined;
346
432
 
347
433
  const maxTokens = maxOutputTokens ?? maxOutputTokensForModel;
348
434
 
@@ -362,9 +448,11 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
362
448
  thinking: {
363
449
  type: thinkingType,
364
450
  ...(thinkingBudget != null && { budget_tokens: thinkingBudget }),
451
+ ...(thinkingDisplay != null && { display: thinkingDisplay }),
365
452
  },
366
453
  }),
367
454
  ...((anthropicOptions?.effort ||
455
+ anthropicOptions?.taskBudget ||
368
456
  (useStructuredOutput &&
369
457
  responseFormat?.type === 'json' &&
370
458
  responseFormat.schema != null)) && {
@@ -372,12 +460,21 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
372
460
  ...(anthropicOptions?.effort && {
373
461
  effort: anthropicOptions.effort,
374
462
  }),
463
+ ...(anthropicOptions?.taskBudget && {
464
+ task_budget: {
465
+ type: anthropicOptions.taskBudget.type,
466
+ total: anthropicOptions.taskBudget.total,
467
+ ...(anthropicOptions.taskBudget.remaining != null && {
468
+ remaining: anthropicOptions.taskBudget.remaining,
469
+ }),
470
+ },
471
+ }),
375
472
  ...(useStructuredOutput &&
376
473
  responseFormat?.type === 'json' &&
377
474
  responseFormat.schema != null && {
378
475
  format: {
379
476
  type: 'json_schema',
380
- schema: responseFormat.schema,
477
+ schema: sanitizeJsonSchema(responseFormat.schema),
381
478
  },
382
479
  }),
383
480
  },
@@ -385,9 +482,19 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
385
482
  ...(anthropicOptions?.speed && {
386
483
  speed: anthropicOptions.speed,
387
484
  }),
485
+ ...(anthropicOptions?.inferenceGeo && {
486
+ inference_geo: anthropicOptions.inferenceGeo,
487
+ }),
488
+ ...(anthropicOptions?.fallbacks &&
489
+ anthropicOptions.fallbacks.length > 0 && {
490
+ fallbacks: anthropicOptions.fallbacks,
491
+ }),
388
492
  ...(anthropicOptions?.cacheControl && {
389
493
  cache_control: anthropicOptions.cacheControl,
390
494
  }),
495
+ ...(anthropicOptions?.metadata?.userId != null && {
496
+ metadata: { user_id: anthropicOptions.metadata.userId },
497
+ }),
391
498
 
392
499
  // mcp servers:
393
500
  ...(anthropicOptions?.mcpServers &&
@@ -416,7 +523,13 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
416
523
  id: anthropicOptions.container.id,
417
524
  skills: anthropicOptions.container.skills.map(skill => ({
418
525
  type: skill.type,
419
- skill_id: skill.skillId,
526
+ skill_id:
527
+ skill.type === 'custom'
528
+ ? resolveProviderReference({
529
+ reference: skill.providerReference,
530
+ provider: 'anthropic',
531
+ })
532
+ : skill.skillId,
420
533
  version: skill.version,
421
534
  })),
422
535
  } satisfies AnthropicContainer)
@@ -532,8 +645,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
532
645
  // adjust max tokens to account for thinking:
533
646
  baseArgs.max_tokens = maxTokens + (thinkingBudget ?? 0);
534
647
  } else {
535
- // Only check temperature/topP mutual exclusivity when thinking is not enabled
536
- if (topP != null && temperature != null) {
648
+ // Only check temperature/topP mutual exclusivity for known Anthropic models
649
+ // when thinking is not enabled. Non-Anthropic models using the Anthropic-compatible
650
+ // API (e.g. Minimax) may require both parameters to be set.
651
+ if (isAnthropicModel && topP != null && temperature != null) {
537
652
  warnings.push({
538
653
  type: 'unsupported',
539
654
  feature: 'topP',
@@ -598,19 +713,21 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
598
713
  }
599
714
  }
600
715
 
601
- if (anthropicOptions?.effort) {
602
- betas.add('effort-2025-11-24');
716
+ if (anthropicOptions?.taskBudget) {
717
+ betas.add('task-budgets-2026-03-13');
603
718
  }
604
719
 
605
720
  if (anthropicOptions?.speed === 'fast') {
606
721
  betas.add('fast-mode-2026-02-01');
607
722
  }
608
723
 
609
- // only when streaming: enable fine-grained tool streaming
610
- if (stream && (anthropicOptions?.toolStreaming ?? true)) {
611
- betas.add('fine-grained-tool-streaming-2025-05-14');
724
+ if (anthropicOptions?.fallbacks && anthropicOptions.fallbacks.length > 0) {
725
+ betas.add('server-side-fallback-2026-06-01');
612
726
  }
613
727
 
728
+ const defaultEagerInputStreaming =
729
+ stream && (anthropicOptions?.toolStreaming ?? true);
730
+
614
731
  const {
615
732
  tools: anthropicTools,
616
733
  toolChoice: anthropicToolChoice,
@@ -625,6 +742,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
625
742
  cacheControlValidator,
626
743
  supportsStructuredOutput: false,
627
744
  supportsStrictTools,
745
+ defaultEagerInputStreaming,
628
746
  }
629
747
  : {
630
748
  tools: tools ?? [],
@@ -633,6 +751,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
633
751
  cacheControlValidator,
634
752
  supportsStructuredOutput,
635
753
  supportsStrictTools,
754
+ defaultEagerInputStreaming,
636
755
  },
637
756
  );
638
757
 
@@ -668,7 +787,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
668
787
  headers: Record<string, string | undefined> | undefined;
669
788
  }) {
670
789
  return combineHeaders(
671
- await resolve(this.config.headers),
790
+ this.config.headers ? await resolve(this.config.headers) : undefined,
672
791
  headers,
673
792
  betas.size > 0 ? { 'anthropic-beta': Array.from(betas).join(',') } : {},
674
793
  );
@@ -677,9 +796,11 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
677
796
  private async getBetasFromHeaders(
678
797
  requestHeaders: Record<string, string | undefined> | undefined,
679
798
  ) {
680
- const configHeaders = await resolve(this.config.headers);
799
+ const configHeaders = this.config.headers
800
+ ? await resolve(this.config.headers)
801
+ : undefined;
681
802
 
682
- const configBetaHeader = configHeaders['anthropic-beta'] ?? '';
803
+ const configBetaHeader = configHeaders?.['anthropic-beta'] ?? '';
683
804
  const requestBetaHeader = requestHeaders?.['anthropic-beta'] ?? '';
684
805
 
685
806
  return new Set(
@@ -785,7 +906,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
785
906
  body: this.transformRequestBody(args, betas),
786
907
  failedResponseHandler: anthropicFailedResponseHandler,
787
908
  successfulResponseHandler: createJsonResponseHandler(
788
- anthropicMessagesResponseSchema,
909
+ anthropicResponseSchema,
789
910
  ),
790
911
  abortSignal: options.abortSignal,
791
912
  fetch: this.config.fetch,
@@ -900,12 +1021,23 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
900
1021
  part.name === 'text_editor_code_execution' ||
901
1022
  part.name === 'bash_code_execution'
902
1023
  ) {
1024
+ // map tool names for the code execution 20250825 tool:
1025
+ // both map to 'code_execution'.
1026
+ const providerToolName = 'code_execution';
903
1027
  content.push({
904
1028
  type: 'tool-call',
905
1029
  toolCallId: part.id,
906
1030
  toolName: toolNameMapping.toCustomToolName('code_execution'),
907
1031
  input: JSON.stringify({ type: part.name, ...part.input }),
908
1032
  providerExecuted: true,
1033
+ // Specific 'web_fetch' or 'web_search' tools may need code execution, which the Anthropic API
1034
+ // implicitly allows. In this scenario, we need to allow 'code_execution' tool calls even if the
1035
+ // tool was not explicitly provided. We therefore bypass the general validation by marking the
1036
+ // tool as dynamic.
1037
+ ...(markCodeExecutionDynamic &&
1038
+ providerToolName === 'code_execution'
1039
+ ? { dynamic: true }
1040
+ : {}),
909
1041
  });
910
1042
  } else if (
911
1043
  part.name === 'web_search' ||
@@ -928,8 +1060,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
928
1060
  toolName: toolNameMapping.toCustomToolName(part.name),
929
1061
  input: JSON.stringify(inputToSerialize),
930
1062
  providerExecuted: true,
931
- // We want this 'code_execution' tool call to be allowed even if the tool is not explicitly provided.
932
- // Since the validation generally bypasses dynamic tools, we mark this specific tool as dynamic.
1063
+ // Specific 'web_fetch' or 'web_search' tools may need code execution, which the Anthropic API
1064
+ // implicitly allows. In this scenario, we need to allow 'code_execution' tool calls even if the
1065
+ // tool was not explicitly provided. We therefore bypass the general validation by marking the
1066
+ // tool as dynamic.
933
1067
  ...(markCodeExecutionDynamic && part.name === 'code_execution'
934
1068
  ? { dynamic: true }
935
1069
  : {}),
@@ -946,6 +1080,14 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
946
1080
  input: JSON.stringify(part.input),
947
1081
  providerExecuted: true,
948
1082
  });
1083
+ } else if (part.name === 'advisor') {
1084
+ content.push({
1085
+ type: 'tool-call',
1086
+ toolCallId: part.id,
1087
+ toolName: toolNameMapping.toCustomToolName('advisor'),
1088
+ input: JSON.stringify(part.input),
1089
+ providerExecuted: true,
1090
+ });
949
1091
  }
950
1092
 
951
1093
  break;
@@ -1164,6 +1306,51 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1164
1306
  }
1165
1307
  break;
1166
1308
  }
1309
+
1310
+ // advisor results for advisor_20260301:
1311
+ case 'advisor_tool_result': {
1312
+ const advisorToolName = toolNameMapping.toCustomToolName('advisor');
1313
+ if (part.content.type === 'advisor_result') {
1314
+ content.push({
1315
+ type: 'tool-result',
1316
+ toolCallId: part.tool_use_id,
1317
+ toolName: advisorToolName,
1318
+ result: {
1319
+ type: 'advisor_result',
1320
+ text: part.content.text,
1321
+ },
1322
+ });
1323
+ } else if (part.content.type === 'advisor_redacted_result') {
1324
+ content.push({
1325
+ type: 'tool-result',
1326
+ toolCallId: part.tool_use_id,
1327
+ toolName: advisorToolName,
1328
+ result: {
1329
+ type: 'advisor_redacted_result',
1330
+ encryptedContent: part.content.encrypted_content,
1331
+ },
1332
+ });
1333
+ } else {
1334
+ content.push({
1335
+ type: 'tool-result',
1336
+ toolCallId: part.tool_use_id,
1337
+ toolName: advisorToolName,
1338
+ isError: true,
1339
+ result: {
1340
+ type: 'advisor_tool_result_error',
1341
+ errorCode: part.content.error_code,
1342
+ },
1343
+ });
1344
+ }
1345
+ break;
1346
+ }
1347
+
1348
+ // Server-side fallback marker: the AI SDK has no content primitive for
1349
+ // a model hop, so drop it. The hop is still observable via
1350
+ // usage.iterations.
1351
+ case 'fallback': {
1352
+ break;
1353
+ }
1167
1354
  }
1168
1355
  }
1169
1356
 
@@ -1176,7 +1363,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1176
1363
  }),
1177
1364
  raw: response.stop_reason ?? undefined,
1178
1365
  },
1179
- usage: convertAnthropicMessagesUsage({ usage: response.usage }),
1366
+ usage: convertAnthropicUsage({ usage: response.usage }),
1180
1367
  request: { body: args },
1181
1368
  response: {
1182
1369
  id: response.id ?? undefined,
@@ -1186,18 +1373,34 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1186
1373
  },
1187
1374
  warnings,
1188
1375
  providerMetadata: (() => {
1376
+ const stopDetails = mapAnthropicStopDetails(response.stop_details);
1377
+
1189
1378
  const anthropicMetadata = {
1190
1379
  usage: response.usage as JSONObject,
1191
- cacheCreationInputTokens:
1192
- response.usage.cache_creation_input_tokens ?? null,
1193
1380
  stopSequence: response.stop_sequence ?? null,
1381
+ ...(stopDetails != null ? { stopDetails } : {}),
1194
1382
 
1195
1383
  iterations: response.usage.iterations
1196
- ? response.usage.iterations.map(iter => ({
1197
- type: iter.type,
1198
- inputTokens: iter.input_tokens,
1199
- outputTokens: iter.output_tokens,
1200
- }))
1384
+ ? response.usage.iterations.map(
1385
+ iter =>
1386
+ ({
1387
+ type: iter.type,
1388
+ ...(iter.model != null ? { model: iter.model } : {}),
1389
+ inputTokens: iter.input_tokens,
1390
+ outputTokens: iter.output_tokens,
1391
+ ...(iter.cache_creation_input_tokens
1392
+ ? {
1393
+ cacheCreationInputTokens:
1394
+ iter.cache_creation_input_tokens,
1395
+ }
1396
+ : {}),
1397
+ ...(iter.cache_read_input_tokens
1398
+ ? {
1399
+ cacheReadInputTokens: iter.cache_read_input_tokens,
1400
+ }
1401
+ : {}),
1402
+ }) satisfies AnthropicUsageIteration,
1403
+ )
1201
1404
  : null,
1202
1405
  container: response.container
1203
1406
  ? {
@@ -1233,6 +1436,8 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1233
1436
  async doStream(
1234
1437
  options: LanguageModelV4CallOptions,
1235
1438
  ): Promise<LanguageModelV4StreamResult> {
1439
+ 'use step';
1440
+
1236
1441
  const {
1237
1442
  args: body,
1238
1443
  warnings,
@@ -1262,9 +1467,8 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1262
1467
  headers: await this.getHeaders({ betas, headers: options.headers }),
1263
1468
  body: this.transformRequestBody(body, betas),
1264
1469
  failedResponseHandler: anthropicFailedResponseHandler,
1265
- successfulResponseHandler: createEventSourceResponseHandler(
1266
- anthropicMessagesChunkSchema,
1267
- ),
1470
+ successfulResponseHandler:
1471
+ createEventSourceResponseHandler(anthropicChunkSchema),
1268
1472
  abortSignal: options.abortSignal,
1269
1473
  fetch: this.config.fetch,
1270
1474
  });
@@ -1273,7 +1477,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1273
1477
  unified: 'other',
1274
1478
  raw: undefined,
1275
1479
  };
1276
- const usage: AnthropicMessagesUsage = {
1480
+ const usage: AnthropicUsage = {
1277
1481
  input_tokens: 0,
1278
1482
  output_tokens: 0,
1279
1483
  cache_creation_input_tokens: 0,
@@ -1308,8 +1512,8 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1308
1512
  | AnthropicMessageMetadata['contextManagement']
1309
1513
  | null = null;
1310
1514
  let rawUsage: JSONObject | undefined = undefined;
1311
- let cacheCreationInputTokens: number | null = null;
1312
1515
  let stopSequence: string | null = null;
1516
+ let stopDetails: AnthropicMessageMetadata['stopDetails'] = undefined;
1313
1517
  let container: AnthropicMessageMetadata['container'] | null = null;
1314
1518
  let isJsonResponseFromTool = false;
1315
1519
 
@@ -1325,6 +1529,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1325
1529
  | 'text_editor_code_execution_tool_result'
1326
1530
  | 'bash_code_execution_tool_result'
1327
1531
  | 'tool_search_tool_result'
1532
+ | 'advisor_tool_result'
1328
1533
  | 'mcp_tool_use'
1329
1534
  | 'mcp_tool_result'
1330
1535
  | 'compaction'
@@ -1334,7 +1539,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1334
1539
 
1335
1540
  const transformedStream = response.pipeThrough(
1336
1541
  new TransformStream<
1337
- ParseResult<InferSchema<typeof anthropicMessagesChunkSchema>>,
1542
+ ParseResult<InferSchema<typeof anthropicChunkSchema>>,
1338
1543
  LanguageModelV4StreamPart
1339
1544
  >({
1340
1545
  start(controller) {
@@ -1361,6 +1566,14 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1361
1566
  case 'content_block_start': {
1362
1567
  const part = value.content_block;
1363
1568
  const contentBlockType = part.type;
1569
+
1570
+ // Server-side fallback marker: the AI SDK has no content
1571
+ // primitive for a model hop, so drop it. The hop is still
1572
+ // observable via usage.iterations.
1573
+ if (contentBlockType === 'fallback') {
1574
+ return;
1575
+ }
1576
+
1364
1577
  blockType = contentBlockType;
1365
1578
 
1366
1579
  switch (contentBlockType) {
@@ -1506,12 +1719,16 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1506
1719
  toolName: customToolName,
1507
1720
  input: finalInput,
1508
1721
  providerExecuted: true,
1722
+ // Specific 'web_fetch' or 'web_search' tools may need code execution, which the Anthropic API
1723
+ // implicitly allows. In this scenario, we need to allow 'code_execution' tool calls even if the
1724
+ // tool was not explicitly provided. We therefore bypass the general validation by marking the
1725
+ // tool as dynamic.
1509
1726
  ...(markCodeExecutionDynamic &&
1510
1727
  providerToolName === 'code_execution'
1511
1728
  ? { dynamic: true }
1512
1729
  : {}),
1513
1730
  firstDelta: true,
1514
- providerToolName: part.name,
1731
+ providerToolName,
1515
1732
  };
1516
1733
 
1517
1734
  controller.enqueue({
@@ -1519,6 +1736,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1519
1736
  id: part.id,
1520
1737
  toolName: customToolName,
1521
1738
  providerExecuted: true,
1739
+ // Specific 'web_fetch' or 'web_search' tools may need code execution, which the Anthropic API
1740
+ // implicitly allows. In this scenario, we need to allow 'code_execution' tool calls even if the
1741
+ // tool was not explicitly provided. We therefore bypass the general validation by marking the
1742
+ // tool as dynamic.
1522
1743
  ...(markCodeExecutionDynamic &&
1523
1744
  providerToolName === 'code_execution'
1524
1745
  ? { dynamic: true }
@@ -1543,6 +1764,26 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1543
1764
  providerToolName: part.name,
1544
1765
  };
1545
1766
 
1767
+ controller.enqueue({
1768
+ type: 'tool-input-start',
1769
+ id: part.id,
1770
+ toolName: customToolName,
1771
+ providerExecuted: true,
1772
+ });
1773
+ } else if (part.name === 'advisor') {
1774
+ const customToolName =
1775
+ toolNameMapping.toCustomToolName('advisor');
1776
+
1777
+ contentBlocks[value.index] = {
1778
+ type: 'tool-call',
1779
+ toolCallId: part.id,
1780
+ toolName: customToolName,
1781
+ input: '{}',
1782
+ providerExecuted: true,
1783
+ firstDelta: true,
1784
+ providerToolName: part.name,
1785
+ };
1786
+
1546
1787
  controller.enqueue({
1547
1788
  type: 'tool-input-start',
1548
1789
  id: part.id,
@@ -1754,6 +1995,46 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1754
1995
  return;
1755
1996
  }
1756
1997
 
1998
+ // advisor results for advisor_20260301:
1999
+ // arrives fully formed in a single content_block_start (no deltas).
2000
+ case 'advisor_tool_result': {
2001
+ const advisorToolName =
2002
+ toolNameMapping.toCustomToolName('advisor');
2003
+ if (part.content.type === 'advisor_result') {
2004
+ controller.enqueue({
2005
+ type: 'tool-result',
2006
+ toolCallId: part.tool_use_id,
2007
+ toolName: advisorToolName,
2008
+ result: {
2009
+ type: 'advisor_result',
2010
+ text: part.content.text,
2011
+ },
2012
+ });
2013
+ } else if (part.content.type === 'advisor_redacted_result') {
2014
+ controller.enqueue({
2015
+ type: 'tool-result',
2016
+ toolCallId: part.tool_use_id,
2017
+ toolName: advisorToolName,
2018
+ result: {
2019
+ type: 'advisor_redacted_result',
2020
+ encryptedContent: part.content.encrypted_content,
2021
+ },
2022
+ });
2023
+ } else {
2024
+ controller.enqueue({
2025
+ type: 'tool-result',
2026
+ toolCallId: part.tool_use_id,
2027
+ toolName: advisorToolName,
2028
+ isError: true,
2029
+ result: {
2030
+ type: 'advisor_tool_result_error',
2031
+ errorCode: part.content.error_code,
2032
+ },
2033
+ });
2034
+ }
2035
+ return;
2036
+ }
2037
+
1757
2038
  case 'mcp_tool_use': {
1758
2039
  mcpToolCalls[part.id] = {
1759
2040
  type: 'tool-call',
@@ -1859,6 +2140,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1859
2140
  toolName: contentBlock.toolName,
1860
2141
  input: finalInput,
1861
2142
  providerExecuted: contentBlock.providerExecuted,
2143
+ // Specific 'web_fetch' or 'web_search' tools may need code execution, which the Anthropic API
2144
+ // implicitly allows. In this scenario, we need to allow 'code_execution' tool calls even if the
2145
+ // tool was not explicitly provided. We therefore bypass the general validation by marking the
2146
+ // tool as dynamic.
1862
2147
  ...(markCodeExecutionDynamic &&
1863
2148
  contentBlock.providerToolName === 'code_execution'
1864
2149
  ? { dynamic: true }
@@ -1972,12 +2257,9 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
1972
2257
  // the type to the delta and change the tool name.
1973
2258
  if (
1974
2259
  contentBlock.firstDelta &&
1975
- (contentBlock.providerToolName ===
1976
- 'bash_code_execution' ||
1977
- contentBlock.providerToolName ===
1978
- 'text_editor_code_execution')
2260
+ contentBlock.providerToolName === 'code_execution'
1979
2261
  ) {
1980
- delta = `{"type": "${contentBlock.providerToolName}",${delta.substring(1)}`;
2262
+ delta = `{"type": "programmatic-tool-call",${delta.substring(1)}`;
1981
2263
  }
1982
2264
 
1983
2265
  controller.enqueue({
@@ -2028,9 +2310,6 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2028
2310
  ...(value.message.usage as JSONObject),
2029
2311
  };
2030
2312
 
2031
- cacheCreationInputTokens =
2032
- value.message.usage.cache_creation_input_tokens ?? null;
2033
-
2034
2313
  if (value.message.container != null) {
2035
2314
  container = {
2036
2315
  expiresAt: value.message.container.expires_at,
@@ -2128,8 +2407,6 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2128
2407
  if (value.usage.cache_creation_input_tokens != null) {
2129
2408
  usage.cache_creation_input_tokens =
2130
2409
  value.usage.cache_creation_input_tokens;
2131
- cacheCreationInputTokens =
2132
- value.usage.cache_creation_input_tokens;
2133
2410
  }
2134
2411
  if (value.usage.iterations != null) {
2135
2412
  usage.iterations = value.usage.iterations;
@@ -2144,6 +2421,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2144
2421
  };
2145
2422
 
2146
2423
  stopSequence = value.delta.stop_sequence ?? null;
2424
+ stopDetails = mapAnthropicStopDetails(value.delta.stop_details);
2147
2425
  container =
2148
2426
  value.delta.container != null
2149
2427
  ? {
@@ -2175,14 +2453,30 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2175
2453
  case 'message_stop': {
2176
2454
  const anthropicMetadata = {
2177
2455
  usage: (rawUsage as JSONObject) ?? null,
2178
- cacheCreationInputTokens,
2179
2456
  stopSequence,
2457
+ ...(stopDetails != null ? { stopDetails } : {}),
2180
2458
  iterations: usage.iterations
2181
- ? usage.iterations.map(iter => ({
2182
- type: iter.type,
2183
- inputTokens: iter.input_tokens,
2184
- outputTokens: iter.output_tokens,
2185
- }))
2459
+ ? usage.iterations.map(
2460
+ iter =>
2461
+ ({
2462
+ type: iter.type,
2463
+ ...(iter.model != null ? { model: iter.model } : {}),
2464
+ inputTokens: iter.input_tokens,
2465
+ outputTokens: iter.output_tokens,
2466
+ ...(iter.cache_creation_input_tokens
2467
+ ? {
2468
+ cacheCreationInputTokens:
2469
+ iter.cache_creation_input_tokens,
2470
+ }
2471
+ : {}),
2472
+ ...(iter.cache_read_input_tokens
2473
+ ? {
2474
+ cacheReadInputTokens:
2475
+ iter.cache_read_input_tokens,
2476
+ }
2477
+ : {}),
2478
+ }) satisfies AnthropicUsageIteration,
2479
+ )
2186
2480
  : null,
2187
2481
  container,
2188
2482
  contextManagement,
@@ -2202,7 +2496,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2202
2496
  controller.enqueue({
2203
2497
  type: 'finish',
2204
2498
  finishReason,
2205
- usage: convertAnthropicMessagesUsage({ usage, rawUsage }),
2499
+ usage: convertAnthropicUsage({ usage, rawUsage }),
2206
2500
  providerMetadata,
2207
2501
  });
2208
2502
  return;
@@ -2271,18 +2565,37 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV4 {
2271
2565
  * @see https://docs.claude.com/en/docs/about-claude/models/overview#model-comparison-table
2272
2566
  * @see https://platform.claude.com/docs/en/build-with-claude/structured-outputs
2273
2567
  */
2274
- function getModelCapabilities(modelId: string): {
2568
+ export function getModelCapabilities(modelId: string): {
2275
2569
  maxOutputTokens: number;
2276
2570
  supportsStructuredOutput: boolean;
2571
+ supportsAdaptiveThinking: boolean;
2572
+ rejectsSamplingParameters: boolean;
2573
+ supportsXhighEffort: boolean;
2277
2574
  isKnownModel: boolean;
2278
2575
  } {
2279
2576
  if (
2577
+ modelId.includes('claude-opus-4-8') ||
2578
+ modelId.includes('claude-opus-4-7') ||
2579
+ modelId.includes('claude-fable-5')
2580
+ ) {
2581
+ return {
2582
+ maxOutputTokens: 128000,
2583
+ supportsStructuredOutput: true,
2584
+ supportsAdaptiveThinking: true,
2585
+ rejectsSamplingParameters: true,
2586
+ supportsXhighEffort: true,
2587
+ isKnownModel: true,
2588
+ };
2589
+ } else if (
2280
2590
  modelId.includes('claude-sonnet-4-6') ||
2281
2591
  modelId.includes('claude-opus-4-6')
2282
2592
  ) {
2283
2593
  return {
2284
2594
  maxOutputTokens: 128000,
2285
2595
  supportsStructuredOutput: true,
2596
+ supportsAdaptiveThinking: true,
2597
+ rejectsSamplingParameters: false,
2598
+ supportsXhighEffort: false,
2286
2599
  isKnownModel: true,
2287
2600
  };
2288
2601
  } else if (
@@ -2293,36 +2606,54 @@ function getModelCapabilities(modelId: string): {
2293
2606
  return {
2294
2607
  maxOutputTokens: 64000,
2295
2608
  supportsStructuredOutput: true,
2609
+ supportsAdaptiveThinking: false,
2610
+ rejectsSamplingParameters: false,
2611
+ supportsXhighEffort: false,
2296
2612
  isKnownModel: true,
2297
2613
  };
2298
2614
  } else if (modelId.includes('claude-opus-4-1')) {
2299
2615
  return {
2300
2616
  maxOutputTokens: 32000,
2301
2617
  supportsStructuredOutput: true,
2618
+ supportsAdaptiveThinking: false,
2619
+ rejectsSamplingParameters: false,
2620
+ supportsXhighEffort: false,
2302
2621
  isKnownModel: true,
2303
2622
  };
2304
2623
  } else if (modelId.includes('claude-sonnet-4-')) {
2305
2624
  return {
2306
2625
  maxOutputTokens: 64000,
2307
2626
  supportsStructuredOutput: false,
2627
+ supportsAdaptiveThinking: false,
2628
+ rejectsSamplingParameters: false,
2629
+ supportsXhighEffort: false,
2308
2630
  isKnownModel: true,
2309
2631
  };
2310
2632
  } else if (modelId.includes('claude-opus-4-')) {
2311
2633
  return {
2312
2634
  maxOutputTokens: 32000,
2313
2635
  supportsStructuredOutput: false,
2636
+ supportsAdaptiveThinking: false,
2637
+ rejectsSamplingParameters: false,
2638
+ supportsXhighEffort: false,
2314
2639
  isKnownModel: true,
2315
2640
  };
2316
2641
  } else if (modelId.includes('claude-3-haiku')) {
2317
2642
  return {
2318
2643
  maxOutputTokens: 4096,
2319
2644
  supportsStructuredOutput: false,
2645
+ supportsAdaptiveThinking: false,
2646
+ rejectsSamplingParameters: false,
2647
+ supportsXhighEffort: false,
2320
2648
  isKnownModel: true,
2321
2649
  };
2322
2650
  } else {
2323
2651
  return {
2324
2652
  maxOutputTokens: 4096,
2325
2653
  supportsStructuredOutput: false,
2654
+ supportsAdaptiveThinking: false,
2655
+ rejectsSamplingParameters: false,
2656
+ supportsXhighEffort: false,
2326
2657
  isKnownModel: false,
2327
2658
  };
2328
2659
  }
@@ -2353,6 +2684,54 @@ function hasWebTool20260209WithoutCodeExecution(
2353
2684
  return hasWebTool20260209 && !hasCodeExecutionTool;
2354
2685
  }
2355
2686
 
2687
+ function resolveAnthropicReasoningConfig({
2688
+ reasoning,
2689
+ supportsAdaptiveThinking,
2690
+ supportsXhighEffort,
2691
+ maxOutputTokensForModel,
2692
+ warnings,
2693
+ }: {
2694
+ reasoning: LanguageModelV4CallOptions['reasoning'];
2695
+ supportsAdaptiveThinking: boolean;
2696
+ supportsXhighEffort: boolean;
2697
+ maxOutputTokensForModel: number;
2698
+ warnings: SharedV4Warning[];
2699
+ }): Pick<AnthropicLanguageModelOptions, 'thinking' | 'effort'> | undefined {
2700
+ if (!isCustomReasoning(reasoning)) {
2701
+ return undefined;
2702
+ }
2703
+
2704
+ if (reasoning === 'none') {
2705
+ return { thinking: { type: 'disabled' } };
2706
+ }
2707
+
2708
+ if (supportsAdaptiveThinking) {
2709
+ const effort = mapReasoningToProviderEffort({
2710
+ reasoning,
2711
+ effortMap: {
2712
+ minimal: 'low' as const,
2713
+ low: 'low' as const,
2714
+ medium: 'medium' as const,
2715
+ high: 'high' as const,
2716
+ xhigh: supportsXhighEffort ? ('xhigh' as const) : ('max' as const),
2717
+ },
2718
+ warnings,
2719
+ });
2720
+ return { thinking: { type: 'adaptive' }, effort };
2721
+ }
2722
+
2723
+ const budgetTokens = mapReasoningToProviderBudget({
2724
+ reasoning,
2725
+ maxOutputTokens: maxOutputTokensForModel,
2726
+ maxReasoningBudget: maxOutputTokensForModel,
2727
+ warnings,
2728
+ });
2729
+ if (budgetTokens == null) {
2730
+ return undefined;
2731
+ }
2732
+ return { thinking: { type: 'enabled', budgetTokens } };
2733
+ }
2734
+
2356
2735
  function mapAnthropicResponseContextManagement(
2357
2736
  contextManagement: AnthropicResponseContextManagement | null | undefined,
2358
2737
  ): AnthropicMessageMetadata['contextManagement'] | null {
@@ -2387,3 +2766,22 @@ function mapAnthropicResponseContextManagement(
2387
2766
  }
2388
2767
  : null;
2389
2768
  }
2769
+
2770
+ function mapAnthropicStopDetails(
2771
+ stopDetails: AnthropicStopDetails | null | undefined,
2772
+ ): AnthropicMessageMetadata['stopDetails'] | undefined {
2773
+ if (stopDetails == null) {
2774
+ return undefined;
2775
+ }
2776
+
2777
+ return {
2778
+ type: stopDetails.type,
2779
+ ...(stopDetails.category != null ? { category: stopDetails.category } : {}),
2780
+ ...(stopDetails.explanation != null
2781
+ ? { explanation: stopDetails.explanation }
2782
+ : {}),
2783
+ ...(stopDetails.recommended_model != null
2784
+ ? { recommendedModel: stopDetails.recommended_model }
2785
+ : {}),
2786
+ };
2787
+ }