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:
parent
6e1deb8945
commit
338c7dc528
2 changed files with 109 additions and 4 deletions
|
|
@ -335,8 +335,16 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
|
||||||
|
|
||||||
// Check if this is the user's configured provider
|
// Check if this is the user's configured provider
|
||||||
if slices.Contains(m.providerNames, userProvider) && userModel != "" {
|
if slices.Contains(m.providerNames, userProvider) && userModel != "" {
|
||||||
// Use the user's configured model instead of default
|
// Use the user's configured model instead of default.
|
||||||
mc.Model = buildModelWithProtocol(m.protocol, userModel)
|
// 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 {
|
} else if userProvider == "" && userModel != "" && !legacyModelNameApplied {
|
||||||
// Legacy config: no explicit provider field but model is specified
|
// Legacy config: no explicit provider field but model is specified
|
||||||
// Use userModel as ModelName for the FIRST provider so GetModelConfig(model) can find it
|
// Use userModel as ModelName for the FIRST provider so GetModelConfig(model) can find it
|
||||||
|
|
|
||||||
|
|
@ -327,14 +327,16 @@ func TestConvertProvidersToModelList_MultipleProviders_PreservesUserModel(t *tes
|
||||||
t.Fatalf("len(result) = %d, want 2", len(result))
|
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 {
|
for _, mc := range result {
|
||||||
switch mc.ModelName {
|
switch mc.ModelName {
|
||||||
case "openai":
|
case "openai":
|
||||||
if mc.Model != "openai/gpt-5.2" {
|
if mc.Model != "openai/gpt-5.2" {
|
||||||
t.Errorf("OpenAI Model = %q, want %q (default)", 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" {
|
if mc.Model != "deepseek/deepseek-reasoner" {
|
||||||
t.Errorf("DeepSeek Model = %q, want %q (user's)", 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")
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue