feat: add Kimi/Moonshot and Opencode provider support

- Add "kimi", "kimi-code", "moonshot" provider cases in factory.go
  with default API base https://api.kimi.com/coding/v1
- Add Kimi Code API User-Agent header (KimiCLI/0.77) for api.kimi.com
- Add "opencode" provider with default API base https://opencode.ai/zen/v1
- Add "opencode" to recognized HTTP-compatible protocols in factory_provider
- Add Opencode field to ProvidersConfig, IsEmpty, HasProvidersConfig
- Add opencode migration entry in ConvertProvidersToModelList
- Update moonshot fallback API base from api.moonshot.cn to api.kimi.com
This commit is contained in:
I Putu Eddy Irawan
2026-03-01 08:48:04 +07:00
parent 9c9524f934
commit ec540312da
5 changed files with 48 additions and 4 deletions
+5 -2
View File
@@ -401,6 +401,7 @@ type ProvidersConfig struct {
Antigravity ProviderConfig `json:"antigravity"`
Qwen ProviderConfig `json:"qwen"`
Mistral ProviderConfig `json:"mistral"`
Opencode ProviderConfig `json:"opencode"`
}
// IsEmpty checks if all provider configs are empty (no API keys or API bases set)
@@ -423,7 +424,8 @@ func (p ProvidersConfig) IsEmpty() bool {
p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" &&
p.Antigravity.APIKey == "" && p.Antigravity.APIBase == "" &&
p.Qwen.APIKey == "" && p.Qwen.APIBase == "" &&
p.Mistral.APIKey == "" && p.Mistral.APIBase == ""
p.Mistral.APIKey == "" && p.Mistral.APIBase == "" &&
p.Opencode.APIKey == "" && p.Opencode.APIBase == ""
}
// MarshalJSON implements custom JSON marshaling for ProvidersConfig
@@ -760,7 +762,8 @@ func (c *Config) HasProvidersConfig() bool {
v.GitHubCopilot.APIKey != "" || v.GitHubCopilot.APIBase != "" ||
v.Antigravity.APIKey != "" || v.Antigravity.APIBase != "" ||
v.Qwen.APIKey != "" || v.Qwen.APIBase != "" ||
v.Mistral.APIKey != "" || v.Mistral.APIBase != ""
v.Mistral.APIKey != "" || v.Mistral.APIBase != "" ||
v.Opencode.APIKey != "" || v.Opencode.APIBase != ""
}
// ValidateModelList validates all ModelConfig entries in the model_list.
+17
View File
@@ -356,6 +356,23 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
}, true
},
},
{
providerNames: []string{"opencode"},
protocol: "opencode",
buildConfig: func(p ProvidersConfig) (ModelConfig, bool) {
if p.Opencode.APIKey == "" && p.Opencode.APIBase == "" {
return ModelConfig{}, false
}
return ModelConfig{
ModelName: "opencode",
Model: "opencode/auto",
APIKey: p.Opencode.APIKey,
APIBase: p.Opencode.APIBase,
Proxy: p.Opencode.Proxy,
RequestTimeout: p.Opencode.RequestTimeout,
}, true
},
},
}
// Process each provider migration
+19 -1
View File
@@ -181,6 +181,24 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
sel.apiBase = "https://api.mistral.ai/v1"
}
}
case "opencode":
if cfg.Providers.Opencode.APIKey != "" {
sel.apiKey = cfg.Providers.Opencode.APIKey
sel.apiBase = cfg.Providers.Opencode.APIBase
sel.proxy = cfg.Providers.Opencode.Proxy
if sel.apiBase == "" {
sel.apiBase = "https://opencode.ai/zen/v1"
}
}
case "kimi", "kimi-code", "moonshot":
if cfg.Providers.Moonshot.APIKey != "" {
sel.apiKey = cfg.Providers.Moonshot.APIKey
sel.apiBase = cfg.Providers.Moonshot.APIBase
sel.proxy = cfg.Providers.Moonshot.Proxy
if sel.apiBase == "" {
sel.apiBase = "https://api.kimi.com/coding/v1"
}
}
case "github_copilot", "copilot":
sel.providerType = providerTypeGitHubCopilot
if cfg.Providers.GitHubCopilot.APIBase != "" {
@@ -201,7 +219,7 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
sel.apiBase = cfg.Providers.Moonshot.APIBase
sel.proxy = cfg.Providers.Moonshot.Proxy
if sel.apiBase == "" {
sel.apiBase = "https://api.moonshot.cn/v1"
sel.apiBase = "https://api.kimi.com/coding/v1"
}
case strings.HasPrefix(model, "openrouter/") ||
strings.HasPrefix(model, "anthropic/") ||
+3 -1
View File
@@ -94,7 +94,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
case "openrouter", "groq", "zhipu", "gemini", "nvidia",
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
"volcengine", "vllm", "qwen", "mistral":
"volcengine", "vllm", "qwen", "mistral", "opencode":
// 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)
@@ -206,6 +206,8 @@ func getDefaultAPIBase(protocol string) string {
return "http://localhost:8000/v1"
case "mistral":
return "https://api.mistral.ai/v1"
case "opencode":
return "https://opencode.ai/zen/v1"
default:
return ""
}
+4
View File
@@ -176,6 +176,10 @@ func (p *Provider) Chat(
if p.apiKey != "" {
req.Header.Set("Authorization", "Bearer "+p.apiKey)
}
// Kimi Code API requires a coding agent User-Agent
if strings.Contains(p.apiBase, "api.kimi.com") {
req.Header.Set("User-Agent", "KimiCLI/0.77")
}
resp, err := p.httpClient.Do(req)
if err != nil {