From 52e16c11384646972684c6d53189fc94ceb2f199 Mon Sep 17 00:00:00 2001 From: Almurat Date: Tue, 30 Jun 2026 16:38:01 +0800 Subject: [PATCH] fix: preserve kimi-coding-cn provider identity --- hermes_cli/providers.py | 18 ++++++ .../test_kimi_cn_provider_listing.py | 59 ++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/hermes_cli/providers.py b/hermes_cli/providers.py index c3e8efd22290..128649cc17c9 100644 --- a/hermes_cli/providers.py +++ b/hermes_cli/providers.py @@ -765,6 +765,24 @@ def resolve_provider_full( if user_pdef is not None: return user_pdef + # 0.5 Exact Hermes provider IDs must win over alias collapsing. + # Example: kimi-coding-cn should stay distinct from kimi-coding instead of + # normalizing through the shared models.dev alias "kimi-for-coding". + try: + from hermes_cli.auth import PROVIDER_REGISTRY as _AUTH_PROVIDER_REGISTRY + _pcfg = _AUTH_PROVIDER_REGISTRY.get(raw) + if _pcfg is not None: + return ProviderDef( + id=_pcfg.id, + name=_pcfg.name, + transport="openai_chat", + api_key_env_vars=tuple(_pcfg.api_key_env_vars or ()), + base_url=_pcfg.inference_base_url or "", + source="hermes-auth-registry", + ) + except Exception: + pass + # 1. Built-in (models.dev + overlays) pdef = get_provider(canonical) if pdef is not None: diff --git a/tests/hermes_cli/test_kimi_cn_provider_listing.py b/tests/hermes_cli/test_kimi_cn_provider_listing.py index 5d7f473e3249..1a49b0d8fd1a 100644 --- a/tests/hermes_cli/test_kimi_cn_provider_listing.py +++ b/tests/hermes_cli/test_kimi_cn_provider_listing.py @@ -14,7 +14,12 @@ through. import os from unittest.mock import patch -from hermes_cli.model_switch import list_authenticated_providers +from hermes_cli.model_switch import ( + list_authenticated_providers, + parse_model_flags, + switch_model, +) +from hermes_cli.providers import resolve_provider_full # -- Only KIMI_CN_API_KEY set ------------------------------------------------ @@ -117,3 +122,55 @@ def test_kimi_aliases_not_listed_separately(): f"Alias slug '{bad_slug}' must not appear in picker (resolved to " f"canonical profile)" ) + + +@patch.dict(os.environ, { + "KIMI_API_KEY": "sk-intl-fake", + "KIMI_CN_API_KEY": "sk-cn-fake", +}, clear=False) +def test_resolve_provider_full_preserves_kimi_cn_provider_identity(): + """Explicit kimi-coding-cn must not collapse to shared models.dev alias. + + Regression: resolve_provider_full('kimi-coding-cn') used normalize_provider(), + which mapped both kimi-coding and kimi-coding-cn to the models.dev alias + 'kimi-for-coding'. That silently rewired CN users to the international + endpoint and KIMI_API_KEY. + """ + pdef = resolve_provider_full("kimi-coding-cn", None, None) + assert pdef is not None + assert pdef.id == "kimi-coding-cn" + assert pdef.base_url == "https://api.moonshot.cn/v1" + assert pdef.api_key_env_vars == ("KIMI_CN_API_KEY",) + + +@patch.dict(os.environ, { + "KIMI_API_KEY": "sk-intl-fake", + "KIMI_CN_API_KEY": "sk-cn-fake", +}, clear=False) +def test_switch_model_with_explicit_kimi_cn_provider_stays_on_cn_endpoint(): + """/model ... --provider kimi-coding-cn must stay on moonshot.cn. + + This hits the real switch path used by gateway /model: parse flags first, + then call switch_model() with explicit_provider. The result must not rewrite + the target provider/base_url back to the international Kimi endpoint. + """ + model_input, explicit_provider, *_ = parse_model_flags( + "kimi-k2.6 —provider kimi-coding-cn" + ) + result = switch_model( + raw_input=model_input, + current_provider="deepseek", + current_model="deepseek-v4-flash", + current_base_url="https://api.deepseek.com/v1", + current_api_key="***", + is_global=False, + explicit_provider=explicit_provider, + user_providers={}, + custom_providers=None, + ) + + assert result.success is True + assert result.target_provider == "kimi-coding-cn" + assert result.new_model == "kimi-k2.6" + assert result.base_url == "https://api.moonshot.cn/v1" + assert result.api_key == "sk-cn-fake"