mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 11:10:18 +00:00
Fix MiniMax China provider visibility
This commit is contained in:
@@ -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
@@ -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"):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user