Commit Graph

6 Commits

Author SHA1 Message Date
Dom
5ea4960e65 backup: snapshot post-démo GHT 2026-05-19
Some checks failed
tests / Lint (ruff + black) (push) Successful in 1m50s
tests / Tests unitaires (sans GPU) (push) Failing after 1m50s
tests / Tests sécurité (critique) (push) Has been skipped
Backup état complet après enregistrement vidéo démo de bout en bout.
À utiliser comme point de référence pour la consolidation post-démo.

Changements majeurs de la session 18-19 mai :
- AIVA-URGENCE : page autonome avec preset URL + auto-focus chain
- Workflow Demo_urgence_3_db : merge linux_db + steps AIVA + pause humaine NoMachine
- Bypass LLM (static_result / static_text) dans replay_engine
  pour démos déterministes sans appel Ollama
- Fix api_stream:3013 — replay_paused au premier polling /next
- dag_execute : lift duration_ms vers top-level pour wait runtime
- NPM bypass auth /aiva-urgence/ via location ^~ (proxy_host/10.conf hors git)
- scripts/cancel-replays.sh — workaround Stop VWB qui ne purge pas la queue

Anchors visuels (468) forcés dans le commit pour garantir restorabilité.
DB workflows actuelle + ~12 .bak DB de la journée incluses.

Sujets identifiés pour consolidation post-démo (TODO) :
1. Bug VWB recapture anchor ne régénère pas le PNG
2. Léa client accumule état mémoire (restart périodique requis)
3. Stop VWB ne purge pas la queue serveur (lien manquant vers /replay/cancel)
4. Bug coord client mss tronqué 2560x60 → mapping Y cassé
5. delay_before/delay_after ignorés au runtime (fix partiel duration_ms)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:55:06 +02:00
Dom
f2212e77e3 feat(t2a): bench_t2a_dryrun.py + t2a_mappings.py - mini-bench standalone 11 dossiers POC
Bench standalone qui exécute build_dpi_enriched + appel LLM sur les 11
dossiers POC GHT Sud 95 (docs/clients/ght_sud_95/mockup_easily_assure/data.js),
sans passer par Demo_urgence_2 ni Léa/Windows. Permet de mesurer la
convergence durée/décision Python ↔ LLM sur un panel représentatif AVANT
d'écrire le garde-fou serveur du commit 2.

core/llm/t2a_mappings.py :
- Module partagé TERRAIN_VERS_T2A (4 entrées validées par Dom 12/05)
- Importé par le bench, sera importé aussi par le garde-fou serveur commit 2
- Cas non mappés volontairement documentés (Retour structure d'origine,
  chaîne vide pour statut_attente)

scripts/bench_t2a_dryrun.py :
- Parsing data.js via node (vm.runInContext) → 11 dossiers en JSON
- Reconstruction d'un dpi_raw plat simulant l'OCR scroll auto :
  bandeau Easily Assure répété 5x (1 par onglet) + sections motif /
  examens / imagerie / notes médicales / Synthèse Urgences au format
  LIBELLÉ    VALEUR
- NE bypasse PAS build_dpi_enriched : le dpi_raw est texte plat re-parsé
  par la fonction (test de robustesse réel du parser regex)
- Appel LLM déterministe : temperature=0, seed=42, model=gemma4:31b-cloud
- Vérification empirique du respect du seed (2 appels successifs sur 1er
  dossier, comparaison decision/durée/justif) → warning si bruit cloud
- 4 traces structurées par dossier dans logs/t2a_dryrun/<IPP>_<ts>.log :
  [t2a_dryrun_metadata] / [t2a_dryrun_prompt] / [t2a_dryrun_response]
  ou [t2a_dryrun_error] en cas d'échec API
- Filet data_quality_warning (incohérence âge déclaré vs date naissance,
  motif vs diagnostic principal, décision vide) — filet, pas analyse
  exhaustive ; signale sans corriger (anonymisation v1 incertaine)
- Tableau récap stdout 9 colonnes + CSV scripts/bench_t2a_dryrun_<ts>.csv
- Stats agrégées : convergence durée X/N, convergence décision X/N
  mappés, liste détaillée des divergences avec pointeurs vers logs
- Recommandation auto : réécrire PROMPT 3 ou non selon convergence durée

Activation : T2A_DRYRUN=1 python scripts/bench_t2a_dryrun.py
Options : --ipp <IPP> (1 dossier), --skip-seed-check

Smoke test pré-commit (sans LLM) : parsing + dpi_raw + build_dpi_enriched
sur les 11 dossiers → 11/11 metadata complets, 0 parsing_warning,
durées calculées de 2.0h à 12.02h, décompo décisions terrain conforme
(7 Consultation + 1 Hosp + 1 UHCD + 1 Transfert + 1 Retour structure).

Brief complet : docs/handoffs/2026-05-12_brief_S1_build_dpi_enriched.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:55:57 +02:00
Dom
9872f4510c feat(t2a): build_dpi_enriched - extraction déterministe horaires + classifications cliniques
Préprocesseur Python qui injecte un bloc FAITS_CALCULÉS en tête du DPI
avant l'appel LLM, pour neutraliser l'hallucination de durée (bug "23h"
sur cas MOREL, confusion avec "depuis 23h" de l'Observ. IDE Urg).

Extrait depuis le bandeau Easily Assure et la Synthèse Urgences :
- âge (dateutil.relativedelta)
- date admission / sortie + durée passage (format humain + décimal)
- CCMU / GEMSA libellé complet (parser multi-ligne)
- priorité IAO, mode de venue / médicalisation / mode d'entrée
- diagnostic principal
- decision_terrain + orientation_terrain (metadata only, jamais injectés
  dans le prompt pour ne pas biaiser le LLM)

Retour tuple (dpi_enriched, metadata) pour permettre les garde-fous
serveur Python ↔ LLM au commit 2.

Robustesse :
- re.search 1re occurrence + WARNING si bandeau divergent multi-occurrences
- Synthèse Urgences priorité sur bandeau pour dates
- Valeur exigée sur même ligne que label (évite capture de section title)
- Cas négatif (horaires absents) → "NON CALCULABLE" + parsing_warnings
- Jamais de crash, retour tuple toujours valide

Tests : 4/4 verts (golden MOREL string + metadata, négatif sortie absente,
DPI vide). Pas de régression sur tests/integration/test_t2a_extract.py.

Brief complet : docs/handoffs/2026-05-12_brief_S1_build_dpi_enriched.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:49:49 +02:00
Dom
35b27ae492 fix(stream+vwb): chaîne replay robuste — auth, anchor type_text, lock async, drift, prompt LLM
Six modifications structurelles côté serveur, non destructives, aboutissant à un
pipeline replay bien plus stable pour la démo GHT Sud 95 (Urgences UHCD).

1. visual_workflow_builder/backend/app.py
   load_dotenv() chargeait .env (cwd) au lieu de .env.local racine projet.
   Conséquence : RPA_API_TOKEN absent après chaque restart manuel du backend
   et tous les proxies VWB→streaming échouaient en 401 « Token API invalide ».
   Charge maintenant explicitement .env.local du project root.

2. visual_workflow_builder/backend/api_v3/learned_workflows.py
   Quatre appels proxy /api/v1/traces/stream/* ne portaient pas le Bearer.
   Helper _stream_headers() factorisé et appliqué (workflows list/detail,
   workflow detail, reload-workflows).

3. visual_workflow_builder/backend/api_v3/dag_execute.py
   _ANCHOR_CLICK_TYPES excluait type_text/type_secret : pas de pre-click de
   focus avant la frappe → texte tapé sans focus → textareas vides au replay.
   Helper _inject_anchor_targeting() factorisé (centre bbox + visual_mode +
   target_spec) appliqué aux click_anchor* ET aux type_text/type_secret dès
   qu'un anchor_id est présent. Workflows historiques sans anchor sur
   type_text → comportement inchangé.

4. agent_v0/server_v1/api_stream.py — endpoint /replay/next
   _replay_lock (threading.Lock global) tenu pendant les actions serveur
   lentes (extract_text OCR ~5s, t2a_decision LLM ~8-13s). Comme le handler
   est async def, l'event loop FastAPI était bloqué : les polls clients
   timeout à 5s, leurs actions étaient popped serveur sans destinataire,
   perdues silencieusement. Mesure : 8 actions/25 perdues sur replay Urgence.

   acquire(timeout=4.5) puis run_in_executor pour libérer l'event loop
   pendant l'attente du lock ET pendant les handlers serveur synchrones.
   Pendant un t2a_decision en cours, les polls concurrents reçoivent
   immédiatement {action: null, server_busy: true} → l'agent ne timeout
   plus, aucune action n'est popped sans destinataire.

5. agent_v0/server_v1/resolve_engine.py — _validate_resolution_quality
   Drift > 0.20 par rapport aux coords enregistrées → fallback aux coords
   enregistrées même quand le template matching trouve l'image avec un
   score quasi parfait. Or un score >= 0.95 signifie que l'image EST
   visuellement à l'écran à l'endroit indiqué, le drift reflète juste
   un changement de layout (scroll, F11, redimensionnement), pas une
   erreur. Exception ajoutée : score >= 0.95 sur template_matching →
   ignore drift check, utilise position visuelle.

6. core/llm/t2a_decision.py — prompt T2A/PMSI
   Ancien prompt autorisait « Critère non validé » en fallback creux.
   Nouveau prompt impose au moins une CITATION LITTÉRALE entre « ... »
   du DPI dans chaque preuve_critereN, qu'elle soutienne ou infirme le
   critère. Si non validé : factualisation explicite (« Aucune ... »,
   « Sortie à H+2 ») citée du dossier. Sortie = preuves cliniques
   traçables et professionnelles, pas du remplissage.

État DB : aucun changement net (bbox patchés puis revertés depuis backup
visual_anchors_backup_20260501 ; by_text re-aligné sur 25003284). Le
re-enregistrement du workflow Urgence en conditions bureau standard
(Chrome normal, taille fenêtre standard) est l'étape suivante côté Dom.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:32:57 +02:00
Dom
b584bbabc3 fix(stream): robustesse proxy VWB→streaming + ciblage textuel pour démo UHCD
dag_execute.py /execute-windows :
- Bearer token sur appels VWB→streaming (machines, replay/raw).
  Sans cela : 401 Unauthorized et le workflow ne démarre pas.
- Auto-injection session_id='agent_demo_user' si absent.
  Sans cela : /replay/raw bascule sur l'auto-détection sess_* et lève
  "Aucune session Agent V1 active" après tout restart du streaming server.
- Propagation by_text dans target_spec pour ciblage textuel
  (résolution hybrid_text_direct côté executor) — utile quand
  deux numéros se ressemblent visuellement (ex 25003284 vs 2500341).

t2a_decision.py : prompt enrichi avec decision_court (UHCD / Forfait
Urgences) + 3 critères PMSI (preuve_critereN + critereN_valide booléen)
pour piloter case-à-cocher dans l'arbre décisionnel. num_predict=1500,
num_ctx=16384.

resolve_engine.py : un drift trop grand bascule sur les coords
enregistrées (fallback_recorded_coords, resolved=True) au lieu de
rejeter la résolution. Permet au replay de continuer en cas de scroll
plutôt que de s'arrêter net.

workflows.db : by_text='25003284' sur le step de sélection patient
du workflow Urgence (démo GHT Sud 95).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:52:22 +02:00
Dom
964856ab30 feat(workflow): variables runtime + extract_text serveur + t2a_decision LLM
Pipeline streaming étendu pour supporter des actions exécutées entièrement
côté serveur (jamais transmises à l'Agent V1) qui produisent des variables
réutilisables dans les steps suivants via templating {{var}} ou {{var.field}}.

== Variables d'exécution ==
- replay_state["variables"] : Dict[str, Any] initialisé vide à la création
- _resolve_runtime_vars() : résout {{var}} et {{var.field}} récursivement
  dans str/dict/list. Variables absentes laissées intactes.
- /replay/next applique la résolution sur l'action AVANT toute interception
  ou envoi à l'Agent V1.

== Boucle d'exécution serveur ==
- _SERVER_SIDE_ACTION_TYPES = {"extract_text", "t2a_decision"}
- /replay/next pop+execute en boucle ces actions jusqu'à trouver une action
  visuelle (à transmettre Agent V1) ou un pause_for_human (qui bloque).
- Latence acceptable : t2a_decision = 5-10s côté serveur, l'Agent V1 attend
  la réponse HTTP.

== Action extract_text ==
- Handler côté serveur réutilisant le dernier heartbeat (max 5s d'âge)
- core/llm/ocr_extractor.py : EasyOCR fr+en singleton + extract_text_from_image
- Stockage dans replay_state["variables"][output_var]
- Robuste : pas de heartbeat → variable = "" + log warning, pipeline continue

== Action t2a_decision ==
- core/llm/t2a_decision.py : refactor de demo_app.py query_model en module
  importable. Prompt expert DIM T2A/PMSI, qwen2.5:7b par défaut (100% bench).
- Handler côté serveur appelle analyze_dpi(input_template_resolved)
- Stockage du JSON décision dans replay_state["variables"][output_var]
- Erreurs (Ollama down, parse) → variable = INDETERMINE + _error, pipeline continue

== VWB UI ==
- types.ts : nouveau type 't2a_decision' (icône 🧠 catégorie logic)
- extract_text refondu : needsAnchor=false, paramètre output_var (au lieu de
  variable_name legacy — bridge accepte les deux pour compat)
- Bridge VWB→core : passthrough des deux types + paramètres préservés

== Tests ==
- tests/integration/test_t2a_extract.py : 25 tests verts
  - templating runtime (8 tests)
  - handler extract_text (3 tests, OCR mocké)
  - handler t2a_decision (3 tests, analyze_dpi mocké)
  - edge → action normalisée (2 tests)
  - bridge VWB → core (5 tests)
  - workflow chain extract→t2a→pause→clic (1 test)

Total branche : 82/82 verts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:47:31 +02:00