Fix MiniMax China provider visibility

This commit is contained in:
Frank Song
2026-04-29 15:50:32 +08:00
parent 72b4ff66f0
commit b277e195fe
6 changed files with 118 additions and 7 deletions
+3
View File
@@ -2,6 +2,9 @@
## [Unreleased]
### Fixed
- **MiniMax China provider visible in model picker**`MINIMAX_CN_API_KEY` now maps to the `minimax-cn` provider instead of being collapsed into global `minimax`; WebUI includes a static MiniMax (China) model catalog/display label so `providers.minimax-cn: {}` can render a populated picker group. (`api/config.py`, `api/providers.py`) @franksong2702 — Closes #1236
## [v0.50.237] — 2026-04-29
### Added
+12 -1
View File
@@ -536,6 +536,7 @@ _PROVIDER_DISPLAY = {
"kimi-coding": "Kimi / Moonshot",
"deepseek": "DeepSeek",
"minimax": "MiniMax",
"minimax-cn": "MiniMax (China)",
"google": "Google",
"meta-llama": "Meta Llama",
"huggingface": "HuggingFace",
@@ -581,6 +582,8 @@ _PROVIDER_ALIASES = {
"claude": "anthropic",
"claude-code": "anthropic",
"deep-seek": "deepseek",
"minimax-china": "minimax-cn",
"minimax_cn": "minimax-cn",
"opencode": "opencode-zen",
"grok": "xai",
"x-ai": "xai",
@@ -688,6 +691,12 @@ _PROVIDER_MODELS = {
{"id": "MiniMax-M2.5-highspeed", "label": "MiniMax M2.5 Highspeed"},
{"id": "MiniMax-M2.1", "label": "MiniMax M2.1"},
],
"minimax-cn": [
{"id": "MiniMax-M2.7", "label": "MiniMax M2.7"},
{"id": "MiniMax-M2.5", "label": "MiniMax M2.5"},
{"id": "MiniMax-M2.1", "label": "MiniMax M2.1"},
{"id": "MiniMax-M2", "label": "MiniMax M2"},
],
# GitHub Copilot — model IDs served via the Copilot API
"copilot": [
{"id": "gpt-5.5", "label": "GPT-5.5"},
@@ -1574,8 +1583,10 @@ def get_available_models() -> dict:
detected_providers.add("zai")
if all_env.get("KIMI_API_KEY"):
detected_providers.add("kimi-coding")
if all_env.get("MINIMAX_API_KEY") or all_env.get("MINIMAX_CN_API_KEY"):
if all_env.get("MINIMAX_API_KEY"):
detected_providers.add("minimax")
if all_env.get("MINIMAX_CN_API_KEY"):
detected_providers.add("minimax-cn")
if all_env.get("DEEPSEEK_API_KEY"):
detected_providers.add("deepseek")
if all_env.get("XAI_API_KEY"):
+1
View File
@@ -39,6 +39,7 @@ _PROVIDER_ENV_VAR: dict[str, str] = {
"kimi-coding": "KIMI_API_KEY",
"deepseek": "DEEPSEEK_API_KEY",
"minimax": "MINIMAX_API_KEY",
"minimax-cn": "MINIMAX_CN_API_KEY",
"mistralai": "MISTRAL_API_KEY",
"x-ai": "XAI_API_KEY",
"opencode-zen": "OPENCODE_ZEN_API_KEY",
@@ -104,3 +104,6 @@ class TestProviderModelsCompleteness:
def test_has_openrouter(self):
# openrouter uses _FALLBACK_MODELS, not _PROVIDER_MODELS
pass # intentionally no assertion
def test_has_minimax_cn(self):
assert "minimax-cn" in _PROVIDER_MODELS_KEYS
+93 -1
View File
@@ -3,7 +3,7 @@ Tests for MiniMax provider support in the model/provider discovery layer.
Covers:
- MiniMax models appear in the fallback model list
- MINIMAX_API_KEY env var is scanned and detected from os.environ
- MINIMAX_API_KEY / MINIMAX_CN_API_KEY env vars are scanned and detected
- @minimax: provider hint routing works correctly
- minimax/MiniMax-M2.7 (slash format) is routed via openrouter when active provider differs
"""
@@ -12,6 +12,36 @@ import pytest
import api.config as config
def _force_env_fallback(monkeypatch):
"""Force get_available_models() down the explicit env-var fallback path."""
import builtins
real_import = builtins.__import__
def fake_import(name, globals=None, locals=None, fromlist=(), level=0):
if name in ("hermes_cli.models", "hermes_cli.auth"):
raise ImportError(name)
return real_import(name, globals, locals, fromlist, level)
monkeypatch.setattr(builtins, "__import__", fake_import)
def _run_available_models_with_cfg(monkeypatch, tmp_path, cfg):
old_cfg = dict(config.cfg)
old_mtime = config._cfg_mtime
monkeypatch.setattr(config, "_models_cache_path", tmp_path / "models_cache.json")
monkeypatch.setattr(config, "_get_config_path", lambda: tmp_path / "missing-config.yaml")
config.cfg.clear()
config.cfg.update(cfg)
config._cfg_mtime = 0.0
try:
return config.get_available_models()
finally:
config.cfg.clear()
config.cfg.update(old_cfg)
config._cfg_mtime = old_mtime
@pytest.fixture(autouse=True)
def _isolate_models_cache():
"""Invalidate the models TTL cache before and after every test in this file."""
@@ -91,6 +121,19 @@ def test_minimax_provider_models_has_highspeed():
)
def test_minimax_cn_provider_models_match_hermes_agent_catalog():
"""minimax-cn must have its own static catalog so an empty config provider still shows models."""
models = config._PROVIDER_MODELS.get('minimax-cn', [])
ids = [m['id'] for m in models]
assert ids == [
'MiniMax-M2.7',
'MiniMax-M2.5',
'MiniMax-M2.1',
'MiniMax-M2',
]
assert config._PROVIDER_DISPLAY.get('minimax-cn') == 'MiniMax (China)'
# ── MINIMAX_API_KEY env var detection ─────────────────────────────────────────
def test_minimax_api_key_in_env_scan_tuple():
@@ -132,6 +175,55 @@ def test_minimax_detected_from_os_environ(monkeypatch):
config.cfg.update(old_cfg)
def test_minimax_cn_detected_from_os_environ(monkeypatch, tmp_path):
"""MINIMAX_CN_API_KEY should show MiniMax (China), not the global MiniMax provider."""
_force_env_fallback(monkeypatch)
monkeypatch.delenv('MINIMAX_API_KEY', raising=False)
monkeypatch.setenv('MINIMAX_CN_API_KEY', 'test-cn-key-from-env')
result = _run_available_models_with_cfg(monkeypatch, tmp_path, {'model': {}})
groups = {g['provider_id']: g for g in result['groups']}
assert 'minimax-cn' in groups, f"minimax-cn group missing: {groups.keys()}"
assert groups['minimax-cn']['provider'] == 'MiniMax (China)'
assert {m['id'] for m in groups['minimax-cn']['models']} == {
'MiniMax-M2.7',
'MiniMax-M2.5',
'MiniMax-M2.1',
'MiniMax-M2',
}
assert 'minimax' not in groups, (
"MINIMAX_CN_API_KEY must not be collapsed into the global minimax provider"
)
def test_minimax_cn_empty_config_provider_gets_static_models(monkeypatch, tmp_path):
"""providers.minimax-cn: {} should still render a populated model group."""
_force_env_fallback(monkeypatch)
monkeypatch.delenv('MINIMAX_API_KEY', raising=False)
monkeypatch.delenv('MINIMAX_CN_API_KEY', raising=False)
result = _run_available_models_with_cfg(
monkeypatch,
tmp_path,
{
'model': {'provider': 'minimax-cn', 'default': 'MiniMax-M2.7'},
'providers': {'minimax-cn': {}},
},
)
groups = {g['provider_id']: g for g in result['groups']}
assert 'minimax-cn' in groups, f"minimax-cn group missing: {groups.keys()}"
assert groups['minimax-cn']['models'], "minimax-cn group must not be empty"
def test_minimax_cn_key_can_be_managed_from_provider_settings():
"""Provider settings should use the Hermes Agent env var for minimax-cn."""
from api.providers import _PROVIDER_ENV_VAR
assert _PROVIDER_ENV_VAR.get('minimax-cn') == 'MINIMAX_CN_API_KEY'
# ── Model routing ─────────────────────────────────────────────────────────────
def test_provider_hint_minimax_m2_7():
+6 -5
View File
@@ -365,8 +365,9 @@ def test_unknown_providers_do_not_inherit_default_model(monkeypatch):
"""Detected providers without their own model catalog must not be filled
with the global default_model placeholder.
Regression guard for the bug where Alibaba / Minimax-Cn ended up showing
gpt-5.4-mini even though those providers do not serve it.
Regression guard for the bug where unknown providers ended up showing
gpt-5.4-mini even though those providers do not serve it. Minimax-Cn is
now known and should show its own catalog instead.
"""
import sys, types
@@ -392,12 +393,12 @@ def test_unknown_providers_do_not_inherit_default_model(monkeypatch):
assert 'Alibaba' not in groups, (
f"Alibaba should not inherit the default model placeholder: {groups}"
)
assert 'Minimax-Cn' not in groups, (
f"Minimax-Cn should not inherit the default model placeholder: {groups}"
assert 'MiniMax (China)' in groups, (
f"Minimax-Cn should render its own static catalog: {groups}"
)
assert not any(
norm(mid) == 'gpt-5.4-mini'
for mid in groups.get('Alibaba', []) + groups.get('Minimax-Cn', [])
for mid in groups.get('Alibaba', []) + groups.get('MiniMax (China)', [])
), (
f"Unknown provider groups still inherited the default model: {groups}"
)