feat(chat): conversations persistantes + mémoire multi-tour (v0.36.0)
Store SQLite conversations/messages (propriété par actor, fenêtre 20), historique rejoué au LLM (runAgentTurn history), ChatService persiste et renvoie conversationId. API GET/DELETE /conversations + chat avec conversationId. UI Chat: sidebar conversations (drawer mobile), nouvelle, reprise, suppression. docs/conversations.md. 83 tests verts, build web vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016w5jRe87MGdd6AMvXQcHNi
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# Conversations persistantes (mémoire multi-tour)
|
||||
|
||||
CHLOVA stocke chaque conversation et **rejoue l'historique récent** au LLM, pour
|
||||
qu'il garde le contexte d'un message à l'autre et que l'utilisateur puisse
|
||||
**reprendre** une conversation plus tard.
|
||||
|
||||
## Modèle
|
||||
|
||||
SQLite (`node:sqlite`, même fichier que la table assets — `CHLOVA_DB_PATH`).
|
||||
|
||||
| Table | Colonnes |
|
||||
|---|---|
|
||||
| `conversations` | `id` (uuid), `actor`, `title`, `created_at`, `updated_at` |
|
||||
| `messages` | `id`, `conversation_id`, `role` (`user`/`assistant`), `content`, `ts` |
|
||||
|
||||
- **Propriété** : une conversation appartient à un `actor` (API = `api:owner`,
|
||||
Telegram = `telegram:<userId>`). Accès à une conversation d'un autre acteur
|
||||
refusé (`ForbiddenConversationError` → HTTP 403 / 404 selon l'endpoint).
|
||||
- **Titre** : dérivé du 1er message (tronqué à 60 car.).
|
||||
- **Fenêtre de contexte** : les `HISTORY_WINDOW` (= 20) derniers messages sont
|
||||
rejoués au LLM (`ChatService` → `runAgentTurn({ history })`). Au-delà, l'ancien
|
||||
n'est pas envoyé (pas de résumé/troncature par tokens en v1 — amélioration
|
||||
possible si les conversations deviennent longues).
|
||||
|
||||
## Flux
|
||||
|
||||
1. `POST /api/chat { message, conversationId? }` :
|
||||
- sans `conversationId` → nouvelle conversation créée, son id renvoyé ;
|
||||
- avec `conversationId` → historique chargé + rejoué, message ajouté.
|
||||
- réponse : `{ reply, conversationId }`.
|
||||
2. `GET /api/conversations` → liste (id, titre, dates) de l'acteur, par récence.
|
||||
3. `GET /api/conversations/:id` → messages complets (reprise).
|
||||
4. `DELETE /api/conversations/:id` → suppression (messages + conversation).
|
||||
|
||||
## UI
|
||||
|
||||
Onglet **Chat** : barre latérale des conversations (drawer sur mobile, fixe en
|
||||
`md+`), bouton **Nouvelle**, sélection pour reprendre, suppression. L'envoi
|
||||
transmet le `conversationId` courant ; une nouvelle conversation rafraîchit la
|
||||
liste.
|
||||
|
||||
## Surfaces
|
||||
|
||||
- **API/UI** : pleinement câblé (création, reprise, suppression).
|
||||
- **Telegram** (si activé) : `conversationId` stable = `telegram:<userId>` →
|
||||
mémoire continue par utilisateur.
|
||||
- **Mobile (Expo)** : chat encore sans état — branchement des conversations à
|
||||
faire (réutilise les mêmes endpoints).
|
||||
|
||||
## Sécurité / vie privée
|
||||
|
||||
Les messages sont stockés en clair dans la base (volume `chlova-data`). Pas de
|
||||
secret n'y transite normalement (l'agent manipule des références). La base n'est
|
||||
jamais exposée ; seul le backend y accède.
|
||||
Reference in New Issue
Block a user