fix(anonymizer): handle FC14 practitioner OGC rules
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
---
|
||||
from: claude
|
||||
to: dom
|
||||
date: 2026-06-08T00:00:00+02:00
|
||||
topic: blocage-rebuild-c40441d
|
||||
status: open
|
||||
priority: blocker
|
||||
references:
|
||||
- mission: docs/coordination/inbox/for-claude/2026-06-08_11-12_dom-via-codex_rebuild-hotfix-perf-c40441d.md
|
||||
- commit: c40441d
|
||||
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
|
||||
---
|
||||
|
||||
# Blocage rebuild Windows c40441d — commit non poussé sur Gitea
|
||||
|
||||
## Diagnostic (vérifié, pas déduit)
|
||||
|
||||
| Élément | État |
|
||||
|---|---|
|
||||
| `c40441d` en local Linux | ✅ présent, = HEAD de `feature/q1-quarantine-mvp` |
|
||||
| `c40441d` sur un remote | ❌ **sur aucun remote** (`git branch -r --contains c40441d` vide) |
|
||||
| `gitea/feature/q1-quarantine-mvp` | à `15f73f8` (base bêta), **7 commits derrière** le local |
|
||||
| Relation local↔distant | **fast-forward** (non destructif) — le distant est ancêtre direct |
|
||||
| HEAD machine Windows (192.168.1.11) | `15f73f8`, branche `feature/q1-quarantine-mvp`, working tree propre |
|
||||
| `c40441d` côté Windows | ❌ `fatal: Not a valid object name` |
|
||||
| Remote Gitea côté Windows | `http://192.168.1.40:3100/Dom/anonymisation.git` (accessible) |
|
||||
|
||||
**Conclusion** : impossible de builder `c40441d` côté Windows tant qu'il n'est pas
|
||||
poussé sur Gitea. Conformément à la mission (« ne pas faire de push destructif »,
|
||||
contrainte « pas de push sans demande explicite »), **je n'ai pas poussé**. Je
|
||||
signale et propose la commande.
|
||||
|
||||
## Résolution proposée (2 étapes, GO Dom requis pour l'étape 1)
|
||||
|
||||
**Étape 1 — push Linux → Gitea** (fast-forward, non destructif) :
|
||||
```bash
|
||||
cd /home/dom/ai/anonymisation
|
||||
git push gitea feature/q1-quarantine-mvp
|
||||
```
|
||||
Pousse 7 commits (6 docs coordination + le hotfix perf `c40441d`). Aucune
|
||||
réécriture d'historique, aucun force.
|
||||
|
||||
**Étape 2 — récupération côté Windows** (je peux la lancer dès l'étape 1 faite) :
|
||||
```cmd
|
||||
cd C:\Users\dom\ai\anonymisation
|
||||
git fetch gitea && git merge --ff-only gitea/feature/q1-quarantine-mvp
|
||||
git cat-file -t c40441d :: doit retourner "commit"
|
||||
```
|
||||
|
||||
## Prêt à enchaîner dès le commit disponible côté Windows
|
||||
|
||||
Au GO, je lance le rebuild EXE interne (via `schtasks` + log, builds longs — le MCP
|
||||
SSH tue les process au retour de commande), **build interne de mesure uniquement,
|
||||
aucun upload / diffusion / package public** (D-16). Puis je relève temps total,
|
||||
CPU moyen/pic, RAM pic, lignes `torch threads config`, lignes `PERF`, mode raster
|
||||
(`threads`/`sequential`/`processes`), présence/absence de fallback raster — et je
|
||||
teste ton PDF lent si tu me le rends accessible (sinon je prépare le build candidat
|
||||
et te le réclame).
|
||||
|
||||
## Décision attendue de Dom
|
||||
|
||||
1. **GO pour `git push gitea feature/q1-quarantine-mvp`** (par toi/Codex, ou tu
|
||||
m'autorises explicitement à le faire) ?
|
||||
2. Le **PDF lent** de ton test Windows : chemin sur 192.168.1.11, ou tu me le
|
||||
déposes quelque part d'accessible ?
|
||||
|
||||
— Claude
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
from: qwen
|
||||
to: dom
|
||||
date: 2026-06-08T11:55:00+02:00
|
||||
topic: validation-independante-c40441d
|
||||
status: open
|
||||
priority: high
|
||||
references:
|
||||
- commit: c40441d
|
||||
- mission: docs/coordination/inbox/for-qwen/2026-06-08_11-12_dom-via-codex_validation-c40441d-perf-fp.md
|
||||
---
|
||||
|
||||
# Validation independante c40441d
|
||||
|
||||
## 1. Resume du commit
|
||||
|
||||
`c40441d fix(perf): apply MVP threading hotfix` — 120 lignes modifiees dans
|
||||
`anonymizer_core_refactored_onnx.py`, 1 ligne dans `config/dictionnaires.default.yml`,
|
||||
3 lignes dans `config/profiles.yml`, 20 fichiers archives (coordination).
|
||||
|
||||
### Modifications code
|
||||
|
||||
| Zone | Changement | Lignes |
|
||||
|---|---|---|
|
||||
| Env vars multi-cœur (H1) | `os.environ.setdefault` pour OMP/MKL/OPENBLAS/NUMEXPR/VECLIB AVANT imports lourds | 10 |
|
||||
| `_configure_torch_threads()` | Idempotent, `set_num_threads(n_cpus)` + `set_num_interop_threads(min(n,8))` avec guard | 27 |
|
||||
| `_get_doctr_model()` | Cache + appel `_configure_torch_threads()` avant chargement | 6 |
|
||||
| `_run_ner_on_original_text()` | Appel `_configure_torch_threads()` (PDF natif sans OCR) | 5 |
|
||||
| `redact_pdf_raster()` | ThreadPoolExecutor en frozen + fallback + log PERF + env `ANON_DISABLE_RASTER_THREADS` | ~40 |
|
||||
| `dictionnaires.default.yml` | CONCERTATION retire des force_terms | 1 |
|
||||
| `profiles.yml` | Force_terms redondants retires | 3 |
|
||||
|
||||
## 2. Validation des risques techniques
|
||||
|
||||
### 2.1 Env vars posees avant imports lourds
|
||||
|
||||
**✅ VERIFIE** — `os.environ.setdefault` aux lignes 32-34, premier import lourd
|
||||
(pdfplumber) ligne 45. PIL ligne 48, fitz ligne 51, numpy ligne 1299.
|
||||
|
||||
L'ordre est correct : les variables sont lues par numpy/torch/onnxruntime a
|
||||
leur initialisation, donc elles doivent etre posees avant tout import transif.
|
||||
C'est le cas.
|
||||
|
||||
### 2.2 Idempotence `torch.set_num_interop_threads`
|
||||
|
||||
**✅ VERIFIE** — Le flag global `_TORCH_THREADS_CONFIGURED` empeche un 2e appel.
|
||||
`set_num_interop_threads` est dans un `try/except` interne avec `pass` si
|
||||
l'API refuse (deja figuree par un travail torch anterieur).
|
||||
|
||||
Test empirique : 3 appels successifs sans exception, log unique
|
||||
`torch threads config: intra=32 inter=8 (CPUs=32)`.
|
||||
|
||||
### 2.3 Appel H1 pour OCR et PDF natif/NER
|
||||
|
||||
**✅ VERIFIE** — Deux points d'appel :
|
||||
- `_get_doctr_model()` → chemin OCR (PDF scanne, texte extrait via docTR)
|
||||
- `_run_ner_on_original_text()` → chemin PDF natif (texte riche, OCR saute)
|
||||
|
||||
Les deux chemins couvrent les deux modes de traitement. L'appel dans
|
||||
`_run_ner_on_original_text()` est un complement de Claude par rapport a ma
|
||||
version initiale (qui ne couvrait que l'OCR). C'est necessaire car le NER
|
||||
torch (EDS-Pseudo, GLiNER) tourne sur le texte original et serait mono-thread
|
||||
sans cet appel.
|
||||
|
||||
### 2.4 Rollback `ANON_DISABLE_RASTER_THREADS=1`
|
||||
|
||||
**✅ VERIFIE** — Le code lit :
|
||||
```python
|
||||
disable_threads = os.getenv("ANON_DISABLE_RASTER_THREADS", "").lower() in {"1", "true", "yes", "on"}
|
||||
```
|
||||
5 valeurs reconnues. En mode frozen avec cette variable, le raster revient
|
||||
en sequentiel. Le log indique `reason=env_disabled`.
|
||||
|
||||
### 2.5 Risque thread-safety PyMuPDF/Pillow en frozen
|
||||
|
||||
**⚠️ ATTENTION — risque identifie**
|
||||
|
||||
`ThreadPoolExecutor` partage le meme processus. PyMuPDF (`fitz`) et Pillow
|
||||
sont-ils thread-safe ?
|
||||
|
||||
- **PyMuPDF** : la doc officielle dit que chaque objet `fitz.Document` et
|
||||
`fitz.Page` doit etre utilise dans un seul thread. Le code raster utilise
|
||||
un `fitz.open()` **par thread** (dans `_rasterize_page`), donc pas de
|
||||
partage d'objet entre threads. ✅ OK.
|
||||
- **Pillow** : `Image.frombytes` et `Image.save` sont thread-safe pour des
|
||||
operations independantes sur des objets separes. ✅ OK.
|
||||
- **GIL Python** : les operations lourdes (rasterisation PyMuPDF, encodage
|
||||
PNG Pillow) liberent le GIL car ce sont des extensions C. Le
|
||||
multi-threading apporte donc un vrai gain parallele. ✅ OK.
|
||||
|
||||
**Conclusion** : le risque est mitigé par l'isolation des objets `fitz` par
|
||||
thread. Aucun conflit identifie.
|
||||
|
||||
### 2.6 Absence de changement de detection PII
|
||||
|
||||
**✅ VERIFIE** — Le diff ne modifie aucune logique de detection. Seuls les
|
||||
points suivants changent :
|
||||
- CONCERTATION retire de `dictionnaires.default.yml` (force_terms)
|
||||
- Force-terms redondants retires de `profiles.yml`
|
||||
- Commentaire mis a jour dans `_kv_value_only_mask` (`CHUXX, sigle local...`)
|
||||
|
||||
Aucune modification des regex, NER, gazetteers, ou logique de propagation.
|
||||
|
||||
## 3. Tests unitaires
|
||||
|
||||
**98 passed, 0 failed** avec `.venv/bin/python -m pytest tests/unit -q`.
|
||||
|
||||
Le test 009 (Biarritz, pyahocorasick) passe dans le venv car la dependance
|
||||
est installee. Mon test precedent avec `python3` systeme (97 passed) etait
|
||||
un artefact d'environnement, pas une regression.
|
||||
|
||||
## 4. Mini-corpus pdf_natif (6 PDF natifs)
|
||||
|
||||
| Fichier | PII hits | Force terms | CONCERTATION |
|
||||
|---|---|---|---|
|
||||
| FC14.pdf | 45 | 0 | ✅ absent |
|
||||
| FC16.pdf | 45 | 0 | ✅ absent |
|
||||
| FC17.pdf | 45 | 0 | ✅ absent |
|
||||
| FC19.pdf | 45 | 0 | ✅ absent |
|
||||
| FC21.pdf | 45 | 0 | ✅ absent |
|
||||
| FC8.pdf | 44 | 0 | ✅ absent |
|
||||
|
||||
**Evaluateur qualite** :
|
||||
|
||||
```
|
||||
SCORE GLOBAL : 100.0/100 [A+]
|
||||
Leak score : 100.0/100
|
||||
FP score : 100/100
|
||||
|
||||
Fuites noms audit : 0
|
||||
Fuites regex (PII) : 0
|
||||
Noms INSEE (contexte fort) : 0
|
||||
Termes médicaux masqués : 0
|
||||
Alertes sur-masquage : 0
|
||||
```
|
||||
|
||||
**CONCERTATION** : ✅ Aucun force-term genere sur les 6 PDF. Le retrait du
|
||||
dictionnaire est valide.
|
||||
|
||||
## 5. Matrice de validation Windows
|
||||
|
||||
### Scenarios de test
|
||||
|
||||
| # | Scenario | Fichier attendu | Mesures |
|
||||
|---|---|---|---|
|
||||
| 1 | PDF natif court (<5 pages) | FC8.pdf ou equivalent | Temps <5s, CPU >30%, RAM <4 Go |
|
||||
| 2 | PDF natif moyen (10-30 pages) | FC14.pdf (4 pages) ou plus long | Temps proportionnel, CPU >30% |
|
||||
| 3 | PDF scanne court (<5 pages) | PDF scanne disponible | Temps <30s, CPU >30%, mode raster=threads |
|
||||
| 4 | PDF reel lent Dom | Fourni par Dom | Temps avant/apres, CPU, RAM, mode raster |
|
||||
|
||||
### Mesures attendues dans `anonymisation.log`
|
||||
|
||||
| Ligne log | Valeur attendue (avant) | Valeur attendue (apres) |
|
||||
|---|---|---|
|
||||
| `torch threads config: intra=N inter=M (CPUs=X)` | Absente | `intra=8 inter=8 (CPUs=8)` (machine Dom) |
|
||||
| `Raster PDF: mode=threads pages=N workers=W dpi=D frozen=1` | Absente (sequential) | Present si PDF >2 pages |
|
||||
| `PERF ... stage=...` | Present | Present (unchanged) |
|
||||
| `Raster PDF: mode=sequential ... reason=env_disabled` | N/A | Si `ANON_DISABLE_RASTER_THREADS=1` |
|
||||
| CPU processus | ~12% | >40% |
|
||||
| RAM pic | 16 Go | Similaire ou legerement superieur |
|
||||
|
||||
### Criteres GO/NO-GO
|
||||
|
||||
| Critere | GO | NO-GO |
|
||||
|---|---|---|
|
||||
| Leak score | 100/100 | <100 |
|
||||
| FP score | 100/100 | <95 |
|
||||
| Temps total PDF lent | <50% du temps precedent | >= temps precedent |
|
||||
| CPU moyen | >30% | <20% |
|
||||
| Crash/erreur | Aucun | 1+ |
|
||||
| RAM pic | <20 Go | >24 Go |
|
||||
|
||||
## 6. Avis
|
||||
|
||||
**GO conditionnel** pour rebuild Windows, sous reserve que :
|
||||
|
||||
1. Le push `c40441d` vers Gitea soit fait (bloquant pour Windows)
|
||||
2. Le test PDF reel de Dom confirme le gain CPU/temps
|
||||
3. Aucun crash thread-safety ne remonte
|
||||
|
||||
**NO-GO** si le PDF reel montre :
|
||||
- Une regression leak (force_term retire trop tot pour un cas non teste)
|
||||
- Un crash PyMuPDF en mode threads (rare mais possible avec certains PDF)
|
||||
- Un gain CPU negligible (<10%) malgre la config threads
|
||||
Reference in New Issue
Block a user