Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
cc01dd1
feat: Add custom VPC endpoint support for AWS Bedrock
May 11, 2025
91b5557
fix: Fix TypeScript error in Bedrock.tsx
May 11, 2025
3d3433b
fix: Update VPC endpoint UI to match Cline's implementation
May 11, 2025
019f2c6
Fix AWS Bedrock VPC endpoint UI implementation
May 11, 2025
941dcdf
Fix AWS Bedrock VPC endpoint UI implementation to match Cline exactly
May 11, 2025
5fcef88
Fix AWS Bedrock VPC endpoint UI implementation with proper event hand…
May 12, 2025
cae91f2
Update Bedrock VPC endpoint tests with proper test IDs
May 12, 2025
8e9c4fa
Improve AWS Bedrock VPC endpoint text field alignment
May 12, 2025
24fe42b
Preserve AWS Bedrock VPC endpoint URL when toggling checkbox
May 12, 2025
1b16465
Implement AWS Bedrock Custom VPC Endpoint functionality
May 12, 2025
06e28fa
Merge remote-tracking branch 'origin/main' into bedrock-custom-vpc
May 12, 2025
8f4d7c3
Merge branch 'main' into bedrock-custom-vpc
kcwhite May 23, 2025
c5763b2
Merge remote-tracking branch 'my-fork/bedrock-custom-vpc'
May 23, 2025
c9f7980
fix: update ApiConfiguration to ProviderSettings in Bedrock tests and…
May 23, 2025
eb42c37
fix: update all instances of ApiConfiguration to ProviderSettings in …
May 23, 2025
77328cf
Merge branch 'refs/heads/main' into my-main
May 24, 2025
e00780a
Merge branch 'RooCodeInc:main' into main
kcwhite May 24, 2025
5cae994
Merge branch 'main' into my-main
May 24, 2025
03d5c03
Merge remote-tracking branch 'my-fork/main' into my-main
May 24, 2025
a74b6fd
Fixed broken unit test
May 24, 2025
0b0ea16
Merge branch 'RooCodeInc:main' into main
kcwhite May 24, 2025
8bd3701
Merge remote-tracking branch 'my-fork/main' into my-main
May 24, 2025
617e68e
Add changeset for Bedrock VPC endpoint support
May 25, 2025
57a6fdf
Merge branch 'RooCodeInc:main' into main
kcwhite May 26, 2025
7f90298
informative placeholder
daniel-lxs May 30, 2025
e40db2a
Merge branch 'main' into my-main
May 30, 2025
2720d07
Merge branch 'main' into my-main
May 30, 2025
b1e5152
Merge branch 'RooCodeInc:main' into main
kcwhite May 30, 2025
bd398e4
Merge remote-tracking branch 'my-fork/main' into my-main
May 30, 2025
fae7a6e
Bug fixes
May 30, 2025
42be6dc
Fixed failing tests
May 30, 2025
f32f146
Add example URLs to Bedrock VPC endpoint section and update tests
May 31, 2025
01c7d2f
Fix truncated test assertion in Bedrock.test.tsx that was breaking th…
May 31, 2025
c5d2bf4
Refactor mock components in Bedrock.test.tsx for improved data-testid…
May 31, 2025
1ad74a4
Merge branch 'RooCodeInc:main' into main
kcwhite May 31, 2025
e2f12af
feat(i18n): add VPC endpoint translations for AWS Bedrock settings
daniel-lxs May 31, 2025
afb7663
test: update Bedrock component tests for internationalized strings
daniel-lxs May 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bedrock-vpc-endpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"roo-cline": patch
---

Add AWS Bedrock VPC endpoint support, allowing users to connect to Bedrock through private VPC endpoints. This includes UI configuration options, updated types, and comprehensive test coverage.
4 changes: 4 additions & 0 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ const bedrockSchema = apiModelIdProviderModelSchema.extend({
awsProfile: z.string().optional(),
awsUseProfile: z.boolean().optional(),
awsCustomArn: z.string().optional(),
awsBedrockEndpointEnabled: z.boolean().optional(),
awsBedrockEndpoint: z.string().optional(),
})

const vertexSchema = apiModelIdProviderModelSchema.extend({
Expand Down Expand Up @@ -283,6 +285,8 @@ export const PROVIDER_SETTINGS_KEYS = keysOf<ProviderSettings>()([
"awsProfile",
"awsUseProfile",
"awsCustomArn",
"awsBedrockEndpointEnabled",
"awsBedrockEndpoint",
// Google Vertex
"vertexKeyFile",
"vertexJsonCredentials",
Expand Down
178 changes: 178 additions & 0 deletions src/api/providers/__tests__/bedrock-vpc-endpoint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Mock AWS SDK credential providers
jest.mock("@aws-sdk/credential-providers", () => {
const mockFromIni = jest.fn().mockReturnValue({
accessKeyId: "profile-access-key",
secretAccessKey: "profile-secret-key",
})
return { fromIni: mockFromIni }
})

// Mock BedrockRuntimeClient and ConverseStreamCommand
const mockBedrockRuntimeClient = jest.fn()
const mockSend = jest.fn().mockResolvedValue({
stream: [],
})

jest.mock("@aws-sdk/client-bedrock-runtime", () => ({
BedrockRuntimeClient: mockBedrockRuntimeClient.mockImplementation(() => ({
send: mockSend,
})),
ConverseStreamCommand: jest.fn(),
ConverseCommand: jest.fn(),
}))

import { AwsBedrockHandler } from "../bedrock"

describe("AWS Bedrock VPC Endpoint Functionality", () => {
beforeEach(() => {
// Clear all mocks before each test
jest.clearAllMocks()
})

// Test Scenario 1: Input Validation Test
describe("VPC Endpoint URL Validation", () => {
it("should configure client with endpoint URL when both URL and enabled flag are provided", () => {
// Create handler with endpoint URL and enabled flag
new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsBedrockEndpoint: "https://bedrock-vpc.example.com",
awsBedrockEndpointEnabled: true,
})

// Verify the client was created with the correct endpoint
expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
expect.objectContaining({
region: "us-east-1",
endpoint: "https://bedrock-vpc.example.com",
}),
)
})

it("should not configure client with endpoint URL when URL is provided but enabled flag is false", () => {
// Create handler with endpoint URL but disabled flag
new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsBedrockEndpoint: "https://bedrock-vpc.example.com",
awsBedrockEndpointEnabled: false,
})

// Verify the client was created without the endpoint
expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
expect.objectContaining({
region: "us-east-1",
}),
)

// Verify the endpoint property is not present
const clientConfig = mockBedrockRuntimeClient.mock.calls[0][0]
expect(clientConfig).not.toHaveProperty("endpoint")
})
})

// Test Scenario 2: Edge Case Tests
describe("Edge Cases", () => {
it("should handle empty endpoint URL gracefully", () => {
// Create handler with empty endpoint URL but enabled flag
new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsBedrockEndpoint: "",
awsBedrockEndpointEnabled: true,
})

// Verify the client was created without the endpoint (since it's empty)
expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
expect.objectContaining({
region: "us-east-1",
}),
)

// Verify the endpoint property is not present
const clientConfig = mockBedrockRuntimeClient.mock.calls[0][0]
expect(clientConfig).not.toHaveProperty("endpoint")
})

it("should handle undefined endpoint URL gracefully", () => {
// Create handler with undefined endpoint URL but enabled flag
new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsBedrockEndpoint: undefined,
awsBedrockEndpointEnabled: true,
})

// Verify the client was created without the endpoint
expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
expect.objectContaining({
region: "us-east-1",
}),
)

// Verify the endpoint property is not present
const clientConfig = mockBedrockRuntimeClient.mock.calls[0][0]
expect(clientConfig).not.toHaveProperty("endpoint")
})
})

// Test Scenario 4: Error Handling Tests
describe("Error Handling", () => {
it("should handle invalid endpoint URLs by passing them directly to AWS SDK", () => {
// Create handler with an invalid URL format
new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsBedrockEndpoint: "invalid-url-format",
awsBedrockEndpointEnabled: true,
})

// Verify the client was created with the invalid endpoint
// (AWS SDK will handle the validation/errors)
expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
expect.objectContaining({
region: "us-east-1",
endpoint: "invalid-url-format",
}),
)
})
})

// Test Scenario 5: Persistence Tests
describe("Persistence", () => {
it("should maintain consistent behavior across multiple requests", async () => {
// Create handler with endpoint URL and enabled flag
const handler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsBedrockEndpoint: "https://bedrock-vpc.example.com",
awsBedrockEndpointEnabled: true,
})

// Reset mock to clear the constructor call
mockBedrockRuntimeClient.mockClear()

// Make a request
try {
await handler.completePrompt("Test prompt")
} catch (error) {
// Ignore errors, we're just testing the client configuration
}

// Verify the client was configured with the endpoint
expect(mockSend).toHaveBeenCalled()
})
})
})
3 changes: 3 additions & 0 deletions src/api/providers/bedrock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH

const clientConfig: BedrockRuntimeClientConfig = {
region: this.options.awsRegion,
// Add the endpoint configuration when specified and enabled
...(this.options.awsBedrockEndpoint &&
this.options.awsBedrockEndpointEnabled && { endpoint: this.options.awsBedrockEndpoint }),
}

if (this.options.awsUseProfile && this.options.awsProfile) {
Expand Down
6 changes: 2 additions & 4 deletions src/core/config/__tests__/importExport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,8 @@ describe("importExport", () => {
customModesManager: mockCustomModesManager,
})

expect(result).toEqual({
success: false,
error: "Expected property name or '}' in JSON at position 2",
})
expect(result.success).toBe(false)
expect(result.error).toMatch(/^Expected property name or '}' in JSON at position 2/)
expect(fs.readFile).toHaveBeenCalledWith("/mock/path/settings.json", "utf-8")
expect(mockProviderSettingsManager.import).not.toHaveBeenCalled()
expect(mockContextProxy.setValues).not.toHaveBeenCalled()
Expand Down
33 changes: 32 additions & 1 deletion webview-ui/src/components/settings/providers/Bedrock.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback } from "react"
import { useCallback, useState, useEffect } from "react"
import { Checkbox } from "vscrui"
import { VSCodeTextField, VSCodeRadio, VSCodeRadioGroup } from "@vscode/webview-ui-toolkit/react"

Expand All @@ -17,6 +17,12 @@ type BedrockProps = {

export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedModelInfo }: BedrockProps) => {
const { t } = useAppTranslation()
const [awsEndpointSelected, setAwsEndpointSelected] = useState(!!apiConfiguration?.awsBedrockEndpointEnabled)

// Update the endpoint enabled state when the configuration changes
useEffect(() => {
setAwsEndpointSelected(!!apiConfiguration?.awsBedrockEndpointEnabled)
}, [apiConfiguration?.awsBedrockEndpointEnabled])

const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
Expand Down Expand Up @@ -120,6 +126,31 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
{t("settings:providers.cacheUsageNote")}
</div>
</div>
<Checkbox
checked={awsEndpointSelected}
onChange={(isChecked) => {
setAwsEndpointSelected(isChecked)
setApiConfigurationField("awsBedrockEndpointEnabled", isChecked)
}}>
{t("settings:providers.awsBedrockVpc.useCustomVpcEndpoint")}
</Checkbox>
{awsEndpointSelected && (
<>
<VSCodeTextField
value={apiConfiguration?.awsBedrockEndpoint || ""}
style={{ width: "100%", marginTop: 3, marginBottom: 5 }}
type="url"
onInput={handleInputChange("awsBedrockEndpoint")}
placeholder={t("settings:providers.awsBedrockVpc.vpcEndpointUrlPlaceholder")}
data-testid="vpc-endpoint-input"
/>
<div className="text-sm text-vscode-descriptionForeground ml-6 mt-1 mb-3">
{t("settings:providers.awsBedrockVpc.examples")}
<div className="ml-2">• https://vpce-xxx.bedrock.region.vpce.amazonaws.com/</div>
<div className="ml-2">• https://gateway.my-company.com/route/app/bedrock</div>
</div>
</>
)}
</>
)
}
Loading
Loading