on main help sign in
fix: respect disable_default_providers (#2177)

* fix: respect disable_default_providers closes #1949 Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: use x/slice Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: fail if no providers Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * test: fix Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

commitd1032ba98b7e20300f1fa75b4e8e33d601ec19d4 authorCarlos Alexandro Becker <caarlos0@users.noreply.github.com> dateTue 10 Feb 2026, 20:25 (8 months ago) parent47ca98d
5 files changed+20−34
go.mod+1−1 go.sum+2−2 internal/config/load.go+5 internal/config/load_test.go+3−3 internal/ui/dialog/models.go+9−28
go.mod+1−1
@@ -29,7 +29,7 @@ require (
2929 github.com/charmbracelet/x/exp/charmtone v0.0.0-20260109001716-2fbdffcb221f
3030 github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f
3131 github.com/charmbracelet/x/exp/ordered v0.1.0
32− github.com/charmbracelet/x/exp/slice v0.0.0-20251201173703-9f73bfd934ff
32+ github.com/charmbracelet/x/exp/slice v0.0.0-20260209194814-eeb2896ac759
3333 github.com/charmbracelet/x/exp/strings v0.1.0
3434 github.com/charmbracelet/x/powernap v0.0.0-20260209132835-6b065b8ba62c
3535 github.com/charmbracelet/x/term v0.2.2
go.sum+2−2
@@ -116,8 +116,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6g
116116github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
117117github.com/charmbracelet/x/exp/ordered v0.1.0 h1:55/qLwjIh0gL0Vni+QAWk7T/qRVP6sBf+2agPBgnOFE=
118118github.com/charmbracelet/x/exp/ordered v0.1.0/go.mod h1:5UHwmG+is5THxMyCJHNPCn2/ecI07aKNrW+LcResjJ8=
119−github.com/charmbracelet/x/exp/slice v0.0.0-20251201173703-9f73bfd934ff h1:Uwr+/JS+qnRcO/++xjYEDtW7x+P5E4+4cBiOHTt2Xfk=
120−github.com/charmbracelet/x/exp/slice v0.0.0-20251201173703-9f73bfd934ff/go.mod h1:vqEfX6xzqW1pKKZUUiFOKg0OQ7bCh54Q2vR/tserrRA=
119+github.com/charmbracelet/x/exp/slice v0.0.0-20260209194814-eeb2896ac759 h1:96wFGlst+IDv3dIf5q29nw470wJYB3YAgemiciLZcG0=
120+github.com/charmbracelet/x/exp/slice v0.0.0-20260209194814-eeb2896ac759/go.mod h1:vqEfX6xzqW1pKKZUUiFOKg0OQ7bCh54Q2vR/tserrRA=
121121github.com/charmbracelet/x/exp/strings v0.1.0 h1:i69S2XI7uG1u4NLGeJPSYU++Nmjvpo9nwd6aoEm7gkA=
122122github.com/charmbracelet/x/exp/strings v0.1.0/go.mod h1:/ehtMPNh9K4odGFkqYJKpIYyePhdp1hLBRvyY4bWkH8=
123123github.com/charmbracelet/x/json v0.2.0 h1:DqB+ZGx2h+Z+1s98HOuOyli+i97wsFQIxP2ZQANTPrQ=
internal/config/load.go+5
@@ -331,6 +331,11 @@ func (c *Config) configureProviders(env env.Env, resolver VariableResolver, know
331331
332332 c.Providers.Set(id, providerConfig)
333333 }
334+
335+ if c.Providers.Len() == 0 && c.Options.DisableDefaultProviders {
336+ return fmt.Errorf("default providers are disabled and there are no custom providers are configured")
337+ }
338+
334339 return nil
335340}
336341
internal/config/load_test.go+3−3
@@ -1127,7 +1127,7 @@ func TestConfig_configureProvidersDisableDefaultProviders(t *testing.T) {
11271127 })
11281128 resolver := NewEnvironmentVariableResolver(env)
11291129 err := cfg.configureProviders(env, resolver, knownProviders)
1130− require.NoError(t, err)
1130+ require.ErrorContains(t, err, "no custom providers")
11311131
11321132 // openai should NOT be present because it lacks base_url and models.
11331133 require.Equal(t, 0, cfg.Providers.Len())
@@ -1252,7 +1252,7 @@ func TestConfig_configureProvidersDisableDefaultProviders(t *testing.T) {
12521252 env := env.NewFromMap(map[string]string{})
12531253 resolver := NewEnvironmentVariableResolver(env)
12541254 err := cfg.configureProviders(env, resolver, []catwalk.Provider{})
1255− require.NoError(t, err)
1255+ require.ErrorContains(t, err, "no custom providers")
12561256
12571257 // Provider should be rejected for missing models.
12581258 require.Equal(t, 0, cfg.Providers.Len())
@@ -1276,7 +1276,7 @@ func TestConfig_configureProvidersDisableDefaultProviders(t *testing.T) {
12761276 env := env.NewFromMap(map[string]string{})
12771277 resolver := NewEnvironmentVariableResolver(env)
12781278 err := cfg.configureProviders(env, resolver, []catwalk.Provider{})
1279− require.NoError(t, err)
1279+ require.ErrorContains(t, err, "no custom providers")
12801280
12811281 // Provider should be rejected for missing base_url.
12821282 require.Equal(t, 0, cfg.Providers.Len())
internal/ui/dialog/models.go+9−28
@@ -4,7 +4,6 @@ import (
44 "cmp"
55 "fmt"
66 "slices"
7− "strings"
87
98 "charm.land/bubbles/v2/help"
109 "charm.land/bubbles/v2/key"
@@ -15,6 +14,7 @@ import (
1514 "github.com/charmbracelet/crush/internal/ui/common"
1615 "github.com/charmbracelet/crush/internal/ui/util"
1716 uv "github.com/charmbracelet/ultraviolet"
17+ xslice "github.com/charmbracelet/x/exp/slice"
1818)
1919
2020// ModelType represents the type of model to select.
@@ -143,12 +143,14 @@ func NewModels(com *common.Common, isOnboarding bool) (*Models, error) {
143143 )
144144 m.keyMap.Close = CloseKey
145145
146− providers, err := getFilteredProviders(com.Config())
147− if err != nil {
148− return nil, fmt.Errorf("failed to get providers: %w", err)
149− }
150−
151− m.providers = providers
146+ m.providers = slices.Collect(
147+ xslice.Map(
148+ com.Config().Providers.Seq(),
149+ func(pc config.ProviderConfig) catwalk.Provider {
150+ return pc.ToProvider()
151+ },
152+ ),
153+ )
152154 if err := m.setProviderItems(); err != nil {
153155 return nil, fmt.Errorf("failed to set provider items: %w", err)
154156 }
@@ -521,27 +523,6 @@ func (m *Models) setProviderItems() error {
521523 return nil
522524}
523525
524−func getFilteredProviders(cfg *config.Config) ([]catwalk.Provider, error) {
525− providers, err := config.Providers(cfg)
526− if err != nil {
527− return nil, fmt.Errorf("failed to get providers: %w", err)
528− }
529− var filteredProviders []catwalk.Provider
530− for _, p := range providers {
531− var (
532− isAzure = p.ID == catwalk.InferenceProviderAzure
533− isCopilot = p.ID == catwalk.InferenceProviderCopilot
534− isHyper = string(p.ID) == "hyper"
535− hasAPIKeyEnv = strings.HasPrefix(p.APIKey, "$")
536− _, isConfigured = cfg.Providers.Get(string(p.ID))
537− )
538− if isAzure || isCopilot || isHyper || hasAPIKeyEnv || isConfigured {
539− filteredProviders = append(filteredProviders, p)
540− }
541− }
542− return filteredProviders, nil
543−}
544−
545526func modelKey(providerID, modelID string) string {
546527 if providerID == "" || modelID == "" {
547528 return ""