feat(assistant): #94-#97 historique permanent, sidebar IA, panneau + et bouton rond
CI / lint (push) Successful in 1m28s
CI / security (push) Successful in 58s
CI / test (push) Successful in 2m9s
CI / build (push) Successful in 1m3s
CI / e2e (push) Successful in 11m12s

This commit is contained in:
2026-09-16 14:56:45 -04:00
parent 0841778b99
commit c9e3240ae2
23 changed files with 1351 additions and 151 deletions
+100
View File
@@ -0,0 +1,100 @@
# #94–#97 — Assistant IA : historique permanent, accès sidebar, bouton rond et panneau « + »
> **Statut :** ✅ Livré (en attente de validation utilisateur)
> **Effort :** ~7 jours | **Impact :** 🟡
> **Références :** [Roadmap](../ROADMAP.md) · [Changelog](../../CHANGELOG.md)
- **Description :** quatre améliorations de l'Assistant IA livrées ensemble :
1. **#94** — bouton de soumission **circulaire** avec icône Lucide `arrow-up`
(remplace l'avion ✈️).
2. **#95** — **historique permanent** des conversations **persisté côté backend**
(les échanges survivent aux rechargements de page et aux navigateurs).
3. **#96** — accès rapide à l'**historique depuis la sidebar de navigation** (onglet dédié).
4. **#97** — panneau **« + » extensible** remplaçant « Attach an image » (modules :
fichiers, image, contextes, skills, Deep Research, web, Canva).
## A. Backend — persistance des conversations (#95) — ✅ livré
- [x] **`backend/ai_history.py`** (nouveau) : stockage JSON par utilisateur
(`data/ai_history/{username}.json`), écriture atomique (tmp + `shutil.move`),
plafond `MAX_SESSIONS = 200` (purge des plus anciennes), tri par `updatedAt` desc.
- [x] `list_sessions`, `get_session`, `upsert_session`, `delete_session` ;
la liste renvoie des **résumés sans messages** (`_summary` : titre, mode, vault,
contexte, `message_count`, `preview`) pour rester légère.
- [x] **`backend/bookslm_routes.py`** : modèle Pydantic `BookslmSession` et 4 endpoints :
- `GET /api/ai/bookslm/history` — liste des conversations (résumés).
- `GET /api/ai/bookslm/history/{session_id}` — conversation complète (404 si absente).
- `PUT /api/ai/bookslm/history/{session_id}` — création/mise à jour (l'`id` du corps
est forcé à l'`id` du path → jamais d'écriture sous une autre clé).
- `DELETE /api/ai/bookslm/history/{session_id}` — suppression (booléen `ok`).
- [x] Isolation par utilisateur (`require_auth`) ; saisies défensives (username/id vides) →
`[]` ou `None`.
## B. Frontend — sync serveur + cache local (#95) — ✅ livré
- [x] `frontend/js/bookslm.js` : clé local globale `bookslm-sessions-all-{username}` ;
**migration** des anciennes clés périmées `bookslm-sessions-<ctx>` et
`bookslm-history-<ctx>` à la première ouverture.
- [x] `_loadHistory(preferredSessionId)` **async** au chargement du panneau :
lecture serveur (`GET /history`), hydratation du localStorage, repli hors-ligne
silencieux si le serveur ne répond pas.
- [x] Synchronisation **debounced 600 ms** (`_syncHistoryToServer` → `_flushHistoryToServer`)
via `_dirtySessionIds` / `_deletedIds` : `PUT`/`DELETE` seulement pour les sessions
modifiées ; pas d'appel réseau à la simple ouverture.
- [x] `_positionSession` : nouvelle session insérée en tête, session rechargée remise à jour.
- [x] `openContext(opts, preferredSessionId)` **async** ; `openWithSession(sessionOrId)`
public pour ouvrir une conversation connue (utilisé par la sidebar #96).
- [x] `_notifyHistoryChanged()` : événement `bookslm:history-updated` pour rafraîchir la
sidebar #96 sans rechargement.
## C. Frontend — sidebar & historique (#96) — ✅ livré
- [x] `frontend/index.html` : cinquième onglet `#sidebar-tab-ai` (icône Lucide
`messages-square`) et panneau `#sidebar-panel-ai` (`#ai-history-list`,
`#ai-history-empty`).
- [x] `frontend/js/config.js` : `loadAISessionList()` / `renderAIHistoryList()` (réutilise
les classes `.recent-*` de l'UI), placeholder « Aucune conversation », listener
`bookslm:history-updated` monté par `initSidebarTabs()` et actif quand l'onglet `ai`
est affiché.
- [x] Le clic sur une conversation l'ouvre dans le panneau Assistant IA
(via `openWithSession`) et bascule la sidebar.
## D. Frontend — panneau « + » extensible (#97) — ✅ livré
- [x] Bouton « Attach an image » remplacé par un bouton **« + »** circulaire
(`frontend/js/bookslm.js`, `.bookslm-btn-plus`).
- [x] Panneau overlay `.bookslm-ext-menu` (dropdown au-dessus de la zone de saisie,
fermeture au clic extérieur / Échap) listant les modules.
- [x] Architecture **modulaire** : registre `_extensions` (objet d'enregistrement
simple) — chaque entrée : `id`, icône, libellé i18n, action ;
`renderExtMenu()` construit la liste ; un nouveau module s'ajoute en une entrée.
- [x] Modules livrés : **Fichiers** (sélecteur général `.bookslm-files-input`),
**Image** (joindre une image), **Contextes/fichiers**, **Skills**,
**Deep Research** (mode agent + prompt pré-rempli), **Recherche sur Internet**,
**Canva**.
- [x] `web`/`canva` affichés **désactivés** avec badge « Bientôt » (`.bookslm-ext-soon`)
— le catalogue d'outils #92 les alimentera.
- [x] `_startDeepResearch()` : active le mode agent, injecte le prompt Deep Research et
déclenche l'envoi.
## E. UI — bouton rond & icône (#94) — ✅ livré
- [x] `.bookslm-btn-send` circulaire (40 px, `border-radius: 50%`), icône Lucide
`arrow-up` à la place de l'emoji ✈️, aligné en bas à droite de la zone de saisie.
- [x] i18n FR/EN : `bookslm.add_options`, `bookslm.ext_files`, `bookslm.ext_image_hint`,
`bookslm.ext_contexts`, `bookslm.ext_skills`, `bookslm.ext_deep_research`,
`bookslm.ext_web`, `bookslm.ext_canva`, `bookslm.coming_soon`, `bookslm.soon`,
`bookslm.ext_more_coming`, `bookslm.deep_research_prompt`,
`bookslm.deep_research_started`, `bookslm.mode_*`.
## F. Tests — ✅ livré
- [x] `tests/test_bookslm.py` : `TestBooksLMSessionHistoryEndpoints` (7) + `TestAIGatewayHistoryStore` (4) —
liste résumée sans messages, full 404, upsert qui force l'id et isole par utilisateur,
delete, cap à 200, purge.
- [x] `tests/frontend/ai.test.mjs` : persistance/reouverture/suppression de sessions,
migration de l'historique legacy, menu d'historique.
- [x] Vérifs : pytest **1088 passed / 6 skipped**, ruff 0, mypy 0 (71 fichiers),
frontend `ai.test.mjs` **84/84**, `unit.test.mjs` 9/9, `validate-imports` 38 modules.
## G. Points d'attention
- Le localStorage reste un **cache** : le serveur est la source de vérité (`data/ai_history/`).
En cas de données locales corrompues, un `localStorage.clear()` réinitialise proprement.
- `MAX_SESSIONS = 200` : le comportement de purge est testé (`TestAIGatewayHistoryStore`)
; la rétention configurable (item #95 du backlog) pourra s'appuyer sur ce plafond.
- Les modules web/Canva du panneau « + » sont volontairement désactivés tant que
l'écosystème d'outils phase 2 (#92) n'est pas livré.
+4
View File
@@ -57,6 +57,10 @@
- [x] **D2. Régénérer** : Bouton pour régénérer la dernière réponse (utile si la réponse est hors-sujet).
- [x] **D3. Exporter la conversation** : Bouton pour exporter l'historique en Markdown → sauvegarder comme note dans le répertoire courant.
- [x] **D4. Historique des conversations** : Stockage dans `localStorage` par clé `bookslm-history-{vault}-{directory}`. Liste déroulante dans le header pour charger une conversation précédente.
> **Évolué (#95, 2026-09-16) :** l'historique est désormais **persisté côté backend**
> (`data/ai_history/{user}.json`), le localStorage ne sert plus que de cache,
> et les anciennes clés `bookslm-sessions-*` / `bookslm-history-*` sont migrées.
> Voir [ai-assistant-history.md](./ai-assistant-history.md).
- [x] **D5. Indicateur de contexte (barre de progression %) — non retenu, compteur fichiers/caractères affiché)** : Barre de progression montrant l'utilisation du contexte (% de la limite `BOOKSLM_MAX_TOTAL_CHARS`). Si le répertoire est trop gros, suggérer de réduire le scope.
- [x] **D6. Suggestions de questions** : Après l'indexation, afficher 3 questions suggérées basées sur les titres et métadonnées des fichiers (« Résume ce répertoire », « Quels sont les thèmes principaux ? », « Y a-t-il des contradictions entre ces documents ? »).