fix(admin,ai): redirect admin.html + modèles fallback + picker provider/modèle par requête
CI / lint (push) Successful in 41s
CI / security (push) Successful in 28s
CI / test (push) Successful in 48s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m5s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
CI / lint (push) Successful in 41s
CI / security (push) Successful in 28s
CI / test (push) Successful in 48s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m5s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Trois bugs corrigés + une amélioration demandée :
1. **fix(admin): /admin.html redirigeait toujours vers /**
- admin.js _gateAdmin() lisait /api/auth/status qui ne contient PAS le rôle user
- Remplacé par /api/auth/me (retourne username, role, vaults)
- Le code distingue maintenant le cas 'auth désactivé' (admin anonyme) du
cas 'auth requise non admin' (affiche écran forbidden)
2. **fix(ai): les modèles Nvidia/Xiaomi ne se chargeaient pas dans les dropdowns**
- Xiaomi : l'endpoint public /v1/models est instable, échec réseau fréquent
- Toutes les erreurs réseau/d'API renvoyaient models=[] → dropdown vide
- Ajout d'un fallback curé : _FALLBACK_MODELS dict avec 4-8 modèles populaires
par provider, TOUJOURS retourné si la clé manque OU si l'API distante échoue
- Le frontend voit désormais 'fallback' vs 'live' comme hint pour l'utilisateur
- Gemini parsing : strip du préfixe 'models/' retourné par l'API Gemini
- 7 nouveaux tests pytest pour _FALLBACK_MODELS + endpoint /api/config/ai-models
3. **feat(ai): picker provider/model dans la toolbar AI + BooksLM**
- Plusieurs providers peuvent maintenant être activés simultanément
- Sélection provider+model par section (Forge, BooksLM, etc.) via dropdown
- État persisté en localStorage (le choix suit l'utilisateur entre sections)
- Chaque appel AI passe maintenant {provider, model} au backend
- ai.js : aiAction() lit le picker et l'injecte dans le body
- bookslm.js : envoie provider+model à /api/ai/bookslm/chat
- ai_routes.py + bookslm_routes.py : AIRequest et BooksLMChatRequest
acceptent provider+model, avec save/restore du modèle original
pour ne pas affecter les autres requêtes concurrentes
- 7 nouvelles clés i18n (ai.provider, ai.model, ai.model_loading, etc.)
- 1 nouveau test bookslm vérifie que le schema accepte provider+model
- validate-imports.mjs : fix faux positif sur 'export { X as Y }'
Vérifié :
- pytest : 502 passed, 5 skipped (494 baseline + 7 AI models + 1 BooksLM)
- frontend unit : 7 passed
- validate-imports : 30 modules / 204 exports / 0 erreur
- pane-manager JSDOM : 9/9
- ruff check backend/ : All checks passed
This commit is contained in:
@@ -536,3 +536,41 @@ class TestBooksLMChatEndpoint:
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_chat_request_accepts_provider_and_model(self, bookslm_client):
|
||||
"""The chat endpoint schema accepts provider + model fields without rejecting.
|
||||
|
||||
We don't actually call the AI (would need a live key) — we just verify
|
||||
the request schema is wired correctly so a missing key is the only
|
||||
failure mode, not a 422 validation error.
|
||||
"""
|
||||
token, _ = _login_bookslm(bookslm_client)
|
||||
# Build a tiny valid directory so the chat endpoint doesn't 404.
|
||||
from pathlib import Path
|
||||
vault_dir = Path(os.environ.get("VAULT_1_PATH", "test-vault"))
|
||||
sub = vault_dir / "for_chat_test"
|
||||
sub.mkdir(exist_ok=True)
|
||||
(sub / "note.md").write_text("# hello\n", encoding="utf-8")
|
||||
try:
|
||||
resp = bookslm_client.post(
|
||||
"/api/ai/bookslm/chat",
|
||||
json={
|
||||
"vault": "TestVault",
|
||||
"directory": "for_chat_test",
|
||||
"message": "ping",
|
||||
"provider": "deepseek",
|
||||
"model": "deepseek-chat",
|
||||
},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
# Either 200 (if a real key is configured) or 500 (no key / quota).
|
||||
# Must NOT be 422 — the schema must accept the fields.
|
||||
assert resp.status_code in (200, 500), (
|
||||
f"unexpected status {resp.status_code}: {resp.text[:200]}"
|
||||
)
|
||||
assert resp.status_code != 422, (
|
||||
f"schema rejected provider/model fields: {resp.text[:300]}"
|
||||
)
|
||||
finally:
|
||||
import shutil
|
||||
shutil.rmtree(sub, ignore_errors=True)
|
||||
|
||||
Reference in New Issue
Block a user