@@ -760,3 +760,390 @@ func TestConfig_configureProvidersEnhancedCredentialValidation(t *testing.T) {
760 assert.True(t, exists)
760 assert.True(t, exists)
764+func TestConfig_defaultModelSelection(t *testing.T) {
765+ t.Run("default behavior uses the default models for given provider", func(t *testing.T) {
766+ knownProviders := []provider.Provider{
770+ DefaultLargeModelID: "large-model",
771+ DefaultSmallModelID: "small-model",
772+ Models: []provider.Model{
775+ DefaultMaxTokens: 1000,
779+ DefaultMaxTokens: 500,
786+ cfg.setDefaults("/tmp")
787+ env := env.NewFromMap(map[string]string{})
788+ resolver := NewEnvironmentVariableResolver(env)
789+ err := cfg.configureProviders(env, resolver, knownProviders)
790+ assert.NoError(t, err)
792+ large, small, err := cfg.defaultModelSelection(knownProviders)
793+ assert.NoError(t, err)
794+ assert.Equal(t, "large-model", large.Model)
795+ assert.Equal(t, "openai", large.Provider)
796+ assert.Equal(t, int64(1000), large.MaxTokens)
797+ assert.Equal(t, "small-model", small.Model)
798+ assert.Equal(t, "openai", small.Provider)
799+ assert.Equal(t, int64(500), small.MaxTokens)
801+ t.Run("should error if no providers configured", func(t *testing.T) {
802+ knownProviders := []provider.Provider{
805+ APIKey: "$MISSING_KEY",
806+ DefaultLargeModelID: "large-model",
807+ DefaultSmallModelID: "small-model",
808+ Models: []provider.Model{
811+ DefaultMaxTokens: 1000,
815+ DefaultMaxTokens: 500,
822+ cfg.setDefaults("/tmp")
823+ env := env.NewFromMap(map[string]string{})
824+ resolver := NewEnvironmentVariableResolver(env)
825+ err := cfg.configureProviders(env, resolver, knownProviders)
826+ assert.NoError(t, err)
828+ _, _, err = cfg.defaultModelSelection(knownProviders)
829+ assert.Error(t, err)
831+ t.Run("should error if model is missing", func(t *testing.T) {
832+ knownProviders := []provider.Provider{
836+ DefaultLargeModelID: "large-model",
837+ DefaultSmallModelID: "small-model",
838+ Models: []provider.Model{
840+ ID: "not-large-model",
841+ DefaultMaxTokens: 1000,
845+ DefaultMaxTokens: 500,
852+ cfg.setDefaults("/tmp")
853+ env := env.NewFromMap(map[string]string{})
854+ resolver := NewEnvironmentVariableResolver(env)
855+ err := cfg.configureProviders(env, resolver, knownProviders)
856+ assert.NoError(t, err)
857+ _, _, err = cfg.defaultModelSelection(knownProviders)
858+ assert.Error(t, err)
861+ t.Run("should configure the default models with a custom provider", func(t *testing.T) {
862+ knownProviders := []provider.Provider{
865+ APIKey: "$MISSING", // will not be included in the config
866+ DefaultLargeModelID: "large-model",
867+ DefaultSmallModelID: "small-model",
868+ Models: []provider.Model{
870+ ID: "not-large-model",
871+ DefaultMaxTokens: 1000,
875+ DefaultMaxTokens: 500,
882+ Providers: map[string]ProviderConfig{
885+ BaseURL: "https://api.custom.com/v1",
886+ Models: []provider.Model{
889+ DefaultMaxTokens: 600,
895+ cfg.setDefaults("/tmp")
896+ env := env.NewFromMap(map[string]string{})
897+ resolver := NewEnvironmentVariableResolver(env)
898+ err := cfg.configureProviders(env, resolver, knownProviders)
899+ assert.NoError(t, err)
900+ large, small, err := cfg.defaultModelSelection(knownProviders)
901+ assert.NoError(t, err)
902+ assert.Equal(t, "model", large.Model)
903+ assert.Equal(t, "custom", large.Provider)
904+ assert.Equal(t, int64(600), large.MaxTokens)
905+ assert.Equal(t, "model", small.Model)
906+ assert.Equal(t, "custom", small.Provider)
907+ assert.Equal(t, int64(600), small.MaxTokens)
910+ t.Run("should fail if no model configured", func(t *testing.T) {
911+ knownProviders := []provider.Provider{
914+ APIKey: "$MISSING", // will not be included in the config
915+ DefaultLargeModelID: "large-model",
916+ DefaultSmallModelID: "small-model",
917+ Models: []provider.Model{
919+ ID: "not-large-model",
920+ DefaultMaxTokens: 1000,
924+ DefaultMaxTokens: 500,
931+ Providers: map[string]ProviderConfig{
934+ BaseURL: "https://api.custom.com/v1",
935+ Models: []provider.Model{},
939+ cfg.setDefaults("/tmp")
940+ env := env.NewFromMap(map[string]string{})
941+ resolver := NewEnvironmentVariableResolver(env)
942+ err := cfg.configureProviders(env, resolver, knownProviders)
943+ assert.NoError(t, err)
944+ _, _, err = cfg.defaultModelSelection(knownProviders)
945+ assert.Error(t, err)
947+ t.Run("should use the default provider first", func(t *testing.T) {
948+ knownProviders := []provider.Provider{
952+ DefaultLargeModelID: "large-model",
953+ DefaultSmallModelID: "small-model",
954+ Models: []provider.Model{
957+ DefaultMaxTokens: 1000,
961+ DefaultMaxTokens: 500,
968+ Providers: map[string]ProviderConfig{
971+ BaseURL: "https://api.custom.com/v1",
972+ Models: []provider.Model{
975+ DefaultMaxTokens: 1000,
981+ cfg.setDefaults("/tmp")
982+ env := env.NewFromMap(map[string]string{})
983+ resolver := NewEnvironmentVariableResolver(env)
984+ err := cfg.configureProviders(env, resolver, knownProviders)
985+ assert.NoError(t, err)
986+ large, small, err := cfg.defaultModelSelection(knownProviders)
987+ assert.NoError(t, err)
988+ assert.Equal(t, "large-model", large.Model)
989+ assert.Equal(t, "openai", large.Provider)
990+ assert.Equal(t, int64(1000), large.MaxTokens)
991+ assert.Equal(t, "small-model", small.Model)
992+ assert.Equal(t, "openai", small.Provider)
993+ assert.Equal(t, int64(500), small.MaxTokens)
997+func TestConfig_configureSelectedModels(t *testing.T) {
998+ t.Run("should override defaults", func(t *testing.T) {
999+ knownProviders := []provider.Provider{
1003+ DefaultLargeModelID: "large-model",
1004+ DefaultSmallModelID: "small-model",
1005+ Models: []provider.Model{
1007+ ID: "larger-model",
1008+ DefaultMaxTokens: 2000,
1012+ DefaultMaxTokens: 1000,
1016+ DefaultMaxTokens: 500,
1023+ Models: map[SelectedModelType]SelectedModel{
1025+ Model: "larger-model",
1029+ cfg.setDefaults("/tmp")
1030+ env := env.NewFromMap(map[string]string{})
1031+ resolver := NewEnvironmentVariableResolver(env)
1032+ err := cfg.configureProviders(env, resolver, knownProviders)
1033+ assert.NoError(t, err)
1035+ err = cfg.configureSelectedModels(knownProviders)
1036+ assert.NoError(t, err)
1037+ large := cfg.Models[SelectedModelTypeLarge]
1038+ small := cfg.Models[SelectedModelTypeSmall]
1039+ assert.Equal(t, "larger-model", large.Model)
1040+ assert.Equal(t, "openai", large.Provider)
1041+ assert.Equal(t, int64(2000), large.MaxTokens)
1042+ assert.Equal(t, "small-model", small.Model)
1043+ assert.Equal(t, "openai", small.Provider)
1044+ assert.Equal(t, int64(500), small.MaxTokens)
1046+ t.Run("should be possible to use multiple providers", func(t *testing.T) {
1047+ knownProviders := []provider.Provider{
1051+ DefaultLargeModelID: "large-model",
1052+ DefaultSmallModelID: "small-model",
1053+ Models: []provider.Model{
1056+ DefaultMaxTokens: 1000,
1060+ DefaultMaxTokens: 500,
1067+ DefaultLargeModelID: "a-large-model",
1068+ DefaultSmallModelID: "a-small-model",
1069+ Models: []provider.Model{
1071+ ID: "a-large-model",
1072+ DefaultMaxTokens: 1000,
1075+ ID: "a-small-model",
1076+ DefaultMaxTokens: 200,
1083+ Models: map[SelectedModelType]SelectedModel{
1085+ Model: "a-small-model",
1086+ Provider: "anthropic",
1091+ cfg.setDefaults("/tmp")
1092+ env := env.NewFromMap(map[string]string{})
1093+ resolver := NewEnvironmentVariableResolver(env)
1094+ err := cfg.configureProviders(env, resolver, knownProviders)
1095+ assert.NoError(t, err)
1097+ err = cfg.configureSelectedModels(knownProviders)
1098+ assert.NoError(t, err)
1099+ large := cfg.Models[SelectedModelTypeLarge]
1100+ small := cfg.Models[SelectedModelTypeSmall]
1101+ assert.Equal(t, "large-model", large.Model)
1102+ assert.Equal(t, "openai", large.Provider)
1103+ assert.Equal(t, int64(1000), large.MaxTokens)
1104+ assert.Equal(t, "a-small-model", small.Model)
1105+ assert.Equal(t, "anthropic", small.Provider)
1106+ assert.Equal(t, int64(300), small.MaxTokens)
1109+ t.Run("should override the max tokens only", func(t *testing.T) {
1110+ knownProviders := []provider.Provider{
1114+ DefaultLargeModelID: "large-model",
1115+ DefaultSmallModelID: "small-model",
1116+ Models: []provider.Model{
1119+ DefaultMaxTokens: 1000,
1123+ DefaultMaxTokens: 500,
1130+ Models: map[SelectedModelType]SelectedModel{
1136+ cfg.setDefaults("/tmp")
1137+ env := env.NewFromMap(map[string]string{})
1138+ resolver := NewEnvironmentVariableResolver(env)
1139+ err := cfg.configureProviders(env, resolver, knownProviders)
1140+ assert.NoError(t, err)
1142+ err = cfg.configureSelectedModels(knownProviders)
1143+ assert.NoError(t, err)
1144+ large := cfg.Models[SelectedModelTypeLarge]
1145+ assert.Equal(t, "large-model", large.Model)
1146+ assert.Equal(t, "openai", large.Provider)
1147+ assert.Equal(t, int64(100), large.MaxTokens)