Skip to content

Commit cc8efc2

Browse files
lee101claude
andcommitted
Add Z.AI (Zhipu/GLM) provider and x-ai/Grok OpenRouter routing
- Add built-in zhipu provider with GLM coding API (api.z.ai/api/coding/paas/v4) - Auto-detect GLM-* models to zhipu provider when ZAI_API_KEY is set - Auto-detect x-ai/* models (Grok) to OpenRouter - Add model families for GLM-5, GLM-4, and Grok models - Support zhipu/ prefix stripping in effective_model_name Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 91a39a3 commit cc8efc2

3 files changed

Lines changed: 55 additions & 7 deletions

File tree

codex-rs/Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codex-rs/core/src/model_family.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,21 @@ pub fn find_family_for_model(slug: &str) -> Option<ModelFamily> {
238238
needs_special_apply_patch_instructions: true,
239239
support_verbosity: true,
240240
)
241+
} else if slug.starts_with("glm-5") || slug.starts_with("GLM-5") {
242+
model_family!(
243+
slug, "glm-5",
244+
needs_special_apply_patch_instructions: true,
245+
)
246+
} else if slug.starts_with("glm-4") || slug.starts_with("GLM-4") {
247+
model_family!(
248+
slug, "glm-4",
249+
needs_special_apply_patch_instructions: true,
250+
)
251+
} else if slug.contains("grok") {
252+
model_family!(
253+
slug, "grok",
254+
needs_special_apply_patch_instructions: true,
255+
)
241256
} else {
242257
None
243258
}

codex-rs/model-provider-info/src/lib.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const GEMINI_PROVIDER_NAME: &str = "Google Gemini";
3737
pub const GEMINI_PROVIDER_ID: &str = "gemini";
3838
const OPENROUTER_PROVIDER_NAME: &str = "OpenRouter";
3939
pub const OPENROUTER_PROVIDER_ID: &str = "openrouter";
40+
const ZHIPU_PROVIDER_NAME: &str = "Z.AI (Zhipu)";
41+
pub const ZHIPU_PROVIDER_ID: &str = "zhipu";
4042
const CHAT_WIRE_API_REMOVED_ERROR: &str = "`wire_api = \"chat\"` is no longer supported.\nHow to fix: set `wire_api = \"responses\"` in your provider config.\nMore info: https://github.com/openai/codex/discussions/7782";
4143
pub const LEGACY_OLLAMA_CHAT_PROVIDER_ID: &str = "ollama-chat";
4244
pub const OLLAMA_CHAT_PROVIDER_REMOVED_ERROR: &str = "`ollama-chat` is no longer supported.\nHow to fix: replace `ollama-chat` with `ollama` in `model_provider`, `oss_provider`, or `--local-provider`.\nMore info: https://github.com/openai/codex/discussions/7782";
@@ -342,13 +344,38 @@ impl ModelProviderInfo {
342344
}
343345
}
344346

347+
pub fn create_zhipu_provider() -> ModelProviderInfo {
348+
ModelProviderInfo {
349+
name: ZHIPU_PROVIDER_NAME.into(),
350+
base_url: Some("https://api.z.ai/api/coding/paas/v4".into()),
351+
env_key: Some("ZAI_API_KEY".into()),
352+
env_key_instructions: Some(
353+
"Get your API key from https://z.ai and set ZAI_API_KEY".into(),
354+
),
355+
experimental_bearer_token: None,
356+
auth: None,
357+
wire_api: WireApi::Responses,
358+
query_params: None,
359+
http_headers: None,
360+
env_http_headers: None,
361+
request_max_retries: None,
362+
stream_max_retries: None,
363+
stream_idle_timeout_ms: None,
364+
websocket_connect_timeout_ms: None,
365+
requires_openai_auth: false,
366+
supports_websockets: false,
367+
}
368+
}
369+
345370
pub fn is_openai(&self) -> bool {
346371
self.name == OPENAI_PROVIDER_NAME
347372
}
348373

349374
pub fn effective_model_name<'a>(&self, slug: &'a str) -> &'a str {
350375
if self.name == GEMINI_PROVIDER_NAME {
351376
slug.strip_prefix("google/").unwrap_or(slug)
377+
} else if self.name == ZHIPU_PROVIDER_NAME {
378+
slug.strip_prefix("zhipu/").unwrap_or(slug)
352379
} else {
353380
slug
354381
}
@@ -379,6 +406,7 @@ pub fn built_in_model_providers(
379406
(OPENAI_PROVIDER_ID, openai_provider),
380407
(OPENROUTER_PROVIDER_ID, P::create_openrouter_provider()),
381408
(GEMINI_PROVIDER_ID, P::create_gemini_provider()),
409+
(ZHIPU_PROVIDER_ID, P::create_zhipu_provider()),
382410
(
383411
OLLAMA_OSS_PROVIDER_ID,
384412
create_oss_provider(DEFAULT_OLLAMA_PORT, WireApi::Responses),
@@ -400,14 +428,19 @@ fn non_empty_env_var(name: &str) -> bool {
400428
}
401429

402430
pub fn infer_builtin_provider_id_for_model(model: &str) -> Option<&'static str> {
431+
let lower = model.to_lowercase();
432+
if lower.starts_with("glm-") && non_empty_env_var("ZAI_API_KEY") {
433+
return Some(ZHIPU_PROVIDER_ID);
434+
}
403435
match model.split_once('/') {
404436
Some(("google", _)) if non_empty_env_var("GEMINI_API_KEY") => Some(GEMINI_PROVIDER_ID),
405437
Some(("google", _)) if non_empty_env_var("OPENROUTER_API_KEY") => {
406438
Some(OPENROUTER_PROVIDER_ID)
407439
}
408-
Some(("anthropic" | "z-ai", _)) if non_empty_env_var("OPENROUTER_API_KEY") => {
440+
Some(("anthropic" | "z-ai" | "x-ai", _)) if non_empty_env_var("OPENROUTER_API_KEY") => {
409441
Some(OPENROUTER_PROVIDER_ID)
410442
}
443+
Some(("zhipu", _)) if non_empty_env_var("ZAI_API_KEY") => Some(ZHIPU_PROVIDER_ID),
411444
_ => None,
412445
}
413446
}

0 commit comments

Comments
 (0)