Fix Xiaomi API key env detection
This commit is contained in:
1 parent
b997067ae8
commit
128e734df4
13 files changed
+95
-4
No files matched your search
@@ -1,5 +1,11 @@
|
||||
# Hermes Web UI -- Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **fix(providers): detect Xiaomi MiMo from `XIAOMI_API_KEY`** ([#2025](https://github.com/nesquena/hermes-webui/issues/2025)). WebUI now treats Xiaomi like the other API-key providers: exported or `.env`-stored `XIAOMI_API_KEY` enables the Xiaomi model group, Settings provider-key detection, and onboarding help text without requiring a manual provider config entry.
|
||||
|
||||
## [v0.51.39] — 2026-05-10 — Release O (4-PR contributor batch — Railway docker fix + Stop-button race + provider resolver + live context tracking)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -375,7 +375,7 @@ across 100+ test files.
|
||||
|
||||
### Chat and agent
|
||||
- Streaming responses via SSE (tokens appear as they are generated)
|
||||
- Multi-provider model support -- any Hermes API provider (OpenAI, Anthropic, Google, DeepSeek, Nous Portal, OpenRouter, MiniMax, Z.AI); dynamic model dropdown populated from configured keys
|
||||
- Multi-provider model support -- any Hermes API provider (OpenAI, Anthropic, Google, DeepSeek, Nous Portal, OpenRouter, MiniMax, Xiaomi MiMo, Z.AI); dynamic model dropdown populated from configured keys
|
||||
- Send a message while one is processing -- it queues automatically
|
||||
- Edit any past user message inline and regenerate from that point
|
||||
- Retry the last assistant response with one click
|
||||
|
||||
@@ -2760,6 +2760,7 @@ def get_available_models() -> dict:
|
||||
"GLM_API_KEY",
|
||||
"KIMI_API_KEY",
|
||||
"DEEPSEEK_API_KEY",
|
||||
"XIAOMI_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY",
|
||||
"OPENCODE_GO_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
@@ -2795,6 +2796,8 @@ def get_available_models() -> dict:
|
||||
detected_providers.add("minimax-cn")
|
||||
if all_env.get("DEEPSEEK_API_KEY"):
|
||||
detected_providers.add("deepseek")
|
||||
if all_env.get("XIAOMI_API_KEY"):
|
||||
detected_providers.add("xiaomi")
|
||||
if all_env.get("XAI_API_KEY"):
|
||||
detected_providers.add("x-ai")
|
||||
if all_env.get("MISTRAL_API_KEY"):
|
||||
|
||||
@@ -139,6 +139,15 @@ _SUPPORTED_PROVIDER_SETUPS = {
|
||||
"models": list(_PROVIDER_MODELS.get("deepseek", [])),
|
||||
"category": "specialized",
|
||||
},
|
||||
"xiaomi": {
|
||||
"label": "Xiaomi MiMo",
|
||||
"env_var": "XIAOMI_API_KEY",
|
||||
"default_model": "mimo-v2.5-pro",
|
||||
"default_base_url": "https://api.xiaomimimo.com/v1",
|
||||
"requires_base_url": False,
|
||||
"models": list(_PROVIDER_MODELS.get("xiaomi", [])),
|
||||
"category": "specialized",
|
||||
},
|
||||
"zai": {
|
||||
"label": "Z.AI / GLM (智谱)",
|
||||
"env_var": "GLM_API_KEY",
|
||||
|
||||
@@ -102,6 +102,7 @@ _PROVIDER_ENV_VAR: dict[str, str] = {
|
||||
"minimax-cn": "MINIMAX_CN_API_KEY",
|
||||
"mistralai": "MISTRAL_API_KEY",
|
||||
"x-ai": "XAI_API_KEY",
|
||||
"xiaomi": "XIAOMI_API_KEY",
|
||||
"opencode-zen": "OPENCODE_ZEN_API_KEY",
|
||||
"opencode-go": "OPENCODE_GO_API_KEY",
|
||||
# NOTE: bare "ollama" (local) deliberately omitted — local Ollama is keyless
|
||||
|
||||
+1
-1
@@ -291,7 +291,7 @@ def test_server():
|
||||
for _k in list(env):
|
||||
if any(_k.startswith(p) for p in (
|
||||
'OPENROUTER_API_KEY', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY',
|
||||
'GOOGLE_API_KEY', 'DEEPSEEK_API_KEY',
|
||||
'GOOGLE_API_KEY', 'DEEPSEEK_API_KEY', 'XIAOMI_API_KEY',
|
||||
)):
|
||||
del env[_k]
|
||||
env.update({
|
||||
|
||||
@@ -85,7 +85,7 @@ def _setup_clean_config(monkeypatch, tmp_path):
|
||||
"OPENROUTER_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY",
|
||||
"GOOGLE_API_KEY", "GEMINI_API_KEY", "GLM_API_KEY",
|
||||
"KIMI_API_KEY", "DEEPSEEK_API_KEY", "MINIMAX_API_KEY",
|
||||
"MISTRAL_API_KEY", "XAI_API_KEY", "OLLAMA_API_KEY",
|
||||
"XIAOMI_API_KEY", "MISTRAL_API_KEY", "XAI_API_KEY", "OLLAMA_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY", "OPENCODE_GO_API_KEY",
|
||||
]
|
||||
for var in _provider_env_vars:
|
||||
|
||||
@@ -22,6 +22,7 @@ _PROVIDER_ENV_VARS = (
|
||||
"GLM_API_KEY",
|
||||
"KIMI_API_KEY",
|
||||
"DEEPSEEK_API_KEY",
|
||||
"XIAOMI_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY",
|
||||
"OPENCODE_GO_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
|
||||
@@ -243,7 +243,7 @@ class TestIssue1420LMStudioProviderEnvVar:
|
||||
"OPENAI_API_KEY", "OPENROUTER_API_KEY", "ANTHROPIC_API_KEY",
|
||||
"GH_TOKEN", "GITHUB_TOKEN", "OLLAMA_API_KEY", "GOOGLE_API_KEY",
|
||||
"GEMINI_API_KEY", "DEEPSEEK_API_KEY", "MINIMAX_API_KEY",
|
||||
"MINIMAX_CN_API_KEY", "MISTRAL_API_KEY", "XAI_API_KEY",
|
||||
"MINIMAX_CN_API_KEY", "XIAOMI_API_KEY", "MISTRAL_API_KEY", "XAI_API_KEY",
|
||||
"GLM_API_KEY", "KIMI_API_KEY", "OPENCODE_ZEN_API_KEY",
|
||||
"OPENCODE_GO_API_KEY", "NVIDIA_API_KEY", "LMSTUDIO_API_KEY",
|
||||
):
|
||||
|
||||
@@ -126,6 +126,7 @@ def _scrub_provider_env(monkeypatch):
|
||||
"OLLAMA_CLOUD_API_KEY", "OLLAMA_API_KEY",
|
||||
"GLM_API_KEY", "KIMI_API_KEY", "MOONSHOT_API_KEY",
|
||||
"MINIMAX_API_KEY", "MINIMAX_CN_API_KEY",
|
||||
"XIAOMI_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY", "OPENCODE_GO_API_KEY",
|
||||
"NOUS_API_KEY", "NVIDIA_API_KEY", "LM_API_KEY", "LMSTUDIO_API_KEY",
|
||||
):
|
||||
|
||||
@@ -127,6 +127,7 @@ def _scrub_provider_env(monkeypatch):
|
||||
"OLLAMA_CLOUD_API_KEY", "OLLAMA_API_KEY",
|
||||
"GLM_API_KEY", "KIMI_API_KEY", "MOONSHOT_API_KEY",
|
||||
"MINIMAX_API_KEY", "MINIMAX_CN_API_KEY",
|
||||
"XIAOMI_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY", "OPENCODE_GO_API_KEY",
|
||||
"NOUS_API_KEY", "NVIDIA_API_KEY", "LM_API_KEY", "LMSTUDIO_API_KEY",
|
||||
):
|
||||
|
||||
@@ -85,6 +85,7 @@ def _scrub_provider_env(monkeypatch):
|
||||
"OLLAMA_CLOUD_API_KEY", "OLLAMA_API_KEY",
|
||||
"GLM_API_KEY", "KIMI_API_KEY", "MOONSHOT_API_KEY",
|
||||
"MINIMAX_API_KEY", "MINIMAX_CN_API_KEY",
|
||||
"XIAOMI_API_KEY",
|
||||
"OPENCODE_ZEN_API_KEY", "OPENCODE_GO_API_KEY",
|
||||
"NOUS_API_KEY", "NVIDIA_API_KEY", "LM_API_KEY", "LMSTUDIO_API_KEY",
|
||||
):
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Regression coverage for #2025: Xiaomi MiMo should honor XIAOMI_API_KEY."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import builtins
|
||||
|
||||
import api.config as config
|
||||
import api.onboarding as onboarding
|
||||
import api.providers as providers
|
||||
|
||||
|
||||
def _force_env_fallback(monkeypatch):
|
||||
"""Force get_available_models() down its explicit env-var fallback path."""
|
||||
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")
|
||||
monkeypatch.setattr("api.profiles.get_active_hermes_home", lambda: tmp_path, raising=False)
|
||||
config.cfg.clear()
|
||||
config.cfg.update(cfg)
|
||||
config._cfg_mtime = 0.0
|
||||
config.invalidate_models_cache()
|
||||
try:
|
||||
return config.get_available_models()
|
||||
finally:
|
||||
config.cfg.clear()
|
||||
config.cfg.update(old_cfg)
|
||||
config._cfg_mtime = old_mtime
|
||||
config.invalidate_models_cache()
|
||||
|
||||
|
||||
def test_xiaomi_api_key_env_var_detects_model_group(monkeypatch, tmp_path):
|
||||
_force_env_fallback(monkeypatch)
|
||||
monkeypatch.setenv("XIAOMI_API_KEY", "test-xiaomi-key")
|
||||
|
||||
result = _run_available_models_with_cfg(monkeypatch, tmp_path, {"model": {}})
|
||||
groups = {group["provider_id"]: group for group in result["groups"]}
|
||||
|
||||
assert "xiaomi" in groups
|
||||
assert groups["xiaomi"]["provider"] == "Xiaomi"
|
||||
assert "mimo-v2.5-pro" in {model["id"] for model in groups["xiaomi"]["models"]}
|
||||
|
||||
|
||||
def test_xiaomi_provider_settings_detects_env_key(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(providers, "_get_hermes_home", lambda: tmp_path)
|
||||
monkeypatch.setenv("XIAOMI_API_KEY", "test-xiaomi-key")
|
||||
|
||||
assert providers._PROVIDER_ENV_VAR["xiaomi"] == "XIAOMI_API_KEY"
|
||||
assert providers._provider_has_key("xiaomi") is True
|
||||
|
||||
|
||||
def test_onboarding_lists_xiaomi_api_key_help():
|
||||
setup = onboarding._SUPPORTED_PROVIDER_SETUPS["xiaomi"]
|
||||
|
||||
assert setup["env_var"] == "XIAOMI_API_KEY"
|
||||
assert setup["default_base_url"] == "https://api.xiaomimimo.com/v1"
|
||||
assert {model["id"] for model in setup["models"]} >= {"mimo-v2.5-pro"}
|
||||
Reference in new issue
Block a user