diff --git a/site/src/pages/AISettingsPage/ModelsPage/ModelsPageView.stories.tsx b/site/src/pages/AISettingsPage/ModelsPage/ModelsPageView.stories.tsx index 85bf8b6635..7c7a81d410 100644 --- a/site/src/pages/AISettingsPage/ModelsPage/ModelsPageView.stories.tsx +++ b/site/src/pages/AISettingsPage/ModelsPage/ModelsPageView.stories.tsx @@ -5,7 +5,9 @@ import type { ChatModelConfig } from "#/api/typesGenerated"; import ModelsPageView from "./ModelsPageView"; import { MockAnthropicProviderState, + MockBedrockProviderState, MockOpenAIProviderState, + mockBedrockClaude, mockClaude, mockDisabledModel, mockGPT5, @@ -17,8 +19,12 @@ const meta: Meta = { args: { isLoading: false, error: null, - models: [mockGPT5, mockClaude, mockDisabledModel], - providerStates: [MockOpenAIProviderState, MockAnthropicProviderState], + models: [mockGPT5, mockClaude, mockDisabledModel, mockBedrockClaude], + providerStates: [ + MockOpenAIProviderState, + MockAnthropicProviderState, + MockBedrockProviderState, + ], }, parameters: { reactRouter: reactRouterParameters({ @@ -45,6 +51,12 @@ export const Default: Story = { await expect(canvas.getByText("Claude Sonnet 4.5")).toBeInTheDocument(); await expect(canvas.getAllByText("OpenAI").length).toBeGreaterThan(0); await expect(canvas.getByText("Anthropic")).toBeInTheDocument(); + await expect( + canvas.getByText("Claude Sonnet 4.5 (Bedrock)"), + ).toBeInTheDocument(); + await expect(canvas.getByText("AWS Bedrock")).toBeInTheDocument(); + // The Bedrock model config should render the Bedrock provider icon. + await expect(canvas.getByAltText("AWS Bedrock")).toBeInTheDocument(); await expect(canvas.getAllByText("Enabled").length).toBeGreaterThan(0); await expect(canvas.getByText("Default")).toBeInTheDocument(); await expect(canvas.getByText("Disabled")).toBeInTheDocument(); diff --git a/site/src/pages/AISettingsPage/ModelsPage/testFixtures.ts b/site/src/pages/AISettingsPage/ModelsPage/testFixtures.ts index 752936fe1e..1803aec00a 100644 --- a/site/src/pages/AISettingsPage/ModelsPage/testFixtures.ts +++ b/site/src/pages/AISettingsPage/ModelsPage/testFixtures.ts @@ -83,6 +83,31 @@ export const MockAnthropicProviderState: ProviderState = { modelConfigs: [mockClaude], }; +const MockBedrockProviderConfig: ChatProviderConfig = { + ...MockOpenAIProviderConfig, + id: "prov-bedrock", + provider: "bedrock", + display_name: "AWS Bedrock", +}; + +export const mockBedrockClaude: ChatModelConfig = { + ...mockClaude, + id: "model-bedrock-claude", + provider: "bedrock", + ai_provider_id: "prov-bedrock", + model: "anthropic.claude-sonnet-4-5", + display_name: "Claude Sonnet 4.5 (Bedrock)", +}; + +export const MockBedrockProviderState: ProviderState = { + ...MockOpenAIProviderState, + key: "prov-bedrock", + provider: "bedrock", + label: "AWS Bedrock", + providerConfig: MockBedrockProviderConfig, + modelConfigs: [mockBedrockClaude], +}; + export const MockCopilotProviderState: ProviderState = { ...MockOpenAIProviderState, key: "prov-copilot", diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.test.ts b/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.test.ts index a721fd6e82..43f1c76762 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.test.ts +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.test.ts @@ -139,8 +139,12 @@ describe("parseBedrockRegionFromBaseUrl", () => { }); describe("isBedrockProvider", () => { - it("recognises a discriminated bedrock provider", () => { - expect(isBedrockProvider(MockAIProviderBedrock)).toBe(true); + it("recognises a legacy bedrock provider stored as type=anthropic", () => { + const provider: AIProvider = { + ...MockAIProviderBedrock, + type: "anthropic", + }; + expect(isBedrockProvider(provider)).toBe(true); }); it("recognises a provider with explicit bedrock type", () => { @@ -332,9 +336,9 @@ describe("providerFormValuesToCreate", () => { }); describe("Bedrock", () => { - it('maps Bedrock to a wire `type:"anthropic"`', () => { + it('maps Bedrock to a wire `type:"bedrock"`', () => { const req = providerFormValuesToCreate(baseBedrockFormValues); - expect(req.type).toBe("anthropic"); + expect(req.type).toBe("bedrock"); }); it("derives the region from a canonical AWS URL", () => { @@ -669,10 +673,10 @@ describe("aiProviderToFormValues", () => { expect(values.smallFastModel).toBe("anthropic.claude-haiku-4-5"); }); - it("seeds Bedrock form values from an explicit Bedrock provider type", () => { + it("seeds Bedrock form values from a legacy anthropic-typed provider", () => { const provider: AIProvider = { ...MockAIProviderBedrock, - type: "bedrock", + type: "anthropic", }; const values = aiProviderToFormValues(provider); expect(values.type).toBe("bedrock"); @@ -723,12 +727,12 @@ describe("aiProviderToFormValues", () => { it("handles a Bedrock provider whose settings are null", () => { // `isBedrockProvider` will return false, so the provider falls - // through to the anthropic branch. The helper must not throw. + // through to the generic branch. The helper must not throw. const provider: AIProvider = { ...MockAIProviderBedrock, settings: null as unknown as AIProvider["settings"], }; const values = aiProviderToFormValues(provider); - expect(values.type).toBe("anthropic"); + expect(values.type).toBe("bedrock"); }); }); diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.ts b/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.ts index 2eaaa65a4f..92df168a44 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.ts +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/providerFormApiMap.ts @@ -146,7 +146,7 @@ export const providerFormValuesToCreate = ( values.roleArn.trim(), ); return { - type: "anthropic", + type: "bedrock", ...base, settings: settings as AIProviderSettings, }; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 108889e6fa..51ad6c7fda 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -5574,13 +5574,14 @@ export const MockAIProviderAnthropic: TypesGen.AIProvider = { }; /** - * Bedrock providers come over the wire with `type: "anthropic"` and a - * `settings._type: "bedrock"` discriminator. `isBedrockProvider` and the - * backend (see `coderd/ai_providers.go`) enforce this convention. + * Bedrock providers come over the wire with `type: "bedrock"` and a + * `settings._type: "bedrock"` discriminator. `isBedrockProvider` checks + * both the type and the settings discriminator, and also accepts legacy + * providers stored as `type: "anthropic"` with Bedrock settings. */ export const MockAIProviderBedrock: TypesGen.AIProvider = { id: "9c2e3b41-2e9f-4c97-9a4f-2e1a3d8f9f21", - type: "anthropic", + type: "bedrock", name: "bedrock", display_name: "Bedrock", base_url: "https://bedrock-runtime.us-east-2.amazonaws.com",