fix(config): resolve nvidia provider model lookup failure for custom model names

When a user configured provider=nvidia with a model like kimi-k2.5 (or
moonshotai/kimi-k2.5), ConvertProvidersToModelList updated mc.Model but
left mc.ModelName as "nvidia". GetModelConfig then searched for
ModelName=="kimi-k2.5" and found nothing, producing the "no API key
configured for model" error.

Two issues fixed:
- mc.ModelName is now set to userModel so GetModelConfig(userModel) finds
  the migrated entry.
- The protocol prefix is added with a strict HasPrefix check, so model IDs
  that already contain "/" but are NOT protocol-prefixed (e.g. Nvidia's
  "moonshotai/kimi-k2.5") are correctly wrapped as "nvidia/moonshotai/kimi-k2.5"
  instead of being passed through with the wrong protocol.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mrigankad 2026-02-20 18:14:37 +05:30
parent 6e1deb8945
commit 338c7dc528
2 changed files with 109 additions and 4 deletions

View file

@ -335,8 +335,16 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
// Check if this is the user's configured provider
if slices.Contains(m.providerNames, userProvider) && userModel != "" {
// Use the user's configured model instead of default
mc.Model = buildModelWithProtocol(m.protocol, userModel)
// Use the user's configured model instead of default.
// Add protocol prefix unless the model already starts with it.
// This handles API-specific model IDs that contain "/" (e.g. "moonshotai/kimi-k2.5" on Nvidia).
if strings.HasPrefix(strings.ToLower(userModel), m.protocol+"/") {
mc.Model = userModel
} else {
mc.Model = m.protocol + "/" + userModel
}
// Update ModelName so GetModelConfig(userModel) can find this entry.
mc.ModelName = userModel
} else if userProvider == "" && userModel != "" && !legacyModelNameApplied {
// Legacy config: no explicit provider field but model is specified
// Use userModel as ModelName for the FIRST provider so GetModelConfig(model) can find it

View file

@ -327,14 +327,16 @@ func TestConvertProvidersToModelList_MultipleProviders_PreservesUserModel(t *tes
t.Fatalf("len(result) = %d, want 2", len(result))
}
// Find each provider and verify model
// Find each provider and verify model.
// After migration, user-selected provider ModelName becomes the user's model name.
for _, mc := range result {
switch mc.ModelName {
case "openai":
if mc.Model != "openai/gpt-5.2" {
t.Errorf("OpenAI Model = %q, want %q (default)", mc.Model, "openai/gpt-5.2")
}
case "deepseek":
case "deepseek-reasoner":
// The user's explicit provider (deepseek) now uses the user's model as ModelName
if mc.Model != "deepseek/deepseek-reasoner" {
t.Errorf("DeepSeek Model = %q, want %q (user's)", mc.Model, "deepseek/deepseek-reasoner")
}
@ -549,3 +551,98 @@ func TestConvertProvidersToModelList_LegacyModelWithProtocolPrefix(t *testing.T)
t.Errorf("Model = %q, want %q (should not duplicate prefix)", result[0].Model, "openrouter/auto")
}
}
// Tests for nvidia provider with custom (third-party) model names.
// Nvidia's API hosts models from various vendors using identifiers like "moonshotai/kimi-k2.5".
func TestConvertProvidersToModelList_Nvidia_SimpleModel(t *testing.T) {
cfg := &Config{
Agents: AgentsConfig{
Defaults: AgentDefaults{
Provider: "nvidia",
Model: "kimi-k2.5",
},
},
Providers: ProvidersConfig{
Nvidia: ProviderConfig{APIKey: "nvapi-xxx", APIBase: "https://integrate.api.nvidia.com/v1"},
},
}
result := ConvertProvidersToModelList(cfg)
if len(result) != 1 {
t.Fatalf("len(result) = %d, want 1", len(result))
}
// ModelName must match the user's model so GetModelConfig("kimi-k2.5") can find it
if result[0].ModelName != "kimi-k2.5" {
t.Errorf("ModelName = %q, want %q", result[0].ModelName, "kimi-k2.5")
}
// Model should be prefixed with the nvidia protocol
if result[0].Model != "nvidia/kimi-k2.5" {
t.Errorf("Model = %q, want %q", result[0].Model, "nvidia/kimi-k2.5")
}
}
func TestConvertProvidersToModelList_Nvidia_VendorPrefixedModel(t *testing.T) {
// Nvidia hosts third-party models like "moonshotai/kimi-k2.5".
// The "/" is part of the model ID, not a protocol separator.
cfg := &Config{
Agents: AgentsConfig{
Defaults: AgentDefaults{
Provider: "nvidia",
Model: "moonshotai/kimi-k2.5",
},
},
Providers: ProvidersConfig{
Nvidia: ProviderConfig{APIKey: "nvapi-xxx", APIBase: "https://integrate.api.nvidia.com/v1"},
},
}
result := ConvertProvidersToModelList(cfg)
if len(result) != 1 {
t.Fatalf("len(result) = %d, want 1", len(result))
}
// ModelName must match the user's model so GetModelConfig("moonshotai/kimi-k2.5") can find it
if result[0].ModelName != "moonshotai/kimi-k2.5" {
t.Errorf("ModelName = %q, want %q", result[0].ModelName, "moonshotai/kimi-k2.5")
}
// Model should be wrapped with nvidia/ protocol prefix so the provider is resolved correctly
if result[0].Model != "nvidia/moonshotai/kimi-k2.5" {
t.Errorf("Model = %q, want %q", result[0].Model, "nvidia/moonshotai/kimi-k2.5")
}
}
func TestConvertProvidersToModelList_Nvidia_ExplicitNvidiaPrefix(t *testing.T) {
// User specifies nvidia/kimi-k2.5 - should not double-prefix.
cfg := &Config{
Agents: AgentsConfig{
Defaults: AgentDefaults{
Provider: "nvidia",
Model: "nvidia/kimi-k2.5",
},
},
Providers: ProvidersConfig{
Nvidia: ProviderConfig{APIKey: "nvapi-xxx"},
},
}
result := ConvertProvidersToModelList(cfg)
if len(result) != 1 {
t.Fatalf("len(result) = %d, want 1", len(result))
}
if result[0].ModelName != "nvidia/kimi-k2.5" {
t.Errorf("ModelName = %q, want %q", result[0].ModelName, "nvidia/kimi-k2.5")
}
// Should NOT produce nvidia/nvidia/kimi-k2.5
if result[0].Model != "nvidia/kimi-k2.5" {
t.Errorf("Model = %q, want %q (should not double-prefix)", result[0].Model, "nvidia/kimi-k2.5")
}
}