@depup/ai-sdk__anthropic 3.0.58-depup.0

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 (54) hide show
  1. package/CHANGELOG.md +2521 -0
  2. package/LICENSE +13 -0
  3. package/README.md +25 -0
  4. package/changes.json +5 -0
  5. package/dist/index.d.mts +1090 -0
  6. package/dist/index.d.ts +1090 -0
  7. package/dist/index.js +5157 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +5233 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/internal/index.d.mts +960 -0
  12. package/dist/internal/index.d.ts +960 -0
  13. package/dist/internal/index.js +5057 -0
  14. package/dist/internal/index.js.map +1 -0
  15. package/dist/internal/index.mjs +5125 -0
  16. package/dist/internal/index.mjs.map +1 -0
  17. package/docs/05-anthropic.mdx +1321 -0
  18. package/internal.d.ts +1 -0
  19. package/package.json +96 -0
  20. package/src/anthropic-error.ts +26 -0
  21. package/src/anthropic-message-metadata.ts +143 -0
  22. package/src/anthropic-messages-api.ts +1344 -0
  23. package/src/anthropic-messages-language-model.ts +2377 -0
  24. package/src/anthropic-messages-options.ts +246 -0
  25. package/src/anthropic-prepare-tools.ts +404 -0
  26. package/src/anthropic-provider.ts +177 -0
  27. package/src/anthropic-tools.ts +238 -0
  28. package/src/convert-anthropic-messages-usage.ts +73 -0
  29. package/src/convert-to-anthropic-messages-prompt.ts +1119 -0
  30. package/src/forward-anthropic-container-id-from-last-step.ts +38 -0
  31. package/src/get-cache-control.ts +63 -0
  32. package/src/index.ts +17 -0
  33. package/src/internal/index.ts +4 -0
  34. package/src/map-anthropic-stop-reason.ts +30 -0
  35. package/src/tool/bash_20241022.ts +33 -0
  36. package/src/tool/bash_20250124.ts +33 -0
  37. package/src/tool/code-execution_20250522.ts +61 -0
  38. package/src/tool/code-execution_20250825.ts +281 -0
  39. package/src/tool/code-execution_20260120.ts +315 -0
  40. package/src/tool/computer_20241022.ts +87 -0
  41. package/src/tool/computer_20250124.ts +130 -0
  42. package/src/tool/computer_20251124.ts +151 -0
  43. package/src/tool/memory_20250818.ts +62 -0
  44. package/src/tool/text-editor_20241022.ts +69 -0
  45. package/src/tool/text-editor_20250124.ts +69 -0
  46. package/src/tool/text-editor_20250429.ts +70 -0
  47. package/src/tool/text-editor_20250728.ts +86 -0
  48. package/src/tool/tool-search-bm25_20251119.ts +99 -0
  49. package/src/tool/tool-search-regex_20251119.ts +111 -0
  50. package/src/tool/web-fetch-20250910.ts +145 -0
  51. package/src/tool/web-fetch-20260209.ts +145 -0
  52. package/src/tool/web-search_20250305.ts +136 -0
  53. package/src/tool/web-search_20260209.ts +136 -0
  54. package/src/version.ts +6 -0
@@ -0,0 +1,1119 @@
1
+ import {
2
+ SharedV3Warning,
3
+ LanguageModelV3DataContent,
4
+ LanguageModelV3Message,
5
+ LanguageModelV3Prompt,
6
+ SharedV3ProviderMetadata,
7
+ UnsupportedFunctionalityError,
8
+ } from '@ai-sdk/provider';
9
+ import {
10
+ convertBase64ToUint8Array,
11
+ convertToBase64,
12
+ parseProviderOptions,
13
+ validateTypes,
14
+ isNonNullable,
15
+ ToolNameMapping,
16
+ } from '@ai-sdk/provider-utils';
17
+ import {
18
+ AnthropicAssistantMessage,
19
+ AnthropicMessagesPrompt,
20
+ anthropicReasoningMetadataSchema,
21
+ AnthropicToolResultContent,
22
+ AnthropicUserMessage,
23
+ AnthropicWebFetchToolResultContent,
24
+ } from './anthropic-messages-api';
25
+ import { anthropicFilePartProviderOptions } from './anthropic-messages-options';
26
+ import { CacheControlValidator } from './get-cache-control';
27
+ import { codeExecution_20250522OutputSchema } from './tool/code-execution_20250522';
28
+ import { codeExecution_20250825OutputSchema } from './tool/code-execution_20250825';
29
+ import { codeExecution_20260120OutputSchema } from './tool/code-execution_20260120';
30
+ import { toolSearchRegex_20251119OutputSchema as toolSearchOutputSchema } from './tool/tool-search-regex_20251119';
31
+ import { webFetch_20250910OutputSchema } from './tool/web-fetch-20250910';
32
+ import { webSearch_20250305OutputSchema } from './tool/web-search_20250305';
33
+
34
+ function convertToString(data: LanguageModelV3DataContent): string {
35
+ if (typeof data === 'string') {
36
+ return new TextDecoder().decode(convertBase64ToUint8Array(data));
37
+ }
38
+
39
+ if (data instanceof Uint8Array) {
40
+ return new TextDecoder().decode(data);
41
+ }
42
+
43
+ if (data instanceof URL) {
44
+ throw new UnsupportedFunctionalityError({
45
+ functionality: 'URL-based text documents are not supported for citations',
46
+ });
47
+ }
48
+
49
+ throw new UnsupportedFunctionalityError({
50
+ functionality: `unsupported data type for text documents: ${typeof data}`,
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Checks if data is a URL (either a URL object or a URL string).
56
+ */
57
+ function isUrlData(
58
+ data: LanguageModelV3DataContent,
59
+ ): data is URL | (string & { __brand: 'url-string' }) {
60
+ return data instanceof URL || isUrlString(data);
61
+ }
62
+
63
+ function isUrlString(data: LanguageModelV3DataContent): boolean {
64
+ return typeof data === 'string' && /^https?:\/\//i.test(data);
65
+ }
66
+
67
+ function getUrlString(data: LanguageModelV3DataContent): string {
68
+ return data instanceof URL ? data.toString() : (data as string);
69
+ }
70
+
71
+ export async function convertToAnthropicMessagesPrompt({
72
+ prompt,
73
+ sendReasoning,
74
+ warnings,
75
+ cacheControlValidator,
76
+ toolNameMapping,
77
+ }: {
78
+ prompt: LanguageModelV3Prompt;
79
+ sendReasoning: boolean;
80
+ warnings: SharedV3Warning[];
81
+ cacheControlValidator?: CacheControlValidator;
82
+ toolNameMapping: ToolNameMapping;
83
+ }): Promise<{
84
+ prompt: AnthropicMessagesPrompt;
85
+ betas: Set<string>;
86
+ }> {
87
+ const betas = new Set<string>();
88
+ const blocks = groupIntoBlocks(prompt);
89
+ const validator = cacheControlValidator || new CacheControlValidator();
90
+
91
+ let system: AnthropicMessagesPrompt['system'] = undefined;
92
+ const messages: AnthropicMessagesPrompt['messages'] = [];
93
+
94
+ async function shouldEnableCitations(
95
+ providerMetadata: SharedV3ProviderMetadata | undefined,
96
+ ): Promise<boolean> {
97
+ const anthropicOptions = await parseProviderOptions({
98
+ provider: 'anthropic',
99
+ providerOptions: providerMetadata,
100
+ schema: anthropicFilePartProviderOptions,
101
+ });
102
+
103
+ return anthropicOptions?.citations?.enabled ?? false;
104
+ }
105
+
106
+ async function getDocumentMetadata(
107
+ providerMetadata: SharedV3ProviderMetadata | undefined,
108
+ ): Promise<{ title?: string; context?: string }> {
109
+ const anthropicOptions = await parseProviderOptions({
110
+ provider: 'anthropic',
111
+ providerOptions: providerMetadata,
112
+ schema: anthropicFilePartProviderOptions,
113
+ });
114
+
115
+ return {
116
+ title: anthropicOptions?.title,
117
+ context: anthropicOptions?.context,
118
+ };
119
+ }
120
+
121
+ for (let i = 0; i < blocks.length; i++) {
122
+ const block = blocks[i];
123
+ const isLastBlock = i === blocks.length - 1;
124
+ const type = block.type;
125
+
126
+ switch (type) {
127
+ case 'system': {
128
+ if (system != null) {
129
+ throw new UnsupportedFunctionalityError({
130
+ functionality:
131
+ 'Multiple system messages that are separated by user/assistant messages',
132
+ });
133
+ }
134
+
135
+ system = block.messages.map(({ content, providerOptions }) => ({
136
+ type: 'text',
137
+ text: content,
138
+ cache_control: validator.getCacheControl(providerOptions, {
139
+ type: 'system message',
140
+ canCache: true,
141
+ }),
142
+ }));
143
+
144
+ break;
145
+ }
146
+
147
+ case 'user': {
148
+ // combines all user and tool messages in this block into a single message:
149
+ const anthropicContent: AnthropicUserMessage['content'] = [];
150
+
151
+ for (const message of block.messages) {
152
+ const { role, content } = message;
153
+ switch (role) {
154
+ case 'user': {
155
+ for (let j = 0; j < content.length; j++) {
156
+ const part = content[j];
157
+
158
+ // cache control: first add cache control from part.
159
+ // for the last part of a message,
160
+ // check also if the message has cache control.
161
+ const isLastPart = j === content.length - 1;
162
+
163
+ const cacheControl =
164
+ validator.getCacheControl(part.providerOptions, {
165
+ type: 'user message part',
166
+ canCache: true,
167
+ }) ??
168
+ (isLastPart
169
+ ? validator.getCacheControl(message.providerOptions, {
170
+ type: 'user message',
171
+ canCache: true,
172
+ })
173
+ : undefined);
174
+
175
+ switch (part.type) {
176
+ case 'text': {
177
+ anthropicContent.push({
178
+ type: 'text',
179
+ text: part.text,
180
+ cache_control: cacheControl,
181
+ });
182
+ break;
183
+ }
184
+
185
+ case 'file': {
186
+ if (part.mediaType.startsWith('image/')) {
187
+ anthropicContent.push({
188
+ type: 'image',
189
+ source: isUrlData(part.data)
190
+ ? {
191
+ type: 'url',
192
+ url: getUrlString(part.data),
193
+ }
194
+ : {
195
+ type: 'base64',
196
+ media_type:
197
+ part.mediaType === 'image/*'
198
+ ? 'image/jpeg'
199
+ : part.mediaType,
200
+ data: convertToBase64(part.data),
201
+ },
202
+ cache_control: cacheControl,
203
+ });
204
+ } else if (part.mediaType === 'application/pdf') {
205
+ betas.add('pdfs-2024-09-25');
206
+
207
+ const enableCitations = await shouldEnableCitations(
208
+ part.providerOptions,
209
+ );
210
+
211
+ const metadata = await getDocumentMetadata(
212
+ part.providerOptions,
213
+ );
214
+
215
+ anthropicContent.push({
216
+ type: 'document',
217
+ source: isUrlData(part.data)
218
+ ? {
219
+ type: 'url',
220
+ url: getUrlString(part.data),
221
+ }
222
+ : {
223
+ type: 'base64',
224
+ media_type: 'application/pdf',
225
+ data: convertToBase64(part.data),
226
+ },
227
+ title: metadata.title ?? part.filename,
228
+ ...(metadata.context && { context: metadata.context }),
229
+ ...(enableCitations && {
230
+ citations: { enabled: true },
231
+ }),
232
+ cache_control: cacheControl,
233
+ });
234
+ } else if (part.mediaType === 'text/plain') {
235
+ const enableCitations = await shouldEnableCitations(
236
+ part.providerOptions,
237
+ );
238
+
239
+ const metadata = await getDocumentMetadata(
240
+ part.providerOptions,
241
+ );
242
+
243
+ anthropicContent.push({
244
+ type: 'document',
245
+ source: isUrlData(part.data)
246
+ ? {
247
+ type: 'url',
248
+ url: getUrlString(part.data),
249
+ }
250
+ : {
251
+ type: 'text',
252
+ media_type: 'text/plain',
253
+ data: convertToString(part.data),
254
+ },
255
+ title: metadata.title ?? part.filename,
256
+ ...(metadata.context && { context: metadata.context }),
257
+ ...(enableCitations && {
258
+ citations: { enabled: true },
259
+ }),
260
+ cache_control: cacheControl,
261
+ });
262
+ } else {
263
+ throw new UnsupportedFunctionalityError({
264
+ functionality: `media type: ${part.mediaType}`,
265
+ });
266
+ }
267
+
268
+ break;
269
+ }
270
+ }
271
+ }
272
+
273
+ break;
274
+ }
275
+ case 'tool': {
276
+ for (let i = 0; i < content.length; i++) {
277
+ const part = content[i];
278
+
279
+ if (part.type === 'tool-approval-response') {
280
+ continue;
281
+ }
282
+
283
+ // cache control: first add cache control from part.
284
+ // for the last part of a message,
285
+ // check also if the message has cache control.
286
+ const isLastPart = i === content.length - 1;
287
+
288
+ const cacheControl =
289
+ validator.getCacheControl(part.providerOptions, {
290
+ type: 'tool result part',
291
+ canCache: true,
292
+ }) ??
293
+ (isLastPart
294
+ ? validator.getCacheControl(message.providerOptions, {
295
+ type: 'tool result message',
296
+ canCache: true,
297
+ })
298
+ : undefined);
299
+
300
+ const output = part.output;
301
+ let contentValue: AnthropicToolResultContent['content'];
302
+ switch (output.type) {
303
+ case 'content':
304
+ contentValue = output.value
305
+ .map(contentPart => {
306
+ switch (contentPart.type) {
307
+ case 'text':
308
+ return {
309
+ type: 'text' as const,
310
+ text: contentPart.text,
311
+ };
312
+ case 'image-data': {
313
+ return {
314
+ type: 'image' as const,
315
+ source: {
316
+ type: 'base64' as const,
317
+ media_type: contentPart.mediaType,
318
+ data: contentPart.data,
319
+ },
320
+ };
321
+ }
322
+ case 'image-url': {
323
+ return {
324
+ type: 'image' as const,
325
+ source: {
326
+ type: 'url' as const,
327
+ url: contentPart.url,
328
+ },
329
+ };
330
+ }
331
+ case 'file-url': {
332
+ return {
333
+ type: 'document' as const,
334
+ source: {
335
+ type: 'url' as const,
336
+ url: contentPart.url,
337
+ },
338
+ };
339
+ }
340
+ case 'file-data': {
341
+ if (contentPart.mediaType === 'application/pdf') {
342
+ betas.add('pdfs-2024-09-25');
343
+ return {
344
+ type: 'document' as const,
345
+ source: {
346
+ type: 'base64' as const,
347
+ media_type: contentPart.mediaType,
348
+ data: contentPart.data,
349
+ },
350
+ };
351
+ }
352
+
353
+ warnings.push({
354
+ type: 'other',
355
+ message: `unsupported tool content part type: ${contentPart.type} with media type: ${contentPart.mediaType}`,
356
+ });
357
+
358
+ return undefined;
359
+ }
360
+ case 'custom': {
361
+ const anthropicOptions = contentPart.providerOptions
362
+ ?.anthropic as
363
+ | { type: string; toolName?: string }
364
+ | undefined;
365
+ if (anthropicOptions?.type === 'tool-reference') {
366
+ return {
367
+ type: 'tool_reference' as const,
368
+ tool_name: anthropicOptions.toolName!,
369
+ };
370
+ }
371
+ warnings.push({
372
+ type: 'other',
373
+ message: `unsupported custom tool content part`,
374
+ });
375
+ return undefined;
376
+ }
377
+ default: {
378
+ warnings.push({
379
+ type: 'other',
380
+ message: `unsupported tool content part type: ${contentPart.type}`,
381
+ });
382
+
383
+ return undefined;
384
+ }
385
+ }
386
+ })
387
+ .filter(isNonNullable);
388
+ break;
389
+ case 'text':
390
+ case 'error-text':
391
+ contentValue = output.value;
392
+ break;
393
+ case 'execution-denied':
394
+ contentValue = output.reason ?? 'Tool execution denied.';
395
+ break;
396
+ case 'json':
397
+ case 'error-json':
398
+ default:
399
+ contentValue = JSON.stringify(output.value);
400
+ break;
401
+ }
402
+
403
+ anthropicContent.push({
404
+ type: 'tool_result',
405
+ tool_use_id: part.toolCallId,
406
+ content: contentValue,
407
+ is_error:
408
+ output.type === 'error-text' || output.type === 'error-json'
409
+ ? true
410
+ : undefined,
411
+ cache_control: cacheControl,
412
+ });
413
+ }
414
+
415
+ break;
416
+ }
417
+ default: {
418
+ const _exhaustiveCheck: never = role;
419
+ throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
420
+ }
421
+ }
422
+ }
423
+
424
+ messages.push({ role: 'user', content: anthropicContent });
425
+
426
+ break;
427
+ }
428
+
429
+ case 'assistant': {
430
+ // combines multiple assistant messages in this block into a single message:
431
+ const anthropicContent: AnthropicAssistantMessage['content'] = [];
432
+
433
+ const mcpToolUseIds = new Set<string>();
434
+
435
+ for (let j = 0; j < block.messages.length; j++) {
436
+ const message = block.messages[j];
437
+ const isLastMessage = j === block.messages.length - 1;
438
+ const { content } = message;
439
+
440
+ for (let k = 0; k < content.length; k++) {
441
+ const part = content[k];
442
+ const isLastContentPart = k === content.length - 1;
443
+
444
+ // cache control: first add cache control from part.
445
+ // for the last part of a message,
446
+ // check also if the message has cache control.
447
+ const cacheControl =
448
+ validator.getCacheControl(part.providerOptions, {
449
+ type: 'assistant message part',
450
+ canCache: true,
451
+ }) ??
452
+ (isLastContentPart
453
+ ? validator.getCacheControl(message.providerOptions, {
454
+ type: 'assistant message',
455
+ canCache: true,
456
+ })
457
+ : undefined);
458
+
459
+ switch (part.type) {
460
+ case 'text': {
461
+ // Check if this is a compaction block (via providerMetadata)
462
+ const textMetadata = part.providerOptions?.anthropic as
463
+ | { type?: string }
464
+ | undefined;
465
+
466
+ if (textMetadata?.type === 'compaction') {
467
+ anthropicContent.push({
468
+ type: 'compaction',
469
+ content: part.text,
470
+ cache_control: cacheControl,
471
+ });
472
+ } else {
473
+ anthropicContent.push({
474
+ type: 'text',
475
+ text:
476
+ // trim the last text part if it's the last message in the block
477
+ // because Anthropic does not allow trailing whitespace
478
+ // in pre-filled assistant responses
479
+ isLastBlock && isLastMessage && isLastContentPart
480
+ ? part.text.trim()
481
+ : part.text,
482
+
483
+ cache_control: cacheControl,
484
+ });
485
+ }
486
+ break;
487
+ }
488
+
489
+ case 'reasoning': {
490
+ if (sendReasoning) {
491
+ const reasoningMetadata = await parseProviderOptions({
492
+ provider: 'anthropic',
493
+ providerOptions: part.providerOptions,
494
+ schema: anthropicReasoningMetadataSchema,
495
+ });
496
+
497
+ if (reasoningMetadata != null) {
498
+ if (reasoningMetadata.signature != null) {
499
+ // Note: thinking blocks cannot have cache_control directly
500
+ // They are cached implicitly when in previous assistant turns
501
+ // Validate to provide helpful error message
502
+ validator.getCacheControl(part.providerOptions, {
503
+ type: 'thinking block',
504
+ canCache: false,
505
+ });
506
+ anthropicContent.push({
507
+ type: 'thinking',
508
+ thinking: part.text,
509
+ signature: reasoningMetadata.signature,
510
+ });
511
+ } else if (reasoningMetadata.redactedData != null) {
512
+ // Note: redacted thinking blocks cannot have cache_control directly
513
+ // They are cached implicitly when in previous assistant turns
514
+ // Validate to provide helpful error message
515
+ validator.getCacheControl(part.providerOptions, {
516
+ type: 'redacted thinking block',
517
+ canCache: false,
518
+ });
519
+ anthropicContent.push({
520
+ type: 'redacted_thinking',
521
+ data: reasoningMetadata.redactedData,
522
+ });
523
+ } else {
524
+ warnings.push({
525
+ type: 'other',
526
+ message: 'unsupported reasoning metadata',
527
+ });
528
+ }
529
+ } else {
530
+ warnings.push({
531
+ type: 'other',
532
+ message: 'unsupported reasoning metadata',
533
+ });
534
+ }
535
+ } else {
536
+ warnings.push({
537
+ type: 'other',
538
+ message:
539
+ 'sending reasoning content is disabled for this model',
540
+ });
541
+ }
542
+ break;
543
+ }
544
+
545
+ case 'tool-call': {
546
+ if (part.providerExecuted) {
547
+ const providerToolName = toolNameMapping.toProviderToolName(
548
+ part.toolName,
549
+ );
550
+ const isMcpToolUse =
551
+ part.providerOptions?.anthropic?.type === 'mcp-tool-use';
552
+
553
+ if (isMcpToolUse) {
554
+ mcpToolUseIds.add(part.toolCallId);
555
+
556
+ const serverName =
557
+ part.providerOptions?.anthropic?.serverName;
558
+
559
+ if (serverName == null || typeof serverName !== 'string') {
560
+ warnings.push({
561
+ type: 'other',
562
+ message:
563
+ 'mcp tool use server name is required and must be a string',
564
+ });
565
+ break;
566
+ }
567
+
568
+ anthropicContent.push({
569
+ type: 'mcp_tool_use',
570
+ id: part.toolCallId,
571
+ name: part.toolName,
572
+ input: part.input,
573
+ server_name: serverName,
574
+ cache_control: cacheControl,
575
+ });
576
+ } else if (
577
+ // code execution 20250825:
578
+ providerToolName === 'code_execution' &&
579
+ part.input != null &&
580
+ typeof part.input === 'object' &&
581
+ 'type' in part.input &&
582
+ typeof part.input.type === 'string' &&
583
+ (part.input.type === 'bash_code_execution' ||
584
+ part.input.type === 'text_editor_code_execution')
585
+ ) {
586
+ anthropicContent.push({
587
+ type: 'server_tool_use',
588
+ id: part.toolCallId,
589
+ name: part.input.type, // map back to subtool name
590
+ input: part.input,
591
+ cache_control: cacheControl,
592
+ });
593
+ } else if (
594
+ // code execution 20250825 programmatic tool calling:
595
+ // Strip the fake 'programmatic-tool-call' type before sending to Anthropic
596
+ providerToolName === 'code_execution' &&
597
+ part.input != null &&
598
+ typeof part.input === 'object' &&
599
+ 'type' in part.input &&
600
+ part.input.type === 'programmatic-tool-call'
601
+ ) {
602
+ const { type: _, ...inputWithoutType } = part.input as {
603
+ type: string;
604
+ code: string;
605
+ };
606
+ anthropicContent.push({
607
+ type: 'server_tool_use',
608
+ id: part.toolCallId,
609
+ name: 'code_execution',
610
+ input: inputWithoutType,
611
+ cache_control: cacheControl,
612
+ });
613
+ } else {
614
+ if (
615
+ providerToolName === 'code_execution' || // code execution 20250522
616
+ providerToolName === 'web_fetch' ||
617
+ providerToolName === 'web_search'
618
+ ) {
619
+ anthropicContent.push({
620
+ type: 'server_tool_use',
621
+ id: part.toolCallId,
622
+ name: providerToolName,
623
+ input: part.input,
624
+ cache_control: cacheControl,
625
+ });
626
+ } else if (
627
+ providerToolName === 'tool_search_tool_regex' ||
628
+ providerToolName === 'tool_search_tool_bm25'
629
+ ) {
630
+ anthropicContent.push({
631
+ type: 'server_tool_use',
632
+ id: part.toolCallId,
633
+ name: providerToolName,
634
+ input: part.input,
635
+ cache_control: cacheControl,
636
+ });
637
+ } else {
638
+ warnings.push({
639
+ type: 'other',
640
+ message: `provider executed tool call for tool ${part.toolName} is not supported`,
641
+ });
642
+ }
643
+ }
644
+
645
+ break;
646
+ }
647
+
648
+ // Extract caller info from provider options for programmatic tool calling
649
+ const callerOptions = part.providerOptions?.anthropic as
650
+ | { caller?: { type: string; toolId?: string } }
651
+ | undefined;
652
+ const caller = callerOptions?.caller
653
+ ? (callerOptions.caller.type === 'code_execution_20250825' ||
654
+ callerOptions.caller.type ===
655
+ 'code_execution_20260120') &&
656
+ callerOptions.caller.toolId
657
+ ? {
658
+ type: callerOptions.caller.type as
659
+ | 'code_execution_20250825'
660
+ | 'code_execution_20260120',
661
+ tool_id: callerOptions.caller.toolId,
662
+ }
663
+ : callerOptions.caller.type === 'direct'
664
+ ? { type: 'direct' as const }
665
+ : undefined
666
+ : undefined;
667
+
668
+ anthropicContent.push({
669
+ type: 'tool_use',
670
+ id: part.toolCallId,
671
+ name: part.toolName,
672
+ input: part.input,
673
+ ...(caller && { caller }),
674
+ cache_control: cacheControl,
675
+ });
676
+ break;
677
+ }
678
+
679
+ case 'tool-result': {
680
+ const providerToolName = toolNameMapping.toProviderToolName(
681
+ part.toolName,
682
+ );
683
+
684
+ if (mcpToolUseIds.has(part.toolCallId)) {
685
+ const output = part.output;
686
+
687
+ if (output.type !== 'json' && output.type !== 'error-json') {
688
+ warnings.push({
689
+ type: 'other',
690
+ message: `provider executed tool result output type ${output.type} for tool ${part.toolName} is not supported`,
691
+ });
692
+
693
+ break;
694
+ }
695
+
696
+ anthropicContent.push({
697
+ type: 'mcp_tool_result',
698
+ tool_use_id: part.toolCallId,
699
+ is_error: output.type === 'error-json',
700
+ content: output.value as unknown as
701
+ | string
702
+ | Array<{ type: 'text'; text: string }>,
703
+ cache_control: cacheControl,
704
+ });
705
+ } else if (providerToolName === 'code_execution') {
706
+ const output = part.output;
707
+
708
+ // Handle error types for code_execution tools (e.g., from programmatic tool calling)
709
+ if (
710
+ output.type === 'error-text' ||
711
+ output.type === 'error-json'
712
+ ) {
713
+ let errorInfo: { type?: string; errorCode?: string } = {};
714
+ try {
715
+ if (typeof output.value === 'string') {
716
+ errorInfo = JSON.parse(output.value);
717
+ } else if (
718
+ typeof output.value === 'object' &&
719
+ output.value !== null
720
+ ) {
721
+ errorInfo = output.value as typeof errorInfo;
722
+ }
723
+ } catch {}
724
+
725
+ if (errorInfo.type === 'code_execution_tool_result_error') {
726
+ anthropicContent.push({
727
+ type: 'code_execution_tool_result',
728
+ tool_use_id: part.toolCallId,
729
+ content: {
730
+ type: 'code_execution_tool_result_error' as const,
731
+ error_code: errorInfo.errorCode ?? 'unknown',
732
+ },
733
+ cache_control: cacheControl,
734
+ });
735
+ } else {
736
+ anthropicContent.push({
737
+ type: 'bash_code_execution_tool_result',
738
+ tool_use_id: part.toolCallId,
739
+ cache_control: cacheControl,
740
+ content: {
741
+ type: 'bash_code_execution_tool_result_error' as const,
742
+ error_code: errorInfo.errorCode ?? 'unknown',
743
+ },
744
+ });
745
+ }
746
+ break;
747
+ }
748
+
749
+ if (output.type !== 'json') {
750
+ warnings.push({
751
+ type: 'other',
752
+ message: `provider executed tool result output type ${output.type} for tool ${part.toolName} is not supported`,
753
+ });
754
+
755
+ break;
756
+ }
757
+
758
+ if (
759
+ output.value == null ||
760
+ typeof output.value !== 'object' ||
761
+ !('type' in output.value) ||
762
+ typeof output.value.type !== 'string'
763
+ ) {
764
+ warnings.push({
765
+ type: 'other',
766
+ message: `provider executed tool result output value is not a valid code execution result for tool ${part.toolName}`,
767
+ });
768
+ break;
769
+ }
770
+
771
+ // to distinguish between code execution 20250522, 20250825,
772
+ // and encrypted results (from web_fetch_20260209/web_search_20260209 injection),
773
+ // we check the type property in output.value
774
+ if (output.value.type === 'code_execution_result') {
775
+ // code execution 20250522
776
+ const codeExecutionOutput = await validateTypes({
777
+ value: output.value,
778
+ schema: codeExecution_20250522OutputSchema,
779
+ });
780
+
781
+ anthropicContent.push({
782
+ type: 'code_execution_tool_result',
783
+ tool_use_id: part.toolCallId,
784
+ content: {
785
+ type: codeExecutionOutput.type,
786
+ stdout: codeExecutionOutput.stdout,
787
+ stderr: codeExecutionOutput.stderr,
788
+ return_code: codeExecutionOutput.return_code,
789
+ content: codeExecutionOutput.content ?? [],
790
+ },
791
+ cache_control: cacheControl,
792
+ });
793
+ } else if (
794
+ output.value.type === 'encrypted_code_execution_result'
795
+ ) {
796
+ // code execution 20260120 encrypted result
797
+ const codeExecutionOutput = await validateTypes({
798
+ value: output.value,
799
+ schema: codeExecution_20260120OutputSchema,
800
+ });
801
+
802
+ if (
803
+ codeExecutionOutput.type ===
804
+ 'encrypted_code_execution_result'
805
+ ) {
806
+ anthropicContent.push({
807
+ type: 'code_execution_tool_result',
808
+ tool_use_id: part.toolCallId,
809
+ content: {
810
+ type: codeExecutionOutput.type,
811
+ encrypted_stdout:
812
+ codeExecutionOutput.encrypted_stdout,
813
+ stderr: codeExecutionOutput.stderr,
814
+ return_code: codeExecutionOutput.return_code,
815
+ content: codeExecutionOutput.content ?? [],
816
+ },
817
+ cache_control: cacheControl,
818
+ });
819
+ }
820
+ } else {
821
+ // code execution 20250825
822
+ const codeExecutionOutput = await validateTypes({
823
+ value: output.value,
824
+ schema: codeExecution_20250825OutputSchema,
825
+ });
826
+
827
+ if (codeExecutionOutput.type === 'code_execution_result') {
828
+ anthropicContent.push({
829
+ type: 'code_execution_tool_result',
830
+ tool_use_id: part.toolCallId,
831
+ content: {
832
+ type: codeExecutionOutput.type,
833
+ stdout: codeExecutionOutput.stdout,
834
+ stderr: codeExecutionOutput.stderr,
835
+ return_code: codeExecutionOutput.return_code,
836
+ content: codeExecutionOutput.content ?? [],
837
+ },
838
+ cache_control: cacheControl,
839
+ });
840
+ } else if (
841
+ codeExecutionOutput.type ===
842
+ 'bash_code_execution_result' ||
843
+ codeExecutionOutput.type ===
844
+ 'bash_code_execution_tool_result_error'
845
+ ) {
846
+ anthropicContent.push({
847
+ type: 'bash_code_execution_tool_result',
848
+ tool_use_id: part.toolCallId,
849
+ cache_control: cacheControl,
850
+ content: codeExecutionOutput,
851
+ });
852
+ } else {
853
+ anthropicContent.push({
854
+ type: 'text_editor_code_execution_tool_result',
855
+ tool_use_id: part.toolCallId,
856
+ cache_control: cacheControl,
857
+ content: codeExecutionOutput,
858
+ });
859
+ }
860
+ }
861
+ break;
862
+ }
863
+
864
+ if (providerToolName === 'web_fetch') {
865
+ const output = part.output;
866
+
867
+ if (output.type === 'error-json') {
868
+ let errorValue: { errorCode?: string } = {};
869
+ try {
870
+ if (typeof output.value === 'string') {
871
+ errorValue = JSON.parse(output.value);
872
+ } else if (
873
+ typeof output.value === 'object' &&
874
+ output.value !== null
875
+ ) {
876
+ errorValue = output.value as typeof errorValue;
877
+ }
878
+ } catch {
879
+ // If parsing fails, treat the value as-is
880
+ const extractedErrorCode = (
881
+ output.value as Record<string, unknown>
882
+ )?.errorCode;
883
+ errorValue = {
884
+ errorCode:
885
+ typeof extractedErrorCode === 'string'
886
+ ? extractedErrorCode
887
+ : 'unknown',
888
+ };
889
+ }
890
+
891
+ anthropicContent.push({
892
+ type: 'web_fetch_tool_result',
893
+ tool_use_id: part.toolCallId,
894
+ content: {
895
+ type: 'web_fetch_tool_result_error',
896
+ error_code: errorValue.errorCode ?? 'unknown',
897
+ },
898
+ cache_control: cacheControl,
899
+ });
900
+
901
+ break;
902
+ }
903
+
904
+ if (output.type !== 'json') {
905
+ warnings.push({
906
+ type: 'other',
907
+ message: `provider executed tool result output type ${output.type} for tool ${part.toolName} is not supported`,
908
+ });
909
+
910
+ break;
911
+ }
912
+
913
+ // ideally we'd switch schema based on the tool version (e.g.
914
+ // web_fetch_20260209 vs web_fetch_20250910), but since both
915
+ // versions share an identical output schema, we use one here.
916
+ const webFetchOutput = await validateTypes({
917
+ value: output.value,
918
+ schema: webFetch_20250910OutputSchema,
919
+ });
920
+
921
+ anthropicContent.push({
922
+ type: 'web_fetch_tool_result',
923
+ tool_use_id: part.toolCallId,
924
+ content: {
925
+ type: 'web_fetch_result',
926
+ url: webFetchOutput.url,
927
+ retrieved_at: webFetchOutput.retrievedAt,
928
+ content: {
929
+ type: 'document',
930
+ title: webFetchOutput.content.title,
931
+ citations: webFetchOutput.content.citations,
932
+ source: {
933
+ type: webFetchOutput.content.source.type,
934
+ media_type: webFetchOutput.content.source.mediaType,
935
+ data: webFetchOutput.content.source.data,
936
+ } as Extract<
937
+ AnthropicWebFetchToolResultContent['content'],
938
+ { type: 'web_fetch_result' }
939
+ >['content']['source'],
940
+ },
941
+ },
942
+ cache_control: cacheControl,
943
+ });
944
+
945
+ break;
946
+ }
947
+
948
+ if (providerToolName === 'web_search') {
949
+ const output = part.output;
950
+
951
+ if (output.type !== 'json') {
952
+ warnings.push({
953
+ type: 'other',
954
+ message: `provider executed tool result output type ${output.type} for tool ${part.toolName} is not supported`,
955
+ });
956
+
957
+ break;
958
+ }
959
+
960
+ // ideally we'd switch schema based on the tool version (e.g.
961
+ // web_search_20260209 vs web_search_20250305), but since both
962
+ // versions share an identical output schema, we use one here.
963
+ const webSearchOutput = await validateTypes({
964
+ value: output.value,
965
+ schema: webSearch_20250305OutputSchema,
966
+ });
967
+
968
+ anthropicContent.push({
969
+ type: 'web_search_tool_result',
970
+ tool_use_id: part.toolCallId,
971
+ content: webSearchOutput.map(result => ({
972
+ url: result.url,
973
+ title: result.title,
974
+ page_age: result.pageAge,
975
+ encrypted_content: result.encryptedContent,
976
+ type: result.type,
977
+ })),
978
+ cache_control: cacheControl,
979
+ });
980
+
981
+ break;
982
+ }
983
+
984
+ if (
985
+ providerToolName === 'tool_search_tool_regex' ||
986
+ providerToolName === 'tool_search_tool_bm25'
987
+ ) {
988
+ const output = part.output;
989
+
990
+ if (output.type !== 'json') {
991
+ warnings.push({
992
+ type: 'other',
993
+ message: `provider executed tool result output type ${output.type} for tool ${part.toolName} is not supported`,
994
+ });
995
+
996
+ break;
997
+ }
998
+
999
+ const toolSearchOutput = await validateTypes({
1000
+ value: output.value,
1001
+ schema: toolSearchOutputSchema,
1002
+ });
1003
+
1004
+ // Convert tool references back to API format
1005
+ const toolReferences = toolSearchOutput.map(ref => ({
1006
+ type: 'tool_reference' as const,
1007
+ tool_name: ref.toolName,
1008
+ }));
1009
+
1010
+ anthropicContent.push({
1011
+ type: 'tool_search_tool_result',
1012
+ tool_use_id: part.toolCallId,
1013
+ content: {
1014
+ type: 'tool_search_tool_search_result',
1015
+ tool_references: toolReferences,
1016
+ },
1017
+ cache_control: cacheControl,
1018
+ });
1019
+
1020
+ break;
1021
+ }
1022
+
1023
+ warnings.push({
1024
+ type: 'other',
1025
+ message: `provider executed tool result for tool ${part.toolName} is not supported`,
1026
+ });
1027
+
1028
+ break;
1029
+ }
1030
+ }
1031
+ }
1032
+ }
1033
+
1034
+ messages.push({ role: 'assistant', content: anthropicContent });
1035
+
1036
+ break;
1037
+ }
1038
+
1039
+ default: {
1040
+ const _exhaustiveCheck: never = type;
1041
+ throw new Error(`content type: ${_exhaustiveCheck}`);
1042
+ }
1043
+ }
1044
+ }
1045
+
1046
+ return {
1047
+ prompt: { system, messages },
1048
+ betas,
1049
+ };
1050
+ }
1051
+
1052
+ type SystemBlock = {
1053
+ type: 'system';
1054
+ messages: Array<LanguageModelV3Message & { role: 'system' }>;
1055
+ };
1056
+ type AssistantBlock = {
1057
+ type: 'assistant';
1058
+ messages: Array<LanguageModelV3Message & { role: 'assistant' }>;
1059
+ };
1060
+ type UserBlock = {
1061
+ type: 'user';
1062
+ messages: Array<LanguageModelV3Message & { role: 'user' | 'tool' }>;
1063
+ };
1064
+
1065
+ function groupIntoBlocks(
1066
+ prompt: LanguageModelV3Prompt,
1067
+ ): Array<SystemBlock | AssistantBlock | UserBlock> {
1068
+ const blocks: Array<SystemBlock | AssistantBlock | UserBlock> = [];
1069
+ let currentBlock: SystemBlock | AssistantBlock | UserBlock | undefined =
1070
+ undefined;
1071
+
1072
+ for (const message of prompt) {
1073
+ const { role } = message;
1074
+ switch (role) {
1075
+ case 'system': {
1076
+ if (currentBlock?.type !== 'system') {
1077
+ currentBlock = { type: 'system', messages: [] };
1078
+ blocks.push(currentBlock);
1079
+ }
1080
+
1081
+ currentBlock.messages.push(message);
1082
+ break;
1083
+ }
1084
+ case 'assistant': {
1085
+ if (currentBlock?.type !== 'assistant') {
1086
+ currentBlock = { type: 'assistant', messages: [] };
1087
+ blocks.push(currentBlock);
1088
+ }
1089
+
1090
+ currentBlock.messages.push(message);
1091
+ break;
1092
+ }
1093
+ case 'user': {
1094
+ if (currentBlock?.type !== 'user') {
1095
+ currentBlock = { type: 'user', messages: [] };
1096
+ blocks.push(currentBlock);
1097
+ }
1098
+
1099
+ currentBlock.messages.push(message);
1100
+ break;
1101
+ }
1102
+ case 'tool': {
1103
+ if (currentBlock?.type !== 'user') {
1104
+ currentBlock = { type: 'user', messages: [] };
1105
+ blocks.push(currentBlock);
1106
+ }
1107
+
1108
+ currentBlock.messages.push(message);
1109
+ break;
1110
+ }
1111
+ default: {
1112
+ const _exhaustiveCheck: never = role;
1113
+ throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
1114
+ }
1115
+ }
1116
+ }
1117
+
1118
+ return blocks;
1119
+ }