Skip to content

Commit ae74fa3

Browse files
authored
Merge pull request #541 from edouard-claude/feat/mistral-provider
feat: add native Mistral AI provider support
2 parents 6d487a1 + 34a8ce5 commit ae74fa3

10 files changed

Lines changed: 60 additions & 9 deletions

File tree

config/config.example.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@
196196
"volcengine": {
197197
"api_key": "",
198198
"api_base": ""
199+
},
200+
"mistral": {
201+
"api_key": "",
202+
"api_base": "https://api.mistral.ai/v1"
199203
}
200204
},
201205
"tools": {

pkg/config/config.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ type ProvidersConfig struct {
324324
GitHubCopilot ProviderConfig `json:"github_copilot"`
325325
Antigravity ProviderConfig `json:"antigravity"`
326326
Qwen ProviderConfig `json:"qwen"`
327+
Mistral ProviderConfig `json:"mistral"`
327328
}
328329

329330
// IsEmpty checks if all provider configs are empty (no API keys or API bases set)
@@ -345,7 +346,8 @@ func (p ProvidersConfig) IsEmpty() bool {
345346
p.VolcEngine.APIKey == "" && p.VolcEngine.APIBase == "" &&
346347
p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" &&
347348
p.Antigravity.APIKey == "" && p.Antigravity.APIBase == "" &&
348-
p.Qwen.APIKey == "" && p.Qwen.APIBase == ""
349+
p.Qwen.APIKey == "" && p.Qwen.APIBase == "" &&
350+
p.Mistral.APIKey == "" && p.Mistral.APIBase == ""
349351
}
350352

351353
// MarshalJSON implements custom JSON marshaling for ProvidersConfig
@@ -644,7 +646,8 @@ func (c *Config) HasProvidersConfig() bool {
644646
v.VolcEngine.APIKey != "" || v.VolcEngine.APIBase != "" ||
645647
v.GitHubCopilot.APIKey != "" || v.GitHubCopilot.APIBase != "" ||
646648
v.Antigravity.APIKey != "" || v.Antigravity.APIBase != "" ||
647-
v.Qwen.APIKey != "" || v.Qwen.APIBase != ""
649+
v.Qwen.APIKey != "" || v.Qwen.APIBase != "" ||
650+
v.Mistral.APIKey != "" || v.Mistral.APIBase != ""
648651
}
649652

650653
// ValidateModelList validates all ModelConfig entries in the model_list.

pkg/config/defaults.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ func DefaultConfig() *Config {
255255
APIKey: "ollama",
256256
},
257257

258+
// Mistral AI - https://console.mistral.ai/api-keys
259+
{
260+
ModelName: "mistral-small",
261+
Model: "mistral/mistral-small-latest",
262+
APIBase: "https://api.mistral.ai/v1",
263+
APIKey: "",
264+
},
265+
258266
// VLLM (local) - http://localhost:8000
259267
{
260268
ModelName: "local-model",

pkg/config/migration.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,22 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
324324
}, true
325325
},
326326
},
327+
{
328+
providerNames: []string{"mistral"},
329+
protocol: "mistral",
330+
buildConfig: func(p ProvidersConfig) (ModelConfig, bool) {
331+
if p.Mistral.APIKey == "" && p.Mistral.APIBase == "" {
332+
return ModelConfig{}, false
333+
}
334+
return ModelConfig{
335+
ModelName: "mistral",
336+
Model: "mistral/mistral-small-latest",
337+
APIKey: p.Mistral.APIKey,
338+
APIBase: p.Mistral.APIBase,
339+
Proxy: p.Mistral.Proxy,
340+
}, true
341+
},
342+
},
327343
}
328344

329345
// Process each provider migration

pkg/config/migration_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,15 @@ func TestConvertProvidersToModelList_AllProviders(t *testing.T) {
131131
GitHubCopilot: ProviderConfig{ConnectMode: "grpc"},
132132
Antigravity: ProviderConfig{AuthMethod: "oauth"},
133133
Qwen: ProviderConfig{APIKey: "key17"},
134+
Mistral: ProviderConfig{APIKey: "key18"},
134135
},
135136
}
136137

137138
result := ConvertProvidersToModelList(cfg)
138139

139-
// All 17 providers should be converted
140-
if len(result) != 17 {
141-
t.Errorf("len(result) = %d, want 17", len(result))
140+
// All 18 providers should be converted
141+
if len(result) != 18 {
142+
t.Errorf("len(result) = %d, want 18", len(result))
142143
}
143144
}
144145

pkg/migrate/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var supportedProviders = map[string]bool{
2222
"qwen": true,
2323
"deepseek": true,
2424
"github_copilot": true,
25+
"mistral": true,
2526
}
2627

2728
var supportedChannels = map[string]bool{

pkg/providers/factory.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,15 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
172172
sel.model = "deepseek-chat"
173173
}
174174
}
175+
case "mistral":
176+
if cfg.Providers.Mistral.APIKey != "" {
177+
sel.apiKey = cfg.Providers.Mistral.APIKey
178+
sel.apiBase = cfg.Providers.Mistral.APIBase
179+
sel.proxy = cfg.Providers.Mistral.Proxy
180+
if sel.apiBase == "" {
181+
sel.apiBase = "https://api.mistral.ai/v1"
182+
}
183+
}
175184
case "github_copilot", "copilot":
176185
sel.providerType = providerTypeGitHubCopilot
177186
if cfg.Providers.GitHubCopilot.APIBase != "" {
@@ -275,6 +284,13 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
275284
if sel.apiBase == "" {
276285
sel.apiBase = "http://localhost:11434/v1"
277286
}
287+
case (strings.Contains(lowerModel, "mistral") || strings.HasPrefix(model, "mistral/")) && cfg.Providers.Mistral.APIKey != "":
288+
sel.apiKey = cfg.Providers.Mistral.APIKey
289+
sel.apiBase = cfg.Providers.Mistral.APIBase
290+
sel.proxy = cfg.Providers.Mistral.Proxy
291+
if sel.apiBase == "" {
292+
sel.apiBase = "https://api.mistral.ai/v1"
293+
}
278294
case cfg.Providers.VLLM.APIBase != "":
279295
sel.apiKey = cfg.Providers.VLLM.APIKey
280296
sel.apiBase = cfg.Providers.VLLM.APIBase

pkg/providers/factory_provider.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
8888

8989
case "openrouter", "groq", "zhipu", "gemini", "nvidia",
9090
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
91-
"volcengine", "vllm", "qwen":
91+
"volcengine", "vllm", "qwen", "mistral":
9292
// All other OpenAI-compatible HTTP providers
9393
if cfg.APIKey == "" && cfg.APIBase == "" {
9494
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 {
186186
return "https://dashscope.aliyuncs.com/compatible-mode/v1"
187187
case "vllm":
188188
return "http://localhost:8000/v1"
189+
case "mistral":
190+
return "https://api.mistral.ai/v1"
189191
default:
190192
return ""
191193
}

pkg/providers/openai_compat/provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func normalizeModel(model, apiBase string) string {
240240

241241
prefix := strings.ToLower(model[:idx])
242242
switch prefix {
243-
case "moonshot", "nvidia", "groq", "ollama", "deepseek", "google", "openrouter", "zhipu":
243+
case "moonshot", "nvidia", "groq", "ollama", "deepseek", "google", "openrouter", "zhipu", "mistral":
244244
return model[idx+1:]
245245
default:
246246
return model

pkg/providers/protocoltypes/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ type ToolCall struct {
44
ID string `json:"id"`
55
Type string `json:"type,omitempty"`
66
Function *FunctionCall `json:"function,omitempty"`
7-
Name string `json:"name,omitempty"`
8-
Arguments map[string]any `json:"arguments,omitempty"`
7+
Name string `json:"-"`
8+
Arguments map[string]any `json:"-"`
99
ThoughtSignature string `json:"-"` // Internal use only
1010
ExtraContent *ExtraContent `json:"extra_content,omitempty"`
1111
}

0 commit comments

Comments
 (0)