1. Batch QC : la validation justifications (gemma3:12b) est différée et
exécutée en une seule passe après tous les documents du groupe.
Réduit les swaps modèle de 2 par document à 2 pour le groupe entier.
2. Pipeline CPU/GPU : prepare_document (extraction, anonymisation, edsnlp)
tourne sur CPU pendant que le GPU traite le document précédent via
un ThreadPoolExecutor(1) en prefetch.
3. QC sur Dell : si la machine secondaire (192.168.1.11) est disponible,
les appels QC sont envoyés là-bas en parallèle du codage principal,
éliminant tout swap modèle sur le GPU principal.
Refactoring associé :
- _postprocess_dossier() extrait la logique vetos/décisions/GHM/finalizer
- call_ollama() accepte ollama_url pour cibler un serveur spécifique
- _is_secondary_available() avec cache 60s pour éviter le polling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Les agents d'optimisation ont splitté _enrich_with_rag en _enrich_dp_only
et _enrich_das_and_actes mais n'ont pas mis à jour les mocks dans test_rag.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Le défaut de 2 était sous-optimal pour la RTX 5070 (12 Go VRAM).
Ollama gère la concurrence interne et queue les requêtes
excédentaires. Un pool de 4 workers Python permet de mieux
saturer le GPU sur les appels DAS/actes parallèles.
Le .env peut toujours override cette valeur via OLLAMA_MAX_PARALLEL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure le pipeline dans extract_medical_info() :
AVANT : DAS_LLM séquentiel → ThreadPool(RAG complet + DP_selector)
APRÈS :
Groupe 1 (ThreadPool max_workers=3) :
- DAS_LLM : extraction DAS supplémentaires
- RAG DP : enrichissement DP seul (via enrich_dp)
- DP selector : sélection NUKE-3
Groupe 2 :
- enrichissement DAS + actes (via enrich_das_and_actes)
Le RAG DP ne dépend pas du DAS_LLM, donc les deux peuvent
s'exécuter en parallèle. Le Groupe 2 attend le DAS_LLM car
il enrichit les DAS trouvés par celui-ci.
Ajoute aussi des timings sur les groupes et la validation QC.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ajoute des mesures time.monotonic() autour de chaque appel LLM dans
rag_search.py : enrich_diagnostic, enrich_acte, extract_das_llm.
Format de log : logger.info("⏱ [RAG-DP] 14.2s — texte")
Découpe enrich_dossier() en 2 fonctions exportées :
- enrich_dp() : enrichit seulement le DP (parallélisable)
- enrich_das_and_actes() : enrichit DAS + actes en parallèle
L'ancienne enrich_dossier() reste comme wrapper rétro-compatible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure le flux CPAM pour regrouper les appels LLM par modèle :
- Phase 1 (cpam model) : extraction + argumentation de TOUS les contrôles
- Phase 2 (validation model, 1 swap) : validation adversariale de tous
- Phase 3 (cpam model, si correction nécessaire) : correction des échoués
Nouvelle fonction generate_cpam_responses_batched() qui orchestre les 3 phases.
L'ancienne generate_cpam_response() reste intacte (rétrocompatible, utilisée
par le viewer pour la régénération unitaire).
Structure intermédiaire _CpamDraft (dataclass) pour transporter l'état entre phases.
Fonctions de phase extraites : _phase_generate, _phase_validate, _phase_correct,
_phase_finalize.
Gain estimé : de ~4*N swaps de modèle à 2-4 swaps (indépendant de N contrôles).
Sur RTX 5070 avec des modèles 24-32b, chaque swap coûte ~10-15s.
Pour 3 contrôles : économie estimée ~60-90s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ajoute des mesures time.time() autour de chaque appel Ollama dans le flux CPAM :
- [CPAM-EXTRACT] : extraction structurée (passe 1, role=cpam)
- [CPAM-GEN] : génération argumentation (passe 2, role=cpam)
- [CPAM-VALID] : validation adversariale (role=validation)
- [CPAM-CORR] : correction post-validation (role=cpam)
Permet de mesurer le temps réel de chaque phase et d'identifier
les coûts de swap de modèle VRAM entre les rôles cpam/validation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Les tests supposaient que le CRH gagnait à spécificité égale — en réalité
le bonus Trackare l'emporte (codage DIM établissement prioritaire).
Aussi : dp_selection synthétique créée par fusion quand aucun source n'existe.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Modifications en cours : pipeline médical (cim10_extractor, dp_finalizer,
dp_selector, fusion, rag_search), viewer (helpers, detail.html),
cache ollama et référentiels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ajoute GET /health retournant un JSON avec :
- status: "ok"
- version: "2.1.0"
- ollama: true/false (connectivité testée avec timeout 2s)
- timestamp: ISO 8601 UTC
Tests couvrent : format JSON, champs requis, Ollama joignable/injoignable,
format timestamp ISO, type booléen du champ ollama.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Couvre les cas non couverts dans test_dp_finalizer.py :
- R6 Z-code non whitelisté remplacé par DAS
- Fonctions internes (_family3, _code_in_candidates, _has_strong_evidence)
- _apply_r5 en appel direct (Z whitelisté, R-code avec candidat non-R)
- Détection source Trackare par document_type/reason/evidence
- Cas limites (code None, candidats vides, pas de dp_selection)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Couvre les règles non couvertes dans le fichier existant :
- Fonctions internes (_norm, _first_float, _parse_normal_range, etc.)
- Nettoyage hiérarchique (.9 vs code spécifique)
- D50 sans preuve martiale → downgrade D64.9
- D69.6 thrombopénie vs plaquettes normales → RULED_OUT
- decision_summaries pour toutes les actions (DOWNGRADE, REMOVE, RULED_OUT, NEED_INFO)
- Détection analytes (sodium, potassium)
- _age_band et cas limites de scoring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Couvre les filtres Jinja2 (confidence_badge, severity_badge, cma_level_badge,
decision_badge, human_where, format_doc_name, etc.), les fonctions de
statistiques (compute_group_stats, compute_dashboard_stats, compute_dim_synthesis),
et les utilitaires (_date_to_iso, _sort_qc_alerts, _compute_jours_restants).
Utilise pytest.mark.parametrize pour les cas multiples.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ajoute validate_config() dans src/config.py :
* Vérifie accessibilité Ollama (warning si injoignable)
* Warning si ANTHROPIC_API_KEY absente (fallback cloud indispo)
* Warning si modèles CPAM et validation identiques
* Vérifie qu'au moins un modèle LLM est configuré
- Appel automatique au démarrage du viewer Flask
- Ne fait jamais crasher l'app (warnings uniquement)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Crée src/__version__.py comme source unique de version (2.1.0)
- pyproject.toml utilise dynamic version via setuptools attr
- Affiche la version dans le footer de la sidebar (base.html)
- Ajoute endpoint /health avec version et status
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Le chemin ExecStart pointait vers /home/dom/ai/t2a/.venv/ (ancien projet)
au lieu de /home/dom/ai/t2a_v2/.venv/ (projet actuel).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- unsloth_compiled_cache/ : cache de compilation Unsloth (fine-tuning LLM, non lié)
- sans titre_diarization.rttm : fichier RTTM vide (transcription audio)
- sans titre_diarized.txt : transcription audio 241 Ko (medical_ai_scribe)
- sans titre_summary_v2.md : résumé audio 11 Ko (medical_ai_scribe)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Prompt DAS_EXTRACTION : ajout consignes anti-hallucination (zero invention,
pas d'inférence de comorbidités, exiger citation exacte du texte)
- Prompt CODING_CIM10 : ajout consignes conditionnel et négation
- diagnostic_extraction.py : détection de négation avant les patterns regex DAS
(bloque "pas d'embolie", "absence de sepsis", "sans signe d'IRC", etc.)
- veto_engine.py : VETO-03 conditionnel cherche maintenant PRÈS du concept
(40 chars), "si" isolé ne déclenche plus de faux positif, ajout cues
(possible, risque de, aspect de, à confirmer, à rechercher)
- veto_engine.py : négation enrichie (ne retrouve pas, sans signe/argument,
écarté, infirmé, pas mis en évidence)
Batch analysis: VETO-02 63% from LLM hallucinations, VETO-03 63% false positives
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Script build_coding_dict.py génère le dictionnaire depuis le batch (240 dossiers)
- coding_dictionary.json : co-occurrences DP→DAS, fréquences, associations bio
- anomaly_stats.py : 8 checks (DP/DAS rare, DAS manquant, bio-DAS, âge atypique)
- Intégré dans le pipeline cim10_extractor post-DIM-senior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Nouveau module rules_manager.py : CRUD YAML pour les regles metier
- Nouveau blueprint bp_rules.py + template admin_rules.html :
interface web pour activer/desactiver/ajouter/supprimer des regles
- Extraction helpers.py depuis app.py (filtres Jinja2, statistiques,
scan dossiers, status systeme) — app.py passe de 1585 a 482 lignes
- Suppression backward-compat re-exports dans cim10_extractor et
cpam_response (imports corriges dans les tests)
- README.md : architecture, modules, installation, utilisation
- pyproject.toml : dependencies completes, config ruff, pytest, coverage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Le LLM générait des tags génériques [BIO-N], [TRT-N] au lieu des vrais tags du dossier,
causant des warnings "preuve non traçable". Corrigé en 3 points :
- cpam_context: liste exhaustive des tags disponibles injectée dans le prompt
- templates: remplacement des patterns génériques par {tags_disponibles_str}
- cpam_validation: guardian step 4b résout les tags génériques résiduels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DIAGNOSIS_ALIASES : mapping acronymes cliniques → CIM-10 (DLBCL→C83.3, SCA→I25.1, EP→I26.9, IDM→I21.9, etc.)
- Scoring 4b étendu : conclusion (+2) ajouté aux sections diagnostiques, matching par alias en plus du terme/code
- _collect_evidence : détection alias dans les sections pour preuves plus complètes
- Garde-fou Trackare : si DP est un R-code (symptôme) et que les sections CRH mentionnent un diagnostic étiologique via alias → verdict REVIEW au lieu de CONFIRMED, alerte DIM
- Case 74 : verdict attendu REVIEW (conclusion mentionne les 2 diagnostics, delta insuffisant)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- crh_parser: 3 nouvelles sections (diag_sortie, diag_principal, synthese)
avec garde-fou début de ligne pour éviter faux positifs mid-sentence
- dp_selector: NUKE-3 sélecteur DP déterministe (548 lignes)
- build_candidates/score_candidates/select_dp
- bonus +4 pour mention dans diag_sortie/diag_principal
- bonus +2 pour mention dans synthese
- hardening DIM : A1 evidence, A2 mono-fragile, A3 confidence cap
- _collect_evidence match par terme OU code CIM-10
- LLM tiebreaker optionnel (DP_RANKER_CONSTRAINED)
- fusion: propagation dp_selection depuis le dossier source du DP retenu
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CPAM : badge quality_tier (A/B/C), bandeau requires_review, warnings catégorisés, force probante dossier
- DP/DAS : code suggestion barré → code final si modifié, ligne grisée si ruled_out, badges décision + règles
- DAS : badge needs_info avec détails, raison ruled_out sous la ligne
- VetoReport : section contestabilité avec verdict, barre score/100, tableau issues HARD/MEDIUM/LOW
- Biologie : badge Suspect avec tooltip, valeurs écartées en details pliable
- Nouveau filtre Jinja2 decision_badge, import _assess_dossier_strength (pas de duplication)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Les deux appels tronquaient systématiquement (done_reason=length),
causant des JSON invalides et des faux positifs adversariaux.
num_predict n'a aucun impact sur VRAM ni sur les réponses courtes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Score 0-10 basé sur les preuves objectives (bio/img/trt/actes).
Dossier faible (score < 3) : prompt LLM adapté + seuil adversarial
abaissé (score 2-3 → Tier B au lieu de C). Les éléments contextuels
(âge, IMC, urgence) restent dans le prompt mais hors du scoring car
ils ne constituent pas des preuves opposables à un contrôleur CPAM.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>