Mise à jour avec la possibilité de mettre des images
This commit is contained in:
176
docs/plan.md
176
docs/plan.md
@@ -332,9 +332,52 @@ Stack retenue : **FastAPI + Ollama local + Architecture Hexagonale** (Ports/Adap
|
||||
- [x] Section "Le savais-tu ?" sur la nature debug-friendly du format SSE (`curl -N` suffit).
|
||||
- [x] Quiz 5 QCM.
|
||||
|
||||
###### b5.7 — À faire plus tard (étendre au reste de l'app)
|
||||
- [ ] Intégration du drawer dans `arc-edit`, `chapter-edit`, `scene-edit` (Campagne). Nécessite un nouveau port `AiChatProvider.streamChatForCampaign(campaignId, messages)` qui charge Campagne courante + Lore associé (asymétrie demandée : une Campagne voit son Lore, un Lore ne voit PAS ses campagnes).
|
||||
- [ ] Persistance optionnelle de la conversation (entité `Conversation` côté Java, historique reprenable).
|
||||
###### b5.7 — Intégration dans la Campagne (arc / chapter / scene) ✅ (20 avril 2026, après-midi)
|
||||
|
||||
> ✅ **Drawer IA disponible sur les 3 écrans de Campagne.** Un MJ peut dialoguer avec l'IA depuis l'arc, le chapitre ou la scène en cours. Le prompt système reçoit automatiquement l'arbre narratif (noms seulement — pas de contenu), les champs de l'entité focus, et — si la campagne est liée à un Lore — les templates + l'arbre des pages de ce Lore. **Asymétrie respectée** : un Lore ne voit PAS ses campagnes (sens unique Campagne → Lore).
|
||||
|
||||
###### b5.7.1 — Value Objects narratifs (core/domain) ✅
|
||||
- [x] `CampaignStructuralContext` : arbre `campaignName + campaignDescription + List<ArcSummary>` avec `ArcSummary(name + chapters)` et `ChapterSummary(name + sceneNames)`. Lombok `@Value @Builder @Singular`.
|
||||
- [x] `NarrativeEntityContext` : VO "focus" avec `entityType ∈ {arc, chapter, scene}` + `title` + `Map<String,String> fields` (description, themes, stakes, playerObjectives, atmosphere…).
|
||||
- [x] `ChatRequest` étendu : `loreContext` nullable, `campaignContext` et `narrativeEntity` ajoutés. Un chat Lore continue à fonctionner inchangé.
|
||||
|
||||
###### b5.7.2 — Builders applicatifs (DRY + cross-context) ✅
|
||||
- [x] `LoreStructuralContextBuilder` extrait en `@Component` partagé : `build(loreId)` lance une exception si absent, `buildOptional(loreId)` retourne `Optional.empty()` pour dégradation gracieuse (Lore supprimé entre deux appels).
|
||||
- [x] `CampaignStructuralContextBuilder` : traverse Campagne → Arcs (triés par `order`) → Chapters (triés) → noms de Scenes (triés). Pas de contenu, juste la structure.
|
||||
- [x] `NarrativeEntityContextBuilder` : switch sur `entityType`, mappe les champs domaine vers la Map via `putIfNotBlank`. Ne fuit aucun secret MJ vers le prompt joueur (c'est un chat MJ, donc tout est exposé — mais le découpage par champ reste explicite).
|
||||
- [x] `StreamChatForCampaignUseCase` : orchestre Campaign → Lore optionnel (via `campaign.isLinkedToLore()`) → Narrative entity optionnelle → délégation au port `AiChatProvider`.
|
||||
- [x] `StreamChatForLoreUseCase` refactoré : 182 → 114 lignes, délègue à `LoreStructuralContextBuilder` (DRY).
|
||||
|
||||
###### b5.7.3 — Pont Java ↔ Python ✅
|
||||
- [x] `BrainAiChatClient.toPayload()` : 4 contextes optionnels (`lore_context`, `page_context`, `campaign_context`, `narrative_entity`) ajoutés au JSON snake_case seulement s'ils existent.
|
||||
- [x] `brain/app/domain/models.py` : dataclasses `ArcSummary`, `ChapterSummary`, `CampaignStructuralContext`, `NarrativeEntityContext`.
|
||||
- [x] `brain/app/application/chat.py` : `_BASE_SYSTEM` rendu générique ("contexte ci-dessous"), `stream(…)` en kw-only args, `_build_system_prompt` assemble les sections conditionnelles. Formatters dédiés `_format_campaign`, `_format_arcs`, `_format_chapter_block`, `_format_narrative_entity`.
|
||||
- [x] `brain/app/main.py` : DTOs `ArcSummaryDTO`, `ChapterSummaryDTO`, `CampaignContextDTO`, `NarrativeEntityDTO` (validation `entity_type` via pattern). `ChatStreamRequestDTO.has_scope()` → HTTP 422 si aucun scope.
|
||||
|
||||
###### b5.7.4 — Controller REST + service Angular ✅
|
||||
- [x] `AiChatController.POST /api/ai/chat/stream-campaign` : `ChatStreamCampaignRequestDTO(campaignId, entityType?, entityId?, messages)`. SSE helpers réutilisés.
|
||||
- [x] `AiChatService.streamChatForCampaign(...)` + type `NarrativeEntityType = 'arc' | 'chapter' | 'scene'`. Helper privé `streamSse(...)` partagé avec `streamChat(...)` (Lore).
|
||||
- [x] `AiChatDrawerComponent` : nouveaux `@Input()` `campaignId`, `entityType`, `entityId`. Dispatch : `campaignId` truthy → mode Campagne, sinon mode Lore (backward compatible).
|
||||
|
||||
###### b5.7.5 — Intégration UI dans les 3 écrans ✅
|
||||
- [x] `arc-edit`, `chapter-edit`, `scene-edit` : bouton `btn-ai` "Assistant IA" dans le header (Sparkles icon + état `active`), `<app-ai-chat-drawer>` injecté avec `entityType` + `entityId` appropriés, `quickSuggestions` adaptées au rôle narratif (thèmes/enjeux pour l'arc, objectifs/tensions pour le chapitre, ambiance/narration/choix pour la scène).
|
||||
- [x] Style global `.btn-ai` extrait en `_buttons.scss` (violet `#a5b4fc`, variante `.active` bordure `#6c63ff`) pour éviter la duplication.
|
||||
- [x] Validation finale : `mvn clean compile` BUILD SUCCESS + `npx tsc --noEmit` 0 erreur.
|
||||
|
||||
###### b5.7.6 — À faire plus tard
|
||||
- [ ] Persistance optionnelle de la conversation (entité `Conversation` côté Java, historique reprenable entre sessions).
|
||||
- [ ] Fiche academy dédiée à la composition de prompts multi-contextes (Lore + Campaign + Entity).
|
||||
|
||||
###### b5.8 — Enrichissement du Structural Context Campagne ✅ (20 avril 2026, après-midi)
|
||||
|
||||
> ✅ **Problème remonté par l'utilisateur** : en éditant une scène, impossible de demander à l'IA "c'est quoi la scène X (qui est ailleurs dans la campagne) ?" — elle ne connaissait QUE les noms. Résolu en ajoutant les descriptions courtes à chaque niveau de l'arbre narratif, sans basculer vers du RAG sémantique.
|
||||
|
||||
- [x] **Domain (core)** : `CampaignStructuralContext.ArcSummary` gagne un champ `description`. `ChapterSummary.sceneNames: List<String>` remplacé par `scenes: List<SceneSummary>` avec `name + description`. `ChapterSummary` gagne également `description`.
|
||||
- [x] **Builder (application)** : `CampaignStructuralContextBuilder` peuple maintenant `arc.description`, `chapter.description`, `scene.description` depuis les entités domaine (qui les exposent déjà — on consommait juste les noms).
|
||||
- [x] **Pont Java ↔ Python** : `BrainAiChatClient` sérialise les nouveaux champs. Côté Python : `models.py` gagne la dataclass `SceneSummary` et les champs description ; `main.py` ajoute `SceneSummaryDTO` + helper `_to_campaign_context` mis à jour.
|
||||
- [x] **System prompt (chat.py)** : `_format_arcs` et `_format_chapter_block` ajoutent une ligne `Synopsis : …` / `Description : …` sous chaque nœud quand renseigné. Format conditionnel (pas de ligne vide si description absente).
|
||||
- [x] **Budget tokens** : ~30 tokens par scène × 100 scènes ≈ 3k tokens. Confortable. Si un jour une campagne explose ce budget, on basculera en Option C (RAG sémantique).
|
||||
- [x] Validation finale : `mvn clean compile` BUILD SUCCESS + `python -m py_compile` sur les 3 fichiers Python + `npx tsc --noEmit` 0 erreur.
|
||||
|
||||
##### Étape b6 — IA dans la création de page (wizard) ✅ (20 avril 2026, nuit)
|
||||
|
||||
@@ -412,6 +455,64 @@ Stack retenue : **FastAPI + Ollama local + Architecture Hexagonale** (Ports/Adap
|
||||
- [ ] **Validation Pydantic** plus stricte : `max_length` sur `prompt`, `max_items` sur `template_fields` (ex: 20 max), longueur du `page_title`.
|
||||
- [ ] **Gestion `output_format` autres que `"json"`** : aujourd'hui on passe la valeur brute à Ollama. Si le Brain doit supporter un adapter qui ne comprend que certains formats, valider côté port.
|
||||
|
||||
## Feature "Illustrations & images" ✅ (20-21 avril 2026, sessions 5 & 6)
|
||||
|
||||
> ✅ **Feature complète livrée en 6 étapes.** Upload d'images via MinIO (S3-compatible), galeries éditables sur Arc/Chapter/Scene, et support d'un nouveau type `IMAGE` dans les champs de Template → les Pages peuvent porter des galeries par champ en plus des textes. Synchro Brain Python pour que l'IA "sache" combien d'illustrations porte chaque entité narrative (sans jamais recevoir les binaires).
|
||||
|
||||
### Étape 1 — Shared Kernel images + MinIO ✅ (2026-04-20 sess.5)
|
||||
Backend Java pur, testable via `curl`. Aucune intégration métier à ce stade.
|
||||
- **Infrastructure** : `docker-compose.yml` (service `minio` + `minio-init` auto-création du bucket `loremind-images`) ; `core/pom.xml` + `io.minio:minio:8.5.11` ; `application.properties` (config `minio.*` + multipart 10 Mo).
|
||||
- **Domaine** : `Image` (VO) + ports `ImageRepository` et `ImageStorage` **séparés** (SRP : la métadonnée DB et le binaire objet-storage sont deux responsabilités distinctes).
|
||||
- **Application** : `ImageService` (validation MIME `jpeg/png/webp/gif`, taille max 10 Mo).
|
||||
- **Adapters** : `MinioConfig` + `MinioImageStorageAdapter` (binaire) ; `ImageJpaEntity` + `PostgresImageRepository` (métadonnée).
|
||||
- **REST** : `ImageController` → `POST /api/images` (multipart), `GET /api/images/{id}`, `GET /api/images/{id}/content` (proxy binaire), `DELETE /api/images/{id}`.
|
||||
- **Academy** : `docs/academy/object-storage.md` + `docs/academy/shared-kernel.md`.
|
||||
- Validation : `mvn compile` OK.
|
||||
|
||||
### Étape 2 — Composants Angular partagés ✅ (2026-04-20 sess.5)
|
||||
Deux composants autonomes, réutilisables partout où une galerie d'images est nécessaire.
|
||||
- `web/src/app/services/image.service.ts` : upload, getById, delete, contentUrl.
|
||||
- `app-image-uploader` (`shared/image-uploader/`) : drop-zone standard OU mode compact (bouton `+ ajouter` pour galerie). Validation client alignée serveur. Gestion 413.
|
||||
- `app-image-gallery` (`shared/image-gallery/`) : grille 120×120 lazy-loading, mode `editable` avec uploader compact intégré + bouton X par vignette (supprime serveur + émet nouvelle liste), **lightbox plein écran** au clic.
|
||||
- Validation : `npx tsc --noEmit` OK.
|
||||
|
||||
### Étape 3 — Illustrations sur Scene / Chapter / Arc ✅ (2026-04-20 sess.5)
|
||||
Première intégration métier : les 3 entités narratives portent une liste d'images.
|
||||
- **Backend** : `List<String> illustrationImageIds` ajouté sur `Arc`/`Chapter`/`Scene` (domaine + JPA avec converter JSON + DTO + Mapper + Postgres repo + Service).
|
||||
- **Frontend** : champ dans `campaign.model.ts` ; section "Illustrations" en tête des `*-view` (lecture) et `*-edit` (galerie éditable) pour les 3 entités.
|
||||
- Validation : `mvn compile` + `npx tsc --noEmit` OK.
|
||||
|
||||
### Étape 4 — Refactor `Template.fields` ✅ (2026-04-21 sess.6)
|
||||
**Pivot structurel** : un champ de template n'est plus juste un nom — il a un **type**. Prépare l'étape 5 (Pages avec champs IMAGE).
|
||||
- **Backend** : nouveau enum `FieldType { TEXT, IMAGE }` + VO `TemplateField(name, type)`. `Template.fields` devient `List<TemplateField>` + helper `textFieldNames()` (utilisé par les use cases IA qui ne savent traiter que du texte).
|
||||
- **Migration BDD transparente** : `TemplateFieldListJsonConverter` lit l'ancien format `["name", ...]` ET le nouveau `[{name, type}]`, écrit toujours au nouveau format → auto-migration à la première sauvegarde (pas de script SQL).
|
||||
- **Tolérance** : `TemplateFieldMapper` traite un type inconnu → `TEXT` (robuste face à une régression DTO).
|
||||
- **Use cases IA mis à jour** : `GeneratePageValuesUseCase` et `StreamChatForLoreUseCase` ne passent à l'IA que les champs TEXT (erreur claire si aucun).
|
||||
- **Frontend** : sélecteur de type dans `template-create/edit`, chip verte (TEXT) vs indigo (IMAGE), bouton toggle inline. `page-view` rend un placeholder pour les champs IMAGE (la vraie UI vient en étape 5). `page-edit` hydrate les TEXT uniquement, `page-create` wizard ne liste que les TEXT.
|
||||
- Validation : `mvn compile` + `npx tsc --noEmit` OK.
|
||||
|
||||
### Étape 5 — Support champs IMAGE dans Pages ✅ (2026-04-21 sess.6)
|
||||
Les Pages gagnent une seconde zone de stockage, parallèle à `values` (TEXT).
|
||||
- **Backend** : nouveau converter `StringListMapJsonConverter` (`Map<String, List<String>>` ↔ JSON). `Page.imageValues` ajouté avec helpers `setImageFieldValue` / `getImageFieldValue`. Nouvelle colonne `image_values_json` sur `PageJpaEntity`. Propagation dans DTO + Mapper + Service.
|
||||
- **Frontend** : `Page.imageValues?: Record<string, string[]>` ; `page-view` affiche une galerie readonly par champ IMAGE via `ImageGalleryComponent` ; `page-edit` hydrate séparément TEXT et IMAGE et rend une galerie éditable par champ IMAGE.
|
||||
- Validation : `mvn compile` + `npx tsc --noEmit` OK.
|
||||
|
||||
### Étape 6 — Brain Python : synchro DTOs ✅ (2026-04-21 sess.6)
|
||||
L'IA ne reçoit **pas** les binaires — juste un signal de présence (`illustration_count`) pour qu'elle puisse en tenir compte dans le prompt.
|
||||
- **Backend Java** : `illustration_count` ajouté sur `ArcSummary` / `ChapterSummary` / `SceneSummary` du `CampaignStructuralContext`. Le builder peuple depuis `getIllustrationImageIds()` (null-safe). `BrainAiChatClient` sérialise **uniquement si > 0** (payload léger pour une campagne sans images).
|
||||
- **Brain Python** : `illustration_count: int = 0` sur les 3 summaries dans `domain/models.py` ; DTOs Pydantic + `_to_campaign_context` mis à jour dans `main.py`. Les champs inconnus (ex: `illustrationImageIds` des Pages) sont silencieusement ignorés par Pydantic v2 (pas d'erreur).
|
||||
- **Prompt** : helper `_illustration_hint()` dans `chat.py` ; les lignes arc/chapter/scene du prompt affichent ` [N illustrations]` si présentes (ex: `- A (arc) [2 illustrations]`).
|
||||
- Validation : `mvn compile` + `python -m py_compile` OK + démo runtime prompt validée.
|
||||
|
||||
### Résidu de scope (non bloquant)
|
||||
- [ ] `PageSummary` (côté LoreStructuralContext) ne porte pas encore de signal sur les `imageValues` des Pages. Symétrique à faire si l'IA doit raisonner sur "cette page PNJ a 3 portraits".
|
||||
|
||||
### Notes transverses
|
||||
- **Docker-compose** ne couvre aujourd'hui QUE MinIO. Postgres/Brain/Web restent lancés à la main.
|
||||
- **Séparation `ImageRepository` / `ImageStorage`** volontaire (SRP, pattern Shared Kernel).
|
||||
- URL publique d'une image : `/api/images/{id}/content` (proxy Java — évite d'exposer MinIO directement).
|
||||
- Validation MIME côté `ImageService` : `jpeg/png/webp/gif` uniquement, max 10 Mo.
|
||||
|
||||
## Structure des dossiers
|
||||
|
||||
```
|
||||
@@ -466,7 +567,7 @@ LoreMind/
|
||||
## Points à surveiller / Dette technique connue
|
||||
Ces points sont à garder en tête pour de futures refactorisations. Pas bloquant aujourd'hui — à traiter quand le besoin se manifestera.
|
||||
|
||||
- **Champ `illustration` manquant sur Arc/Chapter/Scene (front + back)** — les maquettes prévoient une URL d'illustration. Reporté jusqu'à l'implémentation des écrans de détail (affichage) correspondants. Impact : domaine Java + JPA + DTO + migration Postgres + champ Angular.
|
||||
- ~~**Champ `illustration` manquant sur Arc/Chapter/Scene**~~ ✅ Résolu le 20 avril 2026 (feature "Illustrations & images" étape 3 — champ devenu `illustrationImageIds: List<String>` avec galerie MinIO).
|
||||
- **Chargement de l'arbre campagne via N+1 requêtes HTTP** — `campaign-tree.helper.ts` fait `1 arcs + N chapters + M scenes` appels HTTP en forkJoin. Correct pour des campagnes de taille modeste. À remplacer par un endpoint agrégé `GET /api/campaigns/:id/tree` quand les volumes l'exigeront (>20 chapitres par ex).
|
||||
- **Calcul de `order` naïf** — actuellement `order = existingCount + 1`. Ne gère pas les réordonnancements ni les suppressions (risque de collisions). À reprendre avec un pattern de fractional indexing ou recalcul côté backend.
|
||||
- ~~**Duplications de pattern côté Lore**~~ ✅ Résolu le 18 avril 2026 via `lore-sidebar.helper.ts` — utilisé par `lore-detail`, `lore-node-create`, `template-create`, `template-edit`. L'écran de création de page à venir suivra le même pattern.
|
||||
@@ -478,6 +579,71 @@ Ces points sont à garder en tête pour de futures refactorisations. Pas bloquan
|
||||
- **Gestion des migrations DB** — actuellement `spring.jpa.hibernate.ddl-auto=update` (auto-alter). Acceptable en dev, **inutilisable en prod** (perte de données possible). À remplacer par Flyway ou Liquibase avant la mise en prod (chaque changement de schéma devra être versionné en fichier SQL).
|
||||
|
||||
## Dernière mise à jour
|
||||
21 avril 2026 (session 6) — **Feature "Illustrations & images" complète (6 étapes)** : MinIO + galeries Arc/Chapter/Scene + refactor `Template.fields` avec types TEXT/IMAGE + champs IMAGE sur Pages + synchro Brain Python (`illustration_count`). Voir section dédiée au-dessus de "Structure des dossiers".
|
||||
|
||||
20 avril 2026 (soir, session 4) — **Split View ↔ Edit : mode consultation livré sur Page / Arc / Chapter / Scene**.
|
||||
|
||||
> ✅ **Problème UX remonté par l'utilisateur** : consulter et modifier partageaient le même écran (formulaire avec textareas), ce qui est bruité visuellement pour la simple lecture et impose des scrollbars internes à chaque champ. Résolu par un pattern classique *read-first design* : une route de vue distincte par entité, où chaque champ est un bloc titré dont le corps s'étend verticalement selon son contenu (`white-space: pre-wrap`, pas de textarea).
|
||||
|
||||
**Choix produit retenus** :
|
||||
- **Routes séparées** : `/lore/:id/pages/:pid` = vue (défaut, bookmarkable), `/lore/:id/pages/:pid/edit` = édition. Idem pour Arc/Chapter/Scene côté Campagne.
|
||||
- **Style "fiche de jeu"** : chaque champ = bloc avec titre (petit, violet #a5b4fc, uppercase, tracking) et corps texte pleine largeur. Séparateurs fins `#1e1e3a` entre blocs. Variante `--private` rouge discret pour les notes MJ.
|
||||
- **Tout en une passe** : les 4 entités sont livrées ensemble pour garder un design system cohérent.
|
||||
|
||||
**Architecture mise en place** :
|
||||
- Nouveau partial SCSS global `web/src/styles/_view.scss` (+ `@use` dans `styles.scss`) — responsabilité unique : le style "fiche de jeu". Réutilisé par les 4 composants (DRY). Contient `.view-page`, `.view-header`, `.view-section`, `.view-section--private`, `.view-row` (grille 2 colonnes), `.view-chips` (+ variante `.view-chip--tag`).
|
||||
- 4 nouveaux composants standalone : `@app/lore/page-view/`, `@app/campaigns/arc-view/`, `@app/campaigns/chapter-view/`, `@app/campaigns/scene-view/`. Chacun charge les mêmes données que son pendant `-edit` (même sidebar, mêmes services) — le mode est juste cosmétique.
|
||||
- Les 4 SCSS des composants `-view` sont volontairement quasi-vides : tout le style vient du partial global. Laissés en place pour cohérence structurelle avec le reste du projet.
|
||||
|
||||
**Routes (`app.routes.ts`)** :
|
||||
- `/lore/:loreId/pages/:pageId` → `PageViewComponent` (ancien `PageEditComponent`).
|
||||
- `/lore/:loreId/pages/:pageId/edit` → `PageEditComponent` (nouvelle).
|
||||
- `/campaigns/:campaignId/arcs/:arcId` → `ArcViewComponent` (+ `/edit` → `ArcEditComponent`). Idem `chapters` et `scenes`.
|
||||
- Les routes des tree items de sidebar pointent déjà vers `/…/:id` (pas modifié) → par construction, cliquer sur un item de l'arbre ouvre la **vue** (défaut). Parfait.
|
||||
|
||||
**Flux navigationnels ajustés** :
|
||||
- `arc-edit` / `chapter-edit` / `scene-edit` : `cancel()` et redirection post-`Sauvegarder` pointent maintenant sur `/view` de l'entité courante (au lieu de la racine Campagne). UX : « je corrige, je valide, je vois le résultat ».
|
||||
- `page-edit` : bouton **Annuler** (btn-secondary) ajouté dans le header + `save()` navigue vers la vue.
|
||||
- `arc-create` / `chapter-create` / `scene-create` : captent désormais `(created)` et naviguent vers la vue de la nouvelle entité (au lieu de la racine Campagne).
|
||||
- `page-create` (mode classique) : navigue vers `/edit` de la page créée — la coquille est vide, filer directement en édition fait sens. Le mode wizard IA (b6) continue à naviguer vers la vue (les values sont déjà remplies par l'IA).
|
||||
|
||||
**Rendu adaptatif des champs (sans scrollbar)** :
|
||||
- Le texte est rendu dans un `<p class="view-section-body">` natif, jamais dans un textarea. CSS : `white-space: pre-wrap; word-wrap: break-word;` — conserve les sauts de ligne saisis en édition, la hauteur s'adapte automatiquement au contenu. Zéro JS, zéro `rows="…"` à maintenir.
|
||||
- Champs vides : `<p class="view-section-empty">Non renseigné</p>` en italique gris discret. Évite de masquer les champs manquants sans pour autant encombrer visuellement.
|
||||
- Sections entièrement optionnelles (tags, pages liées, notes privées, combat, choix…) affichées uniquement si non-vides (`*ngIf` sur la section entière).
|
||||
|
||||
**Validation** : `npx tsc --noEmit` — 0 erreur.
|
||||
|
||||
**À surveiller / ce qu'il reste à faire** :
|
||||
- La duplication entre `arc-view` / `chapter-view` / `scene-view` reste acceptable car chaque entité a des champs différents. Si le domaine narratif continue à grossir, on pourra extraire un composant générique `<app-entity-view [sections]="…">` piloté par un tableau de sections — **YAGNI** tant qu'on en reste à 3 entités.
|
||||
- `template-edit`, `lore-node-edit`, `campaign-detail`, `lore-detail` gardent leur format actuel (mix consultation/édition inline) — c'est volontaire car ces écrans sont très simples (nom + description). À reconsidérer si leur scope grossit.
|
||||
- Raccourci clavier `Ctrl+E` pour basculer vue ↔ édition = idée backlog.
|
||||
|
||||
---
|
||||
|
||||
20 avril 2026 (soir, session 3) — **Phase 3 étape b9 bouclée : enrichissement du Structural Context Lore (values + tags + liens)**.
|
||||
|
||||
> ✅ **Problème remonté par l'utilisateur** : depuis un arc/chapter/scene, le chat IA ne voyait que les noms des pages du Lore (ex: "Borin le forgeron"), jamais leur contenu. Il ne pouvait donc pas raisonner sur les fiches de PNJ (apparence, motivations, background) ni sur les interconnexions (tags, pages liées). Résolu par un enrichissement symétrique à celui fait en b5.8 pour les scènes de campagne.
|
||||
|
||||
- [x] **Domain (core)** : `LoreStructuralContext.FolderPage` renommé `PageSummary` (Ubiquitous Language : c'est un résumé projeté, pas un conteneur). Nouveaux champs `values: Map<String,String>`, `tags: List<String>`, `relatedPageTitles: List<String>`.
|
||||
- [x] **Builder (application)** : `LoreStructuralContextBuilder` peuple les nouveaux champs. Constante `MAX_VALUE_LENGTH = 500` + méthode privée `truncate()` pour éviter qu'un champ "Histoire" de 5000 caractères ne sature le prompt. Les `relatedPageIds` sont résolus en titres via une map `pageTitleById` construite une seule fois (pas de N²). Les IDs qui ne matchent rien (page supprimée) sont silencieusement ignorés.
|
||||
- [x] **Pont Java ↔ Python** : `BrainAiChatClient.pageSummaryToMap()` sérialise les nouveaux champs en snake_case, **seulement s'ils contiennent de l'info** (payload léger pour un Lore avec beaucoup de pages vierges).
|
||||
- [x] **Python (domain)** : nouveau dataclass `PageSummary` (title, template_name, values, tags, related_page_titles). `LoreStructuralContext.folders` passe de `dict[str, list[tuple[str, str]]]` à `dict[str, list[PageSummary]]`.
|
||||
- [x] **Python (DTOs + mapping)** : `FolderPageDTO` renommé `PageSummaryDTO` avec champs optionnels par défaut vide. Nouveau mapper `_to_page_summary()`. Mapping `_to_lore_context()` mis à jour.
|
||||
- [x] **System prompt (chat.py)** : `_format_folders` affiche pour chaque page une fiche indentée avec les valeurs des champs, les tags, et les pages liées (uniquement si non-vide — prompt compact pour les pages vierges). Format :
|
||||
```
|
||||
- PNJ (dossier)
|
||||
- Borin le forgeron [template: PNJ]
|
||||
· Apparence : Nain barbu au regard perçant…
|
||||
· Motivation : Venger son clan décimé…
|
||||
· tags : aventurier, forgeron
|
||||
· liée à : Le marteau de Durin, Clan Feuillefer
|
||||
```
|
||||
- [x] **Budget tokens** : ~150-200 tokens par page pleine. Tient jusqu'à ~50-100 pages dans un prompt typique. Au-delà, bascule vers RAG sémantique (Option D, backlog).
|
||||
- [x] Validation finale : `mvn -q compile` BUILD SUCCESS + `python -m py_compile` sur les 3 fichiers Python.
|
||||
- [x] **Effet collatéral bénéfique** : cet enrichissement profite AUSSI au chat depuis la Lore (pas uniquement depuis la Campagne) — l'IA voit désormais le contenu de toutes les autres pages du Lore, pas seulement leurs noms.
|
||||
- [x] **`num_ctx` porté à 16384** : sans ça, Ollama tronque silencieusement le prompt à 2048 tokens par défaut (~10 pages enrichies max). Nouveau setting `llm_num_ctx: int = 16384` dans `brain/app/core/config.py`, surchargeable via `LLM_NUM_CTX` dans `.env`. Méthode privée `_build_options()` factorisée dans `OllamaLLMProvider` — `num_ctx` est TOUJOURS injecté dans les deux payloads (`/api/generate` et `/api/chat`). Coût VRAM supplémentaire : ~600 MB de KV cache max vs défaut 2048.
|
||||
|
||||
20 avril 2026 (nuit, session 2) — **Phase 3 étape b8 bouclée : contextualisation page courante injectée côté serveur**.
|
||||
|
||||
**PageContext serveur (b8.1 → b8.3)** :
|
||||
@@ -509,7 +675,7 @@ Ces points sont à garder en tête pour de futures refactorisations. Pas bloquan
|
||||
- **Angular (b5.4)** : service `AiChatService` avec `fetch()` + `ReadableStream` (pas `EventSource` qui ne supporte que GET), composant standalone réutilisable `AiChatDrawerComponent` avec bulles user/assistant, typing indicator, caret clignotant, suggestions rapides, `primaryAction` optionnelle.
|
||||
- **Intégration page-edit (b5.5)** : bouton "Assistant IA" toggle le drawer, one-shot b4 relocalisé en `primaryAction` ("Remplir automatiquement"). Suggestions rapides hardcodées MVP.
|
||||
- **Academy (b5.6)** : fiche `docs/academy/streaming-sse-rag.md` avec analogie JDR (pigeon voyageur), comparaison Full-dump/Structural/RAG sémantique, code des 3 étages, quiz 5 QCM.
|
||||
- **Restera à étendre (b5.7)** : Campagne (asymétrique), page-create en mode wizard, éventuelle persistance de conversations.
|
||||
- **Extension Campagne (b5.7, 20 avril après-midi)** : drawer branché sur `arc-edit`, `chapter-edit`, `scene-edit`. Asymétrie respectée (Campagne voit son Lore, Lore ne voit PAS ses campagnes). 4 contextes optionnels côté prompt (`lore_context`, `page_context`, `campaign_context`, `narrative_entity`). Extraction du shared `LoreStructuralContextBuilder` (DRY). Persistance des conversations restera pour plus tard.
|
||||
|
||||
19 avril 2026 (soir, session 2) — **Phase 3 étape b4 bouclée : chaîne IA de bout en bout opérationnelle**.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user