diff --git a/pkg/providers/factory_provider_test.go b/pkg/providers/factory_provider_test.go index ba21611c5..889b08e98 100644 --- a/pkg/providers/factory_provider_test.go +++ b/pkg/providers/factory_provider_test.go @@ -984,8 +984,8 @@ func TestModelProviderOptions(t *testing.T) { } if option, ok := seen["bedrock"]; !ok { t.Fatal("bedrock option missing") - } else if option.CreateAllowed { - t.Fatal("bedrock should not be creatable from the web form") + } else if !option.CreateAllowed { + t.Fatal("bedrock should be creatable and defer credential/build errors to runtime") } if option, ok := seen["antigravity"]; !ok { t.Fatal("antigravity option missing") diff --git a/pkg/providers/provider_catalog.go b/pkg/providers/provider_catalog.go index 46f398881..077c28944 100644 --- a/pkg/providers/provider_catalog.go +++ b/pkg/providers/provider_catalog.go @@ -36,7 +36,7 @@ var attachedModelProviderMetaByName = map[string]attachedModelProviderMeta{ protocolMeta: protocolMeta{defaultAPIBase: "https://api.anthropic.com/v1"}, createAllowed: true, }, - "bedrock": {}, + "bedrock": {createAllowed: true}, "antigravity": {createAllowed: true, defaultAuthMethod: "oauth", authMethodLocked: true}, "claude-cli": {createAllowed: true}, "codex-cli": {createAllowed: true}, diff --git a/web/backend/api/model_status.go b/web/backend/api/model_status.go index a15ef2415..302231d80 100644 --- a/web/backend/api/model_status.go +++ b/web/backend/api/model_status.go @@ -93,7 +93,6 @@ func hasModelConfiguration(m *config.ModelConfig) bool { if configured, checked := hasStoredOAuthCredential(m); checked { return configured } - return true } if authMethod == "" && providerUsesImplicitOAuth(protocol) { diff --git a/web/backend/api/models_test.go b/web/backend/api/models_test.go index 5b3cfb3a6..38988db6e 100644 --- a/web/backend/api/models_test.go +++ b/web/backend/api/models_test.go @@ -253,6 +253,27 @@ func TestHandleListModels_AvailabilityForOAuthModelWithCredential(t *testing.T) } } +func TestHasModelConfiguration_OAuthWithoutMappedCredentialFallsBackToAPIKey(t *testing.T) { + noKey := &config.ModelConfig{ + Provider: "gemini", + Model: "gemini-2.5-flash", + AuthMethod: "oauth", + } + if hasModelConfiguration(noKey) { + t.Fatal("oauth model without credential mapping and api key should be unconfigured") + } + + withKey := &config.ModelConfig{ + Provider: "gemini", + Model: "gemini-2.5-flash", + AuthMethod: "oauth", + APIKeys: config.SimpleSecureStrings("gemini-key"), + } + if !hasModelConfiguration(withKey) { + t.Fatal("oauth model without credential mapping should fall back to api key configuration") + } +} + func TestHandleListModels_AntigravityImplicitOAuthAvailability(t *testing.T) { configPath, cleanup := setupOAuthTestEnv(t) defer cleanup() diff --git a/web/frontend/src/components/models/models-page.tsx b/web/frontend/src/components/models/models-page.tsx index 69d8a4d41..df372b6b1 100644 --- a/web/frontend/src/components/models/models-page.tsx +++ b/web/frontend/src/components/models/models-page.tsx @@ -47,6 +47,7 @@ export function ModelsPage() { const [settingDefaultIndex, setSettingDefaultIndex] = useState( null, ) + const addDisabled = loading || providerOptions.length === 0 const fetchModels = useCallback(async () => { try { @@ -141,7 +142,12 @@ export function ModelsPage() {
-