Spaces:
Sleeping
Sleeping
File size: 8,103 Bytes
1acc64c d2df0b9 1acc64c d2df0b9 1acc64c d2df0b9 1acc64c d2df0b9 1acc64c d2df0b9 1acc64c d2df0b9 1acc64c d2df0b9 1acc64c 7e28f42 1acc64c d2df0b9 1acc64c 7e28f42 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | # Architecture Picarones — manifeste
Picarones est un **banc d'essai** pour pipelines OCR/HTR sur documents
patrimoniaux. Le code est organisé en **3 cercles concentriques** avec
une règle de dépendance stricte : les flèches d'import vont uniquement
de l'extérieur vers l'intérieur.
```
Cercle 3 (extras, report, cli, web)
│
▼
Cercle 2 (measurements, engines, llm, pipelines, modules)
│
▼
Cercle 1 (core)
```
## Cercle 1 — `picarones/core/` : abstractions de domaine
Pas de logique métier, pas d'I/O. Uniquement des **contrats** que les
cercles supérieurs implémentent.
| Module | Contenu |
|---|---|
| `modules.py` | `BaseModule`, `ArtifactType`, `validate_inputs`/`validate_outputs` |
| `corpus.py` | `Document`, `Corpus`, `GTLevel`, payloads typés (`TextGT`, `AltoGT`, `PageGT`, `EntitiesGT`, `ReadingOrderGT`) |
| `results.py` | `DocumentResult`, `EngineReport`, `BenchmarkResult` |
| `metric_registry.py` | `MetricSpec`, `register_metric`, `select_metrics`, `compute_at_junction` |
| `metric_hooks.py` | `register_document_metric`, `register_corpus_aggregator`, profils de calcul |
| `pipeline.py` | `PipelineRunner`, `PipelineSpec`, `PipelineStep` (DAG de modules) |
| `facts.py` | `Fact`, `FactType`, `FactImportance`, `DetectorRegistry` |
**Règle** : un module du cercle 1 peut importer un autre module du
cercle 1. Il ne peut **rien** importer des cercles 2 ou 3.
## Cercle 2 — implémentations officielles
Les implémentations distribuées par défaut dans le package `picarones`.
### `picarones/measurements/` — métriques (~50 modules)
| Catégorie | Modules |
|---|---|
| Coeur | `metrics.py`, `statistics/` (sous-package), `runner.py`, `builtin_hooks.py`, `builtin_metrics.py`, `normalization.py` |
| Erreurs | `confusion.py`, `taxonomy.py`, `taxonomy_comparison.py`, `taxonomy_cooccurrence.py`, `taxonomy_intra_doc.py` |
| Lignes/structure | `line_metrics.py`, `structure.py`, `worst_lines.py`, `char_scores.py` |
| Calibration/fiabilité | `calibration.py`, `reliability.py`, `hallucination.py` |
| Image | `image_quality.py`, `image_predictive.py`, `difficulty.py` |
| Robustesse | `robustness.py`, `robustness_projection.py` |
| Inter-moteurs | `inter_engine.py`, `specialization.py` |
| Statistique avancée | `baseline_comparison.py`, `longitudinal.py`, `incremental_comparison.py` |
| Contenu | `searchability.py`, `numerical_sequences.py`, `rare_tokens.py`, `readability.py` |
| Structure ALTO | `layout.py`, `reading_order.py`, `ner.py`, `ner_backends.py`, `error_absorption.py` |
| Économie | `cost_projection.py`, `marginal_cost.py`, `pricing.py`, `throughput.py` |
| Philologie historique | `mufi.py`, `abbreviations.py`, `unicode_blocks.py`, `early_modern_typography.py`, `modern_archives.py`, `roman_numerals.py`, `lexical_modernization.py`, `philological_runner.py` |
| Pipelines composées | `pipeline_benchmark.py`, `pipeline_comparison.py`, `pipeline_spec_loader.py`, `alto_metrics.py` |
| Divers | `equivalence_profile.py`, `levers.py`, `module_policy.py`, `history.py` |
| Runners adaptifs | `readability_runner.py`, `searchability_runner.py`, `numerical_sequences_runner.py` |
| Narratif | `narrative/` (arbiter, renderer, registry, 18 détecteurs en 6 familles) |
### `picarones/engines/` — adapters OCR (5)
`tesseract.py`, `pero_ocr.py`, `mistral_ocr.py`, `google_vision.py`,
`azure_doc_intel.py`. Tous héritent de `picarones.core.engine.BaseOCREngine`
(qui vit dans `engines/base.py` pour la lisibilité).
### `picarones/llm/` — adapters LLM (4)
`mistral_adapter.py`, `openai_adapter.py`, `anthropic_adapter.py`,
`ollama_adapter.py`. Interface commune dans `base.py`.
### `picarones/pipelines/` — pipelines OCR+LLM intégrés
`base.py` (`OCRLLMPipeline`, qui hérite de `BaseOCREngine`),
`over_normalization.py`.
### `picarones/modules/` — modules `BaseModule` officiels
Démonstrateurs qui prouvent l'axe B du plan d'évolution :
`alto_text_to_mono_region.py`.
## Cercle 3 — extensions et présentation
### `picarones/extras/importers/` — connecteurs corpus
`iiif.py`, `gallica.py`, `htr_united.py`, `huggingface.py`,
`escriptorium.py`, `_http.py`. Plugins pluggable, certains expérimentaux.
### `picarones/report/` — rendu HTML
| Sous-dossier | Contenu |
|---|---|
| `generator.py` | Orchestration Jinja2 |
| `views/` | 5 vues thématiques (economics, advanced_taxonomy, diagnostics, pipeline, robustness) |
| `templates/` | Jinja2 (base, header, footer, vues, partials) |
| `i18n/` | FR/EN |
| `glossary/` | 25 entrées bilingues |
| `vendor/` | Chart.js |
| `*_render.py` | ~22 renderers (calibration, NER, Pareto, Sankey, etc.) |
Pas de sous-dossier `extras/render/` — tout le rendu est ici.
### `picarones/cli/` — Click (7 fichiers)
Point d'entrée `picarones.cli:cli` (référencé dans `pyproject.toml`).
15 sous-commandes : `run`, `diagnose`, `economics`, `edition`,
`compare`, `metrics`, `engines`, `info`, `report`, `demo`, `serve`,
`history`, `robustness`, `pipeline run/compare`, `import`.
### `picarones/web/` — FastAPI
Interface web (`app.py`).
## Données
| Dossier | Rôle |
|---|---|
| `picarones/prompts/` | Prompts LLM versionnés (8 fichiers, FR + EN) |
| `picarones/data/` | Tables indicatives (pricing, etc.) |
| `picarones/fixtures.py` | Corpus de démonstration |
## Règles de migration
1. **Pas de shim** : un module a un seul emplacement physique. Les
imports pointent directement vers la vraie source.
2. **Pas de double API** : une fonction a un seul nom canonique. Les
alias historiques sont supprimés et les tests mis à jour.
3. **Frontières strictes** : si un module Y du cercle N importe le
module X, alors le cercle de X est ≤ N. Une exception
pragmatique : `engines/base.py` est conceptuellement cercle 1
mais physiquement dans `engines/` pour rester avec ses
implémentations.
4. **Les dépendances optionnelles** (`scipy`, `spacy`, etc.) sont
gérées par try/except à l'import — pas par shim.
## Tests
Organisés par cercle : `tests/core/`, `tests/measurements/`,
`tests/engines/`, `tests/extras/`, `tests/report/`,
`tests/integration/` (tests E2E croisant plusieurs cercles).
Un test du cercle N **n'importe pas** les implémentations des
cercles > N (sauf `tests/integration/`).
## Convention de découpage des modules > 400 lignes
Quand un module Python dépasse 400 lignes ET contient plusieurs
responsabilités disjointes, le découper en **sous-package** plutôt
qu'en plusieurs modules à plat. Modèle de référence :
[`picarones/measurements/statistics/`](../picarones/measurements/statistics/)
issu du sprint « découpage de statistics.py » (mai 2026).
Convention :
1. **Renommer** `X.py` en `X/__init__.py` via `git mv` (préserve
l'historique du fichier original).
2. **Créer** dans `X/` un sous-module par famille fonctionnelle
(`bootstrap.py`, `wilcoxon.py`, `friedman_nemenyi.py`, etc.).
Chaque sous-module doit faire moins de ~400 lignes ; sinon
re-décomposer.
3. **`X/__init__.py`** ne contient QUE des ré-exports rétrocompat —
tous les symboles publics de l'ancien `X.py` doivent rester
importables via `from picarones.X import …`. Les symboles privés
ré-exportés doivent être ceux **réellement** consommés par les
tests (vérifié par grep, pas par supposition).
4. **`__all__`** explicite dans chaque sous-module et dans le
`__init__.py`.
5. **Tests architecture** (`tests/architecture/test_*.py`) doivent
continuer à passer : si nécessaire, étendre `_measurements_modules()`
ou `_imports_target_*` pour reconnaître les sous-packages.
6. **Préfixer les modules de rendu** par leur domaine
(`cdd_render.py` plutôt que `render_cdd.py`) pour cohérence avec
`picarones/report/*_render.py`.
**Quand NE PAS découper** : si les responsabilités sont fortement
couplées (ex: un orchestrateur qui appelle 12 sous-fonctions au
même endroit), le maintien dans un seul fichier > 400 lignes est
acceptable. Le budget par fichier (`tests/architecture/test_file_budgets.py`)
documente ces dérogations conscientes.
|