feat: add native Mistral AI provider support
Add Mistral as a first-class provider alongside the 17 existing ones. Mistral uses the OpenAI-compatible API at https://api.mistral.ai/v1 with provider-specific model prefix stripping (mistral/model → model). Changes: - Add Mistral to ProvidersConfig, IsEmpty(), HasProvidersConfig() - Add mistral entry in default model_list (defaults.go) - Add mistral protocol in factory_provider.go and getDefaultAPIBase() - Add mistral prefix stripping in openai_compat normalizeModel() - Add mistral case in legacy factory.go resolveProviderSelection() - Add mistral migration entry in ConvertProvidersToModelList() - Add mistral to supported providers in migrate/config.go - Add mistral section in config.example.json - Update AllProviders test (17 → 18 providers) Tested end-to-end with mistral-small-latest model.
This commit is contained in:
parent
cec6fd4cd4
commit
65422a16a4
9 changed files with 58 additions and 7 deletions
|
|
@ -196,6 +196,10 @@
|
||||||
"volcengine": {
|
"volcengine": {
|
||||||
"api_key": "",
|
"api_key": "",
|
||||||
"api_base": ""
|
"api_base": ""
|
||||||
|
},
|
||||||
|
"mistral": {
|
||||||
|
"api_key": "",
|
||||||
|
"api_base": "https://api.mistral.ai/v1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,7 @@ type ProvidersConfig struct {
|
||||||
GitHubCopilot ProviderConfig `json:"github_copilot"`
|
GitHubCopilot ProviderConfig `json:"github_copilot"`
|
||||||
Antigravity ProviderConfig `json:"antigravity"`
|
Antigravity ProviderConfig `json:"antigravity"`
|
||||||
Qwen ProviderConfig `json:"qwen"`
|
Qwen ProviderConfig `json:"qwen"`
|
||||||
|
Mistral ProviderConfig `json:"mistral"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty checks if all provider configs are empty (no API keys or API bases set)
|
// 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.VolcEngine.APIKey == "" && p.VolcEngine.APIBase == "" &&
|
||||||
p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" &&
|
p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" &&
|
||||||
p.Antigravity.APIKey == "" && p.Antigravity.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
|
// MarshalJSON implements custom JSON marshaling for ProvidersConfig
|
||||||
|
|
@ -636,7 +638,8 @@ func (c *Config) HasProvidersConfig() bool {
|
||||||
v.VolcEngine.APIKey != "" || v.VolcEngine.APIBase != "" ||
|
v.VolcEngine.APIKey != "" || v.VolcEngine.APIBase != "" ||
|
||||||
v.GitHubCopilot.APIKey != "" || v.GitHubCopilot.APIBase != "" ||
|
v.GitHubCopilot.APIKey != "" || v.GitHubCopilot.APIBase != "" ||
|
||||||
v.Antigravity.APIKey != "" || v.Antigravity.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.
|
// ValidateModelList validates all ModelConfig entries in the model_list.
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,14 @@ func DefaultConfig() *Config {
|
||||||
APIKey: "ollama",
|
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
|
// VLLM (local) - http://localhost:8000
|
||||||
{
|
{
|
||||||
ModelName: "local-model",
|
ModelName: "local-model",
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,22 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
|
||||||
}, true
|
}, 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
|
// Process each provider migration
|
||||||
|
|
|
||||||
|
|
@ -131,14 +131,15 @@ func TestConvertProvidersToModelList_AllProviders(t *testing.T) {
|
||||||
GitHubCopilot: ProviderConfig{ConnectMode: "grpc"},
|
GitHubCopilot: ProviderConfig{ConnectMode: "grpc"},
|
||||||
Antigravity: ProviderConfig{AuthMethod: "oauth"},
|
Antigravity: ProviderConfig{AuthMethod: "oauth"},
|
||||||
Qwen: ProviderConfig{APIKey: "key17"},
|
Qwen: ProviderConfig{APIKey: "key17"},
|
||||||
|
Mistral: ProviderConfig{APIKey: "key18"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
result := ConvertProvidersToModelList(cfg)
|
result := ConvertProvidersToModelList(cfg)
|
||||||
|
|
||||||
// All 17 providers should be converted
|
// All 18 providers should be converted
|
||||||
if len(result) != 17 {
|
if len(result) != 18 {
|
||||||
t.Errorf("len(result) = %d, want 17", len(result))
|
t.Errorf("len(result) = %d, want 18", len(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ var supportedProviders = map[string]bool{
|
||||||
"qwen": true,
|
"qwen": true,
|
||||||
"deepseek": true,
|
"deepseek": true,
|
||||||
"github_copilot": true,
|
"github_copilot": true,
|
||||||
|
"mistral": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedChannels = map[string]bool{
|
var supportedChannels = map[string]bool{
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,15 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
|
||||||
sel.model = "deepseek-chat"
|
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":
|
case "github_copilot", "copilot":
|
||||||
sel.providerType = providerTypeGitHubCopilot
|
sel.providerType = providerTypeGitHubCopilot
|
||||||
if cfg.Providers.GitHubCopilot.APIBase != "" {
|
if cfg.Providers.GitHubCopilot.APIBase != "" {
|
||||||
|
|
@ -275,6 +284,13 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
|
||||||
if sel.apiBase == "" {
|
if sel.apiBase == "" {
|
||||||
sel.apiBase = "http://localhost:11434/v1"
|
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 != "":
|
case cfg.Providers.VLLM.APIBase != "":
|
||||||
sel.apiKey = cfg.Providers.VLLM.APIKey
|
sel.apiKey = cfg.Providers.VLLM.APIKey
|
||||||
sel.apiBase = cfg.Providers.VLLM.APIBase
|
sel.apiBase = cfg.Providers.VLLM.APIBase
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
|
||||||
|
|
||||||
case "openrouter", "groq", "zhipu", "gemini", "nvidia",
|
case "openrouter", "groq", "zhipu", "gemini", "nvidia",
|
||||||
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
|
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
|
||||||
"volcengine", "vllm", "qwen":
|
"volcengine", "vllm", "qwen", "mistral":
|
||||||
// All other OpenAI-compatible HTTP providers
|
// All other OpenAI-compatible HTTP providers
|
||||||
if cfg.APIKey == "" && cfg.APIBase == "" {
|
if cfg.APIKey == "" && cfg.APIBase == "" {
|
||||||
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
|
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"
|
return "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
case "vllm":
|
case "vllm":
|
||||||
return "http://localhost:8000/v1"
|
return "http://localhost:8000/v1"
|
||||||
|
case "mistral":
|
||||||
|
return "https://api.mistral.ai/v1"
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ func normalizeModel(model, apiBase string) string {
|
||||||
|
|
||||||
prefix := strings.ToLower(model[:idx])
|
prefix := strings.ToLower(model[:idx])
|
||||||
switch prefix {
|
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:]
|
return model[idx+1:]
|
||||||
default:
|
default:
|
||||||
return model
|
return model
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue