UNPKG

@ai-sdk/anthropic

Version:

The **[Anthropic provider](https://ai-sdk.dev/providers/ai-sdk-providers/anthropic)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post).

1 lines 383 kB
{"version":3,"sources":["../src/anthropic-provider.ts","../src/version.ts","../src/anthropic-messages-language-model.ts","../src/anthropic-error.ts","../src/anthropic-messages-api.ts","../src/anthropic-messages-options.ts","../src/anthropic-prepare-tools.ts","../src/get-cache-control.ts","../src/tool/text-editor_20250728.ts","../src/tool/web-search_20260209.ts","../src/tool/web-search_20250305.ts","../src/tool/web-fetch-20260209.ts","../src/tool/web-fetch-20250910.ts","../src/convert-anthropic-messages-usage.ts","../src/convert-to-anthropic-messages-prompt.ts","../src/tool/code-execution_20250522.ts","../src/tool/code-execution_20250825.ts","../src/tool/code-execution_20260120.ts","../src/tool/tool-search-regex_20251119.ts","../src/map-anthropic-stop-reason.ts","../src/tool/bash_20241022.ts","../src/tool/bash_20250124.ts","../src/tool/computer_20241022.ts","../src/tool/computer_20250124.ts","../src/tool/computer_20251124.ts","../src/tool/memory_20250818.ts","../src/tool/text-editor_20241022.ts","../src/tool/text-editor_20250124.ts","../src/tool/text-editor_20250429.ts","../src/tool/tool-search-bm25_20251119.ts","../src/anthropic-tools.ts","../src/forward-anthropic-container-id-from-last-step.ts"],"sourcesContent":["import {\n InvalidArgumentError,\n LanguageModelV3,\n NoSuchModelError,\n ProviderV3,\n} from '@ai-sdk/provider';\nimport {\n FetchFunction,\n generateId,\n loadApiKey,\n loadOptionalSetting,\n withoutTrailingSlash,\n withUserAgentSuffix,\n} from '@ai-sdk/provider-utils';\nimport { VERSION } from './version';\nimport { AnthropicMessagesLanguageModel } from './anthropic-messages-language-model';\nimport { AnthropicMessagesModelId } from './anthropic-messages-options';\nimport { anthropicTools } from './anthropic-tools';\n\nexport interface AnthropicProvider extends ProviderV3 {\n /**\n * Creates a model for text generation.\n */\n (modelId: AnthropicMessagesModelId): LanguageModelV3;\n\n /**\n * Creates a model for text generation.\n */\n languageModel(modelId: AnthropicMessagesModelId): LanguageModelV3;\n\n chat(modelId: AnthropicMessagesModelId): LanguageModelV3;\n\n messages(modelId: AnthropicMessagesModelId): LanguageModelV3;\n\n /**\n * @deprecated Use `embeddingModel` instead.\n */\n textEmbeddingModel(modelId: string): never;\n\n /**\n * Anthropic-specific computer use tool.\n */\n tools: typeof anthropicTools;\n}\n\nexport interface AnthropicProviderSettings {\n /**\n * Use a different URL prefix for API calls, e.g. to use proxy servers.\n * The default prefix is `https://api.anthropic.com/v1`.\n */\n baseURL?: string;\n\n /**\n * API key that is being send using the `x-api-key` header.\n * It defaults to the `ANTHROPIC_API_KEY` environment variable.\n * Only one of `apiKey` or `authToken` is required.\n */\n apiKey?: string;\n\n /**\n * Auth token that is being sent using the `Authorization: Bearer` header.\n * It defaults to the `ANTHROPIC_AUTH_TOKEN` environment variable.\n * Only one of `apiKey` or `authToken` is required.\n */\n authToken?: string;\n\n /**\n * Custom headers to include in the requests.\n */\n headers?: Record<string, string>;\n\n /**\n * Custom fetch implementation. You can use it as a middleware to intercept requests,\n * or to provide a custom fetch implementation for e.g. testing.\n */\n fetch?: FetchFunction;\n\n generateId?: () => string;\n\n /**\n * Custom provider name\n * Defaults to 'anthropic.messages'.\n */\n name?: string;\n}\n\n/**\n * Create an Anthropic provider instance.\n */\nexport function createAnthropic(\n options: AnthropicProviderSettings = {},\n): AnthropicProvider {\n const baseURL =\n withoutTrailingSlash(\n loadOptionalSetting({\n settingValue: options.baseURL,\n environmentVariableName: 'ANTHROPIC_BASE_URL',\n }),\n ) ?? 'https://api.anthropic.com/v1';\n\n const providerName = options.name ?? 'anthropic.messages';\n\n // Only error if both are explicitly provided in options\n if (options.apiKey && options.authToken) {\n throw new InvalidArgumentError({\n argument: 'apiKey/authToken',\n message:\n 'Both apiKey and authToken were provided. Please use only one authentication method.',\n });\n }\n\n const getHeaders = () => {\n const authHeaders: Record<string, string> = options.authToken\n ? { Authorization: `Bearer ${options.authToken}` }\n : {\n 'x-api-key': loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'ANTHROPIC_API_KEY',\n description: 'Anthropic',\n }),\n };\n\n return withUserAgentSuffix(\n {\n 'anthropic-version': '2023-06-01',\n ...authHeaders,\n ...options.headers,\n },\n `ai-sdk/anthropic/${VERSION}`,\n );\n };\n\n const createChatModel = (modelId: AnthropicMessagesModelId) =>\n new AnthropicMessagesLanguageModel(modelId, {\n provider: providerName,\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n generateId: options.generateId ?? generateId,\n supportedUrls: () => ({\n 'image/*': [/^https?:\\/\\/.*$/],\n 'application/pdf': [/^https?:\\/\\/.*$/],\n }),\n });\n\n const provider = function (modelId: AnthropicMessagesModelId) {\n if (new.target) {\n throw new Error(\n 'The Anthropic model function cannot be called with the new keyword.',\n );\n }\n\n return createChatModel(modelId);\n };\n\n provider.specificationVersion = 'v3' as const;\n provider.languageModel = createChatModel;\n provider.chat = createChatModel;\n provider.messages = createChatModel;\n\n provider.embeddingModel = (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });\n };\n provider.textEmbeddingModel = provider.embeddingModel;\n provider.imageModel = (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: 'imageModel' });\n };\n\n provider.tools = anthropicTools;\n\n return provider;\n}\n\n/**\n * Default Anthropic provider instance.\n */\nexport const anthropic = createAnthropic();\n","// Version string of this package injected at build time.\ndeclare const __PACKAGE_VERSION__: string | undefined;\nexport const VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n","import {\n APICallError,\n JSONObject,\n LanguageModelV3,\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3FunctionTool,\n LanguageModelV3GenerateResult,\n LanguageModelV3Prompt,\n LanguageModelV3Source,\n LanguageModelV3StreamPart,\n LanguageModelV3StreamResult,\n LanguageModelV3ToolCall,\n SharedV3ProviderMetadata,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createEventSourceResponseHandler,\n createJsonResponseHandler,\n createToolNameMapping,\n FetchFunction,\n generateId,\n InferSchema,\n parseProviderOptions,\n ParseResult,\n postJsonToApi,\n Resolvable,\n resolve,\n} from '@ai-sdk/provider-utils';\nimport { anthropicFailedResponseHandler } from './anthropic-error';\nimport { AnthropicMessageMetadata } from './anthropic-message-metadata';\nimport {\n AnthropicContainer,\n anthropicMessagesChunkSchema,\n anthropicMessagesResponseSchema,\n AnthropicReasoningMetadata,\n AnthropicResponseContextManagement,\n AnthropicTool,\n Citation,\n} from './anthropic-messages-api';\nimport {\n AnthropicMessagesModelId,\n anthropicLanguageModelOptions,\n} from './anthropic-messages-options';\nimport { prepareTools } from './anthropic-prepare-tools';\nimport {\n AnthropicMessagesUsage,\n convertAnthropicMessagesUsage,\n} from './convert-anthropic-messages-usage';\nimport { convertToAnthropicMessagesPrompt } from './convert-to-anthropic-messages-prompt';\nimport { CacheControlValidator } from './get-cache-control';\nimport { mapAnthropicStopReason } from './map-anthropic-stop-reason';\n\nfunction createCitationSource(\n citation: Citation,\n citationDocuments: Array<{\n title: string;\n filename?: string;\n mediaType: string;\n }>,\n generateId: () => string,\n): LanguageModelV3Source | undefined {\n if (citation.type === 'web_search_result_location') {\n return {\n type: 'source' as const,\n sourceType: 'url' as const,\n id: generateId(),\n url: citation.url,\n title: citation.title,\n providerMetadata: {\n anthropic: {\n citedText: citation.cited_text,\n encryptedIndex: citation.encrypted_index,\n },\n } satisfies SharedV3ProviderMetadata,\n };\n }\n\n if (citation.type !== 'page_location' && citation.type !== 'char_location') {\n return;\n }\n\n const documentInfo = citationDocuments[citation.document_index];\n\n if (!documentInfo) {\n return;\n }\n\n return {\n type: 'source' as const,\n sourceType: 'document' as const,\n id: generateId(),\n mediaType: documentInfo.mediaType,\n title: citation.document_title ?? documentInfo.title,\n filename: documentInfo.filename,\n providerMetadata: {\n anthropic:\n citation.type === 'page_location'\n ? {\n citedText: citation.cited_text,\n startPageNumber: citation.start_page_number,\n endPageNumber: citation.end_page_number,\n }\n : {\n citedText: citation.cited_text,\n startCharIndex: citation.start_char_index,\n endCharIndex: citation.end_char_index,\n },\n } satisfies SharedV3ProviderMetadata,\n };\n}\n\ntype AnthropicMessagesConfig = {\n provider: string;\n baseURL: string;\n headers: Resolvable<Record<string, string | undefined>>;\n fetch?: FetchFunction;\n buildRequestUrl?: (baseURL: string, isStreaming: boolean) => string;\n transformRequestBody?: (\n args: Record<string, any>,\n betas: Set<string>,\n ) => Record<string, any>;\n supportedUrls?: () => LanguageModelV3['supportedUrls'];\n generateId?: () => string;\n\n /**\n * When false, the model will use JSON tool fallback for structured outputs.\n */\n supportsNativeStructuredOutput?: boolean;\n};\n\nexport class AnthropicMessagesLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = 'v3';\n\n readonly modelId: AnthropicMessagesModelId;\n\n private readonly config: AnthropicMessagesConfig;\n private readonly generateId: () => string;\n\n constructor(\n modelId: AnthropicMessagesModelId,\n config: AnthropicMessagesConfig,\n ) {\n this.modelId = modelId;\n this.config = config;\n this.generateId = config.generateId ?? generateId;\n }\n\n supportsUrl(url: URL): boolean {\n return url.protocol === 'https:';\n }\n\n get provider(): string {\n return this.config.provider;\n }\n\n /**\n * Extracts the dynamic provider name from the config.provider string.\n * e.g., 'my-custom-anthropic.messages' -> 'my-custom-anthropic'\n */\n private get providerOptionsName(): string {\n const provider = this.config.provider;\n const dotIndex = provider.indexOf('.');\n return dotIndex === -1 ? provider : provider.substring(0, dotIndex);\n }\n\n get supportedUrls() {\n return this.config.supportedUrls?.() ?? {};\n }\n\n private async getArgs({\n userSuppliedBetas,\n prompt,\n maxOutputTokens,\n temperature,\n topP,\n topK,\n frequencyPenalty,\n presencePenalty,\n stopSequences,\n responseFormat,\n seed,\n tools,\n toolChoice,\n providerOptions,\n stream,\n }: LanguageModelV3CallOptions & {\n stream: boolean;\n userSuppliedBetas: Set<string>;\n }) {\n const warnings: SharedV3Warning[] = [];\n\n if (frequencyPenalty != null) {\n warnings.push({ type: 'unsupported', feature: 'frequencyPenalty' });\n }\n\n if (presencePenalty != null) {\n warnings.push({ type: 'unsupported', feature: 'presencePenalty' });\n }\n\n if (seed != null) {\n warnings.push({ type: 'unsupported', feature: 'seed' });\n }\n\n if (temperature != null && temperature > 1) {\n warnings.push({\n type: 'unsupported',\n feature: 'temperature',\n details: `${temperature} exceeds anthropic maximum of 1.0. clamped to 1.0`,\n });\n temperature = 1;\n } else if (temperature != null && temperature < 0) {\n warnings.push({\n type: 'unsupported',\n feature: 'temperature',\n details: `${temperature} is below anthropic minimum of 0. clamped to 0`,\n });\n temperature = 0;\n }\n\n if (responseFormat?.type === 'json') {\n if (responseFormat.schema == null) {\n warnings.push({\n type: 'unsupported',\n feature: 'responseFormat',\n details:\n 'JSON response format requires a schema. ' +\n 'The response format is ignored.',\n });\n }\n }\n\n const providerOptionsName = this.providerOptionsName;\n\n // Parse provider options from both canonical 'anthropic' key and custom key\n const canonicalOptions = await parseProviderOptions({\n provider: 'anthropic',\n providerOptions,\n schema: anthropicLanguageModelOptions,\n });\n\n const customProviderOptions =\n providerOptionsName !== 'anthropic'\n ? await parseProviderOptions({\n provider: providerOptionsName,\n providerOptions,\n schema: anthropicLanguageModelOptions,\n })\n : null;\n\n // Track if custom key was explicitly used\n const usedCustomProviderKey = customProviderOptions != null;\n\n // Merge options\n const anthropicOptions = Object.assign(\n {},\n canonicalOptions ?? {},\n customProviderOptions ?? {},\n );\n\n const {\n maxOutputTokens: maxOutputTokensForModel,\n supportsStructuredOutput: modelSupportsStructuredOutput,\n isKnownModel,\n } = getModelCapabilities(this.modelId);\n\n const supportsStructuredOutput =\n (this.config.supportsNativeStructuredOutput ?? true) &&\n modelSupportsStructuredOutput;\n\n const structureOutputMode =\n anthropicOptions?.structuredOutputMode ?? 'auto';\n const useStructuredOutput =\n structureOutputMode === 'outputFormat' ||\n (structureOutputMode === 'auto' && supportsStructuredOutput);\n\n const jsonResponseTool: LanguageModelV3FunctionTool | undefined =\n responseFormat?.type === 'json' &&\n responseFormat.schema != null &&\n !useStructuredOutput\n ? {\n type: 'function',\n name: 'json',\n description: 'Respond with a JSON object.',\n inputSchema: responseFormat.schema,\n }\n : undefined;\n\n const contextManagement = anthropicOptions?.contextManagement;\n\n // Create a shared cache control validator to track breakpoints across tools and messages\n const cacheControlValidator = new CacheControlValidator();\n\n const toolNameMapping = createToolNameMapping({\n tools,\n providerToolNames: {\n 'anthropic.code_execution_20250522': 'code_execution',\n 'anthropic.code_execution_20250825': 'code_execution',\n 'anthropic.code_execution_20260120': 'code_execution',\n 'anthropic.computer_20241022': 'computer',\n 'anthropic.computer_20250124': 'computer',\n 'anthropic.text_editor_20241022': 'str_replace_editor',\n 'anthropic.text_editor_20250124': 'str_replace_editor',\n 'anthropic.text_editor_20250429': 'str_replace_based_edit_tool',\n 'anthropic.text_editor_20250728': 'str_replace_based_edit_tool',\n 'anthropic.bash_20241022': 'bash',\n 'anthropic.bash_20250124': 'bash',\n 'anthropic.memory_20250818': 'memory',\n 'anthropic.web_search_20250305': 'web_search',\n 'anthropic.web_search_20260209': 'web_search',\n 'anthropic.web_fetch_20250910': 'web_fetch',\n 'anthropic.web_fetch_20260209': 'web_fetch',\n 'anthropic.tool_search_regex_20251119': 'tool_search_tool_regex',\n 'anthropic.tool_search_bm25_20251119': 'tool_search_tool_bm25',\n },\n });\n\n const { prompt: messagesPrompt, betas } =\n await convertToAnthropicMessagesPrompt({\n prompt,\n sendReasoning: anthropicOptions?.sendReasoning ?? true,\n warnings,\n cacheControlValidator,\n toolNameMapping,\n });\n\n const thinkingType = anthropicOptions?.thinking?.type;\n const isThinking =\n thinkingType === 'enabled' || thinkingType === 'adaptive';\n let thinkingBudget =\n thinkingType === 'enabled'\n ? anthropicOptions?.thinking?.budgetTokens\n : undefined;\n\n const maxTokens = maxOutputTokens ?? maxOutputTokensForModel;\n\n const baseArgs = {\n // model id:\n model: this.modelId,\n\n // standardized settings:\n max_tokens: maxTokens,\n temperature,\n top_k: topK,\n top_p: topP,\n stop_sequences: stopSequences,\n\n // provider specific settings:\n ...(isThinking && {\n thinking: {\n type: thinkingType,\n ...(thinkingBudget != null && { budget_tokens: thinkingBudget }),\n },\n }),\n ...((anthropicOptions?.effort ||\n (useStructuredOutput &&\n responseFormat?.type === 'json' &&\n responseFormat.schema != null)) && {\n output_config: {\n ...(anthropicOptions?.effort && {\n effort: anthropicOptions.effort,\n }),\n ...(useStructuredOutput &&\n responseFormat?.type === 'json' &&\n responseFormat.schema != null && {\n format: {\n type: 'json_schema',\n schema: responseFormat.schema,\n },\n }),\n },\n }),\n ...(anthropicOptions?.speed && {\n speed: anthropicOptions.speed,\n }),\n ...(anthropicOptions?.cacheControl && {\n cache_control: anthropicOptions.cacheControl,\n }),\n\n // mcp servers:\n ...(anthropicOptions?.mcpServers &&\n anthropicOptions.mcpServers.length > 0 && {\n mcp_servers: anthropicOptions.mcpServers.map(server => ({\n type: server.type,\n name: server.name,\n url: server.url,\n authorization_token: server.authorizationToken,\n tool_configuration: server.toolConfiguration\n ? {\n allowed_tools: server.toolConfiguration.allowedTools,\n enabled: server.toolConfiguration.enabled,\n }\n : undefined,\n })),\n }),\n\n // container: For programmatic tool calling (just an ID string) or agent skills (object with id and skills)\n ...(anthropicOptions?.container && {\n container:\n anthropicOptions.container.skills &&\n anthropicOptions.container.skills.length > 0\n ? // Object format when skills are provided (agent skills feature)\n ({\n id: anthropicOptions.container.id,\n skills: anthropicOptions.container.skills.map(skill => ({\n type: skill.type,\n skill_id: skill.skillId,\n version: skill.version,\n })),\n } satisfies AnthropicContainer)\n : // String format for container ID only (programmatic tool calling)\n anthropicOptions.container.id,\n }),\n\n // prompt:\n system: messagesPrompt.system,\n messages: messagesPrompt.messages,\n\n ...(contextManagement && {\n context_management: {\n edits: contextManagement.edits\n .map(edit => {\n const strategy = edit.type;\n switch (strategy) {\n case 'clear_tool_uses_20250919':\n return {\n type: edit.type,\n ...(edit.trigger !== undefined && {\n trigger: edit.trigger,\n }),\n ...(edit.keep !== undefined && { keep: edit.keep }),\n ...(edit.clearAtLeast !== undefined && {\n clear_at_least: edit.clearAtLeast,\n }),\n ...(edit.clearToolInputs !== undefined && {\n clear_tool_inputs: edit.clearToolInputs,\n }),\n ...(edit.excludeTools !== undefined && {\n exclude_tools: edit.excludeTools,\n }),\n };\n\n case 'clear_thinking_20251015':\n return {\n type: edit.type,\n ...(edit.keep !== undefined && { keep: edit.keep }),\n };\n\n case 'compact_20260112':\n return {\n type: edit.type,\n ...(edit.trigger !== undefined && {\n trigger: edit.trigger,\n }),\n ...(edit.pauseAfterCompaction !== undefined && {\n pause_after_compaction: edit.pauseAfterCompaction,\n }),\n ...(edit.instructions !== undefined && {\n instructions: edit.instructions,\n }),\n };\n\n default:\n warnings.push({\n type: 'other',\n message: `Unknown context management strategy: ${strategy}`,\n });\n return undefined;\n }\n })\n .filter(edit => edit !== undefined),\n },\n }),\n };\n\n if (isThinking) {\n if (thinkingType === 'enabled' && thinkingBudget == null) {\n warnings.push({\n type: 'compatibility',\n feature: 'extended thinking',\n details:\n 'thinking budget is required when thinking is enabled. using default budget of 1024 tokens.',\n });\n\n baseArgs.thinking = {\n type: 'enabled',\n budget_tokens: 1024,\n };\n\n thinkingBudget = 1024;\n }\n\n if (baseArgs.temperature != null) {\n baseArgs.temperature = undefined;\n warnings.push({\n type: 'unsupported',\n feature: 'temperature',\n details: 'temperature is not supported when thinking is enabled',\n });\n }\n\n if (topK != null) {\n baseArgs.top_k = undefined;\n warnings.push({\n type: 'unsupported',\n feature: 'topK',\n details: 'topK is not supported when thinking is enabled',\n });\n }\n\n if (topP != null) {\n baseArgs.top_p = undefined;\n warnings.push({\n type: 'unsupported',\n feature: 'topP',\n details: 'topP is not supported when thinking is enabled',\n });\n }\n\n // adjust max tokens to account for thinking:\n baseArgs.max_tokens = maxTokens + (thinkingBudget ?? 0);\n } else {\n // Only check temperature/topP mutual exclusivity when thinking is not enabled\n if (topP != null && temperature != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'topP',\n details: `topP is not supported when temperature is set. topP is ignored.`,\n });\n baseArgs.top_p = undefined;\n }\n }\n\n // limit to max output tokens for known models to enable model switching without breaking it:\n if (isKnownModel && baseArgs.max_tokens > maxOutputTokensForModel) {\n // only warn if max output tokens is provided as input:\n if (maxOutputTokens != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'maxOutputTokens',\n details:\n `${baseArgs.max_tokens} (maxOutputTokens + thinkingBudget) is greater than ${this.modelId} ${maxOutputTokensForModel} max output tokens. ` +\n `The max output tokens have been limited to ${maxOutputTokensForModel}.`,\n });\n }\n baseArgs.max_tokens = maxOutputTokensForModel;\n }\n\n if (\n anthropicOptions?.mcpServers &&\n anthropicOptions.mcpServers.length > 0\n ) {\n betas.add('mcp-client-2025-04-04');\n }\n\n if (contextManagement) {\n betas.add('context-management-2025-06-27');\n\n // Add compaction beta if compact edit is present\n if (contextManagement.edits.some(e => e.type === 'compact_20260112')) {\n betas.add('compact-2026-01-12');\n }\n }\n\n if (\n anthropicOptions?.container &&\n anthropicOptions.container.skills &&\n anthropicOptions.container.skills.length > 0\n ) {\n betas.add('code-execution-2025-08-25');\n betas.add('skills-2025-10-02');\n betas.add('files-api-2025-04-14');\n\n if (\n !tools?.some(\n tool =>\n tool.type === 'provider' &&\n (tool.id === 'anthropic.code_execution_20250825' ||\n tool.id === 'anthropic.code_execution_20260120'),\n )\n ) {\n warnings.push({\n type: 'other',\n message: 'code execution tool is required when using skills',\n });\n }\n }\n\n if (anthropicOptions?.effort) {\n betas.add('effort-2025-11-24');\n }\n\n if (anthropicOptions?.speed === 'fast') {\n betas.add('fast-mode-2026-02-01');\n }\n\n // only when streaming: enable fine-grained tool streaming\n if (stream && (anthropicOptions?.toolStreaming ?? true)) {\n betas.add('fine-grained-tool-streaming-2025-05-14');\n }\n\n const {\n tools: anthropicTools,\n toolChoice: anthropicToolChoice,\n toolWarnings,\n betas: toolsBetas,\n } = await prepareTools(\n jsonResponseTool != null\n ? {\n tools: [...(tools ?? []), jsonResponseTool],\n toolChoice: { type: 'required' },\n disableParallelToolUse: true,\n cacheControlValidator,\n supportsStructuredOutput: false,\n }\n : {\n tools: tools ?? [],\n toolChoice,\n disableParallelToolUse: anthropicOptions?.disableParallelToolUse,\n cacheControlValidator,\n supportsStructuredOutput,\n },\n );\n\n // Extract cache control warnings once at the end\n const cacheWarnings = cacheControlValidator.getWarnings();\n\n return {\n args: {\n ...baseArgs,\n tools: anthropicTools,\n tool_choice: anthropicToolChoice,\n stream: stream === true ? true : undefined, // do not send when not streaming\n },\n warnings: [...warnings, ...toolWarnings, ...cacheWarnings],\n betas: new Set([\n ...betas,\n ...toolsBetas,\n ...userSuppliedBetas,\n ...(anthropicOptions?.anthropicBeta ?? []),\n ]),\n usesJsonResponseTool: jsonResponseTool != null,\n toolNameMapping,\n providerOptionsName,\n usedCustomProviderKey,\n };\n }\n\n private async getHeaders({\n betas,\n headers,\n }: {\n betas: Set<string>;\n headers: Record<string, string | undefined> | undefined;\n }) {\n return combineHeaders(\n await resolve(this.config.headers),\n headers,\n betas.size > 0 ? { 'anthropic-beta': Array.from(betas).join(',') } : {},\n );\n }\n\n private async getBetasFromHeaders(\n requestHeaders: Record<string, string | undefined> | undefined,\n ) {\n const configHeaders = await resolve(this.config.headers);\n\n const configBetaHeader = configHeaders['anthropic-beta'] ?? '';\n const requestBetaHeader = requestHeaders?.['anthropic-beta'] ?? '';\n\n return new Set(\n [\n ...configBetaHeader.toLowerCase().split(','),\n ...requestBetaHeader.toLowerCase().split(','),\n ]\n .map(beta => beta.trim())\n .filter(beta => beta !== ''),\n );\n }\n\n private buildRequestUrl(isStreaming: boolean): string {\n return (\n this.config.buildRequestUrl?.(this.config.baseURL, isStreaming) ??\n `${this.config.baseURL}/messages`\n );\n }\n\n private transformRequestBody(\n args: Record<string, any>,\n betas: Set<string>,\n ): Record<string, any> {\n return this.config.transformRequestBody?.(args, betas) ?? args;\n }\n\n private extractCitationDocuments(prompt: LanguageModelV3Prompt): Array<{\n title: string;\n filename?: string;\n mediaType: string;\n }> {\n const isCitationPart = (part: {\n type: string;\n mediaType?: string;\n providerOptions?: { anthropic?: { citations?: { enabled?: boolean } } };\n }) => {\n if (part.type !== 'file') {\n return false;\n }\n\n if (\n part.mediaType !== 'application/pdf' &&\n part.mediaType !== 'text/plain'\n ) {\n return false;\n }\n\n const anthropic = part.providerOptions?.anthropic;\n const citationsConfig = anthropic?.citations as\n | { enabled?: boolean }\n | undefined;\n return citationsConfig?.enabled ?? false;\n };\n\n return prompt\n .filter(message => message.role === 'user')\n .flatMap(message => message.content)\n .filter(isCitationPart)\n .map(part => {\n // TypeScript knows this is a file part due to our filter\n const filePart = part as Extract<typeof part, { type: 'file' }>;\n return {\n title: filePart.filename ?? 'Untitled Document',\n filename: filePart.filename,\n mediaType: filePart.mediaType,\n };\n });\n }\n\n async doGenerate(\n options: LanguageModelV3CallOptions,\n ): Promise<LanguageModelV3GenerateResult> {\n const {\n args,\n warnings,\n betas,\n usesJsonResponseTool,\n toolNameMapping,\n providerOptionsName,\n usedCustomProviderKey,\n } = await this.getArgs({\n ...options,\n stream: false,\n userSuppliedBetas: await this.getBetasFromHeaders(options.headers),\n });\n\n // Extract citation documents for response processing\n const citationDocuments = [\n ...this.extractCitationDocuments(options.prompt),\n ];\n\n const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(\n args.tools,\n );\n\n const {\n responseHeaders,\n value: response,\n rawValue: rawResponse,\n } = await postJsonToApi({\n url: this.buildRequestUrl(false),\n headers: await this.getHeaders({ betas, headers: options.headers }),\n body: this.transformRequestBody(args, betas),\n failedResponseHandler: anthropicFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n anthropicMessagesResponseSchema,\n ),\n abortSignal: options.abortSignal,\n fetch: this.config.fetch,\n });\n\n const content: Array<LanguageModelV3Content> = [];\n const mcpToolCalls: Record<string, LanguageModelV3ToolCall> = {};\n const serverToolCalls: Record<string, string> = {}; // tool_use_id -> provider tool name\n let isJsonResponseFromTool = false;\n\n // map response content to content array\n for (const part of response.content) {\n switch (part.type) {\n case 'text': {\n if (!usesJsonResponseTool) {\n content.push({ type: 'text', text: part.text });\n\n // Process citations if present\n if (part.citations) {\n for (const citation of part.citations) {\n const source = createCitationSource(\n citation,\n citationDocuments,\n this.generateId,\n );\n\n if (source) {\n content.push(source);\n }\n }\n }\n }\n break;\n }\n case 'thinking': {\n content.push({\n type: 'reasoning',\n text: part.thinking,\n providerMetadata: {\n anthropic: {\n signature: part.signature,\n } satisfies AnthropicReasoningMetadata,\n },\n });\n break;\n }\n case 'redacted_thinking': {\n content.push({\n type: 'reasoning',\n text: '',\n providerMetadata: {\n anthropic: {\n redactedData: part.data,\n } satisfies AnthropicReasoningMetadata,\n },\n });\n break;\n }\n case 'compaction': {\n content.push({\n type: 'text',\n text: part.content,\n providerMetadata: {\n anthropic: {\n type: 'compaction',\n },\n },\n });\n break;\n }\n case 'tool_use': {\n const isJsonResponseTool =\n usesJsonResponseTool && part.name === 'json';\n\n if (isJsonResponseTool) {\n isJsonResponseFromTool = true;\n\n // when a json response tool is used, the tool call becomes the text:\n content.push({\n type: 'text',\n text: JSON.stringify(part.input),\n });\n } else {\n const caller = part.caller;\n const callerInfo = caller\n ? {\n type: caller.type,\n toolId: 'tool_id' in caller ? caller.tool_id : undefined,\n }\n : undefined;\n\n content.push({\n type: 'tool-call',\n toolCallId: part.id,\n toolName: part.name,\n input: JSON.stringify(part.input),\n ...(callerInfo && {\n providerMetadata: {\n anthropic: {\n caller: callerInfo,\n },\n },\n }),\n });\n }\n\n break;\n }\n case 'server_tool_use': {\n // code execution 20250825 needs mapping:\n if (\n part.name === 'text_editor_code_execution' ||\n part.name === 'bash_code_execution'\n ) {\n content.push({\n type: 'tool-call',\n toolCallId: part.id,\n toolName: toolNameMapping.toCustomToolName('code_execution'),\n input: JSON.stringify({ type: part.name, ...part.input }),\n providerExecuted: true,\n });\n } else if (\n part.name === 'web_search' ||\n part.name === 'code_execution' ||\n part.name === 'web_fetch'\n ) {\n // For code_execution, inject 'programmatic-tool-call' type when input has { code } format\n const inputToSerialize =\n part.name === 'code_execution' &&\n part.input != null &&\n typeof part.input === 'object' &&\n 'code' in part.input &&\n !('type' in part.input)\n ? { type: 'programmatic-tool-call', ...part.input }\n : part.input;\n\n content.push({\n type: 'tool-call',\n toolCallId: part.id,\n toolName: toolNameMapping.toCustomToolName(part.name),\n input: JSON.stringify(inputToSerialize),\n providerExecuted: true,\n // We want this 'code_execution' tool call to be allowed even if the tool is not explicitly provided.\n // Since the validation generally bypasses dynamic tools, we mark this specific tool as dynamic.\n ...(markCodeExecutionDynamic && part.name === 'code_execution'\n ? { dynamic: true }\n : {}),\n });\n } else if (\n part.name === 'tool_search_tool_regex' ||\n part.name === 'tool_search_tool_bm25'\n ) {\n serverToolCalls[part.id] = part.name;\n content.push({\n type: 'tool-call',\n toolCallId: part.id,\n toolName: toolNameMapping.toCustomToolName(part.name),\n input: JSON.stringify(part.input),\n providerExecuted: true,\n });\n }\n\n break;\n }\n case 'mcp_tool_use': {\n mcpToolCalls[part.id] = {\n type: 'tool-call',\n toolCallId: part.id,\n toolName: part.name,\n input: JSON.stringify(part.input),\n providerExecuted: true,\n dynamic: true,\n providerMetadata: {\n anthropic: {\n type: 'mcp-tool-use',\n serverName: part.server_name,\n },\n },\n };\n content.push(mcpToolCalls[part.id]);\n break;\n }\n case 'mcp_tool_result': {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: mcpToolCalls[part.tool_use_id].toolName,\n isError: part.is_error,\n result: part.content,\n dynamic: true,\n providerMetadata: mcpToolCalls[part.tool_use_id].providerMetadata,\n });\n break;\n }\n case 'web_fetch_tool_result': {\n if (part.content.type === 'web_fetch_result') {\n citationDocuments.push({\n title: part.content.content.title ?? part.content.url,\n mediaType: part.content.content.source.media_type,\n });\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('web_fetch'),\n result: {\n type: 'web_fetch_result',\n url: part.content.url,\n retrievedAt: part.content.retrieved_at,\n content: {\n type: part.content.content.type,\n title: part.content.content.title,\n citations: part.content.content.citations,\n source: {\n type: part.content.content.source.type,\n mediaType: part.content.content.source.media_type,\n data: part.content.content.source.data,\n },\n },\n },\n });\n } else if (part.content.type === 'web_fetch_tool_result_error') {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('web_fetch'),\n isError: true,\n result: {\n type: 'web_fetch_tool_result_error',\n errorCode: part.content.error_code,\n },\n });\n }\n break;\n }\n case 'web_search_tool_result': {\n if (Array.isArray(part.content)) {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('web_search'),\n result: part.content.map(result => ({\n url: result.url,\n title: result.title,\n pageAge: result.page_age ?? null,\n encryptedContent: result.encrypted_content,\n type: result.type,\n })),\n });\n\n for (const result of part.content) {\n content.push({\n type: 'source',\n sourceType: 'url',\n id: this.generateId(),\n url: result.url,\n title: result.title,\n providerMetadata: {\n anthropic: {\n pageAge: result.page_age ?? null,\n },\n },\n });\n }\n } else {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('web_search'),\n isError: true,\n result: {\n type: 'web_search_tool_result_error',\n errorCode: part.content.error_code,\n },\n });\n }\n break;\n }\n\n // code execution 20250522:\n case 'code_execution_tool_result': {\n if (part.content.type === 'code_execution_result') {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('code_execution'),\n result: {\n type: part.content.type,\n stdout: part.content.stdout,\n stderr: part.content.stderr,\n return_code: part.content.return_code,\n content: part.content.content ?? [],\n },\n });\n } else if (part.content.type === 'encrypted_code_execution_result') {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('code_execution'),\n result: {\n type: part.content.type,\n encrypted_stdout: part.content.encrypted_stdout,\n stderr: part.content.stderr,\n return_code: part.content.return_code,\n content: part.content.content ?? [],\n },\n });\n } else if (part.content.type === 'code_execution_tool_result_error') {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('code_execution'),\n isError: true,\n result: {\n type: 'code_execution_tool_result_error',\n errorCode: part.content.error_code,\n },\n });\n }\n break;\n }\n\n // code execution 20250825:\n case 'bash_code_execution_tool_result':\n case 'text_editor_code_execution_tool_result': {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName('code_execution'),\n result: part.content,\n });\n break;\n }\n\n // tool search tool results:\n case 'tool_search_tool_result': {\n let providerToolName = serverToolCalls[part.tool_use_id];\n\n if (providerToolName == null) {\n const bm25CustomName = toolNameMapping.toCustomToolName(\n 'tool_search_tool_bm25',\n );\n const regexCustomName = toolNameMapping.toCustomToolName(\n 'tool_search_tool_regex',\n );\n\n if (bm25CustomName !== 'tool_search_tool_bm25') {\n providerToolName = 'tool_search_tool_bm25';\n } else if (regexCustomName !== 'tool_search_tool_regex') {\n providerToolName = 'tool_search_tool_regex';\n } else {\n providerToolName = 'tool_search_tool_regex';\n }\n }\n\n if (part.content.type === 'tool_search_tool_search_result') {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName(providerToolName),\n result: part.content.tool_references.map(ref => ({\n type: ref.type,\n toolName: ref.tool_name,\n })),\n });\n } else {\n content.push({\n type: 'tool-result',\n toolCallId: part.tool_use_id,\n toolName: toolNameMapping.toCustomToolName(providerToolName),\n isError: true,\n result: {\n type: 'tool_search_tool_result_error',\n errorCode: part.content.error_code,\n },\n });\n }\n break;\n }\n }\n }\n\n return {\n content,\n finishReason: {\n unified: mapAnthropicStopReason({\n finishReason: response.stop_reason,\n isJsonResponseFromTool,\n }),\n raw: response.stop_reason ?? undefined,\n },\n usage: convertAnthropicMessagesUsage({ usage: response.usage }),\n request: { body: args },\n response: {\n id: response.id ?? undefined,\n modelId: response.model ?? undefined,\n headers: responseHeaders,\n body: rawResponse,\n },\n warnings,\n providerMetadata: (() => {\n const anthropicMetadata = {\n usage: response.usage as JSONObject,\n cacheCreationInputTokens:\n response.usage.cache_creation_input_tokens ?? null,\n stopSequence: response.stop_sequence ?? null,\n\n iterations: response.usage.iterations\n ? response.usage.iterations.map(iter => ({\n type: iter.type,\n inputTokens: iter.input_tokens,\n outputTokens: iter.output_tokens,\n }))\n : null,\n container: response.container\n ? {\n expiresAt: response.container.expires_at,\n id: response.container.id,\n skills:\n response.container.skills?.map(skill => ({\n type: skill.type,\n skillId: skill.skill_id,\n version: skill.version,\n })) ?? null,\n }\n : null,\n contextManagement:\n mapAnthropicResponseContextManagement(\n response.context_management,\n ) ?? null,\n } satisfies AnthropicMessageMetadata;\n\n const providerMetadata: SharedV3ProviderMetadata = {\n anthropic: anthropicMetadata,\n };\n\n if (usedCustomProviderKey && providerOptionsName !== 'anthropic') {\n providerMetadata[providerOptionsName] = anthropicMetadata;\n }\n\n return providerMetadata;\n })(),\n };\n }\n\n async doStream(\n options: LanguageModelV3CallOptions,\n ): Promise<LanguageModelV3StreamResult> {\n const {\n args: body,\n warnings,\n betas,\n usesJsonResponseTool,\n toolNameMapping,\n providerOptionsName,\n usedCustomProviderKey,\n } = await this.getArgs({\n ...options,\n stream: true,\n userSuppliedBetas: await this.getBetasFromHeaders(options.headers),\n });\n\n // Extract citation documents for response processing\n const citationDocuments = [\n ...this.extractCitationDocuments(options.prompt),\n ];\n\n const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(\n body.tools,\n );\n\n const url = this.buildRequestUrl(true);\n const { responseHeaders, value: response } = await postJsonToApi({\n url,\n headers: await this.getHeaders({ betas, headers: options.headers }),\n body: this.transformRequestBody(body, betas),\n failedResponseHandler: anthropicFailedResponseHandler,\n successfulResponseHandler: createEventSourceResponseHandler(\n anthropicMessagesChunkSchema,\n ),\n abortSignal: options.abortSignal,\n fetch: this.config.fetch,\n });\n\n let finishReason: LanguageModelV3FinishReason = {\n unified: 'other',\n raw: undefined,\n };\n const usage: AnthropicMessagesUsage = {\n input_tokens: 0,\n output_tokens: 0,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n iterations: null,\n };\n\n const contentBlocks: Record<\n number,\n | {\n type: 'tool-call';\n toolCallId: string;\n toolName: string;\n input: string;\n providerExecuted?: boolean;\n firstDelta: boolean;\n providerToolName?: string;\n caller?: {\n type:\n | 'code_execution_20250825'\n | 'code_execution_20260120'\n | 'direct';\n toolId?: string;\n };\n }\n | { type: 'text' | 'reasoning' }\n > = {};\n const mcpToolCalls: Record<string, LanguageModelV3ToolCall> = {};\n const serverToolCalls: Record<string, string> = {}; // tool_use_id -> provider tool name\n\n let contextManagement:\n | AnthropicMessageMetadata['contextManagement']\n | null = null;\n let rawUsage: JSONObject | undefined = undefined;\n let cacheCreationInputTokens: number | null = null;\n let stopSequence: string | null = null;\n let container: AnthropicMessageMetadata['container'] | null = null;\n let isJsonResponseFromTool = false;\n\n let blockType:\n | 'text'\n | 'thinking'\n | 'tool_use'\n | 'redacted_thinking'\n | 'server_tool_use'\n | 'web_fetch_tool_result'\n | 'web_search_tool_result'\n | 'code_execution_tool_result'\n | 'text_editor_code_execution_tool_result'\n | 'bash_code_execution_tool_result'\n | 'tool_search_tool_result'\n | 'mcp_tool_use'\n | 'mcp_tool_result'\n | 'compaction'\n | undefined = undefined;\n\n const generateId = this.generateId;\n\n const transformedStream = response.pipeThrough(\n new TransformStream<\n ParseResult<InferSchema<typeof anthropicMessagesChunkSchema>>,\n LanguageModelV3StreamPart\n >({\n start(controller) {\n controller.enqueue({ type: 'stream-start', warn