Plateforme modulaire pour « discuter avec les données » (chatbot, dashboard, actions).
- Frontend: React (Vite). Tout ce qui est visuel vit ici.
- Backend: Python (FastAPI), packagé avec
uv. Toute la logique, l’accès aux données et les services. - Data: stockage des données sources et dérivées (pas de code ici).
frontend/– UI React, pages, composants, services d’appel API.backend/– API FastAPI, routes -> services -> dépôts, schémas.vis-ssr/– serveur Express pour GPT-Vis (SSR) qui génère les visuels côté serveur.data/–processed/,interim/,vector_store/,models/.
Script combiné (depuis la racine):
./start.sh– vérifie que les ports backend/frontend sont libres et échoue explicitement s’ils sont occupés (aucun kill automatique). Le script synchronise ensuite les dépendances (uv sync,npm installsi besoin), puis lance backend et frontend (build +vite preview). Les non-secrets sont lus depuisbackend/config.yamletfrontend/config.yaml; les secrets restent dansbackend/.envetfrontend/.env.development. Le script valide en fail-fast quebackend/config.yaml(ALLOWED_ORIGINS) etfrontend/config.yaml(FRONTEND_URLS/FRONTEND_DEV_URL) sont cohérents../start_full.sh– mêmes étapes questart.sh, mais diffuse dans ce terminal les logs temps réel (si ce script est présent dans votre branche locale).- Exemple: copier
backend/config.example.yaml->backend/config.yamletfrontend/config.example.yaml->frontend/config.yaml, puis lancer./start.sh. - Dépannage: si
./start.shéchoue lors du build frontend (pas de sortie, car redirection), lancernpm run build -- --mode developmentdansfrontend/pour afficher l'erreur TypeScript/Vite.
/api/v1/mindsdb/*supprimé/api/v1/mcp/*supprimé/api/v1/conversations/{id}/datasetsupprimé- Le backend opère désormais en mode AgentInsight sans pipeline NL2SQL/retrieval/MindsDB
Compatibilité shell:
- Les scripts
start.shetstart_full.shsont compatibles avec le Bash macOS 3.2 et/bin/sh.
Avant le premier lancement, copier les exemples:
backend/config.example.yaml->backend/config.yamlbackend/.env.example->backend/.envfrontend/config.example.yaml->frontend/config.yamlfrontend/.env.development.example->frontend/.env.development
Contrat strict:
backend/.envne doit contenir queDATABASE_URL,JWT_SECRET_KEY,ADMIN_PASSWORD.- Toute autre clé backend doit être déplacée dans
backend/config.yaml(fail-fast sinon).
Lancer manuellement si besoin:
Backend (depuis backend/):
- Installer
uvsi nécessaire: voir https://docs.astral.sh/uv - Installer les deps:
uv sync - Lancer:
uv run uvicorn insight_backend.main:app --reload - Copier
backend/config.example.yamlenbackend/config.yaml(non-secrets) puisbackend/.env.exampleenbackend/.env(secrets).config.yamlcontient les paramètres d’exécution (BACKEND_DEV_URL,LLM_MODE,CONTAINER_RUNTIME, etc.) et.envcontient uniquement les secrets (DATABASE_URL,JWT_SECRET_KEY,ADMIN_PASSWORD, ...).
Le backend démarre en fail-fast si backend/config.yaml ou backend/.env manque, si un secret est placé dans le YAML, ou si une clé non-secrète est trouvée dans .env.
Frontend (depuis frontend/):
- Installer deps:
npm ioupnpm iouyarn - Copier
frontend/config.example.yamlenfrontend/config.yaml(non-secrets), puisfrontend/.env.development.exampleenfrontend/.env.development(secrets éventuels). - Lancer:
npm run build && npm run preview(recommandé pour éviter les watchers) ounpm run devsi vous avez besoin du HMR.
SSR GPT-Vis (depuis vis-ssr/):
- Installer deps:
npm install - Copier
config.example.yamlenconfig.yamlet ajusterGPT_VIS_SSR_PORT/VIS_IMAGE_DIR/GPT_VIS_SSR_PUBLIC_URL - Lancer:
npm run start(endpointPOST /generate+ statiques/charts/*, PNG rendu via@antv/gpt-vis-ssr) - Ajuster l'URL du plan/Z/mcp.config.json (variable
VIS_REQUEST_SERVER) en fonction du portGPT_VIS_SSR_PORTchoisi. Par défaut, le SSR écoute sur6363(voirvis-ssr/config.example.yaml) et le fichierplan/Z/mcp.config.jsonréférencehttp://localhost:6363/. Si le domaine public diffère delocalhost, renseignerGPT_VIS_SSR_PUBLIC_URL(URL absolue, http(s)) pour que les liens de rendu retournés par l'API SSR soient corrects.
Configurer le frontend via frontend/config.yaml (FRONTEND_DEV_URL, VITE_API_URL, FRONTEND_URLS). Garder frontend/.env.development pour les secrets éventuels.
Lors du premier lancement, connectez-vous avec admin / admin (ou ADMIN_USERNAME dans backend/config.yaml et ADMIN_PASSWORD dans backend/.env).
- Endpoint:
POST /api/v1/chat/stream(SSEtext/event-stream). - Front: affichage en direct des tokens AgentInsight (
delta) et des appels outils (anim), avec boutonDétailspour l’inspection technique d’un message. - Le chat est désormais en mode unique AgentInsight (plus de boutons de bascule agent/chaîne/graph dans la barre de saisie).
- À la première question d’une nouvelle conversation, le backend crée un workspace isolé (
AGENT_INSIGHT_WORKSPACE_ROOT/conversation_<id>), y construit un snapshotdata/contextuel (tables + périodes sélectionnées quand disponibles, sinon copie complète), puis exécuteopencodedans ce dossier. - Au démarrage d’un workspace conversationnel, le backend crée aussi
knowledge.mdà la racine du dossierconversation_<id>(même vide), à partir du knowledge Markdown du profil utilisateur. Ce fichier n’est pas écrasé ensuite: seules les nouvelles conversations prennent les modifications ultérieures. - Au démarrage d’un workspace conversationnel, le backend initialise aussi
conversation_<id>/documentsà partir dedata/documents(copie des fichiers et sous-dossiers). Sidata/documentsest absent, un dossierdocumentsvide est créé dans le workspace. - Pour la résolution des tables contextuelles (
ticket_table,ticket_sources), AgentInsight cherche en priorité dansDATA_TABLES_DIR, puis dansDATA_ROOT/raw, puis dansDATA_ROOT. - Le run AgentInsight utilise l’agent opencode configuré (
AGENT_INSIGHT_AGENT, défautDataAnalyste) via--agent. - AgentInsight conserve les répertoires runtime (
XDG_DATA_HOME,XDG_STATE_HOME,XDG_CACHE_HOME) dans le workspace de conversation, mais n’écrase plusXDG_CONFIG_HOMEafin d’utiliser les agents/skills OpenCode globaux (ex:~/.config/opencode/skills). - Si un
auth.jsonOpenCode global existe ($XDG_DATA_HOME/opencode/auth.json, sinon~/.local/share/opencode/auth.json), il est synchronisé vers le runtime AgentInsight pour permettre l’usage des providers authentifiés dans le workspace isolé. - Si l’agent configuré n’existe pas et qu’OpenCode tente un fallback implicite vers l’agent par défaut, le backend renvoie maintenant une erreur explicite au lieu de poursuivre silencieusement.
- Les événements JSON OpenCode
type=errorprovoquent désormais un échec explicite même si le process retourne0. - Le run AgentInsight force
NODE_TLS_REJECT_UNAUTHORIZED=0pour le processopencode(équivalent à lancerNODE_TLS_REJECT_UNAUTHORIZED=0 opencode ...). - La réponse AgentInsight est extraite en priorité depuis les événements JSON (
--format json), avec fallback sur le storage local opencode du workspace. - En mode AgentInsight, les sorties
textd’opencode sont relayées endeltaSSE en temps réel; les appels d’outils sont relayés en statutanim. - Si AgentInsight génère des fichiers de graphique (PNG/JPG/WebP/GIF/SVG) dans le workspace de conversation, ils sont détectés automatiquement et affichés dans le chat.
- Les fichiers images présents dans des dossiers techniques (
.venv,venv,node_modules,.git, caches) sont ignorés pour éviter les faux positifs (ex: assets de dépendances Python). - Les
data:image/...;base64,...éventuellement présents dans le texte brut d’AgentInsight sont masqués dans la réponse affichée pour éviter les blocs illisibles. - En mode tickets, le contexte brut filtré est injecté directement dans le chat (sans seuil de taille côté backend).
- En mode tickets par défaut, l’UI pré-charge automatiquement la config (table/colonnes/date min-max) dès l’ouverture du chat pour que la liste des tables soit disponible sans action supplémentaire.
- Plusieurs périodes peuvent être sélectionnées via un double curseur (ex.: septembre 2025 et octobre 2024) et le bouton « + Ajouter une période »; les périodes sont transmises en métadonnées
ticket_periodset filtrent le contexte injecté. - Plusieurs tables peuvent être ajoutées (« + Ajouter une table ») avec leurs propres périodes; le frontend envoie
ticket_sources(table + périodes) en plus du couple principalticket_table/ticket_periodspour compatibilité. - Les actions sont regroupées sous la sélection de dates en boutons courts avec icônes explicites:
Période,Table,Réinit.(flèche loop). - Le panneau Contexte tickets peut être masqué/affiché (bouton « Masquer »/« Afficher ») pour libérer l'espace du chat sans perdre la configuration active.
- Le bandeau du panneau n’affiche plus le nom de table (déjà visible dans le champ Table) et utilise une icône flèche minimaliste pour le masquage.
- Le sélecteur de source de données est positionné en haut du panel de dates pour un accès direct.
- Le header du panel de dates est compacté (moins de vide en tête) pour remonter visuellement la sélection des périodes.
- Le bloc
Discussion #...desktop n’est affiché que lorsqu’une conversation est active, pour éviter un espace vide en haut. - Le panneau de visualisation tickets (colonne gauche desktop) n’affiche plus le titre “Tickets” et démarre plus haut pour supprimer le blanc.
- Le bandeau du panneau affiche toujours le nombre de tickets sélectionnés en haut à droite (sélection manuelle ou auto).
- Sans sélection manuelle, le libellé auto n’affiche plus la période (ex:
552 tickets sélectionnés). - Le compteur de sélection n’est plus dupliqué dans le panel de tickets: le nombre reste affiché uniquement dans le bandeau supérieur.
- La période sélectionnée n’est plus répétée dans la liste de tickets à gauche (elle reste visible dans le sélecteur de dates et le bandeau supérieur).
- La zone d’action « Tout effacer » conserve une hauteur stable pour éviter les sauts visuels du panel lors des sélections.
Du/auapparaît en premier sur la ligne de période, puisou, puis les presets rapides (7j,30j,1 an,Tout) en segmented control.- En mode tickets, le panneau « Ticket exploration » affiche immédiatement l'aperçu des tickets filtrés par les périodes sélectionnées ou par une sélection Explorer active (limite pilotée par
EVIDENCE_LIMIT_DEFAULTcôté backend). En cas de plusieurs tables, un onglet par table est affiché, y compris pour la table issue de l'Explorer. - Le panel « Ticket exploration » gère désormais les noms de colonnes très longs (retour à la ligne automatique + largeur bornée) pour éviter les débordements visuels.
- La sélection de tickets dans le panel utilise un bouton compact, minimaliste (icône seule), avec états visuels contrastés et focus clavier renforcé pour l’accessibilité.
- Lors d'un changement d'onglet (multi‑tables), la vue détail revient à la liste pour éviter un écran vide.
- Les panels multi‑tables utilisent des clés stables pour éviter l'inversion des données lors de la suppression d'une table.
- Lorsqu'une table est supprimée, son aperçu est retiré immédiatement du panel pour éviter les restes visuels.
- Par défaut, la sélection de dates utilise la plage recommandée fournie par le backend (et couvre toute la période disponible en l’absence de recommandation spécifique). Des boutons rapides (7j, 30j, 1 an, Tout) et des champs de saisie permettent une sélection manuelle.
- L’exploration permet de sélectionner des tickets (checkboxes) pour limiter le contexte du chat aux éléments choisis, jusqu’à effacement manuel de la sélection.
- Backend: deux modes LLM (
LLM_MODE=local|api) — vLLM local viaVLLM_BASE_URL, provider externe viaOPENAI_BASE_URL+OPENAI_API_KEY+LLM_MODEL. - Les échanges LLM (question, contexte, réponse) sont tracés dans
logs/llm.log(JSON multi-lignes) configuré parLLM_TRACE_LOG_PATH— fichier local ignoré par Git. - Les réponses du chat sont formatées et rendues en Markdown (titres courts, listes, tableaux, blocs de code) pour une meilleure lisibilité.
LLM_MAX_TOKENS(défaut 1024) impose le plafondmax_tokenssur tous les appels OpenAI-compatibles (explorateur, analyste, rédaction, router, chat) pour éviter les erreurs lorsquemodel_max_tokens - context_tokensdevient négatif.- Les anciens modes
nl2sql,multiagent,sql_mode,chart_modeet la commande/sqlsont rejetés explicitement par l’API chat (400). - En mode tickets, le contexte LLM n’injecte que les colonnes cochées dans l’admin (pas d’ajout automatique de texte/date/ID). Les colonnes texte/date restent nécessaires pour configurer et filtrer le mode tickets.
- En mode tickets, le message système injecté inclut aussi le dictionnaire de données des colonnes activées pour le chat afin d’expliciter leur signification au LLM.
- Le repo versionne le skill
.opencode/skills/data-analyst-graphspour générer des graphiques PNG dans le workspace conversationnel (outputs/graphs/) avecpandas + matplotlibà partir de fichiers CSV (uniquement). - AgentInsight charge les skills OpenCode depuis la configuration globale (
~/.config/opencode/skills): installer ou mettre à jour ce skill avecmkdir -p ~/.config/opencode/skills && rsync -a .opencode/skills/data-analyst-graphs ~/.config/opencode/skills/. - Commande type:
uv run --with pandas --with matplotlib .opencode/skills/data-analyst-graphs/scripts/render_chart.py --input data/raw/tickets.csv --chart line --x date --y volume --output outputs/graphs/tickets_by_day.png --overwrite. - L’agent
.opencode/agents/DataAnalyste.mdest désormais un data analyste généraliste (feedbacks clients/internes, MyFeelback, datasets métier, AQA/Foyer Assurances comme cas d’usage) centré sur l’analyse. - Le repo inclut aussi
.opencode/agents/DataAnalyste2.md, variante orientée exploration robuste des gros datasets sans saturation de contexte. DataAnalyste2applique un protocole strict (discovery -> profiling -> deep dive -> restitution), limite les aperçus (20lignes max,10colonnes max) et privilégie les agrégations/chunks aux dumps bruts.- En cas de besoin LLM avancé,
DataAnalyste2impose un mode explicitelocal(vLLM) ouapi(provider externe) sans fallback implicite. - Conformément à la documentation OpenCode, les règles détaillées de génération de graphes ne sont plus dans
DataAnalyste.mdet sont centralisées dans.opencode/skills/data-analyst-graphs/SKILL.md. - Pour des demandes de graphes custom non couvertes par
render_chart.py, ce skill autorise la création d’un script ad hoc dansscripts/du workspace conversationnel, avec export final en PNG dansoutputs/graphs/. - Le contrat de sortie (PNG, résolution, dossier) et la stratégie
Exception contrôlée (génération de graphes)sont définis dansSKILL.md, avec exécution viauv runet échec explicite sans fallback caché. - Le script
render_chart.pyapplique un contrôle strict de frontière workspace:--inputet--outputsont refusés s’ils résolvent hors du workspace courant.
- Le repo versionne le skill
.opencode/skills/knowledgepour centraliser l’usage du contexte métier viaknowledge.md. - Ce skill s’active uniquement si la question nécessite de la connaissance métier (assurance, Foyer, règles internes, vocabulaire organisationnel).
- Le fichier ciblé est
knowledge.mdà la racine du workspace conversationnel (conversation_<id>/knowledge.md), avec échec explicite si ce contexte est requis mais absent/vide (pas de fallback caché).
- Le repo versionne le skill
.opencode/skills/cg-knowledgepour l’analyse basée sur les Conditions Générales Foyer. - Ce skill s’active uniquement si la question nécessite une interprétation des CG (couverture, exclusions, garanties, obligations contractuelles, définitions).
- La source de vérité est
documents/CG/CG.txtdans le workspace conversationnel (équivalent intentionnel de/documents/CG/CG.txtcôté demande utilisateur), avec échec explicite si le fichier est requis mais absent/vide.
metadata.exclude_tables: string[]— liste de tables à exclure pour la conversation en cours. Validée côté serveur (normalisation, limite de taille, filtrage sur tables connues/permises).metadata.conversation_id: number— pour rattacher le message à une conversation existante (créée automatiquement sinon).metadata.save_as_default: boolean— lorsqu’àtrue, enregistre également les exclusions comme valeur par défaut du compte utilisateur. Par défautfalse(opt‑in) pour éviter les conditions de concurrence entre plusieurs onglets.metadata.ticket_selection: { pk: string, values: string[], table?: string }— restreint le contexte tickets aux identifiants sélectionnés (colonne clépk).metadata.ticket_selection.valuesnormalise les retours à la ligne échappés JSON (\\n) en vraies nouvelles lignes avant filtrage, pour supporter les cellules CSV multi‑lignes.metadata.ticket_sources: Array<{ table?: string, periods?: Array<{from?: string, to?: string}>, selection?: { pk: string, values: string[], table?: string } }>— décrit les sources/périodes multi‑tables; utilisé pour filtrer le snapshot AgentInsight.metadata.agent_insight_mode: boolean— forcé àtruecôté backend (la valeurfalseest refusée).
meta.effective_tables: string[]— tables effectivement actives (permissions – exclusions appliquées) envoyées au début du stream pour synchroniser l’UI.meta.agent_insight: { workspace, session_id?, elapsed_s }— informations de run opencode (workspace conversationnel et session).
- UI: le bouton « Données » n’est plus affiché dans le chat; les exclusions passent par
metadata.exclude_tables(persistées par conversation). - Backend:
GET /api/v1/data/tablesexpose les tables autorisées par l’ACL;POST /chat/streamacceptemetadata.exclude_tables: string[]et publiemeta.effective_tables(tables réellement actives) pendant le streaming. - AgentInsight filtre désormais le snapshot
agentinsight_sessions/conversation_<id>/dataselonmeta.effective_tables(exclusions conversationnelles + ACL), y compris sans filtre tickets explicite. - Persistance: les exclusions sont sauvegardées par conversation (colonne JSON
conversations.settings) et réappliquées automatiquement aux requêtes suivantes de la même conversation. - Sécurité: pas de mécanismes de secours. Si toutes les tables sont exclues, la réponse l’indique explicitement.
- Détails et rationales:
plan/chat-data-visibility.md.
Le garde‑fou bloquant du routeur a été supprimé du flux chat: les messages ne sont plus interrompus par une réponse de refus avant traitement.
- Persistance côté backend des conversations, messages et événements (
conversations,conversation_messages,conversation_events). - SSE
metainclutconversation_idpour qu’un premier message crée la conversation automatiquement. - Endpoints:
GET /api/v1/conversations,GET /api/v1/conversations/{id},POST /api/v1/conversations. - UI (header): « Historique » pour lister/ouvrir une discussion passée, « Chat » pour démarrer une nouvelle session (
?new=1).
- Une fois connecté avec le compte administrateur, l’UI affiche l’onglet Admin permettant de créer de nouveaux couples utilisateur/mot de passe. L’interface expose Nouveau chat, Explorer, Radar, Graph, Historique et Admin dans la navigation latérale.
- L’espace admin est découpé en onglets (Statistiques, Dictionnaire, Explorer, Radar, Chat, Utilisateurs, Feedback). L’ancien chemin
/feedbackredirige vers l’onglet Feedback pour centraliser la revue des avis. - Dans l’onglet Chat (Contexte tickets), les sélections de colonnes restent persistantes lorsque vous passez d’une table à l’autre avant sauvegarde.
- Tout nouvel utilisateur (y compris l’administrateur initial) doit définir un mot de passe définitif lors de sa première connexion. Le backend retourne un code
PASSWORD_RESET_REQUIREDsi un utilisateur tente de se connecter avec son mot de passe provisoire: le frontend affiche alors un formulaire dédié qui impose la saisie du nouveau mot de passe deux fois avant de poursuivre. - L’endpoint backend
POST /api/v1/auth/users(token Bearer requis) accepte{ "username": "...", "password": "..." }et renvoie les métadonnées de l’utilisateur créé. La réponse de connexion contient désormaisusernameetis_adminpour que le frontend sélectionne l’onglet Admin uniquement pour l’administrateur. - L’API
POST /api/v1/auth/reset-password(sans jeton) attend{ username, current_password, new_password, confirm_password }. En cas de succès elle renvoie204; le frontend relance automatiquement la connexion avec le nouveau secret. GET /api/v1/auth/usersexpose désormais un champis_adminpar utilisateur : l’interface s’en sert pour signaler l’administrateur réel et bloque toute modification de ses autorisations dans la matrice.GET /api/v1/admin/stats(admin uniquement) fournit les compteurs globaux (utilisateurs, conversations, messages, graphiques) ainsi que les mêmes métriques par utilisateur et l’activité des 7 derniers jours. Le panneau admin React affiche ces statistiques dans un tableau dédié avec un bouton d’actualisation.DELETE /api/v1/auth/users/{username}(token Bearer requis, admin uniquement) supprime un utilisateur non‑admin ainsi que ses conversations, graphiques et droits associés (cascade). Opération irréversible. Codes d’erreur possibles:400(tentative de suppression de l’admin),404(utilisateur introuvable),403(appelant non‑admin).POST /api/v1/auth/users/{username}/reset-password(admin uniquement) génère un mot de passe temporaire et force l’utilisateur à le changer lors de la prochaine connexion (must_reset_password=true). Le mot de passe généré est renvoyé dans la réponse et n’est pas journalisé côté serveur.- Les tokens d’authentification expirent désormais au bout de 4 heures. Si un token devient invalide, le frontend purge la session et redirige automatiquement vers la page de connexion pour éviter les erreurs silencieuses côté utilisateur.
- Le panneau admin inclut maintenant une matrice des droits sur les tables CSV/TSV présentes dans le répertoire de tables configuré (
DATA_TABLES_DIR, par défautdata/). Chaque case permet d’autoriser ou de retirer l’accès par utilisateur; l’administrateur conserve un accès complet par défaut. - Les droits sont stockés dans la table Postgres
user_table_permissions. Les APIGET /api/v1/auth/users(inventaire des tables + droits) etPUT /api/v1/auth/users/{username}/table-permissions(mise à jour atomique) pilotent ces ACL. - Le backend applique ces restrictions pour les listings/schémas (
GET /api/v1/data/...) ainsi que pour le chat AgentInsight et les graphiques sauvegardés: un utilisateur ne voit ni n’utilise de table qui ne lui a pas été accordée. - Le chat fonctionne en agent unique AgentInsight (plus de bascule SQL/AgentInsight/Graph dans le composeur).
- Les administrateurs peuvent créer/éditer/supprimer les dictionnaires de données directement depuis l’onglet Admin → carte « Dictionnaire de données ». Les fichiers YAML sont persistés dans
DATA_DICTIONARY_DIR(par défautdata/dictionary). API:GET /api/v1/dictionary(liste),GET/PUT /api/v1/dictionary/{table}(lecture/écriture),DELETE /api/v1/dictionary/{table}(suppression). Les colonnes sont validées contre le schéma réel, sans mécanismes de secours. - Les templates LLM sont stockés dans
data/prompts.yml(chemin configurable viaPROMPTS_PATH) et les variables sont écrites en{{variable}}avec validation des placeholders autorisés. L’édition via UI/API admin est retirée. - Un onglet latéral Knowledge permet à chaque utilisateur de gérer un texte Markdown de contexte personnel. API:
GET /api/v1/knowledge,PUT /api/v1/knowledge. - Les administrateurs peuvent lire et éditer le knowledge de tous les profils depuis la même page. API:
GET /api/v1/knowledge/admin,PUT /api/v1/knowledge/admin/{username}.
- L’onglet/page Explorer est désactivé dans la navigation (route retirée) pour alléger l’UI; utiliser l’Explorer (camembert Category/Sub Category) et le Chat pour les parcours principaux.
- API :
GET /api/v1/data/overviewagrège, pour chaque table autorisée, le volume total et les statistiques de toutes les colonnes détectées (avec inférence des dates), en respectant les ACLuser_table_permissions. - Admin : un onglet dédié dans l’espace « Admin » permet d’activer/désactiver les tables visibles dans l’Explorer et de fixer les colonnes Date / Category / Sub Category sans passer par l’UI Explorer.
include_disabled=true(admin uniquement) surGET /api/v1/data/overviewretourne aussi les tables désactivées pour préparer ou revoir leur configuration.PUT /api/v1/data/overview/{source}/explorer-enabledactive/désactive explicitement une table pour l’Explorer.- Admin : les colonnes Date / Category / Sub Category sont configurables par table (persistées via
/data/overview/{source}/column-roles) et pilotent les filtres date, la répartition Category/Sub Category et l’aperçu. - Admin : l’onglet « Chat » configure indépendamment le contexte tickets (table + colonnes texte/date + colonne titre pour le panneau latéral + champs additionnels injectés au LLM), persisté par table via
/tickets/context/config(ciblage possible avec?table=...) et/data/overview/{source}/column-roles. - Admin : l’onglet « Chat » s’appuie sur l’overview léger (
lightweight=true) pour charger les colonnes et rôles même si une colonne configurée n’existe plus; la colonne manquante apparaît vide pour correction. - Admin : l’onglet « Chat » ignore les réponses réseau obsolètes lors d’un changement de table pour éviter un contexte tickets désynchronisé.
- Visualisations Chart.js (lignes + barres) avec palette colorée pour timelines et répartitions des valeurs à partir des colonnes détectées automatiquement.
- Le jeu
tickets_jirainclut désormais les colonnesCategoryetSub Category(classification ITSM) pour alimenter la répartition affichée dans l’Explorer et les filtres associés. - Usage : vérifier la santé et la couverture des jeux de données avant d’ouvrir un chat ou de générer des graphiques.
- Onglet « Explorer » dans le header pour explorer les données par paires
Category/Sub Categoryquand ces colonnes existent. - Le filtre date global est intégré directement au bloc de visualisation (en tête de la première carte source) pour éviter les cartes imbriquées.
- Les colonnes Date / Category / Sub Category sont configurables par l’admin via l’onglet Admin → Explorer et persistées via
/api/v1/data/overview/{source}/column-roles. - Chaque source affichant ces colonnes est listée avec ses catégories et sous-catégories cliquables : un clic déclenche un aperçu (
/api/v1/data/explore/{source}) limité à 25 lignes, avec le volume total de lignes correspondantes. - Si une source ne possède pas les deux colonnes, la vue l’ignore et affiche un message explicite plutôt que de masquer l’erreur.
- Les aperçus sont paginés (25 lignes/page) avec navigation précédente/suivante et un tri
date(desc/asc) directement depuis l’en-tête de la colonne date dans la table. - Le bouton « Discuter avec ces données » (placé en haut du bloc donut) ouvre le chat en mode tickets avec la sélection Category/Sub Category préchargée (capée à 500 tickets, compteur affiché).
- Le panneau tickets du chat se cale sur la sélection Explorer, même si d’autres tables sont ouvertes (onglets séparés).
- Un range slider « date » global (tout en haut) filtre les données et l’aperçu d’un seul coup : la plage est appliquée automatiquement (sans bouton « Appliquer ») côté backend (
/data/overview+/data/explore) et la réinitialisation passe par un bouton icône flèche. - Le donut Category/Sub Category n’est plus encapsulé dans une carte interne: le rendu est homogénéisé (pas de « carré dans carré »).
- Chaque source inclut désormais un camembert Category/Sub Category (Chart.js) cliquable qui déclenche l’aperçu, se recalcule automatiquement quand le filtre date est appliqué et permet un drill-down : clic catégorie → camembert des sous-catégories + mise à jour immédiate de la table sur la sous-catégorie dominante, clic sous-catégorie → ouverture de l’aperçu (bouton retour pour remonter).
- Les actions
Discuter avec ces donnéesetRetour catégoriessont regroupées en haut du bloc donut pour une meilleure visibilité. - La catégorie et la sous-catégorie en cours sont mises en évidence avec des badges « Sélection active » dans l’aperçu pour clarifier le contexte courant.
- Le nom de table est affiché une seule fois par carte pour éviter les répétitions visuelles.
- Une animation courte (fade/slide) est déclenchée lors d’un changement de catégorie/sous-catégorie pour rendre la transition visuelle plus claire.
- Les actions Explorer (sélection dans le donut, tri date, pagination, ajustement du filtre date) gardent l’aperçu visible pendant le chargement pour éviter les flashes et les sauts visuels.
- Le composant donut applique une marge interne renforcée (padding + rayon borné) pour éviter qu’un segment soit tronqué pendant les interactions.
- Le badge central (catégorie/sous-catégorie + volume) a été retiré du donut pour épurer la visualisation et éviter tout chevauchement visuel.
- L’anneau du donut a été légèrement épaissi (
radius90%,cutout72%) pour améliorer la lisibilité visuelle. - Le donut adopte un style plus futuriste: segments en dégradés néon, halo circulaire subtil, séparateurs renforcés et fond grille léger.
- Les répartitions Category/Sub Category renvoient l’ensemble des couples disponibles afin d’éviter des totaux tronqués.
- Les tuiles de synthèse (sources/couples/sélection) ont été retirées de l’Explorer pour alléger l’interface et concentrer l’espace sur l’aperçu et le donut.
- La navigation se fait directement via le donut (clic catégorie puis sous-catégorie).
- L’aperçu des tickets sélectionnés s’affiche désormais sous le donut de la source, et la liste de sous-catégories sous le graphique a été retirée.
- Bouton « Radar » dans le header: affiche les résumés journaliers (sur le jour le plus récent disponible), hebdomadaires et mensuels générés via OpenCode. Les tables visibles sont filtrées selon les droits
user_table_permissions. - Panneau Admin → section « Radar »: configurer plusieurs tables (colonnes texte/date), retirer une table si besoin (
DELETE /api/v1/loop/config/{config_id}), puis relancer la génération pour une table donnée ou pour toutes (POST /api/v1/loop/regenerate?table_name=...). Les résumés sont lus depuis les fichiers générés (pas depuisloop_summaries) et exposés viaGET /api/v1/loop/overview. - Script Airflow:
airflow/dags/trigger_radar.shdéclenche la regen viaPOST /api/v1/loop/regenerateavec auth admin. Le script charge les non-secrets depuisairflow/dags/config.yaml(exemple:airflow/dags/config.example.yaml) et les secrets depuisairflow/dags/.env(exemple:airflow/dags/.env.example). - Workspace Radar:
RADAR_WORKSPACE_ROOT/<table_slug>__<hash>/<run_id>/avecdaily.md,weekly.md,monthly.mdetmetadata.json. Variables dédiées:RADAR_OPENCODE_COMMAND,RADAR_OPENCODE_AGENT,RADAR_OPENCODE_TIMEOUT_S,RADAR_OPENCODE_MODEL(optionnel),RADAR_WORKSPACE_ROOT. - Quota possible via
AGENT_MAX_REQUESTScléradar. Les garde‑fous de contexte restent ceux deLOOP_*(taille des tickets, découpage, périodes daily/weekly/monthly). - En cas d’erreur sur une table pendant une régénération globale, les autres tables continuent. La réponse inclut
regeneration_errors; si toutes les tables échouent, l’API renvoie502. - UI Radar: page épurée avec sélection du mode (journalier/hebdo/mensuel) et de la table, chargement automatique au rendu (sans bouton d’actualisation manuel).
- Routes HTTP minces -> délèguent à des services.
- Services orchestrent logique et dépôts.
- Dépôts encapsulent l’accès aux sources de données.
- Schémas (Pydantic) pour I/O propres et versionnées.
- Pas de mécanismes de secours cachés. Logs utiles uniquement.
Voir le plan d’intégration « Z »: plan/Z/README.md (LLM local/API et MCP).
backend/
pyproject.toml
src/insight_backend/
main.py
api/routes/v1/{health.py, chat.py, data.py, auth.py}
services/{chat_service.py, data_service.py, auth_service.py}
repositories/{data_repository.py, user_repository.py}
schemas/{chat.py, data.py, auth.py}
core/{config.py, logging.py, database.py, security.py}
frontend/
package.json, vite.config.js, index.html
src/{main.jsx, App.jsx, components/, features/, services/}
.env.development.example
data/
raw/, processed/, interim/, external/, vector_store/, models/
Cette base est volontairement minimale et modulaire; elle n’implémente pas la logique métier.
- Les endpoints
POST /api/v1/chat/streametPOST /api/v1/chat/completionspassent uniquement par AgentInsight (opencode). - Les anciens modes/API (
metadata.nl2sql,metadata.multiagent,metadata.sql_mode,metadata.chart_mode,/sql) sont rejetés explicitement (400). - Les métadonnées tickets (
ticket_table,ticket_periods,ticket_sources,ticket_selection) restent prises en compte pour filtrer le snapshotdata/conversationnel. - Le script
start.shest centré sur le lancement backend/frontend. Les opérations MindsDB (sync, SQL, embeddings) se déclenchent explicitement via les endpoints/scripts dédiés.
- Chaque réponse assistant dans le chat expose deux actions pouce haut/bas. Les votes sont persistés avec la conversation et le message cible (pas de fallback silencieux).
- API :
POST /api/v1/feedback(création/mise à jour),DELETE /api/v1/feedback/{id}(suppression) etGET /api/v1/feedback/admin(admin uniquement, liste ordonnée). - Les retours sont consultables depuis l’onglet Feedback du panneau Admin (
/admin?tab=feedback,/feedbackredirige). La liste affiche les votes (auteur, conversation, extrait, date) et permet d'ouvrir directement la conversation correspondante via/chat?conversation_id=...&message_id=....
- Le composeur du chat n’expose plus de bouton de bascule « Graph ».
- Les visuels générés directement par AgentInsight (PNG/JPG/WebP/GIF/SVG) sont affichés dans le flux et peuvent être sauvegardés.
- Chaque graphique généré via le chat peut être sauvegardé grâce au bouton Enregistrer dans le dashboard. Le backend persiste l’URL, le prompt, les métadonnées et la spec JSON.
- Les routes
POST /api/v1/chartsetGET /api/v1/charts(token Bearer requis) gèrent respectivement l’enregistrement et la consultation. Les utilisateurs ne voient que leurs propres graphiques, tandis que l’administrateur (ADMIN_USERNAME) accède à l’ensemble des sauvegardes. - Le dashboard liste désormais ces graphiques, affiche l’aperçu, le prompt associé, et expose un lien direct vers l’URL du rendu. Les administrateurs voient en plus l’utilisateur auteur.
- Chaque carte du dashboard propose un bouton Supprimer : les utilisateurs peuvent retirer leurs propres graphiques sauvegardés, tandis que l’administrateur peut supprimer n’importe quelle entrée.
- 2026-02-20: Refactor mono-agent: le chat passe en mode unique AgentInsight (suppression des 3 boutons du composeur), la barre de saisie n’a plus de bascule agent/chaîne/graph, et l’API
/chat/*rejette explicitement les anciens modes (nl2sql,multiagent,/sql, etc.). - 2026-02-20: Navigation: le bouton latéral
Graphest restauré (visible pouradminou permissioncan_view_graph) et renvoie vers/dashboard. - 2026-02-20: Admin: l’onglet de gestion des prompts est retiré de l’UI pour alléger le panneau.
- 2026-02-20: Ajout de l’onglet Knowledge (édition Markdown par profil + administration globale) et création automatique de
knowledge.mddans chaque nouveau workspaceagentinsight_sessions/conversation_<id>. - 2026-02-20: Backend: la route
/api/v1/prompts(lecture/édition) est supprimée. - 2026-02-06: Le logo du bandeau latéral est affiché en blanc ; le libellé
FoyerInsightutilise la police par défaut du site, avec la casse exacte. - 2026-02-06: Le bandeau latéral est désormais en bleu plein (dégradé Foyer) avec contrastes ajustés pour les états actifs/inactifs; les états UI
success/danger/warningutilisent une palette sémantique Foyer (plus de classesgreen/red/ambernatives). - 2026-02-06: La palette frontend est alignée sur les couleurs Foyer (référence
foyer-colors.css) viaprimaryTailwind (incluantprimary-25) et les couleurs Chart.js (Explorer + donuts) ont été harmonisées pour un rendu plus premium. - 2025-10-21: L'état vide du chat (« Discutez avec vos données ») est maintenant centré via un overlay
fixednon interactif: pas de scroll tant qu'aucun message n'est présent; la barre de saisie reste accessible. - 2025-10-21: Ajout d'un petit avertissement sous la zone de saisie: « L'IA peut faire des erreurs, FoyerInsight aussi. »
- 2026-02-05: Le cadre visuel externe de la colonne chat (bordure/ombre/arrondi du conteneur principal) a été supprimé dans l’onglet Chat pour alléger la vue.
- 2026-02-05: Le fond du conteneur principal du chat est passé en transparent pour reprendre exactement le background global de l’application.
- 2026-02-05: Le numéro de discussion (
Discussion #...) n’est plus affiché dans l’onglet Chat; l’historique utilise désormais le fallback « Conversation sans titre ». - 2026-02-05: Le panneau tickets (colonne gauche) n’affiche plus de ligne vide réservée à « Tout effacer » quand aucune sélection manuelle n’est active, pour aligner visuellement le haut des deux panneaux.
- 2026-02-05: Le flux de messages du panneau chat utilise désormais
flex + gap(au lieu despace-y) pour éviter un décalage vertical quand la toolbar mobile est masquée sur desktop; les deux panneaux démarrent au même niveau. - 2026-02-05: Le header a été réorganisé avec une vraie barre de navigation (Chat/Explorer/Radar/Graph/Historique/Admin), un état actif visible et un comportement responsive horizontal pour améliorer la lisibilité des boutons.
- 2026-02-05: La navigation du header est affichée en bandeau vertical (colonne de boutons pleine largeur) avec maintien de l’état actif.
- 2026-02-05: Le header horizontal est remplacé par un bandeau latéral fixe à gauche (nom + onglets + déconnexion), rabattable via un bouton
<</>>. - 2026-02-05: Sur la page Chat, le layout passe en hauteur pleine (
h-screen+h-full) et retire les anciens offsetscalc(100vh-120px)/top-20pour que le panneau tickets et la zone chat descendent bien jusqu’en bas de la page. - 2026-02-05: Correction du scroll infini sur
/chaten verrouillant la page sur100dvh(Layout+aside+main) et en gardant le scroll uniquement dans les zones internes du chat. - 2026-02-05: Ajustement de la chaîne flex du chat (
min-h-0,overflow-hidden, composershrink-0) pour maintenir durablement la barre « Posez votre question » collée en bas de viewport. - 2026-02-05: Verrouillage explicite du scroll global (
html/body overflow: hidden) sur la route/chatpour supprimer le scroll page résiduel; seul le scroll interne des panneaux reste actif. - 2026-02-05: Ajout d’un léger espacement en haut de la page
/chat(mainenpt-3) pour éviter un rendu trop collé au bord supérieur. - 2026-02-05: Le menu du bandeau latéral n’utilise plus un conteneur “carte” encadré; les onglets sont désormais intégrés directement au rail vertical avec un état actif plus discret.
- 2026-02-05: Le bouton de repli du bandeau latéral passe en icône chevrons (style plus fin) et la sélection d’onglet n’affiche plus de contour/badge encadré.
- 2026-02-05: Le libellé “Navigation” est supprimé du bandeau latéral pour alléger l’en-tête de menu.
- 2026-02-05: La largeur du bandeau latéral est réduite (ouvert/replié) pour une empreinte visuelle plus légère.
- 2026-02-05: Le bandeau latéral est plus compact verticalement en mode ouvert (espacements réduits) et masque désormais le logo en mode replié.
- 2026-02-05: La page
/radarn’affiche plus de bouton « Actualiser »; les données se chargent automatiquement à l’ouverture. - 2026-02-05: Le sélecteur de mode sur
/radarutilise désormais un segmented control (3 segments alignés) avec état actif plus contrasté. - 2026-02-05: La carte de synthèse
/radarn’affiche plus le rappel « Table sélectionnée » pour éviter la redondance avec le sélecteur. - 2026-02-05: La carte de synthèse
/radarn’affiche plus l’intitulé redondant du mode (« Flash du jour », « Panorama du mois », etc.), le mode actif étant déjà visible dans le segmented control. - 2026-02-05: Le bloc de filtres
/radarest réorganisé en pile intégrée: sélection de table en haut, puis segmented control du mode centré juste en dessous. - 2026-02-05: Le bloc de filtres
/radarn’est plus encadré par une carte externe (cadre/ombre retirés) pour un rendu plus léger. - 2026-02-05: La zone d’output
/radarn’utilise plus de cartes imbriquées; le double cadre visuel autour de la synthèse a été supprimé. - 2026-02-05: Le titre de page « Radar » est retiré de
/radarpour alléger l’en-tête (la navigation active sert déjà de repère). - 2026-02-05: Le bandeau latéral remplace l’entrée
ChatparNouveau chat(action/chat?new=1) et le boutonNouveau chatde la barre sticky du flux/chatest retiré. - 2026-02-05:
Nouveau chatréinitialise désormais/chatvers l’état par défaut (tickets visibles, mode tickets actif, filtres reset) et annule les requêtes en cours pour éviter un écran vide. - 2026-02-05: Le clic sur
Nouveau chatdéclenche un fondu court (animate-fade-in) sur la vue/chatpendant la réinitialisation.
- 2026-03-04:
start.shne tue plus automatiquement les processus sur les ports backend/frontend; il échoue désormais explicitement si un port est déjà occupé et affiche les listeners actifs. - 2026-02-25: Ajout du skill OpenCode
cg-knowledge(.opencode/skills/cg-knowledge) pour déclencher la lecture conditionnelle des Conditions Générales Foyer depuisdocuments/CG/CG.txt. - 2026-02-25: Ajout d’un dossier source
data/documents(versionné via.gitkeep) et copie automatique versagentinsight_sessions/conversation_<id>/documentslors de l’initialisation d’un workspace AgentInsight. - 2026-02-25: Ajout du skill OpenCode
knowledge(.opencode/skills/knowledge) et suppression de la lecture systématique deknowledge.mddans.opencode/agents/DataAnalyste.md(déclenchement désormais conditionnel via le skill). - 2026-02-25: Les permissions
skilldans./.opencode/opencode.jsoncpassent en whitelist (*deny +data-analyst-graphs,createur-graph-png,knowledge,cg-knowledgeallow) pour limiter les skills activables à celles du repo. - 2026-02-25: Correction du modèle OpenCode dans
./.opencode/opencode.jsonc(openai/gpt-5.3_codex->openai/gpt-5.3-codex) pour alignement avec les IDs de modèles OpenAI reconnus par OpenCode. - 2026-02-25: L’agent
.opencode/agents/DataAnalyste.mdfixe explicitementmodel: openai/gpt-5.3-codexpour éviter un override implicite par des agents globaux locaux (~/.config/opencode/agents), cause observée deToken refresh failed: 400côté Anthropic OAuth. - 2026-02-20: Suppression du statut UI « DeepSearch mode », du pourcentage de charge contexte et du disclaimer de dépassement; le contexte tickets est désormais injecté sans limite de taille configurée.
- 2026-02-20: Le fichier
.opencode/agents/DataAnalyste.mdest désormais versionné pour garantir la disponibilité de l’agentDataAnalystedans le repo. - 2026-02-20: Les workspaces runtime
AGENT_INSIGHT_WORKSPACE_ROOTetRADAR_WORKSPACE_ROOTpointent désormais par défaut versdata/agentinsight_sessionsetdata/radar_sessions(et ces dossiers sont ignorés par Git). - 2026-02-20: Radar bascule sur OpenCode (
RADAR_OPENCODE_*) avec workspaces isolés par table/exécution (RADAR_WORKSPACE_ROOT/<table_slug>__<hash>/<run_id>/daily.md|weekly.md|monthly.md|metadata.json), lecture directe des fichiers via/loop/overview, et gestion d’erreurs partielles viaregeneration_errors. - 2026-02-20:
./.opencode/opencode.jsoncest désormais versionné (exception ciblée dans.gitignore) pour partager l’agent OpenCoderadarsans versionner le reste du workspace local. - 2026-02-18: Ajout de
.opencode/dans.gitignorepour exclure le workspace local OpenCode du versioning. - 2026-02-18: AgentInsight n’écrase plus
XDG_CONFIG_HOME(les agents/skills OpenCode globaux sont pris en compte) et échoue explicitement si--agentdéclenche un fallback implicite vers l’agent par défaut. - 2026-02-18: AgentInsight synchronise les credentials OpenCode (
auth.json) dans le runtime isolé et traite les événementstype=errorcomme des erreurs bloquantes, même avec exit code0. - 2026-02-18: La détection des graphiques AgentInsight ignore désormais les répertoires techniques (
.venv/venv/node_modules/.git/caches) pour ne pas remonter d’images de dépendances au frontend. - 2026-02-19: Correction de la structure de
.opencode/opencode.jsonc(clésagentetpermissionremises à la racine;mcpne contient plus que les serveurs MCP) pour respecter le schéma OpenCode. - 2025-10-29: Correction d'un échec de build frontend (TS2451) dû à une double déclaration
const metadansfrontend/src/features/chat/Chat.tsx. La duplication a été supprimée. - 2025-11-27: Corrige l’échec de build du frontend (Explorer) en ajoutant les dépendances
chart.js/react-chartjs-2manquantes; relancernpm installpuisnpm run build. - 2026-01-23: Synchronisation dev/main (résolution de conflits sur la configuration du contexte tickets).
Depuis cette branche, le backend applique un garde‑fou de configuration: en environnement non‑développement (ENV différent de development/dev/local), le démarrage échoue si des valeurs par défaut non sûres sont détectées.
Vérifications effectuées:
JWT_SECRET_KEY == "change-me"ADMIN_PASSWORD == "admin"DATABASE_URLcontientpostgres:postgres@
Corrigez ces variables dans backend/.env (secrets) et backend/config.yaml (non-secrets) avant déploiement. En développement, ces valeurs restent acceptées mais des avertissements sont journalisés. Détails dans backend/README.md.
- Objectif: après une requête (SQL MindsDB ou Graph), afficher un panneau latéral listant les éléments de preuve (tickets ou autre entité) réellement utilisés pour produire la réponse.
- Contrat minimal: le pipeline LLM/MCP fournit un
evidence_spec(labels/champs) et/ou desrowsavecpurpose: 'evidence'. Sans spec explicite, le panneau reste désactivé (pas d’heuristique cachée, pas de requête additionnelle). - Cible visuelle: panneau droit coulissant, bouton contextuel « {entity_label} (N) », liste scrollable des éléments avec champs déclarés, ouverture automatique quand des éléments sont présents.
- Capture dataset: dans
frontend/src/features/chat/Chat.tsx, conserver le dernierrowsdontpurpose: 'evidence'(colonnes + lignes +row_count) +evidence_spec; marquersourceMode = 'sql' | 'graph'. - Bouton contextuel: afficher « {entity_label} (N) » selon
evidence_spec.entity_label; bouton désactivé + tooltip si spec absent. - Panneau latéral: desktop → volet fixe à gauche (≈420px). mobile → bottom‑sheet (≈70% hauteur) avec overlay cliquable; fermeture
Escet croix; en‑tête avecentity_label, période éventuelle fournie par le spec, et mode (SQL MindsDB | Graph). - Liste générique: rendu simple réutilisant
Card/styles locaux; champs pris dansdisplay.{title,created_at,status}etpk; tri pardisplay.created_atsi fourni; max 100 lignes (ouspec.limit) avec badge « +N ». - États UI:
loading,vide(« Aucun élément de preuve »),erreur(texte clair); messages discrets uniquement. - Accessibilité: focus piégé dans le panneau, navigation clavier complète, contraste AA; responsive ≥ 360px.
- Le front s’appuie uniquement sur
evidence_specet sur lesrowstaggéspurpose: 'evidence'. - Aucune inférence/heuristique silencieuse si un champ manque; afficher un état désactivé explicite.
- Réinitialiser le dataset à l’envoi d’une nouvelle question.
- Auto‑ouverture: ouvrir le panneau quand ≥1 élément de preuve est détecté et que l’utilisateur n’a pas fermé la vue précédemment.
- Résumé: « N {entity_label} » et période éventuelle fournie par le spec (le front ne la déduit pas seul).
- Actions: lien basé sur
display.link_templatesi présent; sinon aucun lien. - Journalisation: en dev,
console.info('[evidence_panel] opened', { count, entity: entity_label, sourceMode }); en prod, “evidence_panel_opened” si télémétrie existante.
- Tickets: « Combien de tickets en mai 2025 ? » avec
evidence_spec« Tickets » + 5 lignes → bouton « Tickets (5) »; panneau ouvert; 5 lignes datées 2025‑05. - Autre entité (ex. Incidents): même expérience avec
entity_label: "Incidents"et mappages fournis. - Sans spec: bouton désactivé avec tooltip « Aucun evidence_spec reçu »; aucun panneau.
- Dataset volumineux: 250 → 100 visibles + « +150 »; scroll fluide.
- A11y:
Tabcircule;Escferme; focus rendu sur le bouton.
frontend/src/features/chat/Chat.tsx: stockerevidence_spec+ dernier datasetpurpose:'evidence', étatshowEvidence, bouton d’ouverture.- (Optionnel)
frontend/src/features/chat/EvidencePanel.tsx: composant léger, générique; sinon inline pour éviter des artefacts. - Réutiliser
frontend/src/components/ui/Card.tsx; aucun nouveau design system.
- Le panneau s’ouvre automatiquement quand des éléments de preuve sont détectés (via spec) et peut être rouvert via un bouton visible libellé
entity_label. - La liste utilise exclusivement les champs déclarés dans le spec; pas d’heuristiques implicites.
- Pas d’appel réseau supplémentaire déclenché par l’ouverture du panneau.
- Les scénarios de test visuel ci‑dessus passent sur desktop et mobile.