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:
+20
-1
@@ -1,5 +1,6 @@
|
||||
// BooksLM — Directory-scoped AI chat panel (style NotebookLM)
|
||||
import { t } from './i18n.js';
|
||||
import { buildAIPickerUI } from './ai.js';
|
||||
|
||||
class BooksLM {
|
||||
constructor() {
|
||||
@@ -118,6 +119,7 @@ class BooksLM {
|
||||
<div class="bookslm-header">
|
||||
<span>📚</span>
|
||||
<span class="bookslm-title"></span>
|
||||
<span class="bookslm-picker-host" style="margin-left:auto"></span>
|
||||
<button class="bookslm-btn-new" title="${t('bookslm.new_conversation')}">✨</button>
|
||||
<button class="bookslm-btn-export" title="${t('bookslm.export')}">📥</button>
|
||||
<button class="bookslm-btn-fullscreen" title="⛶">⛶</button>
|
||||
@@ -132,6 +134,12 @@ class BooksLM {
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Inject the provider/model picker asynchronously (depends on /api/ai/status)
|
||||
const pickerHost = panel.querySelector('.bookslm-picker-host');
|
||||
buildAIPickerUI().then((picker) => {
|
||||
if (picker && pickerHost) pickerHost.replaceWith(picker);
|
||||
}).catch(() => { /* ignore */ });
|
||||
|
||||
// Wire events
|
||||
panel.querySelector('.bookslm-btn-close').addEventListener('click', () => this.close());
|
||||
panel.querySelector('.bookslm-btn-new').addEventListener('click', () => this.newConversation());
|
||||
@@ -288,6 +296,15 @@ class BooksLM {
|
||||
this._abortCtrl = new AbortController();
|
||||
|
||||
try {
|
||||
// Read provider/model from the picker (localStorage-backed)
|
||||
let provider = null;
|
||||
let model = null;
|
||||
try {
|
||||
const picker = JSON.parse(localStorage.getItem('obsigate_ai_picker') || '{}');
|
||||
provider = picker.provider || null;
|
||||
model = picker.model || null;
|
||||
} catch { /* */ }
|
||||
|
||||
const resp = await fetch('/api/ai/bookslm/chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -295,7 +312,9 @@ class BooksLM {
|
||||
vault: this._vault,
|
||||
directory: this._directory,
|
||||
messages: this._messages.slice(0, -1), // exclude empty assistant
|
||||
context_files: this._contextFiles.map(f => f.path || f)
|
||||
context_files: this._contextFiles.map(f => f.path || f),
|
||||
provider,
|
||||
model,
|
||||
}),
|
||||
signal: this._abortCtrl.signal
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user