diff --git a/.changeset/good-dancers-give.md b/.changeset/good-dancers-give.md new file mode 100644 index 000000000000..d6af53cd64cc --- /dev/null +++ b/.changeset/good-dancers-give.md @@ -0,0 +1,6 @@ +--- +'@ai-sdk/anthropic': minor +'@ai-sdk/provider': minor +--- + +Support for container uploads for claude diff --git a/content/providers/01-ai-sdk-providers/05-anthropic.mdx b/content/providers/01-ai-sdk-providers/05-anthropic.mdx index d221f10e06d6..e3a83bcbc762 100644 --- a/content/providers/01-ai-sdk-providers/05-anthropic.mdx +++ b/content/providers/01-ai-sdk-providers/05-anthropic.mdx @@ -577,6 +577,37 @@ const result = await generateText({ }); ``` +#### Container Uploads + +You can also add files for code execution using container uploads. For example, + +```ts +import { anthropic } from '@ai-sdk/anthropic'; +import { generateText } from 'ai'; +const codeExecutionTool = anthropic.tools.codeExecution_20250522(); +const result = await generateText({ + model: anthropic('claude-opus-4-20250514'), + messages: [ + { + role: 'user', + content: [ + { + type: 'text', + text: 'Analyze this csv file', + }, + { + type: 'container_upload', + file_id: 'file-123', + }, + ], + }, + ], + tools: { + code_execution: codeExecutionTool, + }, +}); +``` + #### Error Handling Code execution errors are handled differently depending on whether you're using streaming or non-streaming: diff --git a/packages/anthropic/src/anthropic-api-types.ts b/packages/anthropic/src/anthropic-api-types.ts index 6af7f305abda..7ff0ea96b728 100644 --- a/packages/anthropic/src/anthropic-api-types.ts +++ b/packages/anthropic/src/anthropic-api-types.ts @@ -18,6 +18,7 @@ export interface AnthropicUserMessage { | AnthropicImageContent | AnthropicDocumentContent | AnthropicToolResultContent + | AnthropicContainerUploadContent >; } @@ -40,6 +41,11 @@ export interface AnthropicTextContent { cache_control: AnthropicCacheControl | undefined; } +export interface AnthropicContainerUploadContent { + type: 'container_upload'; + file_id: string; +} + export interface AnthropicThinkingContent { type: 'thinking'; thinking: string; diff --git a/packages/anthropic/src/convert-to-anthropic-messages-prompt.test.ts b/packages/anthropic/src/convert-to-anthropic-messages-prompt.test.ts index 3afd01bcba67..dbe5e943338a 100644 --- a/packages/anthropic/src/convert-to-anthropic-messages-prompt.test.ts +++ b/packages/anthropic/src/convert-to-anthropic-messages-prompt.test.ts @@ -1,5 +1,5 @@ -import { describe, it, expect } from 'vitest'; import { LanguageModelV2CallWarning } from '@ai-sdk/provider'; +import { describe, expect, it } from 'vitest'; import { convertToAnthropicMessagesPrompt } from './convert-to-anthropic-messages-prompt'; describe('system messages', () => { @@ -1612,4 +1612,34 @@ describe('citations', () => { } `); }); + + it('should convert container upload message parts into anthropic container uploads', async () => { + const result = await convertToAnthropicMessagesPrompt({ + prompt: [ + { + role: 'user', + content: [ + { + type: 'container_upload', + file_id: 'file-123', + }, + ], + }, + ], + sendReasoning: false, + warnings: [], + }); + + expect(result).toEqual({ + prompt: { + messages: [ + { + role: 'user', + content: [{ type: 'container_upload', file_id: 'file-123' }], + }, + ], + }, + betas: new Set(), + }); + }); }); diff --git a/packages/anthropic/src/convert-to-anthropic-messages-prompt.ts b/packages/anthropic/src/convert-to-anthropic-messages-prompt.ts index bda63de9baec..aaf6f717dbf6 100644 --- a/packages/anthropic/src/convert-to-anthropic-messages-prompt.ts +++ b/packages/anthropic/src/convert-to-anthropic-messages-prompt.ts @@ -16,8 +16,8 @@ import { import { anthropicReasoningMetadataSchema } from './anthropic-messages-language-model'; import { anthropicFilePartProviderOptions } from './anthropic-messages-options'; import { getCacheControl } from './get-cache-control'; -import { webSearch_20250305OutputSchema } from './tool/web-search_20250305'; import { codeExecution_20250522OutputSchema } from './tool/code-execution_20250522'; +import { webSearch_20250305OutputSchema } from './tool/web-search_20250305'; function convertToString(data: LanguageModelV2DataContent): string { if (typeof data === 'string') { @@ -227,6 +227,14 @@ export async function convertToAnthropicMessagesPrompt({ break; } + + case 'container_upload': { + anthropicContent.push({ + type: 'container_upload', + file_id: part.file_id, + }); + break; + } } } diff --git a/packages/provider/src/language-model/v2/language-model-v2-prompt.ts b/packages/provider/src/language-model/v2/language-model-v2-prompt.ts index 2ed62eb45ed1..d27fdbe6398c 100644 --- a/packages/provider/src/language-model/v2/language-model-v2-prompt.ts +++ b/packages/provider/src/language-model/v2/language-model-v2-prompt.ts @@ -24,7 +24,11 @@ export type LanguageModelV2Message = } | { role: 'user'; - content: Array; + content: Array< + | LanguageModelV2TextPart + | LanguageModelV2FilePart + | LanguageModelV2ContainerUploadPart + >; } | { role: 'assistant'; @@ -68,6 +72,25 @@ The text content. providerOptions?: SharedV2ProviderOptions; } +/** +Container uploads content part. Supported by Anthropic API. + */ +export interface LanguageModelV2ContainerUploadPart { + type: 'container_upload'; + + /** + * The file id + */ + file_id: string; + + /** + * Additional provider-specific options. They are passed through + * to the provider from the AI SDK and enable provider-specific + * functionality that can be fully encapsulated in the provider. + */ + providerOptions?: SharedV2ProviderOptions; +} + /** Reasoning content part of a prompt. It contains a string of reasoning text. */