Skip to content

agentdouble/PASTEQUE

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,014 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

20_insightv2 – Architecture de base

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).

Dossiers

  • 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/.

Démarrage rapide

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 install si besoin), puis lance backend et frontend (build + vite preview). Les non-secrets sont lus depuis backend/config.yaml et frontend/config.yaml; les secrets restent dans backend/.env et frontend/.env.development. Le script valide en fail-fast que backend/config.yaml (ALLOWED_ORIGINS) et frontend/config.yaml (FRONTEND_URLS/FRONTEND_DEV_URL) sont cohérents.
  • ./start_full.sh – mêmes étapes que start.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.yaml et frontend/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), lancer npm run build -- --mode development dans frontend/ pour afficher l'erreur TypeScript/Vite.

API retirées (refactor actuel)

  • /api/v1/mindsdb/* supprimé
  • /api/v1/mcp/* supprimé
  • /api/v1/conversations/{id}/dataset supprimé
  • Le backend opère désormais en mode AgentInsight sans pipeline NL2SQL/retrieval/MindsDB

Compatibilité shell:

  • Les scripts start.sh et start_full.sh sont compatibles avec le Bash macOS 3.2 et /bin/sh.

Avant le premier lancement, copier les exemples:

  • backend/config.example.yaml -> backend/config.yaml
  • backend/.env.example -> backend/.env
  • frontend/config.example.yaml -> frontend/config.yaml
  • frontend/.env.development.example -> frontend/.env.development

Contrat strict:

  • backend/.env ne doit contenir que DATABASE_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/):

  1. Installer uv si nécessaire: voir https://docs.astral.sh/uv
  2. Installer les deps: uv sync
  3. Lancer: uv run uvicorn insight_backend.main:app --reload
  4. Copier backend/config.example.yaml en backend/config.yaml (non-secrets) puis backend/.env.example en backend/.env (secrets). config.yaml contient les paramètres d’exécution (BACKEND_DEV_URL, LLM_MODE, CONTAINER_RUNTIME, etc.) et .env contient 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/):

  1. Installer deps: npm i ou pnpm i ou yarn
  2. Copier frontend/config.example.yaml en frontend/config.yaml (non-secrets), puis frontend/.env.development.example en frontend/.env.development (secrets éventuels).
  3. Lancer: npm run build && npm run preview (recommandé pour éviter les watchers) ou npm run dev si vous avez besoin du HMR.

SSR GPT-Vis (depuis vis-ssr/):

  1. Installer deps: npm install
  2. Copier config.example.yaml en config.yaml et ajuster GPT_VIS_SSR_PORT / VIS_IMAGE_DIR / GPT_VIS_SSR_PUBLIC_URL
  3. Lancer: npm run start (endpoint POST /generate + statiques /charts/*, PNG rendu via @antv/gpt-vis-ssr)
  4. Ajuster l'URL du plan/Z/mcp.config.json (variable VIS_REQUEST_SERVER) en fonction du port GPT_VIS_SSR_PORT choisi. Par défaut, le SSR écoute sur 6363 (voir vis-ssr/config.example.yaml) et le fichier plan/Z/mcp.config.json référence http://localhost:6363/. Si le domaine public diffère de localhost, renseigner GPT_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).

Streaming Chat

  • Endpoint: POST /api/v1/chat/stream (SSE text/event-stream).
  • Front: affichage en direct des tokens AgentInsight (delta) et des appels outils (anim), avec bouton Détails pour 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 snapshot data/ contextuel (tables + périodes sélectionnées quand disponibles, sinon copie complète), puis exécute opencode dans ce dossier.
  • Au démarrage d’un workspace conversationnel, le backend crée aussi knowledge.md à la racine du dossier conversation_<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 de data/documents (copie des fichiers et sous-dossiers). Si data/documents est absent, un dossier documents vide est créé dans le workspace.
  • Pour la résolution des tables contextuelles (ticket_table, ticket_sources), AgentInsight cherche en priorité dans DATA_TABLES_DIR, puis dans DATA_ROOT/raw, puis dans DATA_ROOT.
  • Le run AgentInsight utilise l’agent opencode configuré (AGENT_INSIGHT_AGENT, défaut DataAnalyste) 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 plus XDG_CONFIG_HOME afin d’utiliser les agents/skills OpenCode globaux (ex: ~/.config/opencode/skills).
  • Si un auth.json OpenCode 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=error provoquent désormais un échec explicite même si le process retourne 0.
  • Le run AgentInsight force NODE_TLS_REJECT_UNAUTHORIZED=0 pour le process opencode (équivalent à lancer NODE_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 text d’opencode sont relayées en delta SSE en temps réel; les appels d’outils sont relayés en statut anim.
  • 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_periods et 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 principal ticket_table/ticket_periods pour 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/au apparaît en premier sur la ligne de période, puis ou, 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_DEFAULT cô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 via VLLM_BASE_URL, provider externe via OPENAI_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é par LLM_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 plafond max_tokens sur tous les appels OpenAI-compatibles (explorateur, analyste, rédaction, router, chat) pour éviter les erreurs lorsque model_max_tokens - context_tokens devient négatif.
  • Les anciens modes nl2sql, multiagent, sql_mode, chart_mode et la commande /sql sont 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.

Skill OpenCode data-analyst-graphs

  • Le repo versionne le skill .opencode/skills/data-analyst-graphs pour générer des graphiques PNG dans le workspace conversationnel (outputs/graphs/) avec pandas + matplotlib à partir de fichiers CSV (uniquement).
  • AgentInsight charge les skills OpenCode depuis la configuration globale (~/.config/opencode/skills): installer ou mettre à jour ce skill avec mkdir -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.md est 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.
  • DataAnalyste2 applique un protocole strict (discovery -> profiling -> deep dive -> restitution), limite les aperçus (20 lignes max, 10 colonnes max) et privilégie les agrégations/chunks aux dumps bruts.
  • En cas de besoin LLM avancé, DataAnalyste2 impose un mode explicite local (vLLM) ou api (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.md et 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 dans scripts/ du workspace conversationnel, avec export final en PNG dans outputs/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 dans SKILL.md, avec exécution via uv run et échec explicite sans fallback caché.
  • Le script render_chart.py applique un contrôle strict de frontière workspace: --input et --output sont refusés s’ils résolvent hors du workspace courant.

Skill OpenCode knowledge

  • Le repo versionne le skill .opencode/skills/knowledge pour centraliser l’usage du contexte métier via knowledge.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é).

Skill OpenCode cg-knowledge

  • Le repo versionne le skill .opencode/skills/cg-knowledge pour 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.txt dans le workspace conversationnel (équivalent intentionnel de /documents/CG/CG.txt côté demande utilisateur), avec échec explicite si le fichier est requis mais absent/vide.

Métadonnées de requête (API)

  • 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éfaut false (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.values normalise 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é à true côté backend (la valeur false est refusée).

Métadonnées de streaming (SSE)

  • 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).

Données utilisées — visibilité + exclusions

  • 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/tables expose les tables autorisées par l’ACL; POST /chat/stream accepte metadata.exclude_tables: string[] et publie meta.effective_tables (tables réellement actives) pendant le streaming.
  • AgentInsight filtre désormais le snapshot agentinsight_sessions/conversation_<id>/data selon meta.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.

Router (à chaque message)

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.

Historique des conversations (branche feature/historique)

  • Persistance côté backend des conversations, messages et événements (conversations, conversation_messages, conversation_events).
  • SSE meta inclut conversation_id pour 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).

Gestion des utilisateurs (admin)

  • 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 /feedback redirige 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_REQUIRED si 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ésormais username et is_admin pour 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 renvoie 204 ; le frontend relance automatiquement la connexion avec le nouveau secret.
  • GET /api/v1/auth/users expose désormais un champ is_admin par 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éfaut data/). 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 API GET /api/v1/auth/users (inventaire des tables + droits) et PUT /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éfaut data/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 via PROMPTS_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}.

Explorer (vision globale des sources)

  • 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/overview agrè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 ACL user_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) sur GET /api/v1/data/overview retourne aussi les tables désactivées pour préparer ou revoir leur configuration. PUT /api/v1/data/overview/{source}/explorer-enabled active/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_jira inclut désormais les colonnes Category et Sub 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.

Explorer (navigation Category/Sub Category)

  • Onglet « Explorer » dans le header pour explorer les données par paires Category / Sub Category quand 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ées et Retour catégories sont 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 (radius 90%, cutout 72%) 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.

Radar – résumés journaliers/hebdo/mensuels

  • 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 depuis loop_summaries) et exposés via GET /api/v1/loop/overview.
  • Script Airflow: airflow/dags/trigger_radar.sh déclenche la regen via POST /api/v1/loop/regenerate avec auth admin. Le script charge les non-secrets depuis airflow/dags/config.yaml (exemple: airflow/dags/config.example.yaml) et les secrets depuis airflow/dags/.env (exemple: airflow/dags/.env.example).
  • Workspace Radar: RADAR_WORKSPACE_ROOT/<table_slug>__<hash>/<run_id>/ avec daily.md, weekly.md, monthly.md et metadata.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_REQUESTS clé radar. Les garde‑fous de contexte restent ceux de LOOP_* (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 renvoie 502.
  • 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).

Principes d’architecture

  • 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).

Arborescence (résumé)

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.

Chat AgentInsight (mode unique)

  • Les endpoints POST /api/v1/chat/stream et POST /api/v1/chat/completions passent 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 snapshot data/ conversationnel.
  • Le script start.sh est centré sur le lancement backend/frontend. Les opérations MindsDB (sync, SQL, embeddings) se déclenchent explicitement via les endpoints/scripts dédiés.

Feedback utilisateur

  • 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) et GET /api/v1/feedback/admin (admin uniquement, liste ordonnée).
  • Les retours sont consultables depuis l’onglet Feedback du panneau Admin (/admin?tab=feedback, /feedback redirige). La liste affiche les votes (auteur, conversation, extrait, date) et permet d'ouvrir directement la conversation correspondante via /chat?conversation_id=...&message_id=....

Visualisations chat

  • 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.

Sauvegarde des graphiques MCP

  • 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/charts et GET /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.

Notes UI

  • 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 Graph est restauré (visible pour admin ou permission can_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.md dans chaque nouveau workspace agentinsight_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é FoyerInsight utilise 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/warning utilisent une palette sémantique Foyer (plus de classes green/red/amber natives).
  • 2026-02-06: La palette frontend est alignée sur les couleurs Foyer (référence foyer-colors.css) via primary Tailwind (incluant primary-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 fixed non 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 de space-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 offsets calc(100vh-120px) / top-20 pour que le panneau tickets et la zone chat descendent bien jusqu’en bas de la page.
  • 2026-02-05: Correction du scroll infini sur /chat en verrouillant la page sur 100dvh (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, composer shrink-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 /chat pour 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 (main en pt-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 /radar n’affiche plus de bouton « Actualiser »; les données se chargent automatiquement à l’ouverture.
  • 2026-02-05: Le sélecteur de mode sur /radar utilise désormais un segmented control (3 segments alignés) avec état actif plus contrasté.
  • 2026-02-05: La carte de synthèse /radar n’affiche plus le rappel « Table sélectionnée » pour éviter la redondance avec le sélecteur.
  • 2026-02-05: La carte de synthèse /radar n’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 /radar est 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 /radar n’est plus encadré par une carte externe (cadre/ombre retirés) pour un rendu plus léger.
  • 2026-02-05: La zone d’output /radar n’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 /radar pour 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 Chat par Nouveau chat (action /chat?new=1) et le bouton Nouveau chat de la barre sticky du flux /chat est retiré.
  • 2026-02-05: Nouveau chat réinitialise désormais /chat vers 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 chat déclenche un fondu court (animate-fade-in) sur la vue /chat pendant la réinitialisation.

Maintenance

  • 2026-03-04: start.sh ne 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 depuis documents/CG/CG.txt.
  • 2026-02-25: Ajout d’un dossier source data/documents (versionné via .gitkeep) et copie automatique vers agentinsight_sessions/conversation_<id>/documents lors 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 de knowledge.md dans .opencode/agents/DataAnalyste.md (déclenchement désormais conditionnel via le skill).
  • 2026-02-25: Les permissions skill dans ./.opencode/opencode.jsonc passent en whitelist (* deny + data-analyst-graphs, createur-graph-png, knowledge, cg-knowledge allow) 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.md fixe explicitement model: openai/gpt-5.3-codex pour éviter un override implicite par des agents globaux locaux (~/.config/opencode/agents), cause observée de Token refresh failed: 400 cô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.md est désormais versionné pour garantir la disponibilité de l’agent DataAnalyste dans le repo.
  • 2026-02-20: Les workspaces runtime AGENT_INSIGHT_WORKSPACE_ROOT et RADAR_WORKSPACE_ROOT pointent désormais par défaut vers data/agentinsight_sessions et data/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 via regeneration_errors.
  • 2026-02-20: ./.opencode/opencode.jsonc est désormais versionné (exception ciblée dans .gitignore) pour partager l’agent OpenCode radar sans versionner le reste du workspace local.
  • 2026-02-18: Ajout de .opencode/ dans .gitignore pour 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 --agent dé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énements type=error comme des erreurs bloquantes, même avec exit code 0.
  • 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és agent et permission remises à la racine; mcp ne 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 meta dans frontend/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-2 manquantes; relancer npm install puis npm run build.
  • 2026-01-23: Synchronisation dev/main (résolution de conflits sur la configuration du contexte tickets).

Sécurité configuration (backend)

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_URL contient postgres: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.

Plan UI — Panneau « Éléments de preuve » (générique) pour /chat

  • 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 des rows avec purpose: '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.

Sous‑tâches front (testables visuellement)

  • Capture dataset: dans frontend/src/features/chat/Chat.tsx, conserver le dernier rows dont purpose: 'evidence' (colonnes + lignes + row_count) + evidence_spec; marquer sourceMode = '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 Esc et croix; en‑tête avec entity_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 dans display.{title,created_at,status} et pk; tri par display.created_at si fourni; max 100 lignes (ou spec.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.

Câblage de données (sans fallback)

  • Le front s’appuie uniquement sur evidence_spec et sur les rows taggés purpose: '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.

Triggers & UX

  • 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_template si 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.

Scénarios de test visuel (acceptation)

  • 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: Tab circule; Esc ferme; focus rendu sur le bouton.

Impact fichiers (prévision)

  • frontend/src/features/chat/Chat.tsx: stocker evidence_spec + dernier dataset purpose:'evidence', état showEvidence, 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.

Définition de fait (DoD)

  • 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.