diff --git a/.changeset/tasty-readers-move.md b/.changeset/tasty-readers-move.md new file mode 100644 index 0000000000..3d0db43570 --- /dev/null +++ b/.changeset/tasty-readers-move.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Added support for AWS provider profiles using the AWS CLI to make the profile. enabling long lived connections to AWS bedrock diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index a82a71b8ef..448190291d 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -3,6 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { ApiHandler } from "../" import { ApiHandlerOptions, bedrockDefaultModelId, BedrockModelId, bedrockModels, ModelInfo } from "../../shared/api" import { ApiStream } from "../transform/stream" +import { fromIni } from "@aws-sdk/credential-providers" // https://docs.anthropic.com/en/api/claude-on-amazon-bedrock export class AwsBedrockHandler implements ApiHandler { @@ -11,17 +12,31 @@ export class AwsBedrockHandler implements ApiHandler { constructor(options: ApiHandlerOptions) { this.options = options - this.client = new AnthropicBedrock({ - // Authenticate by either providing the keys below or use the default AWS credential providers, such as - // using ~/.aws/credentials or the "AWS_SECRET_ACCESS_KEY" and "AWS_ACCESS_KEY_ID" environment variables. - ...(this.options.awsAccessKey ? { awsAccessKey: this.options.awsAccessKey } : {}), - ...(this.options.awsSecretKey ? { awsSecretKey: this.options.awsSecretKey } : {}), - ...(this.options.awsSessionToken ? { awsSessionToken: this.options.awsSessionToken } : {}), - // awsRegion changes the aws region to which the request is made. By default, we read AWS_REGION, - // and if that's not present, we default to us-east-1. Note that we do not read ~/.aws/config for the region. - awsRegion: this.options.awsRegion, - }) + const clientConfig: any = { + awsRegion: this.options.awsRegion || "us-east-1", + } + + if (this.options.awsUseProfile) { + // Use profile-based credentials if enabled + if (this.options.awsProfile) { + clientConfig.credentials = fromIni({ + profile: this.options.awsProfile, + }) + } else { + // Use default profile if no specific profile is set + clientConfig.credentials = fromIni() + } + } else if (this.options.awsAccessKey && this.options.awsSecretKey) { + // Use direct credentials if provided + clientConfig.awsAccessKey = this.options.awsAccessKey + clientConfig.awsSecretKey = this.options.awsSecretKey + if (this.options.awsSessionToken) { + clientConfig.awsSessionToken = this.options.awsSessionToken + } + } + + this.client = new AnthropicBedrock(clientConfig) } async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 2961d8953a..97ab605228 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -55,6 +55,8 @@ type GlobalStateKey = | "apiModelId" | "awsRegion" | "awsUseCrossRegionInference" + | "awsProfile" + | "awsUseProfile" | "vertexProjectId" | "vertexRegion" | "lastShownAnnouncementId" @@ -431,6 +433,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { awsSessionToken, awsRegion, awsUseCrossRegionInference, + awsProfile, + awsUseProfile, vertexProjectId, vertexRegion, openAiBaseUrl, @@ -465,6 +469,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.storeSecret("awsSessionToken", awsSessionToken) await this.updateGlobalState("awsRegion", awsRegion) await this.updateGlobalState("awsUseCrossRegionInference", awsUseCrossRegionInference) + await this.updateGlobalState("awsProfile", awsProfile) + await this.updateGlobalState("awsUseProfile", awsUseProfile) await this.updateGlobalState("vertexProjectId", vertexProjectId) await this.updateGlobalState("vertexRegion", vertexRegion) await this.updateGlobalState("openAiBaseUrl", openAiBaseUrl) @@ -1364,6 +1370,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { awsSessionToken, awsRegion, awsUseCrossRegionInference, + awsProfile, + awsUseProfile, vertexProjectId, vertexRegion, openAiBaseUrl, @@ -1409,6 +1417,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getSecret("awsSessionToken") as Promise, this.getGlobalState("awsRegion") as Promise, this.getGlobalState("awsUseCrossRegionInference") as Promise, + this.getGlobalState("awsProfile") as Promise, + this.getGlobalState("awsUseProfile") as Promise, this.getGlobalState("vertexProjectId") as Promise, this.getGlobalState("vertexRegion") as Promise, this.getGlobalState("openAiBaseUrl") as Promise, @@ -1471,6 +1481,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { awsSessionToken, awsRegion, awsUseCrossRegionInference, + awsProfile, + awsUseProfile, vertexProjectId, vertexRegion, openAiBaseUrl, diff --git a/src/shared/api.ts b/src/shared/api.ts index ea9d42c25e..748f1a7b9d 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -29,6 +29,8 @@ export interface ApiHandlerOptions { awsSessionToken?: string awsRegion?: string awsUseCrossRegionInference?: boolean + awsUseProfile?: boolean + awsProfile?: string vertexProjectId?: string vertexRegion?: string openAiBaseUrl?: string diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index f51b646d00..d11fb5b0d3 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -445,30 +445,56 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is flexDirection: "column", gap: 5, }}> - - AWS Access Key - - - AWS Secret Key - - - AWS Session Token - + { + const value = (e.target as HTMLInputElement)?.value + const useProfile = value === "profile" + setApiConfiguration({ + ...apiConfiguration, + awsUseProfile: useProfile, + }) + }}> + AWS Credentials + AWS Profile + + + {apiConfiguration?.awsUseProfile ? ( + + AWS Profile Name + + ) : ( + <> + + AWS Access Key + + + AWS Secret Key + + + AWS Session Token + + + )}