UNPKG

@ai-sdk/amazon-bedrock

Version:

The **[Amazon Bedrock provider](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the Amazon Bedrock [converse API](https://docs.aws.amazon.com/bedrock/latest/APIR

1 lines 118 kB
{"version":3,"sources":["../src/bedrock-provider.ts","../src/bedrock-chat-language-model.ts","../src/bedrock-api-types.ts","../src/bedrock-chat-options.ts","../src/bedrock-error.ts","../src/bedrock-event-stream-response-handler.ts","../src/bedrock-prepare-tools.ts","../src/convert-to-bedrock-chat-messages.ts","../src/map-bedrock-finish-reason.ts","../src/bedrock-embedding-model.ts","../src/bedrock-embedding-options.ts","../src/bedrock-image-model.ts","../src/bedrock-image-settings.ts","../src/headers-utils.ts","../src/bedrock-sigv4-fetch.ts"],"sourcesContent":["import {\n EmbeddingModelV2,\n ImageModelV2,\n LanguageModelV2,\n ProviderV2,\n} from '@ai-sdk/provider';\nimport {\n FetchFunction,\n generateId,\n loadOptionalSetting,\n loadSetting,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { anthropicTools } from '@ai-sdk/anthropic/internal';\nimport { BedrockChatLanguageModel } from './bedrock-chat-language-model';\nimport { BedrockChatModelId } from './bedrock-chat-options';\nimport { BedrockEmbeddingModel } from './bedrock-embedding-model';\nimport { BedrockEmbeddingModelId } from './bedrock-embedding-options';\nimport { BedrockImageModel } from './bedrock-image-model';\nimport { BedrockImageModelId } from './bedrock-image-settings';\nimport {\n BedrockCredentials,\n createSigV4FetchFunction,\n createApiKeyFetchFunction,\n} from './bedrock-sigv4-fetch';\n\nexport interface AmazonBedrockProviderSettings {\n /**\nThe AWS region to use for the Bedrock provider. Defaults to the value of the\n`AWS_REGION` environment variable.\n */\n region?: string;\n\n /**\nAPI key for authenticating requests using Bearer token authentication.\nWhen provided, this will be used instead of AWS SigV4 authentication.\nDefaults to the value of the `AWS_BEARER_TOKEN_BEDROCK` environment variable.\n\n@example\n```typescript\n// Using API key directly\nconst bedrock = createAmazonBedrock({\n apiKey: 'your-api-key-here',\n region: 'us-east-1'\n});\n\n// Using environment variable AWS_BEARER_TOKEN_BEDROCK\nconst bedrock = createAmazonBedrock({\n region: 'us-east-1'\n});\n```\n\nNote: When `apiKey` is provided, it takes precedence over AWS SigV4 authentication.\nIf neither `apiKey` nor `AWS_BEARER_TOKEN_BEDROCK` environment variable is set,\nthe provider will fall back to AWS SigV4 authentication using AWS credentials.\n */\n apiKey?: string;\n\n /**\nThe AWS access key ID to use for the Bedrock provider. Defaults to the value of the\n`AWS_ACCESS_KEY_ID` environment variable.\n */\n accessKeyId?: string;\n\n /**\nThe AWS secret access key to use for the Bedrock provider. Defaults to the value of the\n`AWS_SECRET_ACCESS_KEY` environment variable.\n */\n secretAccessKey?: string;\n\n /**\nThe AWS session token to use for the Bedrock provider. Defaults to the value of the\n`AWS_SESSION_TOKEN` environment variable.\n */\n sessionToken?: string;\n\n /**\nBase URL for the Bedrock API calls.\n */\n baseURL?: string;\n\n /**\nCustom headers to include in the requests.\n */\n headers?: Record<string, string>;\n\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n\n /**\nThe AWS credential provider to use for the Bedrock provider to get dynamic\ncredentials similar to the AWS SDK. Setting a provider here will cause its\ncredential values to be used instead of the `accessKeyId`, `secretAccessKey`,\nand `sessionToken` settings.\n */\n credentialProvider?: () => PromiseLike<Omit<BedrockCredentials, 'region'>>;\n\n // for testing\n generateId?: () => string;\n}\n\nexport interface AmazonBedrockProvider extends ProviderV2 {\n (modelId: BedrockChatModelId): LanguageModelV2;\n\n languageModel(modelId: BedrockChatModelId): LanguageModelV2;\n\n embedding(modelId: BedrockEmbeddingModelId): EmbeddingModelV2<string>;\n\n /**\nCreates a model for image generation.\n */\n image(modelId: BedrockImageModelId): ImageModelV2;\n\n /**\nCreates a model for image generation.\n */\n imageModel(modelId: BedrockImageModelId): ImageModelV2;\n\n /**\nAnthropic-specific tools that can be used with Anthropic models on Bedrock.\n */\n tools: typeof anthropicTools;\n}\n\n/**\nCreate an Amazon Bedrock provider instance.\n */\nexport function createAmazonBedrock(\n options: AmazonBedrockProviderSettings = {},\n): AmazonBedrockProvider {\n // Check for API key authentication first\n const rawApiKey = loadOptionalSetting({\n settingValue: options.apiKey,\n environmentVariableName: 'AWS_BEARER_TOKEN_BEDROCK',\n });\n\n // FIX 1: Validate API key to ensure proper fallback to SigV4\n // Only use API key if it's a non-empty, non-whitespace string\n const apiKey =\n rawApiKey && rawApiKey.trim().length > 0 ? rawApiKey.trim() : undefined;\n\n // Use API key authentication if available, otherwise fall back to SigV4\n const fetchFunction = apiKey\n ? createApiKeyFetchFunction(apiKey, options.fetch)\n : createSigV4FetchFunction(async () => {\n const region = loadSetting({\n settingValue: options.region,\n settingName: 'region',\n environmentVariableName: 'AWS_REGION',\n description: 'AWS region',\n });\n\n // If a credential provider is provided, use it to get the credentials.\n if (options.credentialProvider) {\n try {\n return {\n ...(await options.credentialProvider()),\n region,\n };\n } catch (error) {\n // Error handling for credential provider failures\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new Error(\n `AWS credential provider failed: ${errorMessage}. ` +\n 'Please ensure your credential provider returns valid AWS credentials ' +\n 'with accessKeyId and secretAccessKey properties.',\n );\n }\n }\n\n // Enhanced error handling for SigV4 credential loading\n try {\n return {\n region,\n accessKeyId: loadSetting({\n settingValue: options.accessKeyId,\n settingName: 'accessKeyId',\n environmentVariableName: 'AWS_ACCESS_KEY_ID',\n description: 'AWS access key ID',\n }),\n secretAccessKey: loadSetting({\n settingValue: options.secretAccessKey,\n settingName: 'secretAccessKey',\n environmentVariableName: 'AWS_SECRET_ACCESS_KEY',\n description: 'AWS secret access key',\n }),\n sessionToken: loadOptionalSetting({\n settingValue: options.sessionToken,\n environmentVariableName: 'AWS_SESSION_TOKEN',\n }),\n };\n } catch (error) {\n // Provide helpful error message for missing AWS credentials\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n if (\n errorMessage.includes('AWS_ACCESS_KEY_ID') ||\n errorMessage.includes('accessKeyId')\n ) {\n throw new Error(\n 'AWS SigV4 authentication requires AWS credentials. Please provide either:\\n' +\n '1. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables\\n' +\n '2. Provide accessKeyId and secretAccessKey in options\\n' +\n '3. Use a credentialProvider function\\n' +\n '4. Use API key authentication with AWS_BEARER_TOKEN_BEDROCK or apiKey option\\n' +\n `Original error: ${errorMessage}`,\n );\n }\n if (\n errorMessage.includes('AWS_SECRET_ACCESS_KEY') ||\n errorMessage.includes('secretAccessKey')\n ) {\n throw new Error(\n 'AWS SigV4 authentication requires both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. ' +\n 'Please ensure both credentials are provided.\\n' +\n `Original error: ${errorMessage}`,\n );\n }\n // Re-throw other errors as-is\n throw error;\n }\n }, options.fetch);\n\n const getBaseUrl = (): string =>\n withoutTrailingSlash(\n options.baseURL ??\n `https://bedrock-runtime.${loadSetting({\n settingValue: options.region,\n settingName: 'region',\n environmentVariableName: 'AWS_REGION',\n description: 'AWS region',\n })}.amazonaws.com`,\n ) ?? `https://bedrock-runtime.us-east-1.amazonaws.com`;\n\n const createChatModel = (modelId: BedrockChatModelId) =>\n new BedrockChatLanguageModel(modelId, {\n baseUrl: getBaseUrl,\n headers: options.headers ?? {},\n fetch: fetchFunction,\n generateId,\n });\n\n const provider = function (modelId: BedrockChatModelId) {\n if (new.target) {\n throw new Error(\n 'The Amazon Bedrock model function cannot be called with the new keyword.',\n );\n }\n\n return createChatModel(modelId);\n };\n\n const createEmbeddingModel = (modelId: BedrockEmbeddingModelId) =>\n new BedrockEmbeddingModel(modelId, {\n baseUrl: getBaseUrl,\n headers: options.headers ?? {},\n fetch: fetchFunction,\n });\n\n const createImageModel = (modelId: BedrockImageModelId) =>\n new BedrockImageModel(modelId, {\n baseUrl: getBaseUrl,\n headers: options.headers ?? {},\n fetch: fetchFunction,\n });\n\n provider.languageModel = createChatModel;\n provider.embedding = createEmbeddingModel;\n provider.textEmbedding = createEmbeddingModel;\n provider.textEmbeddingModel = createEmbeddingModel;\n provider.image = createImageModel;\n provider.imageModel = createImageModel;\n provider.tools = anthropicTools;\n\n return provider;\n}\n\n/**\nDefault Bedrock provider instance.\n */\nexport const bedrock = createAmazonBedrock();\n","import {\n JSONObject,\n LanguageModelV2,\n LanguageModelV2CallWarning,\n LanguageModelV2Content,\n LanguageModelV2FinishReason,\n LanguageModelV2Reasoning,\n LanguageModelV2StreamPart,\n LanguageModelV2Usage,\n SharedV2ProviderMetadata,\n LanguageModelV2FunctionTool,\n} from '@ai-sdk/provider';\nimport {\n FetchFunction,\n ParseResult,\n Resolvable,\n combineHeaders,\n createJsonErrorResponseHandler,\n createJsonResponseHandler,\n parseProviderOptions,\n postJsonToApi,\n resolve,\n} from '@ai-sdk/provider-utils';\nimport { z } from 'zod/v4';\nimport {\n BEDROCK_STOP_REASONS,\n BedrockConverseInput,\n BedrockStopReason,\n} from './bedrock-api-types';\nimport {\n BedrockChatModelId,\n bedrockProviderOptions,\n} from './bedrock-chat-options';\nimport { BedrockErrorSchema } from './bedrock-error';\nimport { createBedrockEventStreamResponseHandler } from './bedrock-event-stream-response-handler';\nimport { prepareTools } from './bedrock-prepare-tools';\nimport { convertToBedrockChatMessages } from './convert-to-bedrock-chat-messages';\nimport { mapBedrockFinishReason } from './map-bedrock-finish-reason';\n\ntype BedrockChatConfig = {\n baseUrl: () => string;\n headers: Resolvable<Record<string, string | undefined>>;\n fetch?: FetchFunction;\n generateId: () => string;\n};\n\nexport class BedrockChatLanguageModel implements LanguageModelV2 {\n readonly specificationVersion = 'v2';\n readonly provider = 'amazon-bedrock';\n\n constructor(\n readonly modelId: BedrockChatModelId,\n private readonly config: BedrockChatConfig,\n ) {}\n\n private async getArgs({\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 }: Parameters<LanguageModelV2['doGenerate']>[0]): Promise<{\n command: BedrockConverseInput;\n warnings: LanguageModelV2CallWarning[];\n usesJsonResponseTool: boolean;\n betas: Set<string>;\n }> {\n // Parse provider options\n const bedrockOptions =\n (await parseProviderOptions({\n provider: 'bedrock',\n providerOptions,\n schema: bedrockProviderOptions,\n })) ?? {};\n\n const warnings: LanguageModelV2CallWarning[] = [];\n\n if (frequencyPenalty != null) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'frequencyPenalty',\n });\n }\n\n if (presencePenalty != null) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'presencePenalty',\n });\n }\n\n if (seed != null) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'seed',\n });\n }\n\n if (\n responseFormat != null &&\n responseFormat.type !== 'text' &&\n responseFormat.type !== 'json'\n ) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'responseFormat',\n details: 'Only text and json response formats are supported.',\n });\n }\n\n if (tools != null && responseFormat?.type === 'json') {\n if (tools.length > 0) {\n warnings.push({\n type: 'other',\n message:\n 'JSON response format does not support tools. ' +\n 'The provided tools are ignored.',\n });\n }\n }\n\n const jsonResponseTool: LanguageModelV2FunctionTool | undefined =\n responseFormat?.type === 'json' && responseFormat.schema != null\n ? {\n type: 'function',\n name: 'json',\n description: 'Respond with a JSON object.',\n inputSchema: responseFormat.schema,\n }\n : undefined;\n\n const { toolConfig, additionalTools, toolWarnings, betas } = prepareTools({\n tools: jsonResponseTool ? [jsonResponseTool, ...(tools ?? [])] : tools,\n toolChoice:\n jsonResponseTool != null\n ? { type: 'tool', toolName: jsonResponseTool.name }\n : toolChoice,\n modelId: this.modelId,\n });\n\n warnings.push(...toolWarnings);\n\n if (additionalTools) {\n bedrockOptions.additionalModelRequestFields = {\n ...bedrockOptions.additionalModelRequestFields,\n ...additionalTools,\n };\n }\n\n const isThinking = bedrockOptions.reasoningConfig?.type === 'enabled';\n const thinkingBudget = bedrockOptions.reasoningConfig?.budgetTokens;\n\n const inferenceConfig = {\n ...(maxOutputTokens != null && { maxOutputTokens }),\n ...(temperature != null && { temperature }),\n ...(topP != null && { topP }),\n ...(topK != null && { topK }),\n ...(stopSequences != null && { stopSequences }),\n };\n\n // Adjust maxOutputTokens if thinking is enabled\n if (isThinking && thinkingBudget != null) {\n if (inferenceConfig.maxOutputTokens != null) {\n inferenceConfig.maxOutputTokens += thinkingBudget;\n } else {\n inferenceConfig.maxOutputTokens = thinkingBudget + 4096; // Default + thinking budget maxOutputTokens = 4096, TODO update default in v5\n }\n // Add them to additional model request fields\n // Add thinking config to additionalModelRequestFields\n bedrockOptions.additionalModelRequestFields = {\n ...bedrockOptions.additionalModelRequestFields,\n thinking: {\n type: bedrockOptions.reasoningConfig?.type,\n budget_tokens: thinkingBudget,\n },\n };\n }\n\n // Remove temperature if thinking is enabled\n if (isThinking && inferenceConfig.temperature != null) {\n delete inferenceConfig.temperature;\n warnings.push({\n type: 'unsupported-setting',\n setting: 'temperature',\n details: 'temperature is not supported when thinking is enabled',\n });\n }\n\n // Remove topP if thinking is enabled\n if (isThinking && inferenceConfig.topP != null) {\n delete inferenceConfig.topP;\n warnings.push({\n type: 'unsupported-setting',\n setting: 'topP',\n details: 'topP is not supported when thinking is enabled',\n });\n }\n\n if (isThinking && inferenceConfig.topK != null) {\n delete inferenceConfig.topK;\n warnings.push({\n type: 'unsupported-setting',\n setting: 'topK',\n details: 'topK is not supported when thinking is enabled',\n });\n }\n\n // Filter tool content from prompt when no tools are available\n const hasAnyTools = (toolConfig.tools?.length ?? 0) > 0 || additionalTools;\n let filteredPrompt = prompt;\n\n if (!hasAnyTools) {\n const hasToolContent = prompt.some(\n message =>\n 'content' in message &&\n Array.isArray(message.content) &&\n message.content.some(\n part => part.type === 'tool-call' || part.type === 'tool-result',\n ),\n );\n\n if (hasToolContent) {\n filteredPrompt = prompt\n .map(message =>\n message.role === 'system'\n ? message\n : {\n ...message,\n content: message.content.filter(\n part =>\n part.type !== 'tool-call' && part.type !== 'tool-result',\n ),\n },\n )\n .filter(\n message => message.role === 'system' || message.content.length > 0,\n ) as typeof prompt;\n\n warnings.push({\n type: 'unsupported-setting',\n setting: 'toolContent',\n details:\n 'Tool calls and results removed from conversation because Bedrock does not support tool content without active tools.',\n });\n }\n }\n\n const { system, messages } =\n await convertToBedrockChatMessages(filteredPrompt);\n\n // Filter out reasoningConfig from providerOptions.bedrock to prevent sending it to Bedrock API\n const { reasoningConfig: _, ...filteredBedrockOptions } =\n providerOptions?.bedrock || {};\n\n return {\n command: {\n system,\n messages,\n additionalModelRequestFields:\n bedrockOptions.additionalModelRequestFields,\n ...(Object.keys(inferenceConfig).length > 0 && {\n inferenceConfig,\n }),\n ...filteredBedrockOptions,\n ...(toolConfig.tools !== undefined && toolConfig.tools.length > 0\n ? { toolConfig }\n : {}),\n },\n warnings,\n usesJsonResponseTool: jsonResponseTool != null,\n betas,\n };\n }\n\n readonly supportedUrls: Record<string, RegExp[]> = {\n // no supported urls for bedrock\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 betas.size > 0 ? { 'anthropic-beta': Array.from(betas).join(',') } : {},\n headers,\n );\n }\n\n async doGenerate(\n options: Parameters<LanguageModelV2['doGenerate']>[0],\n ): Promise<Awaited<ReturnType<LanguageModelV2['doGenerate']>>> {\n const {\n command: args,\n warnings,\n usesJsonResponseTool,\n betas,\n } = await this.getArgs(options);\n\n const url = `${this.getUrl(this.modelId)}/converse`;\n const { value: response, responseHeaders } = await postJsonToApi({\n url,\n headers: await this.getHeaders({ betas, headers: options.headers }),\n body: args,\n failedResponseHandler: createJsonErrorResponseHandler({\n errorSchema: BedrockErrorSchema,\n errorToMessage: error => `${error.message ?? 'Unknown error'}`,\n }),\n successfulResponseHandler: createJsonResponseHandler(\n BedrockResponseSchema,\n ),\n abortSignal: options.abortSignal,\n fetch: this.config.fetch,\n });\n\n const content: Array<LanguageModelV2Content> = [];\n\n // map response content to content array\n for (const part of response.output.message.content) {\n // text\n if (part.text) {\n // when a json response tool is used, the tool call is returned as text,\n // so we ignore the text content:\n if (!usesJsonResponseTool) {\n content.push({ type: 'text', text: part.text });\n }\n }\n\n // reasoning\n if (part.reasoningContent) {\n if ('reasoningText' in part.reasoningContent) {\n const reasoning: LanguageModelV2Reasoning = {\n type: 'reasoning',\n text: part.reasoningContent.reasoningText.text,\n };\n\n if (part.reasoningContent.reasoningText.signature) {\n reasoning.providerMetadata = {\n bedrock: {\n signature: part.reasoningContent.reasoningText.signature,\n } satisfies BedrockReasoningMetadata,\n };\n }\n\n content.push(reasoning);\n } else if ('redactedReasoning' in part.reasoningContent) {\n content.push({\n type: 'reasoning',\n text: '',\n providerMetadata: {\n bedrock: {\n redactedData:\n part.reasoningContent.redactedReasoning.data ?? '',\n } satisfies BedrockReasoningMetadata,\n },\n });\n }\n }\n\n // tool calls\n if (part.toolUse) {\n content.push(\n // when a json response tool is used, the tool call becomes the text:\n usesJsonResponseTool\n ? {\n type: 'text',\n text: JSON.stringify(part.toolUse.input),\n }\n : {\n type: 'tool-call' as const,\n toolCallId: part.toolUse?.toolUseId ?? this.config.generateId(),\n toolName:\n part.toolUse?.name ?? `tool-${this.config.generateId()}`,\n input: JSON.stringify(part.toolUse?.input ?? ''),\n },\n );\n }\n }\n\n // provider metadata:\n const providerMetadata =\n response.trace || response.usage || usesJsonResponseTool\n ? {\n bedrock: {\n ...(response.trace && typeof response.trace === 'object'\n ? { trace: response.trace as JSONObject }\n : {}),\n ...(response.usage?.cacheWriteInputTokens != null && {\n usage: {\n cacheWriteInputTokens: response.usage.cacheWriteInputTokens,\n },\n }),\n ...(usesJsonResponseTool && { isJsonResponseFromTool: true }),\n },\n }\n : undefined;\n\n return {\n content,\n finishReason: mapBedrockFinishReason(\n response.stopReason as BedrockStopReason,\n ),\n usage: {\n inputTokens: response.usage?.inputTokens,\n outputTokens: response.usage?.outputTokens,\n totalTokens: response.usage?.inputTokens + response.usage?.outputTokens,\n cachedInputTokens: response.usage?.cacheReadInputTokens ?? undefined,\n },\n response: {\n // TODO add id, timestamp, etc\n headers: responseHeaders,\n },\n warnings,\n ...(providerMetadata && { providerMetadata }),\n };\n }\n\n async doStream(\n options: Parameters<LanguageModelV2['doStream']>[0],\n ): Promise<Awaited<ReturnType<LanguageModelV2['doStream']>>> {\n const {\n command: args,\n warnings,\n usesJsonResponseTool,\n betas,\n } = await this.getArgs(options);\n const url = `${this.getUrl(this.modelId)}/converse-stream`;\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url,\n headers: await this.getHeaders({ betas, headers: options.headers }),\n body: args,\n failedResponseHandler: createJsonErrorResponseHandler({\n errorSchema: BedrockErrorSchema,\n errorToMessage: error => `${error.type}: ${error.message}`,\n }),\n successfulResponseHandler:\n createBedrockEventStreamResponseHandler(BedrockStreamSchema),\n abortSignal: options.abortSignal,\n fetch: this.config.fetch,\n });\n\n let finishReason: LanguageModelV2FinishReason = 'unknown';\n const usage: LanguageModelV2Usage = {\n inputTokens: undefined,\n outputTokens: undefined,\n totalTokens: undefined,\n };\n let providerMetadata: SharedV2ProviderMetadata | undefined = undefined;\n\n const contentBlocks: Record<\n number,\n | {\n type: 'tool-call';\n toolCallId: string;\n toolName: string;\n jsonText: string;\n }\n | { type: 'text' | 'reasoning' }\n > = {};\n\n return {\n stream: response.pipeThrough(\n new TransformStream<\n ParseResult<z.infer<typeof BedrockStreamSchema>>,\n LanguageModelV2StreamPart\n >({\n start(controller) {\n controller.enqueue({ type: 'stream-start', warnings });\n },\n\n transform(chunk, controller) {\n function enqueueError(bedrockError: Record<string, any>) {\n finishReason = 'error';\n controller.enqueue({ type: 'error', error: bedrockError });\n }\n\n // Emit raw chunk if requested (before anything else)\n if (options.includeRawChunks) {\n controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });\n }\n\n // handle failed chunk parsing / validation:\n if (!chunk.success) {\n enqueueError(chunk.error);\n return;\n }\n\n const value = chunk.value;\n\n // handle errors:\n if (value.internalServerException) {\n enqueueError(value.internalServerException);\n return;\n }\n if (value.modelStreamErrorException) {\n enqueueError(value.modelStreamErrorException);\n return;\n }\n if (value.throttlingException) {\n enqueueError(value.throttlingException);\n return;\n }\n if (value.validationException) {\n enqueueError(value.validationException);\n return;\n }\n\n if (value.messageStop) {\n finishReason = mapBedrockFinishReason(\n value.messageStop.stopReason as BedrockStopReason,\n );\n }\n\n if (value.metadata) {\n usage.inputTokens =\n value.metadata.usage?.inputTokens ?? usage.inputTokens;\n usage.outputTokens =\n value.metadata.usage?.outputTokens ?? usage.outputTokens;\n usage.totalTokens =\n (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n usage.cachedInputTokens =\n value.metadata.usage?.cacheReadInputTokens ??\n usage.cachedInputTokens;\n\n const cacheUsage =\n value.metadata.usage?.cacheWriteInputTokens != null\n ? {\n usage: {\n cacheWriteInputTokens:\n value.metadata.usage.cacheWriteInputTokens,\n },\n }\n : undefined;\n\n const trace = value.metadata.trace\n ? {\n trace: value.metadata.trace as JSONObject,\n }\n : undefined;\n\n if (cacheUsage || trace || usesJsonResponseTool) {\n providerMetadata = {\n bedrock: {\n ...cacheUsage,\n ...trace,\n ...(usesJsonResponseTool && {\n isJsonResponseFromTool: true,\n }),\n },\n };\n }\n }\n\n if (\n value.contentBlockStart?.contentBlockIndex != null &&\n !value.contentBlockStart?.start?.toolUse\n ) {\n const blockIndex = value.contentBlockStart.contentBlockIndex;\n contentBlocks[blockIndex] = { type: 'text' };\n controller.enqueue({\n type: 'text-start',\n id: String(blockIndex),\n });\n }\n\n if (\n value.contentBlockDelta?.delta &&\n 'text' in value.contentBlockDelta.delta &&\n value.contentBlockDelta.delta.text\n ) {\n const blockIndex = value.contentBlockDelta.contentBlockIndex || 0;\n\n if (contentBlocks[blockIndex] == null) {\n contentBlocks[blockIndex] = { type: 'text' };\n\n // when a json response tool is used, we don't emit text events\n if (!usesJsonResponseTool) {\n controller.enqueue({\n type: 'text-start',\n id: String(blockIndex),\n });\n }\n }\n\n // when a json response tool is used, we don't emit text events\n if (!usesJsonResponseTool) {\n controller.enqueue({\n type: 'text-delta',\n id: String(blockIndex),\n delta: value.contentBlockDelta.delta.text,\n });\n }\n }\n\n if (value.contentBlockStop?.contentBlockIndex != null) {\n const blockIndex = value.contentBlockStop.contentBlockIndex;\n const contentBlock = contentBlocks[blockIndex];\n\n if (contentBlock != null) {\n if (contentBlock.type === 'reasoning') {\n controller.enqueue({\n type: 'reasoning-end',\n id: String(blockIndex),\n });\n } else if (contentBlock.type === 'text') {\n // when a json response tool is used, we don't emit text events\n if (!usesJsonResponseTool) {\n controller.enqueue({\n type: 'text-end',\n id: String(blockIndex),\n });\n }\n } else if (contentBlock.type === 'tool-call') {\n if (usesJsonResponseTool) {\n // when a json response tool is used, emit the tool input as text\n controller.enqueue({\n type: 'text-start',\n id: String(blockIndex),\n });\n controller.enqueue({\n type: 'text-delta',\n id: String(blockIndex),\n delta: contentBlock.jsonText,\n });\n controller.enqueue({\n type: 'text-end',\n id: String(blockIndex),\n });\n } else {\n controller.enqueue({\n type: 'tool-input-end',\n id: contentBlock.toolCallId,\n });\n controller.enqueue({\n type: 'tool-call',\n toolCallId: contentBlock.toolCallId,\n toolName: contentBlock.toolName,\n input: contentBlock.jsonText,\n });\n }\n }\n\n delete contentBlocks[blockIndex];\n }\n }\n\n if (\n value.contentBlockDelta?.delta &&\n 'reasoningContent' in value.contentBlockDelta.delta &&\n value.contentBlockDelta.delta.reasoningContent\n ) {\n const blockIndex = value.contentBlockDelta.contentBlockIndex || 0;\n const reasoningContent =\n value.contentBlockDelta.delta.reasoningContent;\n\n if ('text' in reasoningContent && reasoningContent.text) {\n if (contentBlocks[blockIndex] == null) {\n contentBlocks[blockIndex] = { type: 'reasoning' };\n controller.enqueue({\n type: 'reasoning-start',\n id: String(blockIndex),\n });\n }\n\n controller.enqueue({\n type: 'reasoning-delta',\n id: String(blockIndex),\n delta: reasoningContent.text,\n });\n } else if (\n 'signature' in reasoningContent &&\n reasoningContent.signature\n ) {\n controller.enqueue({\n type: 'reasoning-delta',\n id: String(blockIndex),\n delta: '',\n providerMetadata: {\n bedrock: {\n signature: reasoningContent.signature,\n } satisfies BedrockReasoningMetadata,\n },\n });\n } else if ('data' in reasoningContent && reasoningContent.data) {\n controller.enqueue({\n type: 'reasoning-delta',\n id: String(blockIndex),\n delta: '',\n providerMetadata: {\n bedrock: {\n redactedData: reasoningContent.data,\n } satisfies BedrockReasoningMetadata,\n },\n });\n }\n }\n\n const contentBlockStart = value.contentBlockStart;\n if (contentBlockStart?.start?.toolUse != null) {\n const toolUse = contentBlockStart.start.toolUse;\n const blockIndex = contentBlockStart.contentBlockIndex!;\n contentBlocks[blockIndex] = {\n type: 'tool-call',\n toolCallId: toolUse.toolUseId!,\n toolName: toolUse.name!,\n jsonText: '',\n };\n\n // when a json response tool is used, we don't emit tool events\n if (!usesJsonResponseTool) {\n controller.enqueue({\n type: 'tool-input-start',\n id: toolUse.toolUseId!,\n toolName: toolUse.name!,\n });\n }\n }\n\n const contentBlockDelta = value.contentBlockDelta;\n if (\n contentBlockDelta?.delta &&\n 'toolUse' in contentBlockDelta.delta &&\n contentBlockDelta.delta.toolUse\n ) {\n const blockIndex = contentBlockDelta.contentBlockIndex!;\n const contentBlock = contentBlocks[blockIndex];\n\n if (contentBlock?.type === 'tool-call') {\n const delta = contentBlockDelta.delta.toolUse.input ?? '';\n\n // when a json response tool is used, we don't emit tool events\n if (!usesJsonResponseTool) {\n controller.enqueue({\n type: 'tool-input-delta',\n id: contentBlock.toolCallId,\n delta: delta,\n });\n }\n\n contentBlock.jsonText += delta;\n }\n }\n },\n flush(controller) {\n controller.enqueue({\n type: 'finish',\n finishReason,\n usage,\n ...(providerMetadata && { providerMetadata }),\n });\n },\n }),\n ),\n // TODO request?\n response: { headers: responseHeaders },\n };\n }\n\n private getUrl(modelId: string) {\n const encodedModelId = encodeURIComponent(modelId);\n return `${this.config.baseUrl()}/model/${encodedModelId}`;\n }\n}\n\nconst BedrockStopReasonSchema = z.union([\n z.enum(BEDROCK_STOP_REASONS),\n z.string(),\n]);\n\nconst BedrockToolUseSchema = z.object({\n toolUseId: z.string(),\n name: z.string(),\n input: z.unknown(),\n});\n\nconst BedrockReasoningTextSchema = z.object({\n signature: z.string().nullish(),\n text: z.string(),\n});\n\nconst BedrockRedactedReasoningSchema = z.object({\n data: z.string(),\n});\n\n// limited version of the schema, focused on what is needed for the implementation\n// this approach limits breakages when the API changes and increases efficiency\nconst BedrockResponseSchema = z.object({\n metrics: z\n .object({\n latencyMs: z.number(),\n })\n .nullish(),\n output: z.object({\n message: z.object({\n content: z.array(\n z.object({\n text: z.string().nullish(),\n toolUse: BedrockToolUseSchema.nullish(),\n reasoningContent: z\n .union([\n z.object({\n reasoningText: BedrockReasoningTextSchema,\n }),\n z.object({\n redactedReasoning: BedrockRedactedReasoningSchema,\n }),\n ])\n .nullish(),\n }),\n ),\n role: z.string(),\n }),\n }),\n stopReason: BedrockStopReasonSchema,\n trace: z.unknown().nullish(),\n usage: z.object({\n inputTokens: z.number(),\n outputTokens: z.number(),\n totalTokens: z.number(),\n cacheReadInputTokens: z.number().nullish(),\n cacheWriteInputTokens: z.number().nullish(),\n }),\n});\n\n// limited version of the schema, focussed on what is needed for the implementation\n// this approach limits breakages when the API changes and increases efficiency\nconst BedrockStreamSchema = z.object({\n contentBlockDelta: z\n .object({\n contentBlockIndex: z.number(),\n delta: z\n .union([\n z.object({ text: z.string() }),\n z.object({ toolUse: z.object({ input: z.string() }) }),\n z.object({\n reasoningContent: z.object({ text: z.string() }),\n }),\n z.object({\n reasoningContent: z.object({\n signature: z.string(),\n }),\n }),\n z.object({\n reasoningContent: z.object({ data: z.string() }),\n }),\n ])\n .nullish(),\n })\n .nullish(),\n contentBlockStart: z\n .object({\n contentBlockIndex: z.number(),\n start: z\n .object({\n toolUse: BedrockToolUseSchema.nullish(),\n })\n .nullish(),\n })\n .nullish(),\n contentBlockStop: z\n .object({\n contentBlockIndex: z.number(),\n })\n .nullish(),\n internalServerException: z.record(z.string(), z.unknown()).nullish(),\n messageStop: z\n .object({\n additionalModelResponseFields: z\n .record(z.string(), z.unknown())\n .nullish(),\n stopReason: BedrockStopReasonSchema,\n })\n .nullish(),\n metadata: z\n .object({\n trace: z.unknown().nullish(),\n usage: z\n .object({\n cacheReadInputTokens: z.number().nullish(),\n cacheWriteInputTokens: z.number().nullish(),\n inputTokens: z.number(),\n outputTokens: z.number(),\n })\n .nullish(),\n })\n .nullish(),\n modelStreamErrorException: z.record(z.string(), z.unknown()).nullish(),\n throttlingException: z.record(z.string(), z.unknown()).nullish(),\n validationException: z.record(z.string(), z.unknown()).nullish(),\n});\n\nexport const bedrockReasoningMetadataSchema = z.object({\n signature: z.string().optional(),\n redactedData: z.string().optional(),\n});\n\nexport type BedrockReasoningMetadata = z.infer<\n typeof bedrockReasoningMetadataSchema\n>;\n","import { JSONObject } from '@ai-sdk/provider';\n\nexport interface BedrockConverseInput {\n system?: BedrockSystemMessages;\n messages: BedrockMessages;\n toolConfig?: BedrockToolConfiguration;\n inferenceConfig?: {\n maxOutputTokens?: number;\n temperature?: number;\n topP?: number;\n topK?: number;\n stopSequences?: string[];\n };\n additionalModelRequestFields?: Record<string, unknown>;\n guardrailConfig?:\n | BedrockGuardrailConfiguration\n | BedrockGuardrailStreamConfiguration\n | undefined;\n}\n\nexport type BedrockSystemMessages = Array<BedrockSystemContentBlock>;\n\nexport type BedrockMessages = Array<\n BedrockAssistantMessage | BedrockUserMessage\n>;\n\nexport interface BedrockAssistantMessage {\n role: 'assistant';\n content: Array<BedrockContentBlock>;\n}\n\nexport interface BedrockUserMessage {\n role: 'user';\n content: Array<BedrockContentBlock>;\n}\n\nexport const BEDROCK_CACHE_POINT = {\n cachePoint: { type: 'default' },\n} as const;\n\nexport type BedrockCachePoint = { cachePoint: { type: 'default' } };\nexport type BedrockSystemContentBlock = { text: string } | BedrockCachePoint;\n\nexport interface BedrockGuardrailConfiguration {\n guardrails?: Array<{\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n }>;\n}\n\nexport type BedrockGuardrailStreamConfiguration = BedrockGuardrailConfiguration;\n\nexport interface BedrockToolInputSchema {\n json: Record<string, unknown>;\n}\n\nexport interface BedrockTool {\n toolSpec: {\n name: string;\n description?: string;\n inputSchema: { json: JSONObject };\n };\n}\n\nexport interface BedrockToolConfiguration {\n tools?: Array<BedrockTool | BedrockCachePoint>;\n toolChoice?:\n | { tool: { name: string } }\n | { auto: {} }\n | { any: {} }\n | undefined;\n}\n\nexport const BEDROCK_STOP_REASONS = [\n 'stop',\n 'stop_sequence',\n 'end_turn',\n 'length',\n 'max_tokens',\n 'content-filter',\n 'content_filtered',\n 'guardrail_intervened',\n 'tool-calls',\n 'tool_use',\n] as const;\n\nexport type BedrockStopReason = (typeof BEDROCK_STOP_REASONS)[number];\n\n/**\n * @see https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ImageBlock.html\n */\nexport const BEDROCK_IMAGE_MIME_TYPES = {\n 'image/jpeg': 'jpeg',\n 'image/png': 'png',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n} as const;\ntype BedrockImageFormats = typeof BEDROCK_IMAGE_MIME_TYPES;\nexport type BedrockImageFormat = BedrockImageFormats[keyof BedrockImageFormats];\nexport type BedrockImageMimeType = keyof BedrockImageFormats;\n\n/**\n * @see https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_DocumentBlock.html\n */\nexport const BEDROCK_DOCUMENT_MIME_TYPES = {\n 'application/pdf': 'pdf',\n 'text/csv': 'csv',\n 'application/msword': 'doc',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':\n 'docx',\n 'application/vnd.ms-excel': 'xls',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',\n 'text/html': 'html',\n 'text/plain': 'txt',\n 'text/markdown': 'md',\n} as const;\ntype BedrockDocumentFormats = typeof BEDROCK_DOCUMENT_MIME_TYPES;\nexport type BedrockDocumentFormat =\n BedrockDocumentFormats[keyof BedrockDocumentFormats];\nexport type BedrockDocumentMimeType = keyof BedrockDocumentFormats;\n\nexport interface BedrockDocumentBlock {\n document: {\n format: BedrockDocumentFormat;\n name: string;\n source: {\n bytes: string;\n };\n };\n}\n\nexport interface BedrockGuardrailConverseContentBlock {\n guardContent: unknown;\n}\n\nexport interface BedrockImageBlock {\n image: {\n format: BedrockImageFormat;\n source: {\n bytes: string;\n };\n };\n}\n\nexport interface BedrockToolResultBlock {\n toolResult: {\n toolUseId: string;\n content: Array<BedrockTextBlock | BedrockImageBlock>;\n };\n}\n\nexport interface BedrockToolUseBlock {\n toolUse: {\n toolUseId: string;\n name: string;\n input: Record<string, unknown>;\n };\n}\n\nexport interface BedrockTextBlock {\n text: string;\n}\n\nexport interface BedrockReasoningContentBlock {\n reasoningContent: {\n reasoningText: {\n text: string;\n signature?: string;\n };\n };\n}\n\nexport interface BedrockRedactedReasoningContentBlock {\n reasoningContent: {\n redactedReasoning: {\n data: string;\n };\n };\n}\n\nexport type BedrockContentBlock =\n | BedrockDocumentBlock\n | BedrockGuardrailConverseContentBlock\n | BedrockImageBlock\n | BedrockTextBlock\n | BedrockToolResultBlock\n | BedrockToolUseBlock\n | BedrockReasoningContentBlock\n | BedrockRedactedReasoningContentBlock\n | BedrockCachePoint;\n","import { z } from 'zod/v4';\n\n// https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html\nexport type BedrockChatModelId =\n | 'amazon.titan-tg1-large'\n | 'amazon.titan-text-express-v1'\n | 'anthropic.claude-v2'\n | 'anthropic.claude-v2:1'\n | 'anthropic.claude-instant-v1'\n | 'anthropic.claude-sonnet-4-20250514-v1:0'\n | 'anthropic.claude-opus-4-20250514-v1:0'\n | 'anthropic.claude-opus-4-1-20250805-v1:0'\n | 'anthropic.claude-3-7-sonnet-20250219-v1:0'\n | 'anthropic.claude-3-5-sonnet-20240620-v1:0'\n | 'anthropic.claude-3-5-sonnet-20241022-v2:0'\n | 'anthropic.claude-3-5-haiku-20241022-v1:0'\n | 'anthropic.claude-3-sonnet-20240229-v1:0'\n | 'anthropic.claude-3-haiku-20240307-v1:0'\n | 'anthropic.claude-3-opus-20240229-v1:0'\n | 'cohere.command-text-v14'\n | 'cohere.command-light-text-v14'\n | 'cohere.command-r-v1:0'\n | 'cohere.command-r-plus-v1:0'\n | 'meta.llama3-70b-instruct-v1:0'\n | 'meta.llama3-8b-instruct-v1:0'\n | 'meta.llama3-1-405b-instruct-v1:0'\n | 'meta.llama3-1-70b-instruct-v1:0'\n | 'meta.llama3-1-8b-instruct-v1:0'\n | 'meta.llama3-2-11b-instruct-v1:0'\n | 'meta.llama3-2-1b-instruct-v1:0'\n | 'meta.llama3-2-3b-instruct-v1:0'\n | 'meta.llama3-2-90b-instruct-v1:0'\n | 'mistral.mistral-7b-instruct-v0:2'\n | 'mistral.mixtral-8x7b-instruct-v0:1'\n | 'mistral.mistral-large-2402-v1:0'\n | 'mistral.mistral-small-2402-v1:0'\n | 'openai.gpt-oss-120b-1:0'\n | 'openai.gpt-oss-20b-1:0'\n | 'amazon.titan-text-express-v1'\n | 'amazon.titan-text-lite-v1'\n | 'us.amazon.nova-premier-v1:0'\n | 'us.amazon.nova-pro-v1:0'\n | 'us.amazon.nova-micro-v1:0'\n | 'us.amazon.nova-lite-v1:0'\n | 'us.anthropic.claude-3-sonnet-20240229-v1:0'\n | 'us.anthropic.claude-3-opus-20240229-v1:0'\n | 'us.anthropic.claude-3-haiku-20240307-v1:0'\n | 'us.anthropic.claude-3-5-sonnet-20240620-v1:0'\n | 'us.anthropic.claude-3-5-haiku-20241022-v1:0'\n | 'us.anthropic.claude-3-5-sonnet-20241022-v2:0'\n | 'us.anthropic.claude-3-7-sonnet-20250219-v1:0'\n | 'us.anthropic.claude-sonnet-4-20250514-v1:0'\n | 'us.anthropic.claude-opus-4-20250514-v1:0'\n | 'us.anthropic.claude-opus-4-1-20250805-v1:0'\n | 'us.meta.llama3-2-11b-instruct-v1:0'\n | 'us.meta.llama3-2-3b-instruct-v1:0'\n | 'us.meta.llama3-2-90b-instruct-v1:0'\n | 'us.meta.llama3-2-1b-instruct-v1:0'\n | 'us.meta.llama3-1-8b-instruct-v1:0'\n | 'us.meta.llama3-1-70b-instruct-v1:0'\n | 'us.meta.llama3-3-70b-instruct-v1:0'\n | 'us.deepseek.r1-v1:0'\n | 'us.mistral.pixtral-large-2502-v1:0'\n | 'us.meta.llama4-scout-17b-instruct-v1:0'\n | 'us.meta.llama4-maverick-17b-instruct-v1:0'\n | (string & {});\n\nexport const bedrockProviderOptions = z.object({\n /**\n * Additional inference parameters that the model supports,\n * beyond the base set of inference parameters that Converse\n * supports in the inferenceConfig field\n */\n additionalModelRequestFields: z.record(z.string(), z.any()).optional(),\n reasoningConfig: z\n .object({\n type: z.union([z.literal('enabled'), z.literal('disabled')]).optional(),\n budgetTokens: z.number().optional(),\n })\n .optional(),\n});\n\nexport type BedrockProviderOptions = z.infer<typeof bedrockProviderOptions>;\n","import { z } from 'zod/v4';\n\nexport const BedrockErrorSchema = z.object({\n message: z.string(),\n type: z.string().nullish(),\n});\n","import { EmptyResponseBodyError } from '@ai-sdk/provider';\nimport {\n ParseResult,\n safeParseJSON,\n extractResponseHeaders,\n ResponseHandler,\n safeValidateTypes,\n} from '@ai-sdk/provider-utils';\nimport { EventStreamCodec } from '@smithy/eventstream-codec';\nimport { toUtf8, fromUtf8 } from '@smithy/util-utf8';\nimport { ZodType } from 'zod/v4';\n\n// https://docs.aws.amazon.com/lexv2/latest/dg/event-stream-encoding.html\nexport const createBedrockEventStreamResponseHandler =\n <T>(\n chunkSchema: ZodType<T, any>,\n ): ResponseHandler<ReadableStream<ParseResult<T>>> =>\n async ({ response }: { response: Response }) => {\n const responseHeaders = extractResponseHeaders(response);\n\n if (response.body == null) {\n throw new EmptyResponseBodyError({});\n }\n\n const codec = new EventStreamCodec(toUtf8, fromUtf8);\n let buffer = new Uint8Array(0);\n const textDecoder = new TextDecoder();\n\n return {\n responseHeaders,\n value: response.body.pipeThrough(\n new TransformStream<Uint8Array, ParseResult<T>>({\n async transform(chunk, controller) {\n // Append new chunk to buffer.\n const newBuffer = new Uint8Array(buffer.length + chunk.length);\n newBuffer.set(buffer);\n newBuffer.set(chunk, buffer.length);\n buffer = newBuffer;\n\n // Try to decode messages from buffer.\n while (buffer.length >= 4) {\n // The first 4 bytes are the total length (big-endian).\n const totalLength = new DataView(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength,\n ).getUint32(0, false);\n\n // If we don't have the full message yet, wait for more chunks.\n if (buffer.length < totalLength) {\n break;\n }\n\n try {\n // Decode exactly the sub-slice for this event.\n const su