feat(providers): add Z.AI coding plan (global + China)

Providers 17-18 (20th & 21st registry variants), sourced authoritatively
from Pi (earendil-works/pi), the open-source agent whose provider list is
being mirrored. Pi's zai.ts / zai-coding-cn.ts use plain openAICompletionsApi
(NO special thinking dialect — overturns the matrix's 'thinking:{type} → new
dialect' concern), so Kigi maps them to Passthrough. Global: api.z.ai/api/
coding/paas/v4, ZAI_API_KEY, models.dev zai-coding-plan. China (Zhipu
BigModel): open.bigmodel.cn/api/coding/paas/v4, ZAI_CODING_CN_API_KEY,
models.dev zhipuai-coding-plan. Both Bearer + OpenAI listing + ChatCompletions
+ restrict_to_enriched; /models is auth-gated → validator.

id-match is PROVEN (not just assumed like Qwen): Pi's static model ids
[glm-4.5-air, glm-4.7, glm-5-turbo, glm-5.1, glm-5.2, glm-5v-turbo] are
byte-identical to the models.dev zai-coding-plan keys, all tool_call=true, so
restrict keeps every model with no silent-empty risk. Review found no defects.
GLM thinking is not lost (reasoning_content is parsed regardless of dialect).

Tests: e2e proves enrichment-supplied context + non-vacuous restrict (a
non-enriched wire model is dropped) + Passthrough; both variants' validation
tests hit /models and assert the per-variant console host (z.ai vs
open.bigmodel.cn). Registry at 21; picker 22 rows.

Also fixes the welcome login-picker test for the now-taller menu (renders at
a taller viewport to verify content coverage) and logs the real menu-overflow
UX debt: the picker clips rows past the fold with no scroll (q/l shortcuts
still work; only shown when unauthenticated) — deferred to its own cycle.
This commit is contained in:
2026-07-21 19:32:58 -04:00
parent c02b4b1ed7
commit 010f6c3be3
5 changed files with 243 additions and 11 deletions
@@ -623,7 +623,9 @@ mod tests {
"xai",
"qwen-token-plan",
"qwen-token-plan-cn",
"kimi-coding"
"kimi-coding",
"zai",
"zai-coding-cn"
]
);
assert_eq!(default_id(&built), Some(XAI_API_KEY_METHOD_ID));
@@ -665,7 +667,9 @@ mod tests {
"xai",
"qwen-token-plan",
"qwen-token-plan-cn",
"kimi-coding"
"kimi-coding",
"zai",
"zai-coding-cn"
]
);
assert_eq!(default_id(&built), Some(CACHED_TOKEN_AUTH_METHOD_ID));
@@ -700,7 +704,9 @@ mod tests {
"xai",
"qwen-token-plan",
"qwen-token-plan-cn",
"kimi-coding"
"kimi-coding",
"zai",
"zai-coding-cn"
]
);
assert_eq!(default_id(&built), Some(CACHED_TOKEN_AUTH_METHOD_ID));
@@ -738,7 +744,9 @@ mod tests {
"xai",
"qwen-token-plan",
"qwen-token-plan-cn",
"kimi-coding"
"kimi-coding",
"zai",
"zai-coding-cn"
]
);
assert_eq!(default_id(&built), None);
@@ -1094,4 +1102,49 @@ mod tests {
"Invalid API key for kimi-coding \u{2014} check your key on www.kimi.com"
);
}
/// Z.AI (global): /models is auth-gated (401 for a bad key) → validator.
#[tokio::test]
#[serial]
async fn zai_validates_against_models_and_rejects_bad_key() {
use wiremock::matchers::{method, path};
let server = wiremock::MockServer::start().await;
wiremock::Mock::given(method("GET"))
.and(path("/models"))
.respond_with(wiremock::ResponseTemplate::new(401))
.expect(1)
.mount(&server)
.await;
let _base = EnvGuard::set(kigi_models::ZAI_BASE_URL_ENV, &server.uri());
let err = authenticate_platform_api_key(kigi_models::PlatformId::Zai, Some("zai-bad"))
.await
.expect_err("a 401 from /models must reject the key");
assert_eq!(
err.message,
"Invalid API key for zai \u{2014} check your key on z.ai"
);
}
/// Z.AI Coding CN: distinct base URL (Zhipu BigModel) + console host.
#[tokio::test]
#[serial]
async fn zai_coding_cn_validates_against_models_and_rejects_bad_key() {
use wiremock::matchers::{method, path};
let server = wiremock::MockServer::start().await;
wiremock::Mock::given(method("GET"))
.and(path("/models"))
.respond_with(wiremock::ResponseTemplate::new(401))
.expect(1)
.mount(&server)
.await;
let _base = EnvGuard::set(kigi_models::ZAI_CODING_CN_BASE_URL_ENV, &server.uri());
let err =
authenticate_platform_api_key(kigi_models::PlatformId::ZaiCodingCn, Some("zai-cn-bad"))
.await
.expect_err("a 401 from /models must reject the key");
assert_eq!(
err.message,
"Invalid API key for zai-coding-cn \u{2014} check your key on open.bigmodel.cn"
);
}
}