Skip to content

feat(providers): split zai into 4 plans (Global/China × direct API/Coding Plan) - #13500

Closed
yuanmingyi wants to merge 1 commit into
NousResearch:mainfrom
yuanmingyi:feature/zai-model-providers
Closed

yuanmingyi wants to merge 1 commit into
NousResearch:mainfrom
yuanmingyi:feature/zai-model-providers

Conversation

@yuanmingyi

Copy link
Copy Markdown

Summary

Z.AI ships four distinct billing/endpoint combinations (Global direct, China direct, Coding Plan Global, Coding Plan China) behind one user-facing name. The single pre-existing zai provider tried to paper over this at runtime by probing endpoints with the user's key to find the working one — slow, fragile, and gave users no way to pin a plan. This PR splits zai into four explicit Hermes providers so users pick their plan once and credentials resolve deterministically.

Hermes id models.dev id Endpoint Env var Plan
zai zai api.z.ai/api/paas/v4 ZAI_API_KEY Z.AI (Global direct)
zai-cn zhipuai open.bigmodel.cn/api/paas/v4 GLM_API_KEY Zhipu AI (China direct)
zai-coding-global zai-coding-plan api.z.ai/api/coding/paas/v4 ZAI_CODING_API_KEY Z.AI Coding Plan (Global)
zai-coding-cn zhipuai-coding-plan open.bigmodel.cn/api/coding/paas/v4 GLM_CODING_API_KEY Zhipu AI Coding Plan (China)

Display names, model catalogs, and capability metadata are sourced from models.dev's per-plan entries. Legacy provider: zai configs with only GLM_API_KEY set continue to work — see Compatibility below.

Changes

  • hermes_cli/auth.py — four distinct PROVIDER_REGISTRY entries with per-plan env vars and static base URLs. Dropped detect_zai_endpoint / _resolve_zai_base_url — endpoints no longer probed. zai-cn declared before zai so auto-detect with only GLM_API_KEY still prefers the China plan.
  • hermes_cli/providers.pyHERMES_OVERLAYS gains three new entries (zai-cn, zai-coding-cn, zai-coding-global). ALIASES now routes glm/zhipuzai-cn; z-ai/z.aizai unchanged.
  • agent/models_dev.pyPROVIDER_TO_MODELS_DEV maps each Hermes id to its distinct models.dev catalog so /model and /provider pickers show the right per-plan display name and model list.
  • hermes_cli/models.py — curated _PROVIDER_MODELS:
    • direct plans (zai, zai-cn): glm-5.1, glm-5, glm-5v-turbo, glm-5-turbo, glm-4.7, glm-4.7-flashx, glm-4.6, glm-4.5, glm-4.5-air
    • coding plans (zai-coding-*): glm-5.1, glm-5-turbo, glm-4.7, glm-4.5-air
  • agent/auxiliary_client.py — aux-model defaults: zai/zai-cnglm-4.7-flashx; zai-coding-*glm-4.7. Vision override: zai/zai-cnglm-5v-turbo.
  • run_agent.py_anthropic_preserve_dots() extended to all four plans so glm-4.7/glm-5.1 aren't mangled at API-call time.
  • agent/model_metadata.py_PROVIDER_PREFIXES gains the two coding-plan slugs for zai-coding-*/glm-5.1 prefix stripping.
  • hermes_cli/model_normalize.py — same four slugs added to _MATCHING_PREFIX_STRIP_PROVIDERS.
  • hermes_cli/doctor.py — two new _apikey_providers probes (api.z.ai/api/coding/paas/v4/models, open.bigmodel.cn/api/coding/paas/v4/models); _PROVIDER_ENV_HINTS gains the six new env vars so .env with only coding-plan keys is recognised as provider-configured.
  • hermes_cli/status.pykeys + apikey_providers dicts list all four plans; column widened from 12 → 20 to fit Zhipu AI Coding Plan.
  • hermes_cli/dump.py / hermes_cli/setup.py / hermes_cli/config.py / hermes_cli/main.py / cli.py — per-plan env-var mappings, vision-setup labels, fallback config comments, --provider choices, docstrings updated.
  • gateway/platforms/qqbot/adapter.py — STT _PROVIDER_BASE_URLS recognises all four zai slugs (plus legacy glm alias) and picks glm-asr as default model; all China/Global variants map to open.bigmodel.cn/api/paas/v4 since GLM-ASR is hosted only there.
  • trajectory_compressor.py_detect_provider now inspects URL path to distinguish Coding Plan from direct API (previous base_url_host_matches call with a path string was dead code because the helper only compares hostnames).

Compatibility

A user upgrading with model.provider: zai and only GLM_API_KEY set would otherwise break because zai's env tuple is now (ZAI_API_KEY, Z_AI_API_KEY). Rather than add a migration shim, GLM_API_KEY is kept as a last-resort fallback in zai's env tuple. On upgrade:

  • provider: zai + only GLM_API_KEY → key resolves via fallback; request hits api.z.ai (may 401 if the key was issued for Zhipu/bigmodel). Real API error beats the pre-fix empty-key bailout; running hermes model re-picks the correct plan.
  • provider: zai-cn + GLM_API_KEY → clean.
  • Auto-detect with only GLM_API_KEYzai-cn (registry order reversal ensures this).
  • ZAI_API_KEY always wins over GLM_API_KEY when both present.

Validation

.venv/bin/python -m pytest \
  tests/hermes_cli/test_api_key_providers.py \
  tests/hermes_cli/test_model_validation.py \
  tests/hermes_cli/test_model_provider_persistence.py \
  tests/hermes_cli/test_doctor.py \
  tests/hermes_cli/test_status.py \
  tests/hermes_cli/test_model_normalize.py \
  tests/agent/test_auxiliary_client.py \
  tests/agent/test_model_metadata.py \
  tests/gateway/test_qqbot.py \
  -q

All 457 relevant tests pass. Added coverage for per-plan registry entries, legacy GLM_API_KEY fallback, auto-detect priority, and static-endpoint resolution.

🤖 Generated with Claude Code

…obal + China)

Previously a single `zai` provider served Z.AI / GLM across all plans, endpoints,
and billing modes via runtime endpoint-probing. Split into four explicit providers
so users pick their plan once and Hermes routes cleanly:

  - zai                — Z.AI (Global)       — api.z.ai/api/paas/v4
  - zai-cn             — Zhipu AI (China)    — open.bigmodel.cn/api/paas/v4
  - zai-coding-global  — Z.AI Coding Plan    — api.z.ai/api/coding/paas/v4
  - zai-coding-cn      — Zhipu AI Coding Plan — open.bigmodel.cn/api/coding/paas/v4

Key changes:
  - PROVIDER_REGISTRY + HERMES_OVERLAYS declare each plan with its own env vars
    (ZAI_API_KEY, GLM_API_KEY, ZAI_CODING_API_KEY, GLM_CODING_API_KEY) and base-URL
    override vars. Models.dev mapping uses per-plan IDs (zai / zhipuai /
    zai-coding-plan / zhipuai-coding-plan) so /model and /provider show distinct
    catalogs and display names.
  - Curated `_PROVIDER_MODELS` lists per plan: direct-API plans get the full
    9-model catalog (glm-5.1, glm-5, glm-5v-turbo, glm-5-turbo, glm-4.7,
    glm-4.7-flashx, glm-4.6, glm-4.5, glm-4.5-air); coding plans get the
    4-model subset (glm-5.1, glm-5-turbo, glm-4.7, glm-4.5-air).
  - Removed legacy ZAI endpoint-probing (`detect_zai_endpoint`,
    `_resolve_zai_base_url`) — each plan's URL is now static.
  - Aux-client defaults: zai/zai-cn -> glm-4.7-flashx; zai-coding-* -> glm-4.7.
    Vision override: zai/zai-cn -> glm-5v-turbo.
  - Dot-preservation in run_agent._anthropic_preserve_dots() extended to all
    four plans (prevents glm-4.7 from being mangled to glm-47).
  - Doctor / status / dump / setup / config OPTIONAL_ENV_VARS updated with
    per-plan entries and correct env-var mappings.
  - trajectory_compressor._detect_provider now checks host + path to route
    coding-plan URLs correctly (previously relied on base_url_host_matches
    which ignored path components).

Legacy compatibility:
  - `GLM_API_KEY` is retained as a last-resort fallback in zai's env tuple
    so existing `provider: zai` configs with only GLM_API_KEY set keep working.
  - PROVIDER_REGISTRY declares `zai-cn` before `zai` so auto-detect with only
    GLM_API_KEY still prefers `zai-cn` (correct plan), while explicit
    `provider: zai` can still read the GLM key as a fallback.
  - Aliases: `glm`/`zhipu` now map to `zai-cn` (was `zai`); `z-ai`/`z.ai`/`grok`
    unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard provider/zai ZAI provider type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants