fix(ai): BUG-044 capacités des modèles lues chez le fournisseur (Mistral vision)
CI / lint (push) Successful in 1m20s
CI / security (push) Successful in 53s
CI / test (push) Successful in 2m27s
CI / build (push) Successful in 50s
CI / e2e (push) Successful in 10m48s

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.
This commit is contained in:
2026-09-15 09:26:31 -04:00
parent 140a8f6efe
commit b69cb9b0f8
10 changed files with 723 additions and 23 deletions
+29 -2
View File
@@ -113,7 +113,34 @@
et garde-fou sur le câblage `saveAIKeys`/`deleteAIKey` (`tests/frontend/ai.test.mjs`).
## I. Points d'attention
- La table de capacités reste **statique** (`backend/model_capabilities.py`) : un modèle inconnu
retombe sur le défaut du fournisseur.
- 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-models` le 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` / `_applyPickerSelection` dans `frontend/js/bookslm.js`.
## L. Capacités fournies par le provider (BUG-044) — ✅ livré
- [x] `backend/provider_capabilities.py` (nouveau) : lecture des capacités **déclarées** par
l'API du fournisseur (`capabilities` Mistral, `architecture` OpenRouter), détectées par la
**forme** du payload — un fournisseur qui se met à les publier est pris en charge sans
modification de code.
- [x] Snapshot en cache process-wide (TTL 30 min par défaut, `AI_CAPABILITIES_TTL_SECONDS`),
rempli par `GET /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.
- [x] `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
jamais `embedding`, seulement l'absence de `completion_chat`).
- [x] 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 sans `embeddings`
(un modèle inconnu n'est plus présenté comme un modèle d'embeddings).
- [x] `GET /api/ai/model-capabilities` reste **sans appel réseau** : il répond depuis le cache
(froid → table statique), donc la bulle ⓘ ne bloque jamais.
- [x] Tests : `tests/test_provider_capabilities.py` (parsing, cache, TTL, fusion),
`tests/test_model_capabilities.py::TestMistralFamilies` (repli hors ligne) et
`tests/test_ai_models.py::TestDeclaredCapabilities` (bout en bout : payload Mistral simulé →
`capabilities` de `/api/config/ai-models` puis `/api/ai/model-capabilities`).