@fairyhunter13/ai-anthropic 3.0.58-fork.1

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 (53) hide show
  1. package/CHANGELOG.md +2521 -0
  2. package/LICENSE +13 -0
  3. package/README.md +43 -0
  4. package/dist/index.d.mts +1099 -0
  5. package/dist/index.d.ts +1099 -0
  6. package/dist/index.js +5222 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/index.mjs +5298 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/dist/internal/index.d.mts +960 -0
  11. package/dist/internal/index.d.ts +960 -0
  12. package/dist/internal/index.js +5122 -0
  13. package/dist/internal/index.js.map +1 -0
  14. package/dist/internal/index.mjs +5190 -0
  15. package/dist/internal/index.mjs.map +1 -0
  16. package/docs/05-anthropic.mdx +1321 -0
  17. package/internal.d.ts +1 -0
  18. package/package.json +83 -0
  19. package/src/anthropic-error.ts +26 -0
  20. package/src/anthropic-message-metadata.ts +143 -0
  21. package/src/anthropic-messages-api.ts +1348 -0
  22. package/src/anthropic-messages-language-model.ts +2407 -0
  23. package/src/anthropic-messages-options.ts +267 -0
  24. package/src/anthropic-prepare-tools.ts +404 -0
  25. package/src/anthropic-provider.ts +177 -0
  26. package/src/anthropic-tools.ts +238 -0
  27. package/src/convert-anthropic-messages-usage.ts +73 -0
  28. package/src/convert-to-anthropic-messages-prompt.ts +1144 -0
  29. package/src/forward-anthropic-container-id-from-last-step.ts +38 -0
  30. package/src/get-cache-control.ts +63 -0
  31. package/src/index.ts +17 -0
  32. package/src/internal/index.ts +4 -0
  33. package/src/map-anthropic-stop-reason.ts +30 -0
  34. package/src/tool/bash_20241022.ts +33 -0
  35. package/src/tool/bash_20250124.ts +33 -0
  36. package/src/tool/code-execution_20250522.ts +61 -0
  37. package/src/tool/code-execution_20250825.ts +281 -0
  38. package/src/tool/code-execution_20260120.ts +315 -0
  39. package/src/tool/computer_20241022.ts +87 -0
  40. package/src/tool/computer_20250124.ts +130 -0
  41. package/src/tool/computer_20251124.ts +151 -0
  42. package/src/tool/memory_20250818.ts +62 -0
  43. package/src/tool/text-editor_20241022.ts +69 -0
  44. package/src/tool/text-editor_20250124.ts +69 -0
  45. package/src/tool/text-editor_20250429.ts +70 -0
  46. package/src/tool/text-editor_20250728.ts +86 -0
  47. package/src/tool/tool-search-bm25_20251119.ts +99 -0
  48. package/src/tool/tool-search-regex_20251119.ts +111 -0
  49. package/src/tool/web-fetch-20250910.ts +145 -0
  50. package/src/tool/web-fetch-20260209.ts +145 -0
  51. package/src/tool/web-search_20250305.ts +136 -0
  52. package/src/tool/web-search_20260209.ts +136 -0
  53. package/src/version.ts +6 -0
@@ -0,0 +1,2407 @@
1
+ import {
2
+ APICallError,
3
+ JSONObject,
4
+ LanguageModelV3,
5
+ LanguageModelV3CallOptions,
6
+ LanguageModelV3Content,
7
+ LanguageModelV3FinishReason,
8
+ LanguageModelV3FunctionTool,
9
+ LanguageModelV3GenerateResult,
10
+ LanguageModelV3Prompt,
11
+ LanguageModelV3Source,
12
+ LanguageModelV3StreamPart,
13
+ LanguageModelV3StreamResult,
14
+ LanguageModelV3ToolCall,
15
+ SharedV3ProviderMetadata,
16
+ SharedV3Warning,
17
+ } from '@ai-sdk/provider';
18
+ import {
19
+ combineHeaders,
20
+ createEventSourceResponseHandler,
21
+ createJsonResponseHandler,
22
+ createToolNameMapping,
23
+ FetchFunction,
24
+ generateId,
25
+ InferSchema,
26
+ parseProviderOptions,
27
+ ParseResult,
28
+ postJsonToApi,
29
+ Resolvable,
30
+ resolve,
31
+ } from '@ai-sdk/provider-utils';
32
+ import { anthropicFailedResponseHandler } from './anthropic-error';
33
+ import { AnthropicMessageMetadata } from './anthropic-message-metadata';
34
+ import {
35
+ AnthropicContainer,
36
+ anthropicMessagesChunkSchema,
37
+ anthropicMessagesResponseSchema,
38
+ AnthropicReasoningMetadata,
39
+ AnthropicResponseContextManagement,
40
+ AnthropicTool,
41
+ Citation,
42
+ } from './anthropic-messages-api';
43
+ import {
44
+ AnthropicMessagesModelId,
45
+ anthropicLanguageModelOptions,
46
+ } from './anthropic-messages-options';
47
+ import { prepareTools } from './anthropic-prepare-tools';
48
+ import {
49
+ AnthropicMessagesUsage,
50
+ convertAnthropicMessagesUsage,
51
+ } from './convert-anthropic-messages-usage';
52
+ import { convertToAnthropicMessagesPrompt } from './convert-to-anthropic-messages-prompt';
53
+ import { CacheControlValidator } from './get-cache-control';
54
+ import { mapAnthropicStopReason } from './map-anthropic-stop-reason';
55
+
56
+ function createCitationSource(
57
+ citation: Citation,
58
+ citationDocuments: Array<{
59
+ title: string;
60
+ filename?: string;
61
+ mediaType: string;
62
+ }>,
63
+ generateId: () => string,
64
+ ): LanguageModelV3Source | undefined {
65
+ if (citation.type === 'web_search_result_location') {
66
+ return {
67
+ type: 'source' as const,
68
+ sourceType: 'url' as const,
69
+ id: generateId(),
70
+ url: citation.url,
71
+ title: citation.title,
72
+ providerMetadata: {
73
+ anthropic: {
74
+ citedText: citation.cited_text,
75
+ encryptedIndex: citation.encrypted_index,
76
+ },
77
+ } satisfies SharedV3ProviderMetadata,
78
+ };
79
+ }
80
+
81
+ if (citation.type !== 'page_location' && citation.type !== 'char_location') {
82
+ return;
83
+ }
84
+
85
+ const documentInfo = citationDocuments[citation.document_index];
86
+
87
+ if (!documentInfo) {
88
+ return;
89
+ }
90
+
91
+ return {
92
+ type: 'source' as const,
93
+ sourceType: 'document' as const,
94
+ id: generateId(),
95
+ mediaType: documentInfo.mediaType,
96
+ title: citation.document_title ?? documentInfo.title,
97
+ filename: documentInfo.filename,
98
+ providerMetadata: {
99
+ anthropic:
100
+ citation.type === 'page_location'
101
+ ? {
102
+ citedText: citation.cited_text,
103
+ startPageNumber: citation.start_page_number,
104
+ endPageNumber: citation.end_page_number,
105
+ }
106
+ : {
107
+ citedText: citation.cited_text,
108
+ startCharIndex: citation.start_char_index,
109
+ endCharIndex: citation.end_char_index,
110
+ },
111
+ } satisfies SharedV3ProviderMetadata,
112
+ };
113
+ }
114
+
115
+ type AnthropicMessagesConfig = {
116
+ provider: string;
117
+ baseURL: string;
118
+ headers: Resolvable<Record<string, string | undefined>>;
119
+ fetch?: FetchFunction;
120
+ buildRequestUrl?: (baseURL: string, isStreaming: boolean) => string;
121
+ transformRequestBody?: (
122
+ args: Record<string, any>,
123
+ betas: Set<string>,
124
+ ) => Record<string, any>;
125
+ supportedUrls?: () => LanguageModelV3['supportedUrls'];
126
+ generateId?: () => string;
127
+
128
+ /**
129
+ * When false, the model will use JSON tool fallback for structured outputs.
130
+ */
131
+ supportsNativeStructuredOutput?: boolean;
132
+ };
133
+
134
+ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
135
+ readonly specificationVersion = 'v3';
136
+
137
+ readonly modelId: AnthropicMessagesModelId;
138
+
139
+ private readonly config: AnthropicMessagesConfig;
140
+ private readonly generateId: () => string;
141
+
142
+ constructor(
143
+ modelId: AnthropicMessagesModelId,
144
+ config: AnthropicMessagesConfig,
145
+ ) {
146
+ this.modelId = modelId;
147
+ this.config = config;
148
+ this.generateId = config.generateId ?? generateId;
149
+ }
150
+
151
+ supportsUrl(url: URL): boolean {
152
+ return url.protocol === 'https:';
153
+ }
154
+
155
+ get provider(): string {
156
+ return this.config.provider;
157
+ }
158
+
159
+ /**
160
+ * Extracts the dynamic provider name from the config.provider string.
161
+ * e.g., 'my-custom-anthropic.messages' -> 'my-custom-anthropic'
162
+ */
163
+ private get providerOptionsName(): string {
164
+ const provider = this.config.provider;
165
+ const dotIndex = provider.indexOf('.');
166
+ return dotIndex === -1 ? provider : provider.substring(0, dotIndex);
167
+ }
168
+
169
+ get supportedUrls() {
170
+ return this.config.supportedUrls?.() ?? {};
171
+ }
172
+
173
+ private async getArgs({
174
+ userSuppliedBetas,
175
+ prompt,
176
+ maxOutputTokens,
177
+ temperature,
178
+ topP,
179
+ topK,
180
+ frequencyPenalty,
181
+ presencePenalty,
182
+ stopSequences,
183
+ responseFormat,
184
+ seed,
185
+ tools,
186
+ toolChoice,
187
+ providerOptions,
188
+ stream,
189
+ }: LanguageModelV3CallOptions & {
190
+ stream: boolean;
191
+ userSuppliedBetas: Set<string>;
192
+ }) {
193
+ const warnings: SharedV3Warning[] = [];
194
+
195
+ if (frequencyPenalty != null) {
196
+ warnings.push({ type: 'unsupported', feature: 'frequencyPenalty' });
197
+ }
198
+
199
+ if (presencePenalty != null) {
200
+ warnings.push({ type: 'unsupported', feature: 'presencePenalty' });
201
+ }
202
+
203
+ if (seed != null) {
204
+ warnings.push({ type: 'unsupported', feature: 'seed' });
205
+ }
206
+
207
+ if (temperature != null && temperature > 1) {
208
+ warnings.push({
209
+ type: 'unsupported',
210
+ feature: 'temperature',
211
+ details: `${temperature} exceeds anthropic maximum of 1.0. clamped to 1.0`,
212
+ });
213
+ temperature = 1;
214
+ } else if (temperature != null && temperature < 0) {
215
+ warnings.push({
216
+ type: 'unsupported',
217
+ feature: 'temperature',
218
+ details: `${temperature} is below anthropic minimum of 0. clamped to 0`,
219
+ });
220
+ temperature = 0;
221
+ }
222
+
223
+ if (responseFormat?.type === 'json') {
224
+ if (responseFormat.schema == null) {
225
+ warnings.push({
226
+ type: 'unsupported',
227
+ feature: 'responseFormat',
228
+ details:
229
+ 'JSON response format requires a schema. ' +
230
+ 'The response format is ignored.',
231
+ });
232
+ }
233
+ }
234
+
235
+ const providerOptionsName = this.providerOptionsName;
236
+
237
+ // Parse provider options from both canonical 'anthropic' key and custom key
238
+ const canonicalOptions = await parseProviderOptions({
239
+ provider: 'anthropic',
240
+ providerOptions,
241
+ schema: anthropicLanguageModelOptions,
242
+ });
243
+
244
+ const customProviderOptions =
245
+ providerOptionsName !== 'anthropic'
246
+ ? await parseProviderOptions({
247
+ provider: providerOptionsName,
248
+ providerOptions,
249
+ schema: anthropicLanguageModelOptions,
250
+ })
251
+ : null;
252
+
253
+ // Track if custom key was explicitly used
254
+ const usedCustomProviderKey = customProviderOptions != null;
255
+
256
+ // Merge options
257
+ const anthropicOptions = Object.assign(
258
+ {},
259
+ canonicalOptions ?? {},
260
+ customProviderOptions ?? {},
261
+ );
262
+
263
+ const {
264
+ maxOutputTokens: maxOutputTokensForModel,
265
+ supportsStructuredOutput: modelSupportsStructuredOutput,
266
+ isKnownModel,
267
+ } = getModelCapabilities(this.modelId);
268
+
269
+ const supportsStructuredOutput =
270
+ (this.config.supportsNativeStructuredOutput ?? true) &&
271
+ modelSupportsStructuredOutput;
272
+
273
+ const structureOutputMode =
274
+ anthropicOptions?.structuredOutputMode ?? 'auto';
275
+ const useStructuredOutput =
276
+ structureOutputMode === 'outputFormat' ||
277
+ (structureOutputMode === 'auto' && supportsStructuredOutput);
278
+
279
+ const jsonResponseTool: LanguageModelV3FunctionTool | undefined =
280
+ responseFormat?.type === 'json' &&
281
+ responseFormat.schema != null &&
282
+ !useStructuredOutput
283
+ ? {
284
+ type: 'function',
285
+ name: 'json',
286
+ description: 'Respond with a JSON object.',
287
+ inputSchema: responseFormat.schema,
288
+ }
289
+ : undefined;
290
+
291
+ const contextManagement = anthropicOptions?.contextManagement;
292
+
293
+ // Create a shared cache control validator to track breakpoints across tools and messages
294
+ const cacheControlValidator = new CacheControlValidator();
295
+
296
+ const toolNameMapping = createToolNameMapping({
297
+ tools,
298
+ providerToolNames: {
299
+ 'anthropic.code_execution_20250522': 'code_execution',
300
+ 'anthropic.code_execution_20250825': 'code_execution',
301
+ 'anthropic.code_execution_20260120': 'code_execution',
302
+ 'anthropic.computer_20241022': 'computer',
303
+ 'anthropic.computer_20250124': 'computer',
304
+ 'anthropic.text_editor_20241022': 'str_replace_editor',
305
+ 'anthropic.text_editor_20250124': 'str_replace_editor',
306
+ 'anthropic.text_editor_20250429': 'str_replace_based_edit_tool',
307
+ 'anthropic.text_editor_20250728': 'str_replace_based_edit_tool',
308
+ 'anthropic.bash_20241022': 'bash',
309
+ 'anthropic.bash_20250124': 'bash',
310
+ 'anthropic.memory_20250818': 'memory',
311
+ 'anthropic.web_search_20250305': 'web_search',
312
+ 'anthropic.web_search_20260209': 'web_search',
313
+ 'anthropic.web_fetch_20250910': 'web_fetch',
314
+ 'anthropic.web_fetch_20260209': 'web_fetch',
315
+ 'anthropic.tool_search_regex_20251119': 'tool_search_tool_regex',
316
+ 'anthropic.tool_search_bm25_20251119': 'tool_search_tool_bm25',
317
+ },
318
+ });
319
+
320
+ const { prompt: messagesPrompt, betas } =
321
+ await convertToAnthropicMessagesPrompt({
322
+ prompt,
323
+ sendReasoning: anthropicOptions?.sendReasoning ?? true,
324
+ warnings,
325
+ cacheControlValidator,
326
+ toolNameMapping,
327
+ });
328
+
329
+ // Handle assistant prefill: append a partial assistant message
330
+ if (anthropicOptions?.prefill) {
331
+ const lastMessage =
332
+ messagesPrompt.messages[messagesPrompt.messages.length - 1];
333
+ if (lastMessage?.role === 'assistant') {
334
+ // Append to existing assistant message
335
+ (lastMessage.content as any[]).push({
336
+ type: 'text',
337
+ text: anthropicOptions.prefill.trim(),
338
+ cache_control: undefined,
339
+ });
340
+ } else {
341
+ // Add new assistant message
342
+ messagesPrompt.messages.push({
343
+ role: 'assistant',
344
+ content: [
345
+ {
346
+ type: 'text',
347
+ text: anthropicOptions.prefill.trim(),
348
+ cache_control: undefined,
349
+ },
350
+ ],
351
+ });
352
+ }
353
+ }
354
+
355
+ const thinkingType = anthropicOptions?.thinking?.type;
356
+ const isThinking =
357
+ thinkingType === 'enabled' || thinkingType === 'adaptive';
358
+ let thinkingBudget =
359
+ thinkingType === 'enabled'
360
+ ? anthropicOptions?.thinking?.budgetTokens
361
+ : undefined;
362
+
363
+ const maxTokens = maxOutputTokens ?? maxOutputTokensForModel;
364
+
365
+ const baseArgs = {
366
+ // model id:
367
+ model: this.modelId,
368
+
369
+ // standardized settings:
370
+ max_tokens: maxTokens,
371
+ temperature,
372
+ top_k: topK,
373
+ top_p: topP,
374
+ stop_sequences: stopSequences,
375
+
376
+ // provider specific settings:
377
+ ...(isThinking && {
378
+ thinking: {
379
+ type: thinkingType,
380
+ ...(thinkingBudget != null && { budget_tokens: thinkingBudget }),
381
+ ...(anthropicOptions?.thinking?.type !== 'disabled' &&
382
+ anthropicOptions?.thinking?.display && {
383
+ display: anthropicOptions.thinking.display,
384
+ }),
385
+ },
386
+ }),
387
+ ...((anthropicOptions?.effort ||
388
+ (useStructuredOutput &&
389
+ responseFormat?.type === 'json' &&
390
+ responseFormat.schema != null)) && {
391
+ output_config: {
392
+ ...(anthropicOptions?.effort && {
393
+ effort: anthropicOptions.effort,
394
+ }),
395
+ ...(useStructuredOutput &&
396
+ responseFormat?.type === 'json' &&
397
+ responseFormat.schema != null && {
398
+ format: {
399
+ type: 'json_schema',
400
+ schema: responseFormat.schema,
401
+ },
402
+ }),
403
+ },
404
+ }),
405
+ ...(anthropicOptions?.speed && {
406
+ speed: anthropicOptions.speed,
407
+ }),
408
+ ...(anthropicOptions?.cacheControl && {
409
+ cache_control: anthropicOptions.cacheControl,
410
+ }),
411
+
412
+ // mcp servers:
413
+ ...(anthropicOptions?.mcpServers &&
414
+ anthropicOptions.mcpServers.length > 0 && {
415
+ mcp_servers: anthropicOptions.mcpServers.map(server => ({
416
+ type: server.type,
417
+ name: server.name,
418
+ url: server.url,
419
+ authorization_token: server.authorizationToken,
420
+ tool_configuration: server.toolConfiguration
421
+ ? {
422
+ allowed_tools: server.toolConfiguration.allowedTools,
423
+ enabled: server.toolConfiguration.enabled,
424
+ }
425
+ : undefined,
426
+ })),
427
+ }),
428
+
429
+ // container: For programmatic tool calling (just an ID string) or agent skills (object with id and skills)
430
+ ...(anthropicOptions?.container && {
431
+ container:
432
+ anthropicOptions.container.skills &&
433
+ anthropicOptions.container.skills.length > 0
434
+ ? // Object format when skills are provided (agent skills feature)
435
+ ({
436
+ id: anthropicOptions.container.id,
437
+ skills: anthropicOptions.container.skills.map(skill => ({
438
+ type: skill.type,
439
+ skill_id: skill.skillId,
440
+ version: skill.version,
441
+ })),
442
+ } satisfies AnthropicContainer)
443
+ : // String format for container ID only (programmatic tool calling)
444
+ anthropicOptions.container.id,
445
+ }),
446
+
447
+ // prompt:
448
+ system: messagesPrompt.system,
449
+ messages: messagesPrompt.messages,
450
+
451
+ ...(contextManagement && {
452
+ context_management: {
453
+ edits: contextManagement.edits
454
+ .map(edit => {
455
+ const strategy = edit.type;
456
+ switch (strategy) {
457
+ case 'clear_tool_uses_20250919':
458
+ return {
459
+ type: edit.type,
460
+ ...(edit.trigger !== undefined && {
461
+ trigger: edit.trigger,
462
+ }),
463
+ ...(edit.keep !== undefined && { keep: edit.keep }),
464
+ ...(edit.clearAtLeast !== undefined && {
465
+ clear_at_least: edit.clearAtLeast,
466
+ }),
467
+ ...(edit.clearToolInputs !== undefined && {
468
+ clear_tool_inputs: edit.clearToolInputs,
469
+ }),
470
+ ...(edit.excludeTools !== undefined && {
471
+ exclude_tools: edit.excludeTools,
472
+ }),
473
+ };
474
+
475
+ case 'clear_thinking_20251015':
476
+ return {
477
+ type: edit.type,
478
+ ...(edit.keep !== undefined && { keep: edit.keep }),
479
+ };
480
+
481
+ case 'compact_20260112':
482
+ return {
483
+ type: edit.type,
484
+ ...(edit.trigger !== undefined && {
485
+ trigger: edit.trigger,
486
+ }),
487
+ ...(edit.pauseAfterCompaction !== undefined && {
488
+ pause_after_compaction: edit.pauseAfterCompaction,
489
+ }),
490
+ ...(edit.instructions !== undefined && {
491
+ instructions: edit.instructions,
492
+ }),
493
+ };
494
+
495
+ default:
496
+ warnings.push({
497
+ type: 'other',
498
+ message: `Unknown context management strategy: ${strategy}`,
499
+ });
500
+ return undefined;
501
+ }
502
+ })
503
+ .filter(edit => edit !== undefined),
504
+ },
505
+ }),
506
+ };
507
+
508
+ if (isThinking) {
509
+ if (thinkingType === 'enabled' && thinkingBudget == null) {
510
+ warnings.push({
511
+ type: 'compatibility',
512
+ feature: 'extended thinking',
513
+ details:
514
+ 'thinking budget is required when thinking is enabled. using default budget of 1024 tokens.',
515
+ });
516
+
517
+ baseArgs.thinking = {
518
+ type: 'enabled',
519
+ budget_tokens: 1024,
520
+ };
521
+
522
+ thinkingBudget = 1024;
523
+ }
524
+
525
+ if (baseArgs.temperature != null) {
526
+ baseArgs.temperature = undefined;
527
+ warnings.push({
528
+ type: 'unsupported',
529
+ feature: 'temperature',
530
+ details: 'temperature is not supported when thinking is enabled',
531
+ });
532
+ }
533
+
534
+ if (topK != null) {
535
+ baseArgs.top_k = undefined;
536
+ warnings.push({
537
+ type: 'unsupported',
538
+ feature: 'topK',
539
+ details: 'topK is not supported when thinking is enabled',
540
+ });
541
+ }
542
+
543
+ if (topP != null) {
544
+ baseArgs.top_p = undefined;
545
+ warnings.push({
546
+ type: 'unsupported',
547
+ feature: 'topP',
548
+ details: 'topP is not supported when thinking is enabled',
549
+ });
550
+ }
551
+
552
+ // adjust max tokens to account for thinking:
553
+ baseArgs.max_tokens = maxTokens + (thinkingBudget ?? 0);
554
+ } else {
555
+ // Only check temperature/topP mutual exclusivity when thinking is not enabled
556
+ if (topP != null && temperature != null) {
557
+ warnings.push({
558
+ type: 'unsupported',
559
+ feature: 'topP',
560
+ details: `topP is not supported when temperature is set. topP is ignored.`,
561
+ });
562
+ baseArgs.top_p = undefined;
563
+ }
564
+ }
565
+
566
+ // limit to max output tokens for known models to enable model switching without breaking it:
567
+ if (isKnownModel && baseArgs.max_tokens > maxOutputTokensForModel) {
568
+ // only warn if max output tokens is provided as input:
569
+ if (maxOutputTokens != null) {
570
+ warnings.push({
571
+ type: 'unsupported',
572
+ feature: 'maxOutputTokens',
573
+ details:
574
+ `${baseArgs.max_tokens} (maxOutputTokens + thinkingBudget) is greater than ${this.modelId} ${maxOutputTokensForModel} max output tokens. ` +
575
+ `The max output tokens have been limited to ${maxOutputTokensForModel}.`,
576
+ });
577
+ }
578
+ baseArgs.max_tokens = maxOutputTokensForModel;
579
+ }
580
+
581
+ if (
582
+ anthropicOptions?.mcpServers &&
583
+ anthropicOptions.mcpServers.length > 0
584
+ ) {
585
+ betas.add('mcp-client-2025-04-04');
586
+ }
587
+
588
+ if (contextManagement) {
589
+ betas.add('context-management-2025-06-27');
590
+
591
+ // Add compaction beta if compact edit is present
592
+ if (contextManagement.edits.some(e => e.type === 'compact_20260112')) {
593
+ betas.add('compact-2026-01-12');
594
+ }
595
+ }
596
+
597
+ if (
598
+ anthropicOptions?.container &&
599
+ anthropicOptions.container.skills &&
600
+ anthropicOptions.container.skills.length > 0
601
+ ) {
602
+ betas.add('code-execution-2025-08-25');
603
+ betas.add('skills-2025-10-02');
604
+ betas.add('files-api-2025-04-14');
605
+
606
+ if (
607
+ !tools?.some(
608
+ tool =>
609
+ tool.type === 'provider' &&
610
+ (tool.id === 'anthropic.code_execution_20250825' ||
611
+ tool.id === 'anthropic.code_execution_20260120'),
612
+ )
613
+ ) {
614
+ warnings.push({
615
+ type: 'other',
616
+ message: 'code execution tool is required when using skills',
617
+ });
618
+ }
619
+ }
620
+
621
+ if (anthropicOptions?.effort) {
622
+ betas.add('effort-2025-11-24');
623
+ }
624
+
625
+ if (anthropicOptions?.speed === 'fast') {
626
+ betas.add('fast-mode-2026-02-01');
627
+ }
628
+
629
+ // only when streaming: enable fine-grained tool streaming
630
+ if (stream && (anthropicOptions?.toolStreaming ?? true)) {
631
+ betas.add('fine-grained-tool-streaming-2025-05-14');
632
+ }
633
+
634
+ const {
635
+ tools: anthropicTools,
636
+ toolChoice: anthropicToolChoice,
637
+ toolWarnings,
638
+ betas: toolsBetas,
639
+ } = await prepareTools(
640
+ jsonResponseTool != null
641
+ ? {
642
+ tools: [...(tools ?? []), jsonResponseTool],
643
+ toolChoice: { type: 'required' },
644
+ disableParallelToolUse: true,
645
+ cacheControlValidator,
646
+ supportsStructuredOutput: false,
647
+ }
648
+ : {
649
+ tools: tools ?? [],
650
+ toolChoice,
651
+ disableParallelToolUse: anthropicOptions?.disableParallelToolUse,
652
+ cacheControlValidator,
653
+ supportsStructuredOutput,
654
+ },
655
+ );
656
+
657
+ // Extract cache control warnings once at the end
658
+ const cacheWarnings = cacheControlValidator.getWarnings();
659
+
660
+ return {
661
+ args: {
662
+ ...baseArgs,
663
+ tools: anthropicTools,
664
+ tool_choice: anthropicToolChoice,
665
+ stream: stream === true ? true : undefined, // do not send when not streaming
666
+ },
667
+ warnings: [...warnings, ...toolWarnings, ...cacheWarnings],
668
+ betas: new Set([
669
+ ...betas,
670
+ ...toolsBetas,
671
+ ...userSuppliedBetas,
672
+ ...(anthropicOptions?.anthropicBeta ?? []),
673
+ ]),
674
+ usesJsonResponseTool: jsonResponseTool != null,
675
+ toolNameMapping,
676
+ providerOptionsName,
677
+ usedCustomProviderKey,
678
+ };
679
+ }
680
+
681
+ private async getHeaders({
682
+ betas,
683
+ headers,
684
+ }: {
685
+ betas: Set<string>;
686
+ headers: Record<string, string | undefined> | undefined;
687
+ }) {
688
+ return combineHeaders(
689
+ await resolve(this.config.headers),
690
+ headers,
691
+ betas.size > 0 ? { 'anthropic-beta': Array.from(betas).join(',') } : {},
692
+ );
693
+ }
694
+
695
+ private async getBetasFromHeaders(
696
+ requestHeaders: Record<string, string | undefined> | undefined,
697
+ ) {
698
+ const configHeaders = await resolve(this.config.headers);
699
+
700
+ const configBetaHeader = configHeaders['anthropic-beta'] ?? '';
701
+ const requestBetaHeader = requestHeaders?.['anthropic-beta'] ?? '';
702
+
703
+ return new Set(
704
+ [
705
+ ...configBetaHeader.toLowerCase().split(','),
706
+ ...requestBetaHeader.toLowerCase().split(','),
707
+ ]
708
+ .map(beta => beta.trim())
709
+ .filter(beta => beta !== ''),
710
+ );
711
+ }
712
+
713
+ private buildRequestUrl(isStreaming: boolean): string {
714
+ return (
715
+ this.config.buildRequestUrl?.(this.config.baseURL, isStreaming) ??
716
+ `${this.config.baseURL}/messages`
717
+ );
718
+ }
719
+
720
+ private transformRequestBody(
721
+ args: Record<string, any>,
722
+ betas: Set<string>,
723
+ ): Record<string, any> {
724
+ return this.config.transformRequestBody?.(args, betas) ?? args;
725
+ }
726
+
727
+ private extractCitationDocuments(prompt: LanguageModelV3Prompt): Array<{
728
+ title: string;
729
+ filename?: string;
730
+ mediaType: string;
731
+ }> {
732
+ const isCitationPart = (part: {
733
+ type: string;
734
+ mediaType?: string;
735
+ providerOptions?: { anthropic?: { citations?: { enabled?: boolean } } };
736
+ }) => {
737
+ if (part.type !== 'file') {
738
+ return false;
739
+ }
740
+
741
+ if (
742
+ part.mediaType !== 'application/pdf' &&
743
+ part.mediaType !== 'text/plain'
744
+ ) {
745
+ return false;
746
+ }
747
+
748
+ const anthropic = part.providerOptions?.anthropic;
749
+ const citationsConfig = anthropic?.citations as
750
+ | { enabled?: boolean }
751
+ | undefined;
752
+ return citationsConfig?.enabled ?? false;
753
+ };
754
+
755
+ return prompt
756
+ .filter(message => message.role === 'user')
757
+ .flatMap(message => message.content)
758
+ .filter(isCitationPart)
759
+ .map(part => {
760
+ // TypeScript knows this is a file part due to our filter
761
+ const filePart = part as Extract<typeof part, { type: 'file' }>;
762
+ return {
763
+ title: filePart.filename ?? 'Untitled Document',
764
+ filename: filePart.filename,
765
+ mediaType: filePart.mediaType,
766
+ };
767
+ });
768
+ }
769
+
770
+ async doGenerate(
771
+ options: LanguageModelV3CallOptions,
772
+ ): Promise<LanguageModelV3GenerateResult> {
773
+ const {
774
+ args,
775
+ warnings,
776
+ betas,
777
+ usesJsonResponseTool,
778
+ toolNameMapping,
779
+ providerOptionsName,
780
+ usedCustomProviderKey,
781
+ } = await this.getArgs({
782
+ ...options,
783
+ stream: false,
784
+ userSuppliedBetas: await this.getBetasFromHeaders(options.headers),
785
+ });
786
+
787
+ // Extract citation documents for response processing
788
+ const citationDocuments = [
789
+ ...this.extractCitationDocuments(options.prompt),
790
+ ];
791
+
792
+ const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(
793
+ args.tools,
794
+ );
795
+
796
+ const {
797
+ responseHeaders,
798
+ value: response,
799
+ rawValue: rawResponse,
800
+ } = await postJsonToApi({
801
+ url: this.buildRequestUrl(false),
802
+ headers: await this.getHeaders({ betas, headers: options.headers }),
803
+ body: this.transformRequestBody(args, betas),
804
+ failedResponseHandler: anthropicFailedResponseHandler,
805
+ successfulResponseHandler: createJsonResponseHandler(
806
+ anthropicMessagesResponseSchema,
807
+ ),
808
+ abortSignal: options.abortSignal,
809
+ fetch: this.config.fetch,
810
+ });
811
+
812
+ const content: Array<LanguageModelV3Content> = [];
813
+ const mcpToolCalls: Record<string, LanguageModelV3ToolCall> = {};
814
+ const serverToolCalls: Record<string, string> = {}; // tool_use_id -> provider tool name
815
+ let isJsonResponseFromTool = false;
816
+
817
+ // map response content to content array
818
+ for (const part of response.content) {
819
+ switch (part.type) {
820
+ case 'text': {
821
+ if (!usesJsonResponseTool) {
822
+ content.push({ type: 'text', text: part.text });
823
+
824
+ // Process citations if present
825
+ if (part.citations) {
826
+ for (const citation of part.citations) {
827
+ const source = createCitationSource(
828
+ citation,
829
+ citationDocuments,
830
+ this.generateId,
831
+ );
832
+
833
+ if (source) {
834
+ content.push(source);
835
+ }
836
+ }
837
+ }
838
+ }
839
+ break;
840
+ }
841
+ case 'thinking': {
842
+ content.push({
843
+ type: 'reasoning',
844
+ text: part.thinking,
845
+ providerMetadata: {
846
+ anthropic: {
847
+ signature: part.signature,
848
+ } satisfies AnthropicReasoningMetadata,
849
+ },
850
+ });
851
+ break;
852
+ }
853
+ case 'redacted_thinking': {
854
+ content.push({
855
+ type: 'reasoning',
856
+ text: '',
857
+ providerMetadata: {
858
+ anthropic: {
859
+ redactedData: part.data,
860
+ } satisfies AnthropicReasoningMetadata,
861
+ },
862
+ });
863
+ break;
864
+ }
865
+ case 'compaction': {
866
+ content.push({
867
+ type: 'text',
868
+ text: part.content,
869
+ providerMetadata: {
870
+ anthropic: {
871
+ type: 'compaction',
872
+ },
873
+ },
874
+ });
875
+ break;
876
+ }
877
+ case 'tool_use': {
878
+ const isJsonResponseTool =
879
+ usesJsonResponseTool && part.name === 'json';
880
+
881
+ if (isJsonResponseTool) {
882
+ isJsonResponseFromTool = true;
883
+
884
+ // when a json response tool is used, the tool call becomes the text:
885
+ content.push({
886
+ type: 'text',
887
+ text: JSON.stringify(part.input),
888
+ });
889
+ } else {
890
+ const caller = part.caller;
891
+ const callerInfo = caller
892
+ ? {
893
+ type: caller.type,
894
+ toolId: 'tool_id' in caller ? caller.tool_id : undefined,
895
+ }
896
+ : undefined;
897
+
898
+ content.push({
899
+ type: 'tool-call',
900
+ toolCallId: part.id,
901
+ toolName: part.name,
902
+ input: JSON.stringify(part.input),
903
+ ...(callerInfo && {
904
+ providerMetadata: {
905
+ anthropic: {
906
+ caller: callerInfo,
907
+ },
908
+ },
909
+ }),
910
+ });
911
+ }
912
+
913
+ break;
914
+ }
915
+ case 'server_tool_use': {
916
+ // code execution 20250825 needs mapping:
917
+ if (
918
+ part.name === 'text_editor_code_execution' ||
919
+ part.name === 'bash_code_execution'
920
+ ) {
921
+ content.push({
922
+ type: 'tool-call',
923
+ toolCallId: part.id,
924
+ toolName: toolNameMapping.toCustomToolName('code_execution'),
925
+ input: JSON.stringify({ type: part.name, ...part.input }),
926
+ providerExecuted: true,
927
+ });
928
+ } else if (
929
+ part.name === 'web_search' ||
930
+ part.name === 'code_execution' ||
931
+ part.name === 'web_fetch'
932
+ ) {
933
+ // For code_execution, inject 'programmatic-tool-call' type when input has { code } format
934
+ const inputToSerialize =
935
+ part.name === 'code_execution' &&
936
+ part.input != null &&
937
+ typeof part.input === 'object' &&
938
+ 'code' in part.input &&
939
+ !('type' in part.input)
940
+ ? { type: 'programmatic-tool-call', ...part.input }
941
+ : part.input;
942
+
943
+ content.push({
944
+ type: 'tool-call',
945
+ toolCallId: part.id,
946
+ toolName: toolNameMapping.toCustomToolName(part.name),
947
+ input: JSON.stringify(inputToSerialize),
948
+ providerExecuted: true,
949
+ // We want this 'code_execution' tool call to be allowed even if the tool is not explicitly provided.
950
+ // Since the validation generally bypasses dynamic tools, we mark this specific tool as dynamic.
951
+ ...(markCodeExecutionDynamic && part.name === 'code_execution'
952
+ ? { dynamic: true }
953
+ : {}),
954
+ });
955
+ } else if (
956
+ part.name === 'tool_search_tool_regex' ||
957
+ part.name === 'tool_search_tool_bm25'
958
+ ) {
959
+ serverToolCalls[part.id] = part.name;
960
+ content.push({
961
+ type: 'tool-call',
962
+ toolCallId: part.id,
963
+ toolName: toolNameMapping.toCustomToolName(part.name),
964
+ input: JSON.stringify(part.input),
965
+ providerExecuted: true,
966
+ });
967
+ }
968
+
969
+ break;
970
+ }
971
+ case 'mcp_tool_use': {
972
+ mcpToolCalls[part.id] = {
973
+ type: 'tool-call',
974
+ toolCallId: part.id,
975
+ toolName: part.name,
976
+ input: JSON.stringify(part.input),
977
+ providerExecuted: true,
978
+ dynamic: true,
979
+ providerMetadata: {
980
+ anthropic: {
981
+ type: 'mcp-tool-use',
982
+ serverName: part.server_name,
983
+ },
984
+ },
985
+ };
986
+ content.push(mcpToolCalls[part.id]);
987
+ break;
988
+ }
989
+ case 'mcp_tool_result': {
990
+ content.push({
991
+ type: 'tool-result',
992
+ toolCallId: part.tool_use_id,
993
+ toolName: mcpToolCalls[part.tool_use_id].toolName,
994
+ isError: part.is_error,
995
+ result: part.content,
996
+ dynamic: true,
997
+ providerMetadata: mcpToolCalls[part.tool_use_id].providerMetadata,
998
+ });
999
+ break;
1000
+ }
1001
+ case 'web_fetch_tool_result': {
1002
+ if (part.content.type === 'web_fetch_result') {
1003
+ citationDocuments.push({
1004
+ title: part.content.content.title ?? part.content.url,
1005
+ mediaType: part.content.content.source.media_type,
1006
+ });
1007
+ content.push({
1008
+ type: 'tool-result',
1009
+ toolCallId: part.tool_use_id,
1010
+ toolName: toolNameMapping.toCustomToolName('web_fetch'),
1011
+ result: {
1012
+ type: 'web_fetch_result',
1013
+ url: part.content.url,
1014
+ retrievedAt: part.content.retrieved_at,
1015
+ content: {
1016
+ type: part.content.content.type,
1017
+ title: part.content.content.title,
1018
+ citations: part.content.content.citations,
1019
+ source: {
1020
+ type: part.content.content.source.type,
1021
+ mediaType: part.content.content.source.media_type,
1022
+ data: part.content.content.source.data,
1023
+ },
1024
+ },
1025
+ },
1026
+ });
1027
+ } else if (part.content.type === 'web_fetch_tool_result_error') {
1028
+ content.push({
1029
+ type: 'tool-result',
1030
+ toolCallId: part.tool_use_id,
1031
+ toolName: toolNameMapping.toCustomToolName('web_fetch'),
1032
+ isError: true,
1033
+ result: {
1034
+ type: 'web_fetch_tool_result_error',
1035
+ errorCode: part.content.error_code,
1036
+ },
1037
+ });
1038
+ }
1039
+ break;
1040
+ }
1041
+ case 'web_search_tool_result': {
1042
+ if (Array.isArray(part.content)) {
1043
+ content.push({
1044
+ type: 'tool-result',
1045
+ toolCallId: part.tool_use_id,
1046
+ toolName: toolNameMapping.toCustomToolName('web_search'),
1047
+ result: part.content.map(result => ({
1048
+ url: result.url,
1049
+ title: result.title,
1050
+ pageAge: result.page_age ?? null,
1051
+ encryptedContent: result.encrypted_content,
1052
+ type: result.type,
1053
+ })),
1054
+ });
1055
+
1056
+ for (const result of part.content) {
1057
+ content.push({
1058
+ type: 'source',
1059
+ sourceType: 'url',
1060
+ id: this.generateId(),
1061
+ url: result.url,
1062
+ title: result.title,
1063
+ providerMetadata: {
1064
+ anthropic: {
1065
+ pageAge: result.page_age ?? null,
1066
+ },
1067
+ },
1068
+ });
1069
+ }
1070
+ } else {
1071
+ content.push({
1072
+ type: 'tool-result',
1073
+ toolCallId: part.tool_use_id,
1074
+ toolName: toolNameMapping.toCustomToolName('web_search'),
1075
+ isError: true,
1076
+ result: {
1077
+ type: 'web_search_tool_result_error',
1078
+ errorCode: part.content.error_code,
1079
+ },
1080
+ });
1081
+ }
1082
+ break;
1083
+ }
1084
+
1085
+ // code execution 20250522:
1086
+ case 'code_execution_tool_result': {
1087
+ if (part.content.type === 'code_execution_result') {
1088
+ content.push({
1089
+ type: 'tool-result',
1090
+ toolCallId: part.tool_use_id,
1091
+ toolName: toolNameMapping.toCustomToolName('code_execution'),
1092
+ result: {
1093
+ type: part.content.type,
1094
+ stdout: part.content.stdout,
1095
+ stderr: part.content.stderr,
1096
+ return_code: part.content.return_code,
1097
+ content: part.content.content ?? [],
1098
+ },
1099
+ });
1100
+ } else if (part.content.type === 'encrypted_code_execution_result') {
1101
+ content.push({
1102
+ type: 'tool-result',
1103
+ toolCallId: part.tool_use_id,
1104
+ toolName: toolNameMapping.toCustomToolName('code_execution'),
1105
+ result: {
1106
+ type: part.content.type,
1107
+ encrypted_stdout: part.content.encrypted_stdout,
1108
+ stderr: part.content.stderr,
1109
+ return_code: part.content.return_code,
1110
+ content: part.content.content ?? [],
1111
+ },
1112
+ });
1113
+ } else if (part.content.type === 'code_execution_tool_result_error') {
1114
+ content.push({
1115
+ type: 'tool-result',
1116
+ toolCallId: part.tool_use_id,
1117
+ toolName: toolNameMapping.toCustomToolName('code_execution'),
1118
+ isError: true,
1119
+ result: {
1120
+ type: 'code_execution_tool_result_error',
1121
+ errorCode: part.content.error_code,
1122
+ },
1123
+ });
1124
+ }
1125
+ break;
1126
+ }
1127
+
1128
+ // code execution 20250825:
1129
+ case 'bash_code_execution_tool_result':
1130
+ case 'text_editor_code_execution_tool_result': {
1131
+ content.push({
1132
+ type: 'tool-result',
1133
+ toolCallId: part.tool_use_id,
1134
+ toolName: toolNameMapping.toCustomToolName('code_execution'),
1135
+ result: part.content,
1136
+ });
1137
+ break;
1138
+ }
1139
+
1140
+ // tool search tool results:
1141
+ case 'tool_search_tool_result': {
1142
+ let providerToolName = serverToolCalls[part.tool_use_id];
1143
+
1144
+ if (providerToolName == null) {
1145
+ const bm25CustomName = toolNameMapping.toCustomToolName(
1146
+ 'tool_search_tool_bm25',
1147
+ );
1148
+ const regexCustomName = toolNameMapping.toCustomToolName(
1149
+ 'tool_search_tool_regex',
1150
+ );
1151
+
1152
+ if (bm25CustomName !== 'tool_search_tool_bm25') {
1153
+ providerToolName = 'tool_search_tool_bm25';
1154
+ } else if (regexCustomName !== 'tool_search_tool_regex') {
1155
+ providerToolName = 'tool_search_tool_regex';
1156
+ } else {
1157
+ providerToolName = 'tool_search_tool_regex';
1158
+ }
1159
+ }
1160
+
1161
+ if (part.content.type === 'tool_search_tool_search_result') {
1162
+ content.push({
1163
+ type: 'tool-result',
1164
+ toolCallId: part.tool_use_id,
1165
+ toolName: toolNameMapping.toCustomToolName(providerToolName),
1166
+ result: part.content.tool_references.map(ref => ({
1167
+ type: ref.type,
1168
+ toolName: ref.tool_name,
1169
+ })),
1170
+ });
1171
+ } else {
1172
+ content.push({
1173
+ type: 'tool-result',
1174
+ toolCallId: part.tool_use_id,
1175
+ toolName: toolNameMapping.toCustomToolName(providerToolName),
1176
+ isError: true,
1177
+ result: {
1178
+ type: 'tool_search_tool_result_error',
1179
+ errorCode: part.content.error_code,
1180
+ },
1181
+ });
1182
+ }
1183
+ break;
1184
+ }
1185
+ }
1186
+ }
1187
+
1188
+ return {
1189
+ content,
1190
+ finishReason: {
1191
+ unified: mapAnthropicStopReason({
1192
+ finishReason: response.stop_reason,
1193
+ isJsonResponseFromTool,
1194
+ }),
1195
+ raw: response.stop_reason ?? undefined,
1196
+ },
1197
+ usage: convertAnthropicMessagesUsage({ usage: response.usage }),
1198
+ request: { body: args },
1199
+ response: {
1200
+ id: response.id ?? undefined,
1201
+ modelId: response.model ?? undefined,
1202
+ headers: responseHeaders,
1203
+ body: rawResponse,
1204
+ },
1205
+ warnings,
1206
+ providerMetadata: (() => {
1207
+ const anthropicMetadata = {
1208
+ usage: response.usage as JSONObject,
1209
+ cacheCreationInputTokens:
1210
+ response.usage.cache_creation_input_tokens ?? null,
1211
+ stopSequence: response.stop_sequence ?? null,
1212
+
1213
+ iterations: response.usage.iterations
1214
+ ? response.usage.iterations.map(iter => ({
1215
+ type: iter.type,
1216
+ inputTokens: iter.input_tokens,
1217
+ outputTokens: iter.output_tokens,
1218
+ }))
1219
+ : null,
1220
+ container: response.container
1221
+ ? {
1222
+ expiresAt: response.container.expires_at,
1223
+ id: response.container.id,
1224
+ skills:
1225
+ response.container.skills?.map(skill => ({
1226
+ type: skill.type,
1227
+ skillId: skill.skill_id,
1228
+ version: skill.version,
1229
+ })) ?? null,
1230
+ }
1231
+ : null,
1232
+ contextManagement:
1233
+ mapAnthropicResponseContextManagement(
1234
+ response.context_management,
1235
+ ) ?? null,
1236
+ } satisfies AnthropicMessageMetadata;
1237
+
1238
+ const providerMetadata: SharedV3ProviderMetadata = {
1239
+ anthropic: anthropicMetadata,
1240
+ };
1241
+
1242
+ if (usedCustomProviderKey && providerOptionsName !== 'anthropic') {
1243
+ providerMetadata[providerOptionsName] = anthropicMetadata;
1244
+ }
1245
+
1246
+ return providerMetadata;
1247
+ })(),
1248
+ };
1249
+ }
1250
+
1251
+ async doStream(
1252
+ options: LanguageModelV3CallOptions,
1253
+ ): Promise<LanguageModelV3StreamResult> {
1254
+ const {
1255
+ args: body,
1256
+ warnings,
1257
+ betas,
1258
+ usesJsonResponseTool,
1259
+ toolNameMapping,
1260
+ providerOptionsName,
1261
+ usedCustomProviderKey,
1262
+ } = await this.getArgs({
1263
+ ...options,
1264
+ stream: true,
1265
+ userSuppliedBetas: await this.getBetasFromHeaders(options.headers),
1266
+ });
1267
+
1268
+ // Extract citation documents for response processing
1269
+ const citationDocuments = [
1270
+ ...this.extractCitationDocuments(options.prompt),
1271
+ ];
1272
+
1273
+ const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(
1274
+ body.tools,
1275
+ );
1276
+
1277
+ const url = this.buildRequestUrl(true);
1278
+ const { responseHeaders, value: response } = await postJsonToApi({
1279
+ url,
1280
+ headers: await this.getHeaders({ betas, headers: options.headers }),
1281
+ body: this.transformRequestBody(body, betas),
1282
+ failedResponseHandler: anthropicFailedResponseHandler,
1283
+ successfulResponseHandler: createEventSourceResponseHandler(
1284
+ anthropicMessagesChunkSchema,
1285
+ ),
1286
+ abortSignal: options.abortSignal,
1287
+ fetch: this.config.fetch,
1288
+ });
1289
+
1290
+ let finishReason: LanguageModelV3FinishReason = {
1291
+ unified: 'other',
1292
+ raw: undefined,
1293
+ };
1294
+ const usage: AnthropicMessagesUsage = {
1295
+ input_tokens: 0,
1296
+ output_tokens: 0,
1297
+ cache_creation_input_tokens: 0,
1298
+ cache_read_input_tokens: 0,
1299
+ iterations: null,
1300
+ };
1301
+
1302
+ const contentBlocks: Record<
1303
+ number,
1304
+ | {
1305
+ type: 'tool-call';
1306
+ toolCallId: string;
1307
+ toolName: string;
1308
+ input: string;
1309
+ providerExecuted?: boolean;
1310
+ firstDelta: boolean;
1311
+ providerToolName?: string;
1312
+ caller?: {
1313
+ type:
1314
+ | 'code_execution_20250825'
1315
+ | 'code_execution_20260120'
1316
+ | 'direct';
1317
+ toolId?: string;
1318
+ };
1319
+ }
1320
+ | { type: 'text' | 'reasoning' }
1321
+ > = {};
1322
+ const mcpToolCalls: Record<string, LanguageModelV3ToolCall> = {};
1323
+ const serverToolCalls: Record<string, string> = {}; // tool_use_id -> provider tool name
1324
+
1325
+ let contextManagement:
1326
+ | AnthropicMessageMetadata['contextManagement']
1327
+ | null = null;
1328
+ let rawUsage: JSONObject | undefined = undefined;
1329
+ let cacheCreationInputTokens: number | null = null;
1330
+ let stopSequence: string | null = null;
1331
+ let container: AnthropicMessageMetadata['container'] | null = null;
1332
+ let isJsonResponseFromTool = false;
1333
+
1334
+ let blockType:
1335
+ | 'text'
1336
+ | 'thinking'
1337
+ | 'tool_use'
1338
+ | 'redacted_thinking'
1339
+ | 'server_tool_use'
1340
+ | 'web_fetch_tool_result'
1341
+ | 'web_search_tool_result'
1342
+ | 'code_execution_tool_result'
1343
+ | 'text_editor_code_execution_tool_result'
1344
+ | 'bash_code_execution_tool_result'
1345
+ | 'tool_search_tool_result'
1346
+ | 'mcp_tool_use'
1347
+ | 'mcp_tool_result'
1348
+ | 'compaction'
1349
+ | undefined = undefined;
1350
+
1351
+ const generateId = this.generateId;
1352
+
1353
+ const transformedStream = response.pipeThrough(
1354
+ new TransformStream<
1355
+ ParseResult<InferSchema<typeof anthropicMessagesChunkSchema>>,
1356
+ LanguageModelV3StreamPart
1357
+ >({
1358
+ start(controller) {
1359
+ controller.enqueue({ type: 'stream-start', warnings });
1360
+ },
1361
+
1362
+ transform(chunk, controller) {
1363
+ if (options.includeRawChunks) {
1364
+ controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });
1365
+ }
1366
+
1367
+ if (!chunk.success) {
1368
+ controller.enqueue({ type: 'error', error: chunk.error });
1369
+ return;
1370
+ }
1371
+
1372
+ const value = chunk.value;
1373
+
1374
+ switch (value.type) {
1375
+ case 'ping': {
1376
+ return; // ignored
1377
+ }
1378
+
1379
+ case 'content_block_start': {
1380
+ const part = value.content_block;
1381
+ const contentBlockType = part.type;
1382
+ blockType = contentBlockType;
1383
+
1384
+ switch (contentBlockType) {
1385
+ case 'text': {
1386
+ // when a json response tool is used, the tool call is returned as text,
1387
+ // so we ignore the text content:
1388
+ if (usesJsonResponseTool) {
1389
+ return;
1390
+ }
1391
+
1392
+ contentBlocks[value.index] = { type: 'text' };
1393
+ controller.enqueue({
1394
+ type: 'text-start',
1395
+ id: String(value.index),
1396
+ });
1397
+ return;
1398
+ }
1399
+
1400
+ case 'thinking': {
1401
+ contentBlocks[value.index] = { type: 'reasoning' };
1402
+ controller.enqueue({
1403
+ type: 'reasoning-start',
1404
+ id: String(value.index),
1405
+ });
1406
+ return;
1407
+ }
1408
+
1409
+ case 'redacted_thinking': {
1410
+ contentBlocks[value.index] = { type: 'reasoning' };
1411
+ controller.enqueue({
1412
+ type: 'reasoning-start',
1413
+ id: String(value.index),
1414
+ providerMetadata: {
1415
+ anthropic: {
1416
+ redactedData: part.data,
1417
+ } satisfies AnthropicReasoningMetadata,
1418
+ },
1419
+ });
1420
+ return;
1421
+ }
1422
+
1423
+ case 'compaction': {
1424
+ contentBlocks[value.index] = { type: 'text' };
1425
+ controller.enqueue({
1426
+ type: 'text-start',
1427
+ id: String(value.index),
1428
+ providerMetadata: {
1429
+ anthropic: {
1430
+ type: 'compaction',
1431
+ },
1432
+ },
1433
+ });
1434
+ return;
1435
+ }
1436
+
1437
+ case 'tool_use': {
1438
+ const isJsonResponseTool =
1439
+ usesJsonResponseTool && part.name === 'json';
1440
+
1441
+ if (isJsonResponseTool) {
1442
+ isJsonResponseFromTool = true;
1443
+
1444
+ contentBlocks[value.index] = { type: 'text' };
1445
+
1446
+ controller.enqueue({
1447
+ type: 'text-start',
1448
+ id: String(value.index),
1449
+ });
1450
+ } else {
1451
+ // Extract caller info for type-safe access
1452
+ const caller = part.caller;
1453
+ const callerInfo = caller
1454
+ ? {
1455
+ type: caller.type,
1456
+ toolId:
1457
+ 'tool_id' in caller ? caller.tool_id : undefined,
1458
+ }
1459
+ : undefined;
1460
+
1461
+ // Programmatic tool calling: for deferred tool calls from code_execution,
1462
+ // input may be present directly in content_block_start.
1463
+ // Only use if non-empty (empty {} means input comes via deltas)
1464
+ const hasNonEmptyInput =
1465
+ part.input && Object.keys(part.input).length > 0;
1466
+ const initialInput = hasNonEmptyInput
1467
+ ? JSON.stringify(part.input)
1468
+ : '';
1469
+
1470
+ contentBlocks[value.index] = {
1471
+ type: 'tool-call',
1472
+ toolCallId: part.id,
1473
+ toolName: part.name,
1474
+ input: initialInput,
1475
+ firstDelta: initialInput.length === 0,
1476
+ ...(callerInfo && { caller: callerInfo }),
1477
+ };
1478
+
1479
+ controller.enqueue({
1480
+ type: 'tool-input-start',
1481
+ id: part.id,
1482
+ toolName: part.name,
1483
+ });
1484
+ }
1485
+ return;
1486
+ }
1487
+
1488
+ case 'server_tool_use': {
1489
+ if (
1490
+ [
1491
+ 'web_fetch',
1492
+ 'web_search',
1493
+ // code execution 20250825:
1494
+ 'code_execution',
1495
+ // code execution 20250825 text editor:
1496
+ 'text_editor_code_execution',
1497
+ // code execution 20250825 bash:
1498
+ 'bash_code_execution',
1499
+ ].includes(part.name)
1500
+ ) {
1501
+ // map tool names for the code execution 20250825 tool:
1502
+ const providerToolName =
1503
+ part.name === 'text_editor_code_execution' ||
1504
+ part.name === 'bash_code_execution'
1505
+ ? 'code_execution'
1506
+ : part.name;
1507
+
1508
+ const customToolName =
1509
+ toolNameMapping.toCustomToolName(providerToolName);
1510
+
1511
+ // Tools like 'web_fetch_20260209' provide input data here.
1512
+ // Other tools like 'code_execution_20260120' provide input data via deltas.
1513
+ // So we only use this if it's non-empty to avoid conflicts.
1514
+ const finalInput =
1515
+ part.input != null &&
1516
+ typeof part.input === 'object' &&
1517
+ Object.keys(part.input).length > 0
1518
+ ? JSON.stringify(part.input)
1519
+ : '';
1520
+
1521
+ contentBlocks[value.index] = {
1522
+ type: 'tool-call',
1523
+ toolCallId: part.id,
1524
+ toolName: customToolName,
1525
+ input: finalInput,
1526
+ providerExecuted: true,
1527
+ ...(markCodeExecutionDynamic &&
1528
+ providerToolName === 'code_execution'
1529
+ ? { dynamic: true }
1530
+ : {}),
1531
+ firstDelta: true,
1532
+ providerToolName: part.name,
1533
+ };
1534
+
1535
+ controller.enqueue({
1536
+ type: 'tool-input-start',
1537
+ id: part.id,
1538
+ toolName: customToolName,
1539
+ providerExecuted: true,
1540
+ ...(markCodeExecutionDynamic &&
1541
+ providerToolName === 'code_execution'
1542
+ ? { dynamic: true }
1543
+ : {}),
1544
+ });
1545
+ } else if (
1546
+ part.name === 'tool_search_tool_regex' ||
1547
+ part.name === 'tool_search_tool_bm25'
1548
+ ) {
1549
+ serverToolCalls[part.id] = part.name;
1550
+ const customToolName = toolNameMapping.toCustomToolName(
1551
+ part.name,
1552
+ );
1553
+
1554
+ contentBlocks[value.index] = {
1555
+ type: 'tool-call',
1556
+ toolCallId: part.id,
1557
+ toolName: customToolName,
1558
+ input: '',
1559
+ providerExecuted: true,
1560
+ firstDelta: true,
1561
+ providerToolName: part.name,
1562
+ };
1563
+
1564
+ controller.enqueue({
1565
+ type: 'tool-input-start',
1566
+ id: part.id,
1567
+ toolName: customToolName,
1568
+ providerExecuted: true,
1569
+ });
1570
+ }
1571
+
1572
+ return;
1573
+ }
1574
+
1575
+ case 'web_fetch_tool_result': {
1576
+ if (part.content.type === 'web_fetch_result') {
1577
+ citationDocuments.push({
1578
+ title: part.content.content.title ?? part.content.url,
1579
+ mediaType: part.content.content.source.media_type,
1580
+ });
1581
+ controller.enqueue({
1582
+ type: 'tool-result',
1583
+ toolCallId: part.tool_use_id,
1584
+ toolName: toolNameMapping.toCustomToolName('web_fetch'),
1585
+ result: {
1586
+ type: 'web_fetch_result',
1587
+ url: part.content.url,
1588
+ retrievedAt: part.content.retrieved_at,
1589
+ content: {
1590
+ type: part.content.content.type,
1591
+ title: part.content.content.title,
1592
+ citations: part.content.content.citations,
1593
+ source: {
1594
+ type: part.content.content.source.type,
1595
+ mediaType: part.content.content.source.media_type,
1596
+ data: part.content.content.source.data,
1597
+ },
1598
+ },
1599
+ },
1600
+ });
1601
+ } else if (
1602
+ part.content.type === 'web_fetch_tool_result_error'
1603
+ ) {
1604
+ controller.enqueue({
1605
+ type: 'tool-result',
1606
+ toolCallId: part.tool_use_id,
1607
+ toolName: toolNameMapping.toCustomToolName('web_fetch'),
1608
+ isError: true,
1609
+ result: {
1610
+ type: 'web_fetch_tool_result_error',
1611
+ errorCode: part.content.error_code,
1612
+ },
1613
+ });
1614
+ }
1615
+
1616
+ return;
1617
+ }
1618
+
1619
+ case 'web_search_tool_result': {
1620
+ if (Array.isArray(part.content)) {
1621
+ controller.enqueue({
1622
+ type: 'tool-result',
1623
+ toolCallId: part.tool_use_id,
1624
+ toolName: toolNameMapping.toCustomToolName('web_search'),
1625
+ result: part.content.map(result => ({
1626
+ url: result.url,
1627
+ title: result.title,
1628
+ pageAge: result.page_age ?? null,
1629
+ encryptedContent: result.encrypted_content,
1630
+ type: result.type,
1631
+ })),
1632
+ });
1633
+
1634
+ for (const result of part.content) {
1635
+ controller.enqueue({
1636
+ type: 'source',
1637
+ sourceType: 'url',
1638
+ id: generateId(),
1639
+ url: result.url,
1640
+ title: result.title,
1641
+ providerMetadata: {
1642
+ anthropic: {
1643
+ pageAge: result.page_age ?? null,
1644
+ },
1645
+ },
1646
+ });
1647
+ }
1648
+ } else {
1649
+ controller.enqueue({
1650
+ type: 'tool-result',
1651
+ toolCallId: part.tool_use_id,
1652
+ toolName: toolNameMapping.toCustomToolName('web_search'),
1653
+ isError: true,
1654
+ result: {
1655
+ type: 'web_search_tool_result_error',
1656
+ errorCode: part.content.error_code,
1657
+ },
1658
+ });
1659
+ }
1660
+ return;
1661
+ }
1662
+
1663
+ // code execution 20250522:
1664
+ case 'code_execution_tool_result': {
1665
+ if (part.content.type === 'code_execution_result') {
1666
+ controller.enqueue({
1667
+ type: 'tool-result',
1668
+ toolCallId: part.tool_use_id,
1669
+ toolName:
1670
+ toolNameMapping.toCustomToolName('code_execution'),
1671
+ result: {
1672
+ type: part.content.type,
1673
+ stdout: part.content.stdout,
1674
+ stderr: part.content.stderr,
1675
+ return_code: part.content.return_code,
1676
+ content: part.content.content ?? [],
1677
+ },
1678
+ });
1679
+ } else if (
1680
+ part.content.type === 'encrypted_code_execution_result'
1681
+ ) {
1682
+ controller.enqueue({
1683
+ type: 'tool-result',
1684
+ toolCallId: part.tool_use_id,
1685
+ toolName:
1686
+ toolNameMapping.toCustomToolName('code_execution'),
1687
+ result: {
1688
+ type: part.content.type,
1689
+ encrypted_stdout: part.content.encrypted_stdout,
1690
+ stderr: part.content.stderr,
1691
+ return_code: part.content.return_code,
1692
+ content: part.content.content ?? [],
1693
+ },
1694
+ });
1695
+ } else if (
1696
+ part.content.type === 'code_execution_tool_result_error'
1697
+ ) {
1698
+ controller.enqueue({
1699
+ type: 'tool-result',
1700
+ toolCallId: part.tool_use_id,
1701
+ toolName:
1702
+ toolNameMapping.toCustomToolName('code_execution'),
1703
+ isError: true,
1704
+ result: {
1705
+ type: 'code_execution_tool_result_error',
1706
+ errorCode: part.content.error_code,
1707
+ },
1708
+ });
1709
+ }
1710
+
1711
+ return;
1712
+ }
1713
+
1714
+ // code execution 20250825:
1715
+ case 'bash_code_execution_tool_result':
1716
+ case 'text_editor_code_execution_tool_result': {
1717
+ controller.enqueue({
1718
+ type: 'tool-result',
1719
+ toolCallId: part.tool_use_id,
1720
+ toolName:
1721
+ toolNameMapping.toCustomToolName('code_execution'),
1722
+ result: part.content,
1723
+ });
1724
+ return;
1725
+ }
1726
+
1727
+ // tool search tool results:
1728
+ case 'tool_search_tool_result': {
1729
+ let providerToolName = serverToolCalls[part.tool_use_id];
1730
+
1731
+ if (providerToolName == null) {
1732
+ const bm25CustomName = toolNameMapping.toCustomToolName(
1733
+ 'tool_search_tool_bm25',
1734
+ );
1735
+ const regexCustomName = toolNameMapping.toCustomToolName(
1736
+ 'tool_search_tool_regex',
1737
+ );
1738
+
1739
+ if (bm25CustomName !== 'tool_search_tool_bm25') {
1740
+ providerToolName = 'tool_search_tool_bm25';
1741
+ } else if (regexCustomName !== 'tool_search_tool_regex') {
1742
+ providerToolName = 'tool_search_tool_regex';
1743
+ } else {
1744
+ providerToolName = 'tool_search_tool_regex';
1745
+ }
1746
+ }
1747
+
1748
+ if (part.content.type === 'tool_search_tool_search_result') {
1749
+ controller.enqueue({
1750
+ type: 'tool-result',
1751
+ toolCallId: part.tool_use_id,
1752
+ toolName:
1753
+ toolNameMapping.toCustomToolName(providerToolName),
1754
+ result: part.content.tool_references.map(ref => ({
1755
+ type: ref.type,
1756
+ toolName: ref.tool_name,
1757
+ })),
1758
+ });
1759
+ } else {
1760
+ controller.enqueue({
1761
+ type: 'tool-result',
1762
+ toolCallId: part.tool_use_id,
1763
+ toolName:
1764
+ toolNameMapping.toCustomToolName(providerToolName),
1765
+ isError: true,
1766
+ result: {
1767
+ type: 'tool_search_tool_result_error',
1768
+ errorCode: part.content.error_code,
1769
+ },
1770
+ });
1771
+ }
1772
+ return;
1773
+ }
1774
+
1775
+ case 'mcp_tool_use': {
1776
+ mcpToolCalls[part.id] = {
1777
+ type: 'tool-call',
1778
+ toolCallId: part.id,
1779
+ toolName: part.name,
1780
+ input: JSON.stringify(part.input),
1781
+ providerExecuted: true,
1782
+ dynamic: true,
1783
+ providerMetadata: {
1784
+ anthropic: {
1785
+ type: 'mcp-tool-use',
1786
+ serverName: part.server_name,
1787
+ },
1788
+ },
1789
+ };
1790
+ controller.enqueue(mcpToolCalls[part.id]);
1791
+ return;
1792
+ }
1793
+
1794
+ case 'mcp_tool_result': {
1795
+ controller.enqueue({
1796
+ type: 'tool-result',
1797
+ toolCallId: part.tool_use_id,
1798
+ toolName: mcpToolCalls[part.tool_use_id].toolName,
1799
+ isError: part.is_error,
1800
+ result: part.content,
1801
+ dynamic: true,
1802
+ providerMetadata:
1803
+ mcpToolCalls[part.tool_use_id].providerMetadata,
1804
+ });
1805
+ return;
1806
+ }
1807
+
1808
+ default: {
1809
+ const _exhaustiveCheck: never = contentBlockType;
1810
+ throw new Error(
1811
+ `Unsupported content block type: ${_exhaustiveCheck}`,
1812
+ );
1813
+ }
1814
+ }
1815
+ }
1816
+
1817
+ case 'content_block_stop': {
1818
+ // when finishing a tool call block, send the full tool call:
1819
+ if (contentBlocks[value.index] != null) {
1820
+ const contentBlock = contentBlocks[value.index];
1821
+
1822
+ switch (contentBlock.type) {
1823
+ case 'text': {
1824
+ controller.enqueue({
1825
+ type: 'text-end',
1826
+ id: String(value.index),
1827
+ });
1828
+ break;
1829
+ }
1830
+
1831
+ case 'reasoning': {
1832
+ controller.enqueue({
1833
+ type: 'reasoning-end',
1834
+ id: String(value.index),
1835
+ });
1836
+ break;
1837
+ }
1838
+
1839
+ case 'tool-call':
1840
+ // when a json response tool is used, the tool call is returned as text,
1841
+ // so we ignore the tool call content:
1842
+ const isJsonResponseTool =
1843
+ usesJsonResponseTool && contentBlock.toolName === 'json';
1844
+
1845
+ if (!isJsonResponseTool) {
1846
+ controller.enqueue({
1847
+ type: 'tool-input-end',
1848
+ id: contentBlock.toolCallId,
1849
+ });
1850
+
1851
+ // For code_execution, inject 'programmatic-tool-call' type
1852
+ // when input has { code } format (programmatic tool calling)
1853
+ let finalInput =
1854
+ contentBlock.input === '' ? '{}' : contentBlock.input;
1855
+ if (contentBlock.providerToolName === 'code_execution') {
1856
+ try {
1857
+ const parsed = JSON.parse(finalInput);
1858
+ if (
1859
+ parsed != null &&
1860
+ typeof parsed === 'object' &&
1861
+ 'code' in parsed &&
1862
+ !('type' in parsed)
1863
+ ) {
1864
+ finalInput = JSON.stringify({
1865
+ type: 'programmatic-tool-call',
1866
+ ...parsed,
1867
+ });
1868
+ }
1869
+ } catch {
1870
+ // ignore parse errors, use original input
1871
+ }
1872
+ }
1873
+
1874
+ controller.enqueue({
1875
+ type: 'tool-call',
1876
+ toolCallId: contentBlock.toolCallId,
1877
+ toolName: contentBlock.toolName,
1878
+ input: finalInput,
1879
+ providerExecuted: contentBlock.providerExecuted,
1880
+ ...(markCodeExecutionDynamic &&
1881
+ contentBlock.providerToolName === 'code_execution'
1882
+ ? { dynamic: true }
1883
+ : {}),
1884
+ ...(contentBlock.caller && {
1885
+ providerMetadata: {
1886
+ anthropic: {
1887
+ caller: contentBlock.caller,
1888
+ },
1889
+ },
1890
+ }),
1891
+ });
1892
+ }
1893
+ break;
1894
+ }
1895
+
1896
+ delete contentBlocks[value.index];
1897
+ }
1898
+
1899
+ blockType = undefined; // reset block type
1900
+
1901
+ return;
1902
+ }
1903
+
1904
+ case 'content_block_delta': {
1905
+ const deltaType = value.delta.type;
1906
+
1907
+ switch (deltaType) {
1908
+ case 'text_delta': {
1909
+ // when a json response tool is used, the tool call is returned as text,
1910
+ // so we ignore the text content:
1911
+ if (usesJsonResponseTool) {
1912
+ return; // excluding the text-start will also exclude the text-end
1913
+ }
1914
+
1915
+ controller.enqueue({
1916
+ type: 'text-delta',
1917
+ id: String(value.index),
1918
+ delta: value.delta.text,
1919
+ });
1920
+
1921
+ return;
1922
+ }
1923
+
1924
+ case 'thinking_delta': {
1925
+ controller.enqueue({
1926
+ type: 'reasoning-delta',
1927
+ id: String(value.index),
1928
+ delta: value.delta.thinking,
1929
+ });
1930
+
1931
+ return;
1932
+ }
1933
+
1934
+ case 'signature_delta': {
1935
+ // signature are only supported on thinking blocks:
1936
+ if (blockType === 'thinking') {
1937
+ controller.enqueue({
1938
+ type: 'reasoning-delta',
1939
+ id: String(value.index),
1940
+ delta: '',
1941
+ providerMetadata: {
1942
+ anthropic: {
1943
+ signature: value.delta.signature,
1944
+ } satisfies AnthropicReasoningMetadata,
1945
+ },
1946
+ });
1947
+ }
1948
+
1949
+ return;
1950
+ }
1951
+
1952
+ case 'compaction_delta': {
1953
+ if (value.delta.content != null) {
1954
+ controller.enqueue({
1955
+ type: 'text-delta',
1956
+ id: String(value.index),
1957
+ delta: value.delta.content,
1958
+ });
1959
+ }
1960
+
1961
+ return;
1962
+ }
1963
+
1964
+ case 'input_json_delta': {
1965
+ const contentBlock = contentBlocks[value.index];
1966
+ let delta = value.delta.partial_json;
1967
+
1968
+ // skip empty deltas to enable replacing the first character
1969
+ // in the code execution 20250825 tool.
1970
+ if (delta.length === 0) {
1971
+ return;
1972
+ }
1973
+
1974
+ if (isJsonResponseFromTool) {
1975
+ if (contentBlock?.type !== 'text') {
1976
+ return; // exclude reasoning
1977
+ }
1978
+
1979
+ controller.enqueue({
1980
+ type: 'text-delta',
1981
+ id: String(value.index),
1982
+ delta,
1983
+ });
1984
+ } else {
1985
+ if (contentBlock?.type !== 'tool-call') {
1986
+ return;
1987
+ }
1988
+
1989
+ // for the code execution 20250825, we need to add
1990
+ // the type to the delta and change the tool name.
1991
+ if (
1992
+ contentBlock.firstDelta &&
1993
+ (contentBlock.providerToolName ===
1994
+ 'bash_code_execution' ||
1995
+ contentBlock.providerToolName ===
1996
+ 'text_editor_code_execution')
1997
+ ) {
1998
+ delta = `{"type": "${contentBlock.providerToolName}",${delta.substring(1)}`;
1999
+ }
2000
+
2001
+ controller.enqueue({
2002
+ type: 'tool-input-delta',
2003
+ id: contentBlock.toolCallId,
2004
+ delta,
2005
+ });
2006
+
2007
+ contentBlock.input += delta;
2008
+ contentBlock.firstDelta = false;
2009
+ }
2010
+
2011
+ return;
2012
+ }
2013
+
2014
+ case 'citations_delta': {
2015
+ const citation = value.delta.citation;
2016
+ const source = createCitationSource(
2017
+ citation,
2018
+ citationDocuments,
2019
+ generateId,
2020
+ );
2021
+
2022
+ if (source) {
2023
+ controller.enqueue(source);
2024
+ }
2025
+
2026
+ return;
2027
+ }
2028
+
2029
+ default: {
2030
+ const _exhaustiveCheck: never = deltaType;
2031
+ throw new Error(
2032
+ `Unsupported delta type: ${_exhaustiveCheck}`,
2033
+ );
2034
+ }
2035
+ }
2036
+ }
2037
+
2038
+ case 'message_start': {
2039
+ usage.input_tokens = value.message.usage.input_tokens;
2040
+ usage.cache_read_input_tokens =
2041
+ value.message.usage.cache_read_input_tokens ?? 0;
2042
+ usage.cache_creation_input_tokens =
2043
+ value.message.usage.cache_creation_input_tokens ?? 0;
2044
+
2045
+ rawUsage = {
2046
+ ...(value.message.usage as JSONObject),
2047
+ };
2048
+
2049
+ cacheCreationInputTokens =
2050
+ value.message.usage.cache_creation_input_tokens ?? null;
2051
+
2052
+ if (value.message.container != null) {
2053
+ container = {
2054
+ expiresAt: value.message.container.expires_at,
2055
+ id: value.message.container.id,
2056
+ skills: null,
2057
+ };
2058
+ }
2059
+
2060
+ if (value.message.stop_reason != null) {
2061
+ finishReason = {
2062
+ unified: mapAnthropicStopReason({
2063
+ finishReason: value.message.stop_reason,
2064
+ isJsonResponseFromTool,
2065
+ }),
2066
+ raw: value.message.stop_reason,
2067
+ };
2068
+ }
2069
+
2070
+ controller.enqueue({
2071
+ type: 'response-metadata',
2072
+ id: value.message.id ?? undefined,
2073
+ modelId: value.message.model ?? undefined,
2074
+ });
2075
+
2076
+ // Programmatic tool calling: process pre-populated content blocks
2077
+ // (for deferred tool calls, content may be in message_start)
2078
+ if (value.message.content != null) {
2079
+ for (
2080
+ let contentIndex = 0;
2081
+ contentIndex < value.message.content.length;
2082
+ contentIndex++
2083
+ ) {
2084
+ const part = value.message.content[contentIndex];
2085
+ if (part.type === 'tool_use') {
2086
+ const caller = part.caller;
2087
+ const callerInfo = caller
2088
+ ? {
2089
+ type: caller.type,
2090
+ toolId:
2091
+ 'tool_id' in caller ? caller.tool_id : undefined,
2092
+ }
2093
+ : undefined;
2094
+
2095
+ controller.enqueue({
2096
+ type: 'tool-input-start',
2097
+ id: part.id,
2098
+ toolName: part.name,
2099
+ });
2100
+
2101
+ const inputStr = JSON.stringify(part.input ?? {});
2102
+ controller.enqueue({
2103
+ type: 'tool-input-delta',
2104
+ id: part.id,
2105
+ delta: inputStr,
2106
+ });
2107
+
2108
+ controller.enqueue({
2109
+ type: 'tool-input-end',
2110
+ id: part.id,
2111
+ });
2112
+
2113
+ controller.enqueue({
2114
+ type: 'tool-call',
2115
+ toolCallId: part.id,
2116
+ toolName: part.name,
2117
+ input: inputStr,
2118
+ ...(callerInfo && {
2119
+ providerMetadata: {
2120
+ anthropic: {
2121
+ caller: callerInfo,
2122
+ },
2123
+ },
2124
+ }),
2125
+ });
2126
+ }
2127
+ }
2128
+ }
2129
+
2130
+ return;
2131
+ }
2132
+
2133
+ case 'message_delta': {
2134
+ if (
2135
+ value.usage.input_tokens != null &&
2136
+ usage.input_tokens !== value.usage.input_tokens
2137
+ ) {
2138
+ usage.input_tokens = value.usage.input_tokens;
2139
+ }
2140
+ usage.output_tokens = value.usage.output_tokens;
2141
+
2142
+ if (value.usage.cache_read_input_tokens != null) {
2143
+ usage.cache_read_input_tokens =
2144
+ value.usage.cache_read_input_tokens;
2145
+ }
2146
+ if (value.usage.cache_creation_input_tokens != null) {
2147
+ usage.cache_creation_input_tokens =
2148
+ value.usage.cache_creation_input_tokens;
2149
+ cacheCreationInputTokens =
2150
+ value.usage.cache_creation_input_tokens;
2151
+ }
2152
+ if (value.usage.iterations != null) {
2153
+ usage.iterations = value.usage.iterations;
2154
+ }
2155
+
2156
+ finishReason = {
2157
+ unified: mapAnthropicStopReason({
2158
+ finishReason: value.delta.stop_reason,
2159
+ isJsonResponseFromTool,
2160
+ }),
2161
+ raw: value.delta.stop_reason ?? undefined,
2162
+ };
2163
+
2164
+ stopSequence = value.delta.stop_sequence ?? null;
2165
+ container =
2166
+ value.delta.container != null
2167
+ ? {
2168
+ expiresAt: value.delta.container.expires_at,
2169
+ id: value.delta.container.id,
2170
+ skills:
2171
+ value.delta.container.skills?.map(skill => ({
2172
+ type: skill.type,
2173
+ skillId: skill.skill_id,
2174
+ version: skill.version,
2175
+ })) ?? null,
2176
+ }
2177
+ : null;
2178
+
2179
+ if (value.context_management) {
2180
+ contextManagement = mapAnthropicResponseContextManagement(
2181
+ value.context_management,
2182
+ );
2183
+ }
2184
+
2185
+ rawUsage = {
2186
+ ...rawUsage,
2187
+ ...(value.usage as JSONObject),
2188
+ };
2189
+
2190
+ return;
2191
+ }
2192
+
2193
+ case 'message_stop': {
2194
+ const anthropicMetadata = {
2195
+ usage: (rawUsage as JSONObject) ?? null,
2196
+ cacheCreationInputTokens,
2197
+ stopSequence,
2198
+ iterations: usage.iterations
2199
+ ? usage.iterations.map(iter => ({
2200
+ type: iter.type,
2201
+ inputTokens: iter.input_tokens,
2202
+ outputTokens: iter.output_tokens,
2203
+ }))
2204
+ : null,
2205
+ container,
2206
+ contextManagement,
2207
+ } satisfies AnthropicMessageMetadata;
2208
+
2209
+ const providerMetadata: SharedV3ProviderMetadata = {
2210
+ anthropic: anthropicMetadata,
2211
+ };
2212
+
2213
+ if (
2214
+ usedCustomProviderKey &&
2215
+ providerOptionsName !== 'anthropic'
2216
+ ) {
2217
+ providerMetadata[providerOptionsName] = anthropicMetadata;
2218
+ }
2219
+
2220
+ controller.enqueue({
2221
+ type: 'finish',
2222
+ finishReason,
2223
+ usage: convertAnthropicMessagesUsage({ usage, rawUsage }),
2224
+ providerMetadata,
2225
+ });
2226
+ return;
2227
+ }
2228
+
2229
+ case 'error': {
2230
+ controller.enqueue({ type: 'error', error: value.error });
2231
+ return;
2232
+ }
2233
+
2234
+ default: {
2235
+ const _exhaustiveCheck: never = value;
2236
+ throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`);
2237
+ }
2238
+ }
2239
+ },
2240
+ }),
2241
+ );
2242
+
2243
+ // The first chunk needs to be pulled immediately to check if it is an error
2244
+ const [streamForFirstChunk, streamForConsumer] = transformedStream.tee();
2245
+
2246
+ const firstChunkReader = streamForFirstChunk.getReader();
2247
+ try {
2248
+ await firstChunkReader.read(); // streamStart comes first, ignored
2249
+
2250
+ let result = await firstChunkReader.read();
2251
+
2252
+ // when raw chunks are enabled, the first chunk is a raw chunk, so we need to read the next chunk
2253
+ if (result.value?.type === 'raw') {
2254
+ result = await firstChunkReader.read();
2255
+ }
2256
+
2257
+ // The Anthropic API returns 200 responses when there are overloaded errors.
2258
+ // We handle the case where the first chunk is an error here and transform
2259
+ // it into an APICallError.
2260
+ if (result.value?.type === 'error') {
2261
+ const error = result.value.error as { message: string; type: string };
2262
+
2263
+ throw new APICallError({
2264
+ message: error.message,
2265
+ url,
2266
+ requestBodyValues: body,
2267
+ statusCode: error.type === 'overloaded_error' ? 529 : 500,
2268
+ responseHeaders,
2269
+ responseBody: JSON.stringify(error),
2270
+ isRetryable: error.type === 'overloaded_error',
2271
+ });
2272
+ }
2273
+ } finally {
2274
+ firstChunkReader.cancel().catch(() => {});
2275
+ firstChunkReader.releaseLock();
2276
+ }
2277
+
2278
+ return {
2279
+ stream: streamForConsumer,
2280
+ request: { body },
2281
+ response: { headers: responseHeaders },
2282
+ };
2283
+ }
2284
+ }
2285
+
2286
+ /**
2287
+ * Returns the capabilities of a Claude model that are used for defaults and feature selection.
2288
+ *
2289
+ * @see https://docs.claude.com/en/docs/about-claude/models/overview#model-comparison-table
2290
+ * @see https://platform.claude.com/docs/en/build-with-claude/structured-outputs
2291
+ */
2292
+ function getModelCapabilities(modelId: string): {
2293
+ maxOutputTokens: number;
2294
+ supportsStructuredOutput: boolean;
2295
+ isKnownModel: boolean;
2296
+ } {
2297
+ if (
2298
+ modelId.includes('claude-sonnet-4-6') ||
2299
+ modelId.includes('claude-opus-4-6')
2300
+ ) {
2301
+ return {
2302
+ maxOutputTokens: 128000,
2303
+ supportsStructuredOutput: true,
2304
+ isKnownModel: true,
2305
+ };
2306
+ } else if (
2307
+ modelId.includes('claude-sonnet-4-5') ||
2308
+ modelId.includes('claude-opus-4-5') ||
2309
+ modelId.includes('claude-haiku-4-5')
2310
+ ) {
2311
+ return {
2312
+ maxOutputTokens: 64000,
2313
+ supportsStructuredOutput: true,
2314
+ isKnownModel: true,
2315
+ };
2316
+ } else if (modelId.includes('claude-opus-4-1')) {
2317
+ return {
2318
+ maxOutputTokens: 32000,
2319
+ supportsStructuredOutput: true,
2320
+ isKnownModel: true,
2321
+ };
2322
+ } else if (modelId.includes('claude-sonnet-4-')) {
2323
+ return {
2324
+ maxOutputTokens: 64000,
2325
+ supportsStructuredOutput: false,
2326
+ isKnownModel: true,
2327
+ };
2328
+ } else if (modelId.includes('claude-opus-4-')) {
2329
+ return {
2330
+ maxOutputTokens: 32000,
2331
+ supportsStructuredOutput: false,
2332
+ isKnownModel: true,
2333
+ };
2334
+ } else if (modelId.includes('claude-3-haiku')) {
2335
+ return {
2336
+ maxOutputTokens: 4096,
2337
+ supportsStructuredOutput: false,
2338
+ isKnownModel: true,
2339
+ };
2340
+ } else {
2341
+ return {
2342
+ maxOutputTokens: 4096,
2343
+ supportsStructuredOutput: false,
2344
+ isKnownModel: false,
2345
+ };
2346
+ }
2347
+ }
2348
+
2349
+ function hasWebTool20260209WithoutCodeExecution(
2350
+ tools: AnthropicTool[] | undefined,
2351
+ ): boolean {
2352
+ if (!tools) {
2353
+ return false;
2354
+ }
2355
+ let hasWebTool20260209 = false;
2356
+ let hasCodeExecutionTool = false;
2357
+ for (const tool of tools) {
2358
+ if (
2359
+ 'type' in tool &&
2360
+ (tool.type === 'web_fetch_20260209' ||
2361
+ tool.type === 'web_search_20260209')
2362
+ ) {
2363
+ hasWebTool20260209 = true;
2364
+ continue;
2365
+ }
2366
+ if (tool.name === 'code_execution') {
2367
+ hasCodeExecutionTool = true;
2368
+ break;
2369
+ }
2370
+ }
2371
+ return hasWebTool20260209 && !hasCodeExecutionTool;
2372
+ }
2373
+
2374
+ function mapAnthropicResponseContextManagement(
2375
+ contextManagement: AnthropicResponseContextManagement | null | undefined,
2376
+ ): AnthropicMessageMetadata['contextManagement'] | null {
2377
+ return contextManagement
2378
+ ? {
2379
+ appliedEdits: contextManagement.applied_edits
2380
+ .map(edit => {
2381
+ const strategy = edit.type;
2382
+
2383
+ switch (strategy) {
2384
+ case 'clear_tool_uses_20250919':
2385
+ return {
2386
+ type: edit.type,
2387
+ clearedToolUses: edit.cleared_tool_uses,
2388
+ clearedInputTokens: edit.cleared_input_tokens,
2389
+ };
2390
+
2391
+ case 'clear_thinking_20251015':
2392
+ return {
2393
+ type: edit.type,
2394
+ clearedThinkingTurns: edit.cleared_thinking_turns,
2395
+ clearedInputTokens: edit.cleared_input_tokens,
2396
+ };
2397
+
2398
+ case 'compact_20260112':
2399
+ return {
2400
+ type: edit.type,
2401
+ };
2402
+ }
2403
+ })
2404
+ .filter(edit => edit !== undefined),
2405
+ }
2406
+ : null;
2407
+ }