Le panneau de modèle par défaut et la bulle ⓘ n'affichaient aucun modèle Mistral « Vision capable » alors que GET api.mistral.ai/v1/models en déclare 28 : la table de capacités était entièrement statique et aucun de ses motifs ne correspondait aux familles Mistral actuelles (seul `pixtral`, retiré de l'API, les matchait). - backend/provider_capabilities.py (nouveau) : capacités déclarées par le fournisseur (Mistral `capabilities`, OpenRouter `architecture`), détectées par la forme du payload, snapshot en cache process-wide (TTL 30 min, surchargeable par AI_CAPABILITIES_TTL_SECONDS) rempli par GET /api/config/ai-models. - backend/model_capabilities.py : une déclaration prime sur la table statique pour chaque drapeau mentionné ; la table ne comble que le reste (Mistral ne déclare jamais `embedding`). Table corrigée pour le repli hors ligne : familles vision Mistral (ministral, magistral, mistral-small, mistral-medium, mistral-vibe-cli, labs-leanstral), mistral-ocr = vision sans chat, et défaut du fournisseur Mistral sans `embeddings` (mistral-large / codestral n'étaient plus des « embedders »). - backend/ai_routes.py : GET /api/ai/model-capabilities reste sans appel réseau (cache froid → table statique). - Tests : tests/test_provider_capabilities.py (nouveau), TestMistralFamilies et TestDeclaredCapabilities (bout en bout via l'API). - Docs : CHANGELOG [Unreleased], registre + journal ISSUES_TODOLIST, fiche docs/features/ai-provider-picker.md (§L). Vérifié : 28/28 modèles vision déclarés par Mistral détectés (0 avant), 0 écart dans les deux sens ; pytest 1007 passed / 6 skipped ; ruff 0 (backend) ; mypy 0 ; tests frontend unit 9/9 + IA 66/66 + validate-imports 37 modules ; instance de test reconstruite et vérifiée sur http://localhost:2020.
10 KiB
10 KiB
#82 — Assistant IA — Section « Fournisseur & modèle » compacte
Statut : ✅ Livré (en attente de validation utilisateur) Effort : 1 jour | Impact : 🟢 Références : Roadmap · Assistant IA #81 · Changelog
- Description : rendre la barre de sélection du modèle de l'assistant IA plus discrète tout
en conservant les informations utiles :
- Capacités via bulle — la liste complète des types d'endpoints (Chat, Embeddings, Rerank, Images, Video, Audio Speech, Audio Transcriptions, Vision) n'est plus affichée en permanence. Un bouton ⓘ révèle une bulle d'information au survol, au clic ou par appui long (mobile).
- Recherche de modèle — le
<select>natif est remplacé par une liste déroulante compacte dotée d'un champ de recherche qui filtre les modèles en tapant (insensible à la casse et aux accents). - Compatibilité — un
<select class="ai-picker-model">masqué reste présent (source de vérité) pour les commandes admin/modelet l'état persistéobsigate_ai_picker.
A. Picker partagé — ✅ livré
frontend/js/ai.js:buildAIPickerUI()réécrit (provider compact + combobox modèle + bulle capacités). Le picker reste partagé par l'assistant (bookslm.js) et la barre d'édition IA.renderCapabilityList()conservé et affiché dans la bulle (le panneau de configuration#cfg-ai-default-model-capscontinue de l'utiliser directement).- Fermeture des popovers au clic extérieur via un unique listener délégué (pas de fuite).
- i18n FR/EN (
ai.model_search,ai.model_info).
B. Correctifs associés — ✅ livré
- BUG-007 — détection
/et@Unicode + filtrage insensible aux accents + navigation clavier ↑/↓ fiabilisée (jeton de séquence,scrollIntoView). - BUG-008 — contexte ad-hoc
@pris en compte en mode Général côté backend (backend/bookslm_routes.py) et recherche de mention survault=allsans vault courant.
C. Tests — ✅ livré
tests/frontend/ai.test.mjs: filtrage accentué, mentions accentuées, navigation ↑/↓, recherche de modèle + bulle de capacités, plafonnement des longues listes (OpenRouter).tests/test_bookslm.py: contexte ad-hoc en mode Général (/chat).
D. Durcissement anti-cache (BUG-009) — ✅ livré
- Locales chargées avec
cache: 'no-store'(frontend/js/i18n.js) : plus de clés brutes affichées à cause d'unfr.jsonobsolète en cache HTTP. SW_VERSIONincrémenté (frontend/sw.js) pour purger les caches du service worker.- Styles critiques du picker appliqués en ligne (popover absolu, liste en colonne, options
display:block) pour rester corrects même sistyle.cssest servi depuis un cache. - Rendu plafonné à 200 modèles (
MODEL_RENDER_LIMIT) + indicateurai.model_more.
E. Lisibilité & contexte @ (BUG-010, BUG-011) — ✅ livré
- Popover modèle aligné à droite du déclencheur (
right: 0), largeur 340 px bornée à100vw - 24px: ne dépasse plus le bord de la sidebar ancrée à droite (BUG-011). - Noms de modèles sur plusieurs lignes (
overflow-wrap: anywhere, police 0,78 rem) avec attributtitlecomplet. - La sélection
@capture le vault renvoyé par/api/tree-search;_contextVault()propage ce vault aux requêtes/contextet/chatet les recherches suivantes restent dans ce vault (BUG-010, mode Général).
F. Barre latérale épurée & suivi visuel (BUG-012, #82) — ✅ livré
- Section « Fournisseur & modèle » sans intitulés (ni titre de section, ni « Fournisseur : »).
- Description du contexte Général retirée de l'affichage (bandeau de statut masqué) et
exposée via
titleau survol de l'en-tête. - Placeholder « Posez une question sur ces documents… » retiré ; bouton « Envoyer » remplacé par l'emoji compact ✈️.
- Indicateur d'activité (
.bookslm-activity) affichant le workflow : envoi, réception, outil en cours, attente de confirmation, terminé, échec (ai.activity_*). @: le menu est toujours rendu et_mentionVault()retombe sur le vault de contexte puis le premier vault disponible (BUG-012).
G. Robustesse menus & liens (BUG-013, BUG-014) — ✅ livré
- Navigation clavier ↑/↓ des menus
/et@gérée au niveau du panneau en phase de capture : fonctionne quel que soit l'élément focalisé (BUG-014). - Les liens fichiers/répertoires des réponses utilisent
_activeVault()(vault de contexte → vault sélectionné → premier vault), ce qui supprime l'erreur « Aucun vault actif » en mode Général (BUG-013). - Purge des caches élargie :
SW_VERSIONv6 + migration qui supprime tous les cachesobsigate-*(cléobsigate-sw-migration→v3). - Indice clavier sous la zone de saisie retiré pour un composeur plus épuré (#82).
- Sélection clavier des menus
/et@rendue visible : état actif en--bg-hover+ barre d'accent à gauche (--surface2étant identique à--bg-primaryen thème sombre, la sélection était invisible) — BUG-015.
H. Performance @, sélecteurs agrandis & BooksLM racine (#82) — ✅ livré
- Nouvel endpoint
GET /api/vault/{vault}/paths(backend/services/search.py: list_paths,backend/main.py) : index des chemins plat et plafonné (5000 par défaut). - Frontend :
_ensurePaths()précharge cette liste une fois par vault (auopenContext), puis_showMentionMenufiltre côté client → affichage instantané des fichiers/ répertoires, sans requête par frappe (repli serveur si le cache est vide). - Sélecteurs Fournisseur/Modèle agrandis pour s'aligner sur les autres contrôles (police 0,8 rem, hauteur 34 px, rayon 6 px).
- Menu contextuel de la racine d'une vault : ajout de « 🧠 BooksLM » (
frontend/js/ui.js).
J. Alignement à droite & ordre des sélecteurs (#82) — ✅ livré
- Le groupe de sélection Fournisseur/Modèle est aligné sur le bord droit de la barre
(
frontend/style.css:.bookslm-toolbar { justify-content: flex-end; }). - Ordre visuel : capacité du modèle (ⓘ) → menu fournisseurs → menu des modèles
(
frontend/js/ai.js, ordre d'ajout dans_buildPickerUI()). - La bulle de capacités s'ouvre vers la droite (
left: 0, inline + CSS) pour rester dans la barre latérale désormais que le bouton ⓘ est en tête de groupe. - Test JSDOM : l'ordre des trois contrôles visibles est vérifié (
tests/frontend/ai.test.mjs).
K. Synchronisation de la liste des fournisseurs (BUG-043) — ✅ livré
frontend/js/ai.js: nouveaurefreshAIPickers()— reconstruit chaque picker monté dans son emplacement.ai-picker-slot(constantePICKER_SLOT_CLASS). Le slot est conservé même quand aucun picker ne peut être construit (aucun fournisseur configuré), donc le premier fournisseur ajouté s'y monte aussi.frontend/js/bookslm.js: l'emplacement du picker (span.bookslm-picker-host) porte la classeai-picker-slotet reste dans la barre — le picker est monté à l'intérieur au lieu de remplacer le host (replaceChildren).frontend/js/config.js:refreshAIPickers()appelé après l'enregistrement (saveAIKeys()) et la suppression (deleteAIKey()) d'une clé API.- Une sélection persistée dont le fournisseur n'est plus configuré est purgée de
obsigate_ai_picker(fournisseur et modèle effacés → retour au défaut, plus de nom de modèle fantôme dans le déclencheur). - Tests JSDOM : ajout/retrait d'un fournisseur dans la barre de l'assistant,
montage du premier fournisseur dans un slot vide, purge de la sélection orpheline,
et garde-fou sur le câblage
saveAIKeys/deleteAIKey(tests/frontend/ai.test.mjs).
I. Points d'attention
- La table statique (
backend/model_capabilities.py) n'est plus la source principale : elle sert de repli (fournisseur muet, pas de clé API, cache froid) et comble les drapeaux non déclarés. Un modèle inconnu retombe toujours sur le défaut du fournisseur. - Le cache de déclarations est en mémoire, par process : un redémarrage ou un cache froid ne
casse rien (repli statique), et
GET /api/config/ai-modelsle repeuple au premier affichage de la liste des modèles. - Le select masqué est conservé uniquement pour la rétro-compatibilité ; ne pas le supprimer sans
migrer
_cmdSwitchModel/_applyPickerSelectiondansfrontend/js/bookslm.js.
L. Capacités fournies par le provider (BUG-044) — ✅ livré
backend/provider_capabilities.py(nouveau) : lecture des capacités déclarées par l'API du fournisseur (capabilitiesMistral,architectureOpenRouter), détectées par la forme du payload — un fournisseur qui se met à les publier est pris en charge sans modification de code.- Snapshot en cache process-wide (TTL 30 min par défaut,
AI_CAPABILITIES_TTL_SECONDS), rempli parGET /api/config/ai-models(appel déjà effectué pour lister les modèles : aucune requête supplémentaire) ;clear_declared_capabilities()+cache_info()pour les tests et le diagnostic. backend/model_capabilities.py: une déclaration prime sur la table statique pour chaque drapeau qu'elle mentionne ; la table ne comble que le reste (Mistral ne déclare jamaisembedding, seulement l'absence decompletion_chat).- Table statique corrigée pour le repli hors ligne : familles vision Mistral (
ministral,magistral,mistral-small,mistral-medium,mistral-vibe-cli,labs-leanstral),mistral-ocr= vision sans chat, et défaut du fournisseur Mistral sansembeddings(un modèle inconnu n'est plus présenté comme un modèle d'embeddings). GET /api/ai/model-capabilitiesreste sans appel réseau : il répond depuis le cache (froid → table statique), donc la bulle ⓘ ne bloque jamais.- Tests :
tests/test_provider_capabilities.py(parsing, cache, TTL, fusion),tests/test_model_capabilities.py::TestMistralFamilies(repli hors ligne) ettests/test_ai_models.py::TestDeclaredCapabilities(bout en bout : payload Mistral simulé →capabilitiesde/api/config/ai-modelspuis/api/ai/model-capabilities).