feat: add custom region input support for AWS Bedrock provider

- Add awsCustomRegion field to ProviderSettings schema
- Update BEDROCK_REGIONS to include "Custom region..." option
- Modify Bedrock UI component to support custom region input
- Add conditional UI similar to VPC endpoint pattern
- Handle state management for switching between standard and custom regions
- Preserve existing validation logic and maintain backward compatibility
- Add comprehensive tests for custom region functionality

Fixes #5923
This commit is contained in:
Roo Code 2025-07-18 20:22:27 +00:00
parent b6bded9818
commit 24d2adb298
4 changed files with 250 additions and 2 deletions

View file

@ -107,6 +107,7 @@ const bedrockSchema = apiModelIdProviderModelSchema.extend({
awsSecretKey: z.string().optional(),
awsSessionToken: z.string().optional(),
awsRegion: z.string().optional(),
awsCustomRegion: z.string().optional(),
awsUseCrossRegionInference: z.boolean().optional(),
awsUsePromptCache: z.boolean().optional(),
awsProfile: z.string().optional(),

View file

@ -405,4 +405,10 @@ export const BEDROCK_REGIONS = [
{ value: "sa-east-1", label: "sa-east-1" },
{ value: "us-gov-east-1", label: "us-gov-east-1" },
{ value: "us-gov-west-1", label: "us-gov-west-1" },
].sort((a, b) => a.value.localeCompare(b.value))
{ value: "custom", label: "Custom region..." },
].sort((a, b) => {
// Keep "Custom region..." at the end
if (a.value === "custom") return 1
if (b.value === "custom") return -1
return a.value.localeCompare(b.value)
})

View file

@ -18,12 +18,18 @@ type BedrockProps = {
export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedModelInfo }: BedrockProps) => {
const { t } = useAppTranslation()
const [awsEndpointSelected, setAwsEndpointSelected] = useState(!!apiConfiguration?.awsBedrockEndpointEnabled)
const [customRegionSelected, setCustomRegionSelected] = useState(apiConfiguration?.awsRegion === "custom")
// Update the endpoint enabled state when the configuration changes
useEffect(() => {
setAwsEndpointSelected(!!apiConfiguration?.awsBedrockEndpointEnabled)
}, [apiConfiguration?.awsBedrockEndpointEnabled])
// Update the custom region state when the configuration changes
useEffect(() => {
setCustomRegionSelected(apiConfiguration?.awsRegion === "custom")
}, [apiConfiguration?.awsRegion])
const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
field: K,
@ -89,7 +95,14 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
<label className="block font-medium mb-1">{t("settings:providers.awsRegion")}</label>
<Select
value={apiConfiguration?.awsRegion || ""}
onValueChange={(value) => setApiConfigurationField("awsRegion", value)}>
onValueChange={(value) => {
setApiConfigurationField("awsRegion", value)
setCustomRegionSelected(value === "custom")
// Clear custom region when switching to a standard region
if (value !== "custom") {
setApiConfigurationField("awsCustomRegion", "")
}
}}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
@ -102,6 +115,23 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
</SelectContent>
</Select>
</div>
{customRegionSelected && (
<>
<VSCodeTextField
value={apiConfiguration?.awsCustomRegion || ""}
style={{ width: "100%", marginTop: 3, marginBottom: 5 }}
onInput={handleInputChange("awsCustomRegion")}
placeholder={t("settings:placeholders.customRegion")}
data-testid="custom-region-input"
/>
<div className="text-sm text-vscode-descriptionForeground ml-6 mt-1 mb-3">
{t("settings:providers.awsCustomRegion.examples")}
<div className="ml-2"> us-west-3</div>
<div className="ml-2"> eu-central-3</div>
<div className="ml-2"> ap-southeast-3</div>
</div>
</>
)}
<Checkbox
checked={apiConfiguration?.awsUseCrossRegionInference || false}
onChange={handleInputChange("awsUseCrossRegionInference", noTransform)}>

View file

@ -425,4 +425,215 @@ describe("Bedrock Component", () => {
expect(screen.getByTestId("vpc-endpoint-input")).toHaveValue("https://updated-endpoint.aws.com")
})
})
// Test Scenario 6: Custom Region Tests
describe("Custom Region", () => {
it("should show custom region input when 'Custom region...' is selected", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "",
awsUseProfile: true,
}
render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Custom region input should not be visible initially
expect(screen.queryByTestId("custom-region-input")).not.toBeInTheDocument()
// Mock the Select component to simulate selecting "custom"
// Since we're mocking the Select component, we need to simulate the onValueChange call
const selectComponent = screen.getByRole("combobox", { hidden: true })
if (selectComponent) {
// Simulate selecting "custom" region
const onValueChange = (selectComponent as any).onValueChange || (() => {})
onValueChange("custom")
}
// Verify that setApiConfigurationField was called with "custom"
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsRegion", "custom")
})
it("should hide custom region input when switching from custom to standard region", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "us-west-3",
awsUseProfile: true,
}
render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Custom region input should be visible initially
expect(screen.getByTestId("custom-region-input")).toBeInTheDocument()
expect(screen.getByTestId("custom-region-input")).toHaveValue("us-west-3")
// Mock selecting a standard region
const selectComponent = screen.getByRole("combobox", { hidden: true })
if (selectComponent) {
const onValueChange = (selectComponent as any).onValueChange || (() => {})
onValueChange("us-east-1")
}
// Verify that both awsRegion and awsCustomRegion were updated
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsRegion", "us-east-1")
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsCustomRegion", "")
})
it("should handle custom region input changes", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "",
awsUseProfile: true,
}
render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Find the custom region input field
const customRegionInput = screen.getByTestId("custom-region-input")
expect(customRegionInput).toBeInTheDocument()
// Enter a custom region
fireEvent.change(customRegionInput, { target: { value: "us-west-3" } })
// Verify the configuration field was updated
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsCustomRegion", "us-west-3")
})
it("should display example regions when custom region is selected", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "us-west-3",
awsUseProfile: true,
}
render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Check that the custom region input is visible
expect(screen.getByTestId("custom-region-input")).toBeInTheDocument()
// Check for the example regions section
expect(screen.getByText("settings:providers.awsCustomRegion.examples")).toBeInTheDocument()
expect(screen.getByText("• us-west-3")).toBeInTheDocument()
expect(screen.getByText("• eu-central-3")).toBeInTheDocument()
expect(screen.getByText("• ap-southeast-3")).toBeInTheDocument()
})
it("should preserve custom region value when toggling between custom and standard regions", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "us-west-3",
awsUseProfile: true,
}
const { rerender } = render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Initial state: custom region selected with value
expect(screen.getByTestId("custom-region-input")).toBeInTheDocument()
expect(screen.getByTestId("custom-region-input")).toHaveValue("us-west-3")
// Switch to standard region
const updatedConfig: Partial<ProviderSettings> = {
awsRegion: "us-east-1",
awsCustomRegion: "", // This would be cleared by the component logic
awsUseProfile: true,
}
rerender(
<Bedrock
apiConfiguration={updatedConfig as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Custom region input should be hidden
expect(screen.queryByTestId("custom-region-input")).not.toBeInTheDocument()
// Switch back to custom region with preserved value
const backToCustomConfig: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "us-west-3", // Value preserved in parent state
awsUseProfile: true,
}
rerender(
<Bedrock
apiConfiguration={backToCustomConfig as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Custom region input should be visible again with preserved value
expect(screen.getByTestId("custom-region-input")).toBeInTheDocument()
expect(screen.getByTestId("custom-region-input")).toHaveValue("us-west-3")
})
it("should handle empty custom region input", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "us-west-3",
awsUseProfile: true,
}
render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Find the custom region input field
const customRegionInput = screen.getByTestId("custom-region-input")
// Clear the field
fireEvent.change(customRegionInput, { target: { value: "" } })
// Verify the configuration field was updated with empty string
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsCustomRegion", "")
})
it("should initialize with correct state when custom region is pre-selected", () => {
const apiConfiguration: Partial<ProviderSettings> = {
awsRegion: "custom",
awsCustomRegion: "eu-central-3",
awsUseProfile: true,
}
render(
<Bedrock
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
/>,
)
// Verify custom region input is visible and has correct value
expect(screen.getByTestId("custom-region-input")).toBeInTheDocument()
expect(screen.getByTestId("custom-region-input")).toHaveValue("eu-central-3")
// Verify examples are shown
expect(screen.getByText("settings:providers.awsCustomRegion.examples")).toBeInTheDocument()
})
})
})