@@ -1094,6 +1094,217 @@ func TestConfig_defaultModelSelection(t *testing.T) {
10941094 })
10951095}
10961096
1097+func TestConfig_configureProvidersDisableDefaultProviders(t *testing.T) {
1098+ t.Run("when enabled, ignores all default providers and requires full specification", func(t *testing.T) {
1099+ knownProviders := []catwalk.Provider{
1100+ {
1101+ ID: "openai",
1102+ APIKey: "$OPENAI_API_KEY",
1103+ APIEndpoint: "https://api.openai.com/v1",
1104+ Models: []catwalk.Model{{
1105+ ID: "gpt-4",
1106+ }},
1107+ },
1108+ }
1109+
1110+ // User references openai but doesn't fully specify it (no base_url, no
1111+ // models). This should be rejected because disable_default_providers
1112+ // treats all providers as custom.
1113+ cfg := &Config{
1114+ Options: &Options{
1115+ DisableDefaultProviders: true,
1116+ },
1117+ Providers: csync.NewMapFrom(map[string]ProviderConfig{
1118+ "openai": {
1119+ APIKey: "$OPENAI_API_KEY",
1120+ },
1121+ }),
1122+ }
1123+ cfg.setDefaults("/tmp", "")
1124+
1125+ env := env.NewFromMap(map[string]string{
1126+ "OPENAI_API_KEY": "test-key",
1127+ })
1128+ resolver := NewEnvironmentVariableResolver(env)
1129+ err := cfg.configureProviders(env, resolver, knownProviders)
1130+ require.NoError(t, err)
1131+
1132+ // openai should NOT be present because it lacks base_url and models.
1133+ require.Equal(t, 0, cfg.Providers.Len())
1134+ _, exists := cfg.Providers.Get("openai")
1135+ require.False(t, exists, "openai should not be present without full specification")
1136+ })
1137+
1138+ t.Run("when enabled, fully specified providers work", func(t *testing.T) {
1139+ knownProviders := []catwalk.Provider{
1140+ {
1141+ ID: "openai",
1142+ APIKey: "$OPENAI_API_KEY",
1143+ APIEndpoint: "https://api.openai.com/v1",
1144+ Models: []catwalk.Model{{
1145+ ID: "gpt-4",
1146+ }},
1147+ },
1148+ }
1149+
1150+ // User fully specifies their provider.
1151+ cfg := &Config{
1152+ Options: &Options{
1153+ DisableDefaultProviders: true,
1154+ },
1155+ Providers: csync.NewMapFrom(map[string]ProviderConfig{
1156+ "my-llm": {
1157+ APIKey: "$MY_API_KEY",
1158+ BaseURL: "https://my-llm.example.com/v1",
1159+ Models: []catwalk.Model{{
1160+ ID: "my-model",
1161+ }},
1162+ },
1163+ }),
1164+ }
1165+ cfg.setDefaults("/tmp", "")
1166+
1167+ env := env.NewFromMap(map[string]string{
1168+ "MY_API_KEY": "test-key",
1169+ "OPENAI_API_KEY": "test-key",
1170+ })
1171+ resolver := NewEnvironmentVariableResolver(env)
1172+ err := cfg.configureProviders(env, resolver, knownProviders)
1173+ require.NoError(t, err)
1174+
1175+ // Only fully specified provider should be present.
1176+ require.Equal(t, 1, cfg.Providers.Len())
1177+ provider, exists := cfg.Providers.Get("my-llm")
1178+ require.True(t, exists, "my-llm should be present")
1179+ require.Equal(t, "https://my-llm.example.com/v1", provider.BaseURL)
1180+ require.Len(t, provider.Models, 1)
1181+
1182+ // Default openai should NOT be present.
1183+ _, exists = cfg.Providers.Get("openai")
1184+ require.False(t, exists, "openai should not be present")
1185+ })
1186+
1187+ t.Run("when disabled, includes all known providers with valid credentials", func(t *testing.T) {
1188+ knownProviders := []catwalk.Provider{
1189+ {
1190+ ID: "openai",
1191+ APIKey: "$OPENAI_API_KEY",
1192+ APIEndpoint: "https://api.openai.com/v1",
1193+ Models: []catwalk.Model{{
1194+ ID: "gpt-4",
1195+ }},
1196+ },
1197+ {
1198+ ID: "anthropic",
1199+ APIKey: "$ANTHROPIC_API_KEY",
1200+ APIEndpoint: "https://api.anthropic.com/v1",
1201+ Models: []catwalk.Model{{
1202+ ID: "claude-3",
1203+ }},
1204+ },
1205+ }
1206+
1207+ // User only configures openai, both API keys are available, but option
1208+ // is disabled.
1209+ cfg := &Config{
1210+ Options: &Options{
1211+ DisableDefaultProviders: false,
1212+ },
1213+ Providers: csync.NewMapFrom(map[string]ProviderConfig{
1214+ "openai": {
1215+ APIKey: "$OPENAI_API_KEY",
1216+ },
1217+ }),
1218+ }
1219+ cfg.setDefaults("/tmp", "")
1220+
1221+ env := env.NewFromMap(map[string]string{
1222+ "OPENAI_API_KEY": "test-key",
1223+ "ANTHROPIC_API_KEY": "test-key",
1224+ })
1225+ resolver := NewEnvironmentVariableResolver(env)
1226+ err := cfg.configureProviders(env, resolver, knownProviders)
1227+ require.NoError(t, err)
1228+
1229+ // Both providers should be present.
1230+ require.Equal(t, 2, cfg.Providers.Len())
1231+ _, exists := cfg.Providers.Get("openai")
1232+ require.True(t, exists, "openai should be present")
1233+ _, exists = cfg.Providers.Get("anthropic")
1234+ require.True(t, exists, "anthropic should be present")
1235+ })
1236+
1237+ t.Run("when enabled, provider missing models is rejected", func(t *testing.T) {
1238+ cfg := &Config{
1239+ Options: &Options{
1240+ DisableDefaultProviders: true,
1241+ },
1242+ Providers: csync.NewMapFrom(map[string]ProviderConfig{
1243+ "my-llm": {
1244+ APIKey: "test-key",
1245+ BaseURL: "https://my-llm.example.com/v1",
1246+ Models: []catwalk.Model{}, // No models.
1247+ },
1248+ }),
1249+ }
1250+ cfg.setDefaults("/tmp", "")
1251+
1252+ env := env.NewFromMap(map[string]string{})
1253+ resolver := NewEnvironmentVariableResolver(env)
1254+ err := cfg.configureProviders(env, resolver, []catwalk.Provider{})
1255+ require.NoError(t, err)
1256+
1257+ // Provider should be rejected for missing models.
1258+ require.Equal(t, 0, cfg.Providers.Len())
1259+ })
1260+
1261+ t.Run("when enabled, provider missing base_url is rejected", func(t *testing.T) {
1262+ cfg := &Config{
1263+ Options: &Options{
1264+ DisableDefaultProviders: true,
1265+ },
1266+ Providers: csync.NewMapFrom(map[string]ProviderConfig{
1267+ "my-llm": {
1268+ APIKey: "test-key",
1269+ Models: []catwalk.Model{{ID: "model"}},
1270+ // No BaseURL.
1271+ },
1272+ }),
1273+ }
1274+ cfg.setDefaults("/tmp", "")
1275+
1276+ env := env.NewFromMap(map[string]string{})
1277+ resolver := NewEnvironmentVariableResolver(env)
1278+ err := cfg.configureProviders(env, resolver, []catwalk.Provider{})
1279+ require.NoError(t, err)
1280+
1281+ // Provider should be rejected for missing base_url.
1282+ require.Equal(t, 0, cfg.Providers.Len())
1283+ })
1284+}
1285+
1286+func TestConfig_setDefaultsDisableDefaultProvidersEnvVar(t *testing.T) {
1287+ t.Run("sets option from environment variable", func(t *testing.T) {
1288+ t.Setenv("CRUSH_DISABLE_DEFAULT_PROVIDERS", "true")
1289+
1290+ cfg := &Config{}
1291+ cfg.setDefaults("/tmp", "")
1292+
1293+ require.True(t, cfg.Options.DisableDefaultProviders)
1294+ })
1295+
1296+ t.Run("does not override when env var is not set", func(t *testing.T) {
1297+ cfg := &Config{
1298+ Options: &Options{
1299+ DisableDefaultProviders: true,
1300+ },
1301+ }
1302+ cfg.setDefaults("/tmp", "")
1303+
1304+ require.True(t, cfg.Options.DisableDefaultProviders)
1305+ })
1306+}
1307+
10971308func TestConfig_configureSelectedModels(t *testing.T) {
10981309 t.Run("should override defaults", func(t *testing.T) {
10991310 knownProviders := []catwalk.Provider{