fix: complete provider migration from Moonshot/Kimi to Z.ai

The previous migration (c96c882) was incomplete - the agent was still
resolving to Moonshot/Kimi provider configs instead of Z.ai. This
commit fully migrates the provider system:

- Rename Zhipu/Moonshot provider to Zai in ProvidersConfig (with
  backward-compat fields for "zhipu" and "moonshot" JSON keys)
- Add migrateProviders() in LoadConfig to auto-migrate old configs
- Merge moonshot/kimi and zhipu/glm provider cases into unified zai
  case in CreateProvider
- Update default API base to https://api.z.ai/api/paas/v4
- Update status command, migration logic, example config, and tests

https://claude.ai/code/session_01MYemTMPtHrcgidWs8UdjcG
This commit is contained in:
Claude 2026-02-18 12:12:42 +00:00
parent 2cef47c15b
commit 09a03c4ed5
No known key found for this signature in database
8 changed files with 60 additions and 43 deletions

View file

@ -20,7 +20,7 @@
## 🧪 Test Environment & Hardware ## 🧪 Test Environment & Hardware
- **Hardware:** [e.g. Raspberry Pi 5, Orange Pi, PC] - **Hardware:** [e.g. Raspberry Pi 5, Orange Pi, PC]
- **OS:** [e.g. Debian 12, Ubuntu 22.04] - **OS:** [e.g. Debian 12, Ubuntu 22.04]
- **Model/Provider:** [e.g. OpenAI GPT-4o, Kimi k2, DeepSeek-V3] - **Model/Provider:** [e.g. OpenAI GPT-4o, Z.ai GLM-4.7, DeepSeek-V3]
- **Channels:** [e.g. Discord, Telegram, Feishu, ...] - **Channels:** [e.g. Discord, Telegram, Feishu, ...]

View file

@ -729,7 +729,7 @@ func statusCmd() {
hasAnthropic := cfg.Providers.Anthropic.APIKey != "" hasAnthropic := cfg.Providers.Anthropic.APIKey != ""
hasOpenAI := cfg.Providers.OpenAI.APIKey != "" hasOpenAI := cfg.Providers.OpenAI.APIKey != ""
hasGemini := cfg.Providers.Gemini.APIKey != "" hasGemini := cfg.Providers.Gemini.APIKey != ""
hasZhipu := cfg.Providers.Zhipu.APIKey != "" hasZai := cfg.Providers.Zai.APIKey != ""
hasGroq := cfg.Providers.Groq.APIKey != "" hasGroq := cfg.Providers.Groq.APIKey != ""
hasVLLM := cfg.Providers.VLLM.APIBase != "" hasVLLM := cfg.Providers.VLLM.APIBase != ""
@ -743,7 +743,7 @@ func statusCmd() {
fmt.Println("Anthropic API:", status(hasAnthropic)) fmt.Println("Anthropic API:", status(hasAnthropic))
fmt.Println("OpenAI API:", status(hasOpenAI)) fmt.Println("OpenAI API:", status(hasOpenAI))
fmt.Println("Gemini API:", status(hasGemini)) fmt.Println("Gemini API:", status(hasGemini))
fmt.Println("Zhipu API:", status(hasZhipu)) fmt.Println("Z.ai API:", status(hasZai))
fmt.Println("Groq API:", status(hasGroq)) fmt.Println("Groq API:", status(hasGroq))
if hasVLLM { if hasVLLM {
fmt.Printf("vLLM/Local: ✓ %s\n", cfg.Providers.VLLM.APIBase) fmt.Printf("vLLM/Local: ✓ %s\n", cfg.Providers.VLLM.APIBase)

View file

@ -89,8 +89,8 @@
"api_key": "gsk_xxx", "api_key": "gsk_xxx",
"api_base": "" "api_base": ""
}, },
"zhipu": { "zai": {
"api_key": "YOUR_ZHIPU_API_KEY", "api_key": "YOUR_ZAI_API_KEY",
"api_base": "" "api_base": ""
}, },
"gemini": { "gemini": {
@ -107,7 +107,7 @@
"proxy": "http://127.0.0.1:7890" "proxy": "http://127.0.0.1:7890"
}, },
"moonshot": { "moonshot": {
"api_key": "sk-xxx", "api_key": "",
"api_base": "" "api_base": ""
}, },
"ollama": { "ollama": {

View file

@ -171,12 +171,13 @@ type ProvidersConfig struct {
OpenAI ProviderConfig `json:"openai"` OpenAI ProviderConfig `json:"openai"`
OpenRouter ProviderConfig `json:"openrouter"` OpenRouter ProviderConfig `json:"openrouter"`
Groq ProviderConfig `json:"groq"` Groq ProviderConfig `json:"groq"`
Zhipu ProviderConfig `json:"zhipu"` Zai ProviderConfig `json:"zai"`
Zhipu ProviderConfig `json:"zhipu"` // Deprecated: use "zai" instead
VLLM ProviderConfig `json:"vllm"` VLLM ProviderConfig `json:"vllm"`
Gemini ProviderConfig `json:"gemini"` Gemini ProviderConfig `json:"gemini"`
Nvidia ProviderConfig `json:"nvidia"` Nvidia ProviderConfig `json:"nvidia"`
Ollama ProviderConfig `json:"ollama"` Ollama ProviderConfig `json:"ollama"`
Moonshot ProviderConfig `json:"moonshot"` Moonshot ProviderConfig `json:"moonshot"` // Deprecated: use "zai" instead
ShengSuanYun ProviderConfig `json:"shengsuanyun"` ShengSuanYun ProviderConfig `json:"shengsuanyun"`
DeepSeek ProviderConfig `json:"deepseek"` DeepSeek ProviderConfig `json:"deepseek"`
GitHubCopilot ProviderConfig `json:"github_copilot"` GitHubCopilot ProviderConfig `json:"github_copilot"`
@ -311,11 +312,10 @@ func DefaultConfig() *Config {
OpenAI: ProviderConfig{}, OpenAI: ProviderConfig{},
OpenRouter: ProviderConfig{}, OpenRouter: ProviderConfig{},
Groq: ProviderConfig{}, Groq: ProviderConfig{},
Zhipu: ProviderConfig{}, Zai: ProviderConfig{},
VLLM: ProviderConfig{}, VLLM: ProviderConfig{},
Gemini: ProviderConfig{}, Gemini: ProviderConfig{},
Nvidia: ProviderConfig{}, Nvidia: ProviderConfig{},
Moonshot: ProviderConfig{},
ShengSuanYun: ProviderConfig{}, ShengSuanYun: ProviderConfig{},
}, },
Gateway: GatewayConfig{ Gateway: GatewayConfig{
@ -373,9 +373,24 @@ func LoadConfig(path string) (*Config, error) {
return nil, err return nil, err
} }
// Migrate deprecated provider configs to Zai (Z.ai, formerly Zhipu/Moonshot)
cfg.migrateProviders()
return cfg, nil return cfg, nil
} }
// migrateProviders merges deprecated Zhipu and Moonshot provider configs into Zai.
// If Zai is not configured, it falls back to Zhipu first, then Moonshot.
func (c *Config) migrateProviders() {
if c.Providers.Zai.APIKey == "" {
if c.Providers.Zhipu.APIKey != "" {
c.Providers.Zai = c.Providers.Zhipu
} else if c.Providers.Moonshot.APIKey != "" {
c.Providers.Zai = c.Providers.Moonshot
}
}
}
func SaveConfig(path string, cfg *Config) error { func SaveConfig(path string, cfg *Config) error {
cfg.mu.RLock() cfg.mu.RLock()
defer cfg.mu.RUnlock() defer cfg.mu.RUnlock()
@ -414,8 +429,8 @@ func (c *Config) GetAPIKey() string {
if c.Providers.Gemini.APIKey != "" { if c.Providers.Gemini.APIKey != "" {
return c.Providers.Gemini.APIKey return c.Providers.Gemini.APIKey
} }
if c.Providers.Zhipu.APIKey != "" { if c.Providers.Zai.APIKey != "" {
return c.Providers.Zhipu.APIKey return c.Providers.Zai.APIKey
} }
if c.Providers.Groq.APIKey != "" { if c.Providers.Groq.APIKey != "" {
return c.Providers.Groq.APIKey return c.Providers.Groq.APIKey
@ -438,8 +453,8 @@ func (c *Config) GetAPIBase() string {
} }
return "https://openrouter.ai/api/v1" return "https://openrouter.ai/api/v1"
} }
if c.Providers.Zhipu.APIKey != "" { if c.Providers.Zai.APIKey != "" {
return c.Providers.Zhipu.APIBase return c.Providers.Zai.APIBase
} }
if c.Providers.VLLM.APIKey != "" && c.Providers.VLLM.APIBase != "" { if c.Providers.VLLM.APIKey != "" && c.Providers.VLLM.APIBase != "" {
return c.Providers.VLLM.APIBase return c.Providers.VLLM.APIBase

View file

@ -92,8 +92,8 @@ func TestDefaultConfig_Providers(t *testing.T) {
if cfg.Providers.Groq.APIKey != "" { if cfg.Providers.Groq.APIKey != "" {
t.Error("Groq API key should be empty by default") t.Error("Groq API key should be empty by default")
} }
if cfg.Providers.Zhipu.APIKey != "" { if cfg.Providers.Zai.APIKey != "" {
t.Error("Zhipu API key should be empty by default") t.Error("Z.ai API key should be empty by default")
} }
if cfg.Providers.VLLM.APIKey != "" { if cfg.Providers.VLLM.APIKey != "" {
t.Error("VLLM API key should be empty by default") t.Error("VLLM API key should be empty by default")

View file

@ -113,8 +113,8 @@ func ConvertConfig(data map[string]interface{}) (*config.Config, []string, error
cfg.Providers.OpenRouter = pc cfg.Providers.OpenRouter = pc
case "groq": case "groq":
cfg.Providers.Groq = pc cfg.Providers.Groq = pc
case "zhipu": case "zhipu", "zai":
cfg.Providers.Zhipu = pc cfg.Providers.Zai = pc
case "vllm": case "vllm":
cfg.Providers.VLLM = pc cfg.Providers.VLLM = pc
case "gemini": case "gemini":
@ -244,8 +244,8 @@ func MergeConfig(existing, incoming *config.Config) *config.Config {
if existing.Providers.Groq.APIKey == "" { if existing.Providers.Groq.APIKey == "" {
existing.Providers.Groq = incoming.Providers.Groq existing.Providers.Groq = incoming.Providers.Groq
} }
if existing.Providers.Zhipu.APIKey == "" { if existing.Providers.Zai.APIKey == "" {
existing.Providers.Zhipu = incoming.Providers.Zhipu existing.Providers.Zai = incoming.Providers.Zai
} }
if existing.Providers.VLLM.APIKey == "" && existing.Providers.VLLM.APIBase == "" { if existing.Providers.VLLM.APIKey == "" && existing.Providers.VLLM.APIBase == "" {
existing.Providers.VLLM = incoming.Providers.VLLM existing.Providers.VLLM = incoming.Providers.VLLM

View file

@ -158,7 +158,7 @@ func resolveCodexModel(model string) (string, string) {
"anthropic", "anthropic",
"gemini", "gemini",
"google", "google",
"moonshot", "zai",
"kimi", "kimi",
"qwen", "qwen",
"deepseek", "deepseek",
@ -168,6 +168,7 @@ func resolveCodexModel(model string) (string, string) {
"grok", "grok",
"xai", "xai",
"zhipu", "zhipu",
"moonshot",
} }
for _, prefix := range unsupportedPrefixes { for _, prefix := range unsupportedPrefixes {
if strings.HasPrefix(m, prefix) { if strings.HasPrefix(m, prefix) {

View file

@ -53,10 +53,10 @@ func (p *HTTPProvider) Chat(ctx context.Context, messages []Message, tools []Too
return nil, fmt.Errorf("API base not configured") return nil, fmt.Errorf("API base not configured")
} }
// Strip provider prefix from model name (e.g., moonshot/kimi-k2.5 -> kimi-k2.5, groq/openai/gpt-oss-120b -> openai/gpt-oss-120b, ollama/qwen2.5:14b -> qwen2.5:14b) // Strip provider prefix from model name (e.g., zai/glm-4.7 -> glm-4.7, groq/openai/gpt-oss-120b -> openai/gpt-oss-120b, ollama/qwen2.5:14b -> qwen2.5:14b)
if idx := strings.Index(model, "/"); idx != -1 { if idx := strings.Index(model, "/"); idx != -1 {
prefix := model[:idx] prefix := model[:idx]
if prefix == "moonshot" || prefix == "nvidia" || prefix == "groq" || prefix == "ollama" { if prefix == "zai" || prefix == "moonshot" || prefix == "nvidia" || prefix == "groq" || prefix == "ollama" {
model = model[idx+1:] model = model[idx+1:]
} }
} }
@ -272,12 +272,13 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
apiBase = "https://openrouter.ai/api/v1" apiBase = "https://openrouter.ai/api/v1"
} }
} }
case "zhipu", "glm": case "zai", "z.ai", "zhipu", "glm":
if cfg.Providers.Zhipu.APIKey != "" { if cfg.Providers.Zai.APIKey != "" {
apiKey = cfg.Providers.Zhipu.APIKey apiKey = cfg.Providers.Zai.APIKey
apiBase = cfg.Providers.Zhipu.APIBase apiBase = cfg.Providers.Zai.APIBase
proxy = cfg.Providers.Zai.Proxy
if apiBase == "" { if apiBase == "" {
apiBase = "https://open.bigmodel.cn/api/paas/v4" apiBase = "https://api.z.ai/api/paas/v4"
} }
} }
case "gemini", "google": case "gemini", "google":
@ -325,12 +326,12 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
} }
} }
case "moonshot", "kimi": case "moonshot", "kimi":
if cfg.Providers.Moonshot.APIKey != "" { if cfg.Providers.Zai.APIKey != "" {
apiKey = cfg.Providers.Moonshot.APIKey apiKey = cfg.Providers.Zai.APIKey
apiBase = cfg.Providers.Moonshot.APIBase apiBase = cfg.Providers.Zai.APIBase
proxy = cfg.Providers.Moonshot.Proxy proxy = cfg.Providers.Zai.Proxy
if apiBase == "" { if apiBase == "" {
apiBase = "https://api.moonshot.cn/v1" apiBase = "https://api.z.ai/api/paas/v4"
} }
} }
case "github_copilot", "copilot": case "github_copilot", "copilot":
@ -348,12 +349,12 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
// Fallback: detect provider from model name // Fallback: detect provider from model name
if apiKey == "" && apiBase == "" { if apiKey == "" && apiBase == "" {
switch { switch {
case (strings.Contains(lowerModel, "kimi") || strings.Contains(lowerModel, "moonshot") || strings.HasPrefix(model, "moonshot/")) && cfg.Providers.Moonshot.APIKey != "": case (strings.Contains(lowerModel, "kimi") || strings.Contains(lowerModel, "moonshot") || strings.HasPrefix(model, "moonshot/") || strings.Contains(lowerModel, "zai") || strings.HasPrefix(model, "zai/")) && cfg.Providers.Zai.APIKey != "":
apiKey = cfg.Providers.Moonshot.APIKey apiKey = cfg.Providers.Zai.APIKey
apiBase = cfg.Providers.Moonshot.APIBase apiBase = cfg.Providers.Zai.APIBase
proxy = cfg.Providers.Moonshot.Proxy proxy = cfg.Providers.Zai.Proxy
if apiBase == "" { if apiBase == "" {
apiBase = "https://api.moonshot.cn/v1" apiBase = "https://api.z.ai/api/paas/v4"
} }
case strings.HasPrefix(model, "openrouter/") || strings.HasPrefix(model, "anthropic/") || strings.HasPrefix(model, "openai/") || strings.HasPrefix(model, "meta-llama/") || strings.HasPrefix(model, "deepseek/") || strings.HasPrefix(model, "google/"): case strings.HasPrefix(model, "openrouter/") || strings.HasPrefix(model, "anthropic/") || strings.HasPrefix(model, "openai/") || strings.HasPrefix(model, "meta-llama/") || strings.HasPrefix(model, "deepseek/") || strings.HasPrefix(model, "google/"):
@ -395,12 +396,12 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
apiBase = "https://generativelanguage.googleapis.com/v1beta" apiBase = "https://generativelanguage.googleapis.com/v1beta"
} }
case (strings.Contains(lowerModel, "glm") || strings.Contains(lowerModel, "zhipu") || strings.Contains(lowerModel, "zai")) && cfg.Providers.Zhipu.APIKey != "": case (strings.Contains(lowerModel, "glm") || strings.Contains(lowerModel, "zhipu")) && cfg.Providers.Zai.APIKey != "":
apiKey = cfg.Providers.Zhipu.APIKey apiKey = cfg.Providers.Zai.APIKey
apiBase = cfg.Providers.Zhipu.APIBase apiBase = cfg.Providers.Zai.APIBase
proxy = cfg.Providers.Zhipu.Proxy proxy = cfg.Providers.Zai.Proxy
if apiBase == "" { if apiBase == "" {
apiBase = "https://open.bigmodel.cn/api/paas/v4" apiBase = "https://api.z.ai/api/paas/v4"
} }
case (strings.Contains(lowerModel, "groq") || strings.HasPrefix(model, "groq/")) && cfg.Providers.Groq.APIKey != "": case (strings.Contains(lowerModel, "groq") || strings.HasPrefix(model, "groq/")) && cfg.Providers.Groq.APIKey != "":