diff --git a/config/config.example.json b/config/config.example.json index 77a8c0683..e814fcbb8 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -196,6 +196,10 @@ "volcengine": { "api_key": "", "api_base": "" + }, + "mistral": { + "api_key": "", + "api_base": "https://api.mistral.ai/v1" } }, "tools": { diff --git a/pkg/config/config.go b/pkg/config/config.go index 036021e49..2595398c7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -324,6 +324,7 @@ type ProvidersConfig struct { GitHubCopilot ProviderConfig `json:"github_copilot"` Antigravity ProviderConfig `json:"antigravity"` Qwen ProviderConfig `json:"qwen"` + Mistral ProviderConfig `json:"mistral"` } // IsEmpty checks if all provider configs are empty (no API keys or API bases set) @@ -345,7 +346,8 @@ func (p ProvidersConfig) IsEmpty() bool { p.VolcEngine.APIKey == "" && p.VolcEngine.APIBase == "" && p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" && p.Antigravity.APIKey == "" && p.Antigravity.APIBase == "" && - p.Qwen.APIKey == "" && p.Qwen.APIBase == "" + p.Qwen.APIKey == "" && p.Qwen.APIBase == "" && + p.Mistral.APIKey == "" && p.Mistral.APIBase == "" } // MarshalJSON implements custom JSON marshaling for ProvidersConfig @@ -644,7 +646,8 @@ func (c *Config) HasProvidersConfig() bool { v.VolcEngine.APIKey != "" || v.VolcEngine.APIBase != "" || v.GitHubCopilot.APIKey != "" || v.GitHubCopilot.APIBase != "" || v.Antigravity.APIKey != "" || v.Antigravity.APIBase != "" || - v.Qwen.APIKey != "" || v.Qwen.APIBase != "" + v.Qwen.APIKey != "" || v.Qwen.APIBase != "" || + v.Mistral.APIKey != "" || v.Mistral.APIBase != "" } // ValidateModelList validates all ModelConfig entries in the model_list. diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 7654326e7..065273c28 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -255,6 +255,14 @@ func DefaultConfig() *Config { APIKey: "ollama", }, + // Mistral AI - https://console.mistral.ai/api-keys + { + ModelName: "mistral-small", + Model: "mistral/mistral-small-latest", + APIBase: "https://api.mistral.ai/v1", + APIKey: "", + }, + // VLLM (local) - http://localhost:8000 { ModelName: "local-model", diff --git a/pkg/config/migration.go b/pkg/config/migration.go index 689e2312f..30eaa7474 100644 --- a/pkg/config/migration.go +++ b/pkg/config/migration.go @@ -324,6 +324,22 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig { }, true }, }, + { + providerNames: []string{"mistral"}, + protocol: "mistral", + buildConfig: func(p ProvidersConfig) (ModelConfig, bool) { + if p.Mistral.APIKey == "" && p.Mistral.APIBase == "" { + return ModelConfig{}, false + } + return ModelConfig{ + ModelName: "mistral", + Model: "mistral/mistral-small-latest", + APIKey: p.Mistral.APIKey, + APIBase: p.Mistral.APIBase, + Proxy: p.Mistral.Proxy, + }, true + }, + }, } // Process each provider migration diff --git a/pkg/config/migration_test.go b/pkg/config/migration_test.go index 1e8139e68..42165cb71 100644 --- a/pkg/config/migration_test.go +++ b/pkg/config/migration_test.go @@ -131,14 +131,15 @@ func TestConvertProvidersToModelList_AllProviders(t *testing.T) { GitHubCopilot: ProviderConfig{ConnectMode: "grpc"}, Antigravity: ProviderConfig{AuthMethod: "oauth"}, Qwen: ProviderConfig{APIKey: "key17"}, + Mistral: ProviderConfig{APIKey: "key18"}, }, } result := ConvertProvidersToModelList(cfg) - // All 17 providers should be converted - if len(result) != 17 { - t.Errorf("len(result) = %d, want 17", len(result)) + // All 18 providers should be converted + if len(result) != 18 { + t.Errorf("len(result) = %d, want 18", len(result)) } } diff --git a/pkg/migrate/config.go b/pkg/migrate/config.go index 2237a1429..24ce33e94 100644 --- a/pkg/migrate/config.go +++ b/pkg/migrate/config.go @@ -22,6 +22,7 @@ var supportedProviders = map[string]bool{ "qwen": true, "deepseek": true, "github_copilot": true, + "mistral": true, } var supportedChannels = map[string]bool{ diff --git a/pkg/providers/factory.go b/pkg/providers/factory.go index b6f1b5e21..cda4753ea 100644 --- a/pkg/providers/factory.go +++ b/pkg/providers/factory.go @@ -172,6 +172,15 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) { sel.model = "deepseek-chat" } } + case "mistral": + if cfg.Providers.Mistral.APIKey != "" { + sel.apiKey = cfg.Providers.Mistral.APIKey + sel.apiBase = cfg.Providers.Mistral.APIBase + sel.proxy = cfg.Providers.Mistral.Proxy + if sel.apiBase == "" { + sel.apiBase = "https://api.mistral.ai/v1" + } + } case "github_copilot", "copilot": sel.providerType = providerTypeGitHubCopilot if cfg.Providers.GitHubCopilot.APIBase != "" { @@ -275,6 +284,13 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) { if sel.apiBase == "" { sel.apiBase = "http://localhost:11434/v1" } + case (strings.Contains(lowerModel, "mistral") || strings.HasPrefix(model, "mistral/")) && cfg.Providers.Mistral.APIKey != "": + sel.apiKey = cfg.Providers.Mistral.APIKey + sel.apiBase = cfg.Providers.Mistral.APIBase + sel.proxy = cfg.Providers.Mistral.Proxy + if sel.apiBase == "" { + sel.apiBase = "https://api.mistral.ai/v1" + } case cfg.Providers.VLLM.APIBase != "": sel.apiKey = cfg.Providers.VLLM.APIKey sel.apiBase = cfg.Providers.VLLM.APIBase diff --git a/pkg/providers/factory_provider.go b/pkg/providers/factory_provider.go index 74fe8a36c..7d5566eef 100644 --- a/pkg/providers/factory_provider.go +++ b/pkg/providers/factory_provider.go @@ -88,7 +88,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err case "openrouter", "groq", "zhipu", "gemini", "nvidia", "ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras", - "volcengine", "vllm", "qwen": + "volcengine", "vllm", "qwen", "mistral": // All other OpenAI-compatible HTTP providers if cfg.APIKey == "" && cfg.APIBase == "" { return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol) @@ -186,6 +186,8 @@ func getDefaultAPIBase(protocol string) string { return "https://dashscope.aliyuncs.com/compatible-mode/v1" case "vllm": return "http://localhost:8000/v1" + case "mistral": + return "https://api.mistral.ai/v1" default: return "" } diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index b8528953a..236a048c4 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -240,7 +240,7 @@ func normalizeModel(model, apiBase string) string { prefix := strings.ToLower(model[:idx]) switch prefix { - case "moonshot", "nvidia", "groq", "ollama", "deepseek", "google", "openrouter", "zhipu": + case "moonshot", "nvidia", "groq", "ollama", "deepseek", "google", "openrouter", "zhipu", "mistral": return model[idx+1:] default: return model diff --git a/pkg/providers/protocoltypes/types.go b/pkg/providers/protocoltypes/types.go index 3a089ca47..5e1c6d397 100644 --- a/pkg/providers/protocoltypes/types.go +++ b/pkg/providers/protocoltypes/types.go @@ -4,8 +4,8 @@ type ToolCall struct { ID string `json:"id"` Type string `json:"type,omitempty"` Function *FunctionCall `json:"function,omitempty"` - Name string `json:"name,omitempty"` - Arguments map[string]any `json:"arguments,omitempty"` + Name string `json:"-"` + Arguments map[string]any `json:"-"` ThoughtSignature string `json:"-"` // Internal use only ExtraContent *ExtraContent `json:"extra_content,omitempty"` }