From 751922d032a7b54df8c2b1b84c39fef9223be2cd Mon Sep 17 00:00:00 2001 From: dom Date: Mon, 23 Feb 2026 21:31:57 +0100 Subject: [PATCH] fix: align model name to gemma3:27b-cloud + add architecture diagram & deployment zip setup.sh and README.md referenced gemma3:27b-it-qat while config.py uses gemma3:27b-cloud. Added architecture.html (Mermaid pipeline diagram) and t2a-extractor.zip for collaborator deployment. Co-Authored-By: Claude Opus 4.6 --- README.md | 2 +- architecture.html | 115 ++++++++++++++++++++++++++++++++++++++++++++++ setup.sh | 6 +-- t2a-extractor.zip | Bin 0 -> 24258 bytes 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 architecture.html create mode 100644 t2a-extractor.zip diff --git a/README.md b/README.md index 9ade294..2e4fb4d 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ python main.py rapport_ucr.pdf -o /chemin/sortie --csv -v ## Prérequis - Python 3.12+ -- Ollama avec un VLM (gemma3:27b-it-qat par défaut) +- Ollama avec un VLM (gemma3:27b-cloud par défaut) - GPU recommandé pour docTR (fonctionne aussi en CPU) ## Configuration diff --git a/architecture.html b/architecture.html new file mode 100644 index 0000000..d4f0a1a --- /dev/null +++ b/architecture.html @@ -0,0 +1,115 @@ + + + + + T2A Extractor — Architecture + + + +

T2A Extractor

+

Pipeline d'extraction structurée de rapports UCR

+
+flowchart TD + subgraph INPUT["📄 Entrée"] + PDF["PDF UCR<br/>natif + scanné"] + end + + subgraph ETAPE1["📖 Étape 1 — Extraction texte"] + DETECT{"Page native<br/>ou scannée ?"} + PYMUPDF["<b>PyMuPDF</b><br/>texte natif"] + DOCTR["<b>docTR + Torch</b><br/>OCR"] + MERGE(["Texte brut complet"]) + end + + subgraph ETAPE2["✂️ Étape 2 — Segmentation"] + REGEX["Regex<br/>par Champ + OGC"] + OGC_BLOCKS["Blocs OGC<br/>individuels / groupés"] + CHAMP_BLOCKS["Blocs Champ<br/>décisions globales"] + end + + subgraph ETAPE3["🤖 Étape 3 — Extraction structurée"] + OLLAMA["<b>Ollama</b><br/>gemma3:27b-cloud"] + JSON["JSON structuré<br/>11 champs par OGC"] + end + + subgraph ETAPE35["🔧 Étape 3.5 — Normalisation"] + CIM10["Correction codes CIM-10<br/>&bull; OCR chiffre ↔ lettre<br/>&bull; Point manquant / mal placé<br/>&bull; Décimales excédentaires"] + RETENUS["Auto-remplissage<br/>codes_retenus"] + TEXTE["Fallback regex<br/>texte_decision"] + end + + subgraph ETAPE4["✅ Étape 4 — Validation"] + VALID["Vérification formats<br/>CIM-10 / CCAM"] + SAFETY["Safety-net<br/>2e passe normalizer"] + COHERENCE["Cohérence<br/>décision ↔ codes"] + end + + subgraph ETAPE5["📊 Étape 5 — Export"] + EXCEL["<b>Excel</b> .xlsx<br/>coloration décisions"] + CSV["<b>CSV</b><br/>optionnel"] + end + + PDF --> DETECT + DETECT -->|"≥ 50 chars"| PYMUPDF + DETECT -->|"< 50 chars"| DOCTR + PYMUPDF --> MERGE + DOCTR --> MERGE + + MERGE --> REGEX + REGEX --> OGC_BLOCKS + REGEX --> CHAMP_BLOCKS + + OGC_BLOCKS --> OLLAMA + CHAMP_BLOCKS --> OLLAMA + OLLAMA --> JSON + + JSON --> CIM10 + CIM10 --> RETENUS + RETENUS --> TEXTE + + TEXTE --> VALID + VALID --> SAFETY + SAFETY --> COHERENCE + + COHERENCE --> EXCEL + COHERENCE --> CSV + + style INPUT fill:#e8f4fd,stroke:#2196F3,stroke-width:2px,color:#000 + style ETAPE1 fill:#fff3e0,stroke:#FF9800,stroke-width:2px,color:#000 + style ETAPE2 fill:#f3e5f5,stroke:#9C27B0,stroke-width:2px,color:#000 + style ETAPE3 fill:#e8f5e9,stroke:#4CAF50,stroke-width:2px,color:#000 + style ETAPE35 fill:#fce4ec,stroke:#E91E63,stroke-width:2px,color:#000 + style ETAPE4 fill:#fff8e1,stroke:#FFC107,stroke-width:2px,color:#000 + style ETAPE5 fill:#e0f2f1,stroke:#009688,stroke-width:2px,color:#000 +
+ + + + diff --git a/setup.sh b/setup.sh index 6c6eb31..2cb35a9 100755 --- a/setup.sh +++ b/setup.sh @@ -46,10 +46,10 @@ echo "[4/5] Vérification d'Ollama..." if ! command -v ollama &>/dev/null; then echo " ⚠ Ollama non installé." echo " → Installer avec : curl -fsSL https://ollama.com/install.sh | sh" - echo " → Puis : ollama pull gemma3:27b-it-qat" + echo " → Puis : ollama pull gemma3:27b-cloud" else echo " ✓ Ollama installé : $(ollama --version 2>&1 || echo 'version inconnue')" - echo " → Assurez-vous que le modèle est chargé : ollama pull gemma3:27b-it-qat" + echo " → Assurez-vous que le modèle est chargé : ollama pull gemma3:27b-cloud" fi # --- 5. Créer le dossier output --- @@ -73,6 +73,6 @@ echo " python main.py --csv --verbose" echo "" echo " Avant la première utilisation :" echo " 1. Démarrer Ollama : ollama serve" -echo " 2. Charger le modèle : ollama pull gemma3:27b-it-qat" +echo " 2. Charger le modèle : ollama pull gemma3:27b-cloud" echo " 3. Adapter config.py si nécessaire (OLLAMA_MODEL)" echo "" diff --git a/t2a-extractor.zip b/t2a-extractor.zip new file mode 100644 index 0000000000000000000000000000000000000000..c2025349e048609d9452908a7daeefc87c1b7105 GIT binary patch literal 24258 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