fix(xiaomi): URL MiMo + header api-key + fallback polling admin SSE
CI / lint (push) Successful in 37s
CI / security (push) Successful in 32s
CI / test (push) Successful in 48s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m1s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
CI / lint (push) Successful in 37s
CI / security (push) Successful in 32s
CI / test (push) Successful in 48s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m1s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
1. **fix(xiaomi): la clé Xiaomi échouait avec 'Name or service not known'** - URL précédente api.xiaomi.com n'existe pas (DNS fail) - Vraie URL: https://api.xiaomimimo.com/v1/models - Xiaomi MiMo utilise un header custom 'api-key:' (PAS 'Authorization: Bearer') - Modèles par défaut mis à jour: mimo-v2.5-pro, mimo-v2.5, mimo-v2.5-asr, etc. - Le chat dans ai.py supporte maintenant un header custom via auth_header_name 2. **fix(admin): EventSource 503 → fallback polling** - Ajout d'un fallback: si EventSource ne reçoit jamais le premier event (cookie expiré, réseau bloqué, proxy timeout), on bascule sur du polling /api/admin/stats toutes les 5s. Le UI continue de se mettre à jour. - Cleanup correct du timer dans disconnectSSE 3. **fix(test_ai_models)**: 2 nouveaux tests de régression - test_xiaomi_uses_api_key_header_not_bearer: vérifie URL + header - test_xiaomi_test_endpoint_uses_real_url: vérifie /api/config/ai-keys/test - Patche backend.ai ET backend.main (import local) - Header case-insensitive (urllib normalise à 'Api-key', HTTP est insensible) Vérifié : - pytest : 504 passed (502 + 2 nouveaux Xiaomi) - frontend unit : 7 passed - validate-imports : 30 modules / 204 exports - pane-manager JSDOM : 9/9 - ruff check backend/ : All checks passed
This commit is contained in:
+19
-5
@@ -75,9 +75,13 @@ def _load_provider_keys():
|
||||
},
|
||||
"xiaomi": {
|
||||
"api_key": get_ai_key("XIAOMI_API_KEY"),
|
||||
"base_url": "https://api.xiaomi.com/v1",
|
||||
"model": os.getenv("XIAOMI_MODEL", "mimo-v2-pro"),
|
||||
"auth_header": "Bearer {api_key}",
|
||||
"base_url": os.getenv("XIAOMI_BASE_URL", "https://api.xiaomimimo.com/v1"),
|
||||
"model": os.getenv("XIAOMI_MODEL", "mimo-v2.5-pro"),
|
||||
# Xiaomi MiMo uses a dedicated `api-key` header (NOT Authorization: Bearer).
|
||||
# The `_call_deepseek_openrouter` helper substitutes {api_key} verbatim,
|
||||
# so we just emit the raw key value here.
|
||||
"auth_header": "{api_key}",
|
||||
"auth_header_name": "api-key",
|
||||
},
|
||||
"mistral": {
|
||||
"api_key": get_ai_key("MISTRAL_API_KEY"),
|
||||
@@ -110,13 +114,23 @@ def _get_provider_config(provider: ProviderName | None = None) -> dict:
|
||||
|
||||
async def _call_deepseek_openrouter(prompt: str, system: str, provider: ProviderName | None = None,
|
||||
temperature: float = 0.7, max_tokens: int = 2048) -> str:
|
||||
"""Call OpenAI-compatible API (DeepSeek, OpenRouter)."""
|
||||
"""Call OpenAI-compatible API (DeepSeek, OpenRouter, Xiaomi MiMo, etc.)."""
|
||||
cfg = _get_provider_config(provider)
|
||||
# Debug: log masked key to diagnose 401
|
||||
key_preview = cfg["api_key"][:8] + "..." + cfg["api_key"][-4:] if len(cfg["api_key"]) > 12 else "***"
|
||||
logger.info(f"AI call: provider={cfg['name']} model={cfg['model']} key={key_preview}")
|
||||
# Most providers use "Authorization: Bearer KEY". Some (Xiaomi MiMo) use a
|
||||
# dedicated header like "api-key: KEY". We support both via the
|
||||
# `auth_header_name` key in PROVIDERS — defaults to "Authorization".
|
||||
header_name = cfg.get("auth_header_name") or "Authorization"
|
||||
header_value = cfg["auth_header"].format(api_key=cfg["api_key"])
|
||||
# If the auth_header template doesn't include "Bearer " but the default
|
||||
# header is Authorization, prepend it. This preserves backward compatibility
|
||||
# for providers that store just the raw key.
|
||||
if header_name == "Authorization" and not header_value.lower().startswith("bearer "):
|
||||
header_value = "Bearer " + header_value
|
||||
headers = {
|
||||
"Authorization": cfg["auth_header"].format(api_key=cfg["api_key"]),
|
||||
header_name: header_value,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {
|
||||
|
||||
Reference in New Issue
Block a user