feat(zai): fall back to BIGMODEL/ZHIPUAI api keys and add GLM-5.3 aliases and glm-5v-turbo

Resolve the zai api key from ZAI_API_KEY, BIGMODEL_API_KEY or ZHIPUAI_API_KEY
so deployments predating the zai rename keep working. Accept response_format
and max_completion_tokens as supported openai params. Register zai/GLM-5.3
and zai/GLM-5.3-Flash aliases plus the glm-5v-turbo vision model in both the
main and backup cost maps
This commit is contained in:
DewellNaa 2026-10-25 10:36:44 -07:00
parent dddbaa803d
commit 215a60fa1d
4 changed files with 217 additions and 1 deletions

View file

@ -17,7 +17,12 @@ class ZAIChatConfig(OpenAIGPTConfig):
self, api_base: str | None, api_key: str | None
) -> tuple[str | None, str | None]:
api_base = api_base or get_secret_str("ZAI_API_BASE") or ZAI_API_BASE
dynamic_api_key: Final = api_key or get_secret_str("ZAI_API_KEY")
dynamic_api_key: Final = (
api_key
or get_secret_str("ZAI_API_KEY")
or get_secret_str("BIGMODEL_API_KEY")
or get_secret_str("ZHIPUAI_API_KEY")
)
return api_base, dynamic_api_key
def get_openai_compatible_provider_info(
@ -43,6 +48,8 @@ class ZAIChatConfig(OpenAIGPTConfig):
def get_supported_openai_params(self, model: str) -> list:
base_params: Final = [
"max_tokens",
"max_completion_tokens",
"response_format",
"stream",
"stream_options",
"temperature",

View file

@ -54934,6 +54934,21 @@
"supports_reasoning": true,
"supports_tool_choice": true
},
"zai/GLM-5.3": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 2.6e-07,
"input_cost_per_token": 1.4e-06,
"litellm_provider": "zai",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 4.4e-06,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true
},
"zai/glm-5.3-flash": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 3e-08,
@ -54950,6 +54965,46 @@
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_vision": true
},
"zai/GLM-5.3-Flash": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 3e-08,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 5e-07,
"litellm_provider": "zai",
"max_input_tokens": 1048576,
"max_output_tokens": 128000,
"mode": "chat",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_vision": true
},
"zai/glm-5v-turbo": {
"input_cost_per_token": 1.4e-06,
"litellm_provider": "zai",
"max_input_tokens": 200000,
"max_output_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 4.4e-06,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_vision": true
},
"zai/GLM-5V-Turbo": {
"input_cost_per_token": 1.4e-06,
"litellm_provider": "zai",
"max_input_tokens": 200000,
"max_output_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 4.4e-06,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_vision": true
},
"zai/glm-5.1": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 2.6e-07,

View file

@ -54934,6 +54934,21 @@
"supports_reasoning": true,
"supports_tool_choice": true
},
"zai/GLM-5.3": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 2.6e-07,
"input_cost_per_token": 1.4e-06,
"litellm_provider": "zai",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 4.4e-06,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true
},
"zai/glm-5.3-flash": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 3e-08,
@ -54950,6 +54965,46 @@
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_vision": true
},
"zai/GLM-5.3-Flash": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 3e-08,
"input_cost_per_token": 1.5e-07,
"output_cost_per_token": 5e-07,
"litellm_provider": "zai",
"max_input_tokens": 1048576,
"max_output_tokens": 128000,
"mode": "chat",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_vision": true
},
"zai/glm-5v-turbo": {
"input_cost_per_token": 1.4e-06,
"litellm_provider": "zai",
"max_input_tokens": 200000,
"max_output_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 4.4e-06,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_vision": true
},
"zai/GLM-5V-Turbo": {
"input_cost_per_token": 1.4e-06,
"litellm_provider": "zai",
"max_input_tokens": 200000,
"max_output_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 4.4e-06,
"source": "https://docs.z.ai/guides/overview/pricing",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_vision": true
},
"zai/glm-5.1": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 2.6e-07,

View file

@ -0,0 +1,99 @@
import json
from collections.abc import Mapping
from pathlib import Path
from typing import Final
import pytest
import litellm
from litellm.llms.zai.chat.transformation import ZAIChatConfig
REPO_ROOT: Final = Path(__file__).parents[2]
MAIN_PATH: Final = REPO_ROOT / "model_prices_and_context_window.json"
BACKUP_PATH: Final = REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json"
ZAI_API_KEY_ENV_VARS: Final = ("ZAI_API_KEY", "BIGMODEL_API_KEY", "ZHIPUAI_API_KEY")
LEGACY_API_KEY_ENV_VARS: Final = ("BIGMODEL_API_KEY", "ZHIPUAI_API_KEY")
NEW_ZAI_MODEL_KEYS: Final = ("zai/GLM-5.3", "zai/GLM-5.3-Flash", "zai/glm-5v-turbo", "zai/GLM-5V-Turbo")
def _load(path: Path) -> Mapping[str, object]:
with open(path) as f:
return json.load(f)
@pytest.fixture
def local_model_cost_map(monkeypatch: pytest.MonkeyPatch) -> None:
"""Resolve get_model_info against the in-repo cost map instead of the remote one
fetched at import time."""
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
litellm.get_model_info.cache_clear()
yield
litellm.get_model_info.cache_clear()
@pytest.mark.parametrize("legacy_env_var", LEGACY_API_KEY_ENV_VARS)
def test_provider_info_resolves_legacy_api_key(monkeypatch: pytest.MonkeyPatch, legacy_env_var: str) -> None:
for env_var in ZAI_API_KEY_ENV_VARS:
monkeypatch.delenv(env_var, raising=False)
monkeypatch.setenv(legacy_env_var, "legacy-secret")
api_base, api_key = ZAIChatConfig()._get_openai_compatible_provider_info(api_base=None, api_key=None)
assert api_key == "legacy-secret"
assert api_base == "https://api.z.ai/api/paas/v4"
def test_provider_info_prefers_zai_api_key_over_legacy(monkeypatch: pytest.MonkeyPatch) -> None:
for env_var in ZAI_API_KEY_ENV_VARS:
monkeypatch.setenv(env_var, f"{env_var}-secret")
_, api_key = ZAIChatConfig()._get_openai_compatible_provider_info(api_base=None, api_key=None)
assert api_key == "ZAI_API_KEY-secret"
def test_supported_openai_params_include_new_params() -> None:
params: Final = ZAIChatConfig().get_supported_openai_params(model="zai/glm-5.3")
assert "response_format" in params
assert "max_completion_tokens" in params
@pytest.mark.parametrize(
("alias", "canonical"),
[
("zai/GLM-5.3", "zai/glm-5.3"),
("zai/GLM-5.3-Flash", "zai/glm-5.3-flash"),
("zai/GLM-5V-Turbo", "zai/glm-5v-turbo"),
],
)
def test_uppercase_alias_model_info_matches_canonical(local_model_cost_map: None, alias: str, canonical: str) -> None:
alias_info: Final = litellm.get_model_info(alias)
canonical_info: Final = litellm.get_model_info(canonical)
assert alias_info["key"] == alias
assert {k: v for k, v in alias_info.items() if k != "key"} == {
k: v for k, v in canonical_info.items() if k != "key"
}
def test_glm_5v_turbo_model_info(local_model_cost_map: None) -> None:
info: Final = litellm.get_model_info("zai/glm-5v-turbo")
assert info["litellm_provider"] == "zai"
assert info["mode"] == "chat"
assert info["supports_vision"] is True
assert info["supports_function_calling"] is True
assert info["supports_tool_choice"] is True
def test_new_zai_entries_match_between_main_and_backup() -> None:
main_cost: Final = _load(MAIN_PATH)
backup_cost: Final = _load(BACKUP_PATH)
for model in NEW_ZAI_MODEL_KEYS:
assert backup_cost.get(model) == main_cost.get(model), (
f"{model} differs between main and backup model cost maps"
)