feat(ai): add Zhipu provider preset

This commit is contained in:
U1
2026-09-12 09:29:04 +08:00
committed by GitHub
parent 774f175c33
commit 0bfb1035e4
8 changed files with 72 additions and 4 deletions
+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Zhipu</title><path d="M11.991 23.503a.24.24 0 00-.244.248.24.24 0 00.244.249.24.24 0 00.245-.249.24.24 0 00-.22-.247l-.025-.001zM9.671 5.365a1.697 1.697 0 011.099 2.132l-.071.172-.016.04-.018.054c-.07.16-.104.32-.104.498-.035.71.47 1.279 1.186 1.314h.366c1.309.053 2.338 1.173 2.286 2.523-.052 1.332-1.152 2.38-2.478 2.327h-.174c-.715.018-1.274.64-1.239 1.368 0 .124.018.23.053.337.209.373.54.658.96.8.75.23 1.517-.125 1.9-.782l.018-.035c.402-.64 1.17-.96 1.92-.711.854.284 1.378 1.226 1.099 2.167a1.661 1.661 0 01-2.077 1.102 1.711 1.711 0 01-.907-.711l-.017-.035c-.2-.323-.463-.58-.851-.711l-.056-.018a1.646 1.646 0 00-1.954.746 1.66 1.66 0 01-1.065.764 1.677 1.677 0 01-1.989-1.279c-.209-.906.332-1.83 1.257-2.043a1.51 1.51 0 01.296-.035h.018c.68-.071 1.151-.622 1.116-1.333a1.307 1.307 0 00-.227-.693 2.515 2.515 0 01-.366-1.403 2.39 2.39 0 01.366-1.208c.14-.195.21-.444.227-.693.018-.71-.506-1.261-1.186-1.332l-.07-.018a1.43 1.43 0 01-.299-.07l-.05-.019a1.7 1.7 0 01-1.047-2.114 1.68 1.68 0 012.094-1.101zm-5.575 10.11c.26-.264.639-.367.994-.27.355.096.633.379.728.74.095.362-.007.748-.267 1.013-.402.41-1.053.41-1.455 0a1.062 1.062 0 010-1.482zm14.845-.294c.359-.09.738.024.992.297.254.274.344.665.237 1.025-.107.36-.396.634-.756.718-.551.128-1.1-.22-1.23-.781a1.05 1.05 0 01.757-1.26zm-.064-4.39c.314.32.49.753.49 1.206 0 .452-.176.886-.49 1.206-.315.32-.74.5-1.185.5-.444 0-.87-.18-1.184-.5a1.727 1.727 0 010-2.412 1.654 1.654 0 012.369 0zm-11.243.163c.364.484.447 1.128.218 1.691a1.665 1.665 0 01-2.188.923c-.855-.36-1.26-1.358-.907-2.228a1.68 1.68 0 011.33-1.038c.593-.08 1.183.169 1.547.652zm11.545-4.221c.368 0 .708.2.892.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.892.524c-.568 0-1.03-.47-1.03-1.048 0-.579.462-1.048 1.03-1.048zm-14.358 0c.368 0 .707.2.891.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.891.524c-.569 0-1.03-.47-1.03-1.048 0-.579.461-1.048 1.03-1.048zm10.031-1.475c.925 0 1.675.764 1.675 1.706s-.75 1.705-1.675 1.705-1.674-.763-1.674-1.705c0-.942.75-1.706 1.674-1.706zm-2.626-.684c.362-.082.653-.356.761-.718a1.062 1.062 0 00-.238-1.028 1.017 1.017 0 00-.996-.294c-.547.14-.881.7-.752 1.257.13.558.675.907 1.225.783zm0 16.876c.359-.087.644-.36.75-.72a1.062 1.062 0 00-.237-1.019 1.018 1.018 0 00-.985-.301 1.037 1.037 0 00-.762.717c-.108.361-.017.754.239 1.028.245.263.606.377.953.305l.043-.01zM17.19 3.5a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64a.631.631 0 00-.628.64c0 .355.28.64.628.64zm-10.38 0a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64a.631.631 0 00-.628.64c0 .355.279.64.628.64zm-5.182 7.852a.631.631 0 00-.628.64c0 .354.28.639.628.639a.63.63 0 00.627-.606l.001-.034a.62.62 0 00-.628-.64zm5.182 9.13a.631.631 0 00-.628.64c0 .355.279.64.628.64a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm10.38.018a.631.631 0 00-.628.64c0 .355.28.64.628.64a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64zm5.182-9.148a.631.631 0 00-.628.64c0 .354.279.639.628.639a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm-.384-4.992a.24.24 0 00.244-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249c0 .142.122.249.244.249zM11.991.497a.24.24 0 00.245-.248A.24.24 0 0011.99 0a.24.24 0 00-.244.249c0 .133.108.236.223.247l.021.001zM2.011 6.36a.24.24 0 00.245-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249.24.24 0 00.244.249zm0 11.263a.24.24 0 00-.243.248.24.24 0 00.244.249.24.24 0 00.244-.249.252.252 0 00-.244-.248zm19.995-.018a.24.24 0 00-.245.248.24.24 0 00.245.25.24.24 0 00.244-.25.252.252 0 00-.244-.248z" fill="#3859FF" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

+19
View File
@@ -772,6 +772,19 @@ describe("settingsStore AI API key normalization", () => {
expect(normalizeAiConfig({ endpoint: "https://api.moonshot.cn/v1", model: "kimi-k2.5" }).provider).toBe("kimi");
});
it("provides Zhipu defaults and recognizes legacy Zhipu configurations", () => {
expect(AI_PROVIDER_PRESETS.zhipu).toMatchObject({
provider: "zhipu",
endpoint: "https://open.bigmodel.cn/api/paas/v4",
model: "glm-5.3",
apiStyle: "completions",
authMethod: "bearer",
requiresApiKey: true,
});
expect(normalizeAiConfig({ endpoint: "https://open.bigmodel.cn/api/paas/v4", model: "glm-4.6" }).provider).toBe("zhipu");
expect(normalizeAiConfig({ endpoint: "https://api.z.ai/api/paas/v4", model: "glm-5.2" }).provider).toBe("zhipu");
});
it("uses the mainland MiniMax endpoint only for new zh-CN presets", () => {
expect(getAiProviderPresetDefaultEndpoint(AI_PROVIDER_PRESETS.minimax, "zh-CN")).toBe("https://api.minimaxi.com/v1");
expect(getAiProviderPresetDefaultEndpoint(AI_PROVIDER_PRESETS.minimax, "zh-TW")).toBe("https://api.minimax.io/v1");
@@ -779,6 +792,12 @@ describe("settingsStore AI API key normalization", () => {
expect(getAiProviderPresetDefaultEndpoint(AI_PROVIDER_PRESETS.openai, "zh-CN")).toBe(AI_PROVIDER_PRESETS.openai.endpoint);
});
it("uses the international Zhipu endpoint for non-zh-CN presets", () => {
expect(getAiProviderPresetDefaultEndpoint(AI_PROVIDER_PRESETS.zhipu, "zh-CN")).toBe("https://open.bigmodel.cn/api/paas/v4");
expect(getAiProviderPresetDefaultEndpoint(AI_PROVIDER_PRESETS.zhipu, "zh-TW")).toBe("https://api.z.ai/api/paas/v4");
expect(getAiProviderPresetDefaultEndpoint(AI_PROVIDER_PRESETS.zhipu, "en")).toBe("https://api.z.ai/api/paas/v4");
});
it("preserves saved MiniMax endpoints during normalization", () => {
expect(normalizeAiConfig({ provider: "minimax", endpoint: "https://api.minimaxi.com/v1" }).endpoint).toBe("https://api.minimaxi.com/v1");
expect(normalizeAiConfig({ provider: "minimax", endpoint: "https://minimax.example.com/v1" }).endpoint).toBe("https://minimax.example.com/v1");
+14
View File
@@ -301,6 +301,16 @@ export const AI_PROVIDER_PRESETS: Record<AiProvider, AiProviderPreset> = {
authMethod: "bearer",
requiresApiKey: true,
},
zhipu: {
label: "Zhipu",
iconSlug: "zhipu",
provider: "zhipu",
endpoint: "https://open.bigmodel.cn/api/paas/v4",
model: "glm-5.3",
apiStyle: "completions",
authMethod: "bearer",
requiresApiKey: true,
},
minimax: {
label: "MiniMax",
iconSlug: "minimax",
@@ -490,6 +500,9 @@ export function getAiProviderPresetDefaultEndpoint(preset: AiProviderPreset | Ai
if (preset.provider === "minimax" && locale === "zh-CN") {
return "https://api.minimaxi.com/v1";
}
if (preset.provider === "zhipu" && locale !== "zh-CN") {
return "https://api.z.ai/api/paas/v4";
}
return preset.endpoint;
}
@@ -588,6 +601,7 @@ function inferAiProviderFromConfig(config: Partial<AiConfig> | null | undefined)
if (endpoint.includes("deepseek") || model.includes("deepseek")) return "deepseek";
if (endpoint.includes("moonshot") || endpoint.includes("kimi.com") || model.includes("kimi")) return "kimi";
if (endpoint.includes("dashscope") || endpoint.includes("aliyuncs") || model.includes("qwen")) return "qwen";
if (endpoint.includes("bigmodel.cn") || endpoint.includes("api.z.ai") || model.startsWith("glm-")) return "zhipu";
if (endpoint.includes("generativelanguage.googleapis.com") || model.includes("gemini")) return "gemini";
if (endpoint.includes("minimax.io") || endpoint.includes("minimaxi.com") || model.includes("minimax")) return "minimax";
if (endpoint.includes("localhost:11434") || endpoint.includes("127.0.0.1:11434")) return "ollama";
+21 -1
View File
@@ -1,4 +1,24 @@
export type AiProvider = "claude" | "openai" | "gemini" | "deepseek" | "kimi" | "qwen" | "minimax" | "ollama" | "anthropic-compatible" | "openai-compatible" | "claude-code-cli" | "pi-agent-cli" | "codex-cli" | "opencode-cli" | "cursor-cli" | "grok-cli" | "codebuddy-cli" | "qoder-cli" | "custom";
export type AiProvider =
| "claude"
| "openai"
| "gemini"
| "deepseek"
| "kimi"
| "qwen"
| "zhipu"
| "minimax"
| "ollama"
| "anthropic-compatible"
| "openai-compatible"
| "claude-code-cli"
| "pi-agent-cli"
| "codex-cli"
| "opencode-cli"
| "cursor-cli"
| "grok-cli"
| "codebuddy-cli"
| "qoder-cli"
| "custom";
export type AiApiStyle = "completions" | "responses" | "anthropic-messages";
export type AiAuthMethod = "api-key" | "bearer";
export type AiEffortLevel = "low" | "medium" | "high" | "xhigh" | "max";
+7
View File
@@ -88,6 +88,7 @@ pub enum AiProvider {
Deepseek,
Kimi,
Qwen,
Zhipu,
MiniMax,
Ollama,
#[serde(rename = "openai-compatible")]
@@ -121,6 +122,7 @@ impl AiProvider {
AiProvider::Deepseek => "deepseek",
AiProvider::Kimi => "kimi",
AiProvider::Qwen => "qwen",
AiProvider::Zhipu => "zhipu",
AiProvider::MiniMax => "minimax",
AiProvider::Ollama => "ollama",
AiProvider::OpenaiCompatible => "openai-compatible",
@@ -887,6 +889,7 @@ pub fn resolve_endpoint(config: &AiConfig) -> String {
| AiProvider::Deepseek
| AiProvider::Kimi
| AiProvider::Qwen
| AiProvider::Zhipu
| AiProvider::MiniMax
| AiProvider::Ollama
| AiProvider::OpenaiCompatible
@@ -1689,6 +1692,7 @@ fn provider_requires_api_key(provider: &AiProvider) -> bool {
| AiProvider::Deepseek
| AiProvider::Kimi
| AiProvider::Qwen
| AiProvider::Zhipu
| AiProvider::MiniMax
)
}
@@ -2179,6 +2183,7 @@ pub async fn list_models_core(config: &AiConfig) -> Result<Vec<AiModelInfo>, Str
| AiProvider::Deepseek
| AiProvider::Kimi
| AiProvider::Qwen
| AiProvider::Zhipu
| AiProvider::MiniMax
| AiProvider::OpenaiCompatible => list_openai_compatible_models(&client, config).await?,
AiProvider::Custom => {
@@ -3614,6 +3619,7 @@ pub async fn complete(request: &AiCompletionRequest) -> Result<String, String> {
| AiProvider::Deepseek
| AiProvider::Kimi
| AiProvider::Qwen
| AiProvider::Zhipu
| AiProvider::MiniMax
| AiProvider::Ollama
| AiProvider::OpenaiCompatible => {
@@ -3676,6 +3682,7 @@ pub async fn stream(
| AiProvider::Deepseek
| AiProvider::Kimi
| AiProvider::Qwen
| AiProvider::Zhipu
| AiProvider::MiniMax
| AiProvider::Ollama
| AiProvider::OpenaiCompatible => {
+3 -3
View File
@@ -78,7 +78,7 @@ pub fn static_effort_capability(config: &AiConfig, model_id: &str) -> Option<AiE
AiProvider::Deepseek => deepseek_capability(&model, source),
AiProvider::Kimi => kimi_capability(&model, source),
AiProvider::Qwen => qwen_capability(&model, source),
AiProvider::Ollama => ollama_capability(&model, source),
AiProvider::Zhipu | AiProvider::Ollama => ollama_capability(&model, source),
AiProvider::MiniMax if matches_family(&model, "minimax-m3") => Some(boolean_capability(source)),
AiProvider::MiniMax => None,
AiProvider::AnthropicCompatible | AiProvider::OpenaiCompatible | AiProvider::Custom => {
@@ -208,7 +208,7 @@ pub fn registry_source_url(provider: &AiProvider) -> Option<&'static str> {
AiProvider::Deepseek => Some(DEEPSEEK_THINKING_DOCS),
AiProvider::Kimi => Some(KIMI_REASONING_DOCS),
AiProvider::Qwen => Some(QWEN_THINKING_DOCS),
AiProvider::Ollama => Some(OLLAMA_THINKING_DOCS),
AiProvider::Zhipu | AiProvider::Ollama => Some(OLLAMA_THINKING_DOCS),
AiProvider::MiniMax => Some(MINIMAX_THINKING_DOCS),
AiProvider::Claude
| AiProvider::AnthropicCompatible
@@ -287,7 +287,7 @@ pub fn apply_runtime_effort(body: &mut Value, config: &AiConfig) {
AiProvider::Deepseek => apply_deepseek_effort(object, selection),
AiProvider::Kimi => apply_kimi_effort(object, &config.model, selection),
AiProvider::Qwen => apply_qwen_effort(object, selection),
AiProvider::Ollama => apply_openai_effort(object, &config.api_style, selection),
AiProvider::Zhipu | AiProvider::Ollama => apply_openai_effort(object, &config.api_style, selection),
AiProvider::MiniMax => apply_minimax_effort(object, selection),
AiProvider::Openai | AiProvider::OpenaiCompatible => apply_openai_effort(object, &config.api_style, selection),
AiProvider::Custom => {
+1
View File
@@ -92,6 +92,7 @@ pub(crate) fn model_is_assistant_compatible(provider: &AiProvider, model_id: &st
| AiProvider::AnthropicCompatible
| AiProvider::Deepseek
| AiProvider::Kimi
| AiProvider::Zhipu
| AiProvider::Ollama
| AiProvider::OpenaiCompatible
| AiProvider::CodexCli
+6
View File
@@ -892,6 +892,12 @@ test("AI provider presets include common hosted and local providers", () => {
assert.equal(AI_PROVIDER_PRESETS.minimax.requiresApiKey, true);
assert.equal(AI_PROVIDER_PRESETS.minimax.iconSlug, "minimax");
assert.equal(AI_PROVIDER_PRESETS.qwen.endpoint, "https://dashscope.aliyuncs.com/compatible-mode/v1");
assert.equal(AI_PROVIDER_PRESETS.zhipu.endpoint, "https://open.bigmodel.cn/api/paas/v4");
assert.equal(AI_PROVIDER_PRESETS.zhipu.model, "glm-5.3");
assert.equal(AI_PROVIDER_PRESETS.zhipu.apiStyle, "completions");
assert.equal(AI_PROVIDER_PRESETS.zhipu.authMethod, "bearer");
assert.equal(AI_PROVIDER_PRESETS.zhipu.requiresApiKey, true);
assert.equal(AI_PROVIDER_PRESETS.zhipu.iconSlug, "zhipu");
assert.equal(AI_PROVIDER_PRESETS.ollama.endpoint, "http://localhost:11434/v1");
assert.equal(AI_PROVIDER_PRESETS.ollama.requiresApiKey, false);
assert.equal(AI_PROVIDER_PRESETS.claude.authMethod, "api-key");