Claude commited on
Commit
1ef330c
·
unverified ·
1 Parent(s): 478e60e

feat(services): Phase B3-final commit 5 — supprimer le shim legacy_runner_compat

Browse files

Phase B3-final commit 5/7. Vérification que plus aucun caller ne
dépend du shim, puis suppression.

picarones/app/services/legacy_runner_compat.py — SUPPRIMÉ
- 257 LOC nets supprimées.
- Plus aucun import résiduel dans picarones/ ni tests/ — vérifié
par grep avant suppression :
grep -rn 'from picarones.app.services.legacy_runner_compat'
picarones/ tests/ → 0 résultat.

picarones/app/services/benchmark_runner.py
- Mise à jour de la docstring deprecated : pointe vers le pattern
3 étapes (prepare_preset_args + execute_preset + converter) au
lieu du shim disparu.

picarones/interfaces/web/benchmark_utils.py
- Commentaire ajusté (« plus de shim » → « pattern explicite »).

tests/_migration_helpers.py
- Docstring mise à jour : le helper test n'est plus présenté
comme un re-export du shim mais comme un outil de test
autonome. Aucun changement de comportement runtime.

État : aucun shim run_via_orchestrator ne subsiste en
production. Le helper de test du même nom reste dans tests/ —
préfixe _ du module + dossier tests/ rendent clair que c'est
un outil de test, pas de la dette production.

picarones/app/services/benchmark_runner.py CHANGED
@@ -8,9 +8,11 @@
8
  - La fonction ``run_benchmark_via_service`` émet une
9
  ``DeprecationWarning`` à chaque appel.
10
  - Aucun call site actif ne subsiste dans ``picarones/`` —
11
- CLI/Web passent désormais par
12
- ``picarones.app.services.legacy_runner_compat.run_via_orchestrator``.
13
- - Retrait du module prévu **Phase B8** (release suivante).
 
 
14
 
15
  Pour migrer votre code, voir le guide
16
  ``docs/migration/option_b_user_guide.md``.
 
8
  - La fonction ``run_benchmark_via_service`` émet une
9
  ``DeprecationWarning`` à chaque appel.
10
  - Aucun call site actif ne subsiste dans ``picarones/`` —
11
+ CLI/Web utilisent désormais directement le pattern 3 étapes
12
+ ``prepare_preset_args → execute_preset →
13
+ run_result_to_benchmark_result`` (cf.
14
+ :mod:`picarones.app.services.python_helpers`).
15
+ - Retrait du module prévu **Phase B3-final commit 6** (suivant).
16
 
17
  Pour migrer votre code, voir le guide
18
  ``docs/migration/option_b_user_guide.md``.
picarones/app/services/legacy_runner_compat.py DELETED
@@ -1,257 +0,0 @@
1
- """Shim de compatibilité ``run_benchmark_via_service`` → ``RunOrchestrator``.
2
-
3
- Phase B3 (résiduel) du chantier Option B (mai 2026). Fournit
4
- ``run_via_orchestrator()`` comme drop-in remplacement de
5
- ``run_benchmark_via_service`` qui s'appuie sur
6
- ``RunOrchestrator.execute_preset()`` en interne mais préserve la
7
- signature legacy et le retour ``BenchmarkResult``.
8
-
9
- Utilisé par
10
- -----------
11
- - ``picarones.interfaces.cli._workflows`` (commandes ``run``,
12
- ``diagnose``, ``economics``, ``edition``, ``compare``,
13
- ``robustness``).
14
- - ``picarones.interfaces.web.benchmark_utils.run_benchmark_thread_v2``.
15
- - Les tests catégorie A migrés en Phase B4 (via le re-export
16
- ``tests._migration_helpers``).
17
-
18
- Pourquoi un shim dédié et pas un alias direct
19
- ---------------------------------------------
20
- ``RunOrchestrator.execute_preset()`` consomme des objets domain pré-
21
- construits (``CorpusSpec`` couche 1, ``PipelineSpec`` couche 1). Les
22
- callers legacy (CLI/Web/tests) manipulent toujours :
23
-
24
- - ``Corpus`` legacy (couche 3) avec ``Document.image_path``,
25
- ``ground_truth`` in-memory.
26
- - Liste d'instances ``BaseOCRAdapter`` / ``OCRLLMPipelineConfig``.
27
-
28
- Ce shim convertit ces structures en objets domain via les helpers
29
- existants (``corpus_to_corpus_spec``, ``engine_to_pipeline_spec``,
30
- ``build_adapter_resolver``), puis appelle
31
- ``RunOrchestrator.execute_preset()``. Sortie : ``BenchmarkResult``
32
- legacy via ``run_result_to_benchmark_result``.
33
-
34
- Retrait prévu
35
- -------------
36
- Phase B8 (post-deprecation release). Quand ``run_benchmark_via_service``
37
- sera supprimé, ce shim aussi — les callers devront construire leurs
38
- ``RunSpec`` directement (pattern utilisateur documenté dans
39
- ``docs/migration/option_b_user_guide.md``).
40
- """
41
-
42
- from __future__ import annotations
43
-
44
- import tempfile
45
- from pathlib import Path
46
- from typing import TYPE_CHECKING, Any, Callable
47
-
48
- if TYPE_CHECKING:
49
- from picarones.evaluation.benchmark_result import BenchmarkResult
50
- from picarones.evaluation.corpus import Corpus
51
-
52
-
53
- def _dummy_pipeline_yaml(name: str = "preset_pipeline") -> Any:
54
- """Construit un ``PipelineSpecYaml`` minimaliste pour satisfaire
55
- le validator ``RunSpec.pipelines`` (min_length=1).
56
-
57
- Le contenu est **ignoré** par ``execute_preset()`` qui utilise les
58
- ``pipeline_specs`` fournis en kwargs. Le YAML dummy sert
59
- uniquement à passer la validation Pydantic.
60
- """
61
- from picarones.app.schemas.run_spec import PipelineSpecYaml, StepSpec
62
- from picarones.domain.artifacts import ArtifactType
63
- return PipelineSpecYaml(
64
- name=name,
65
- initial_inputs=(ArtifactType.IMAGE,),
66
- steps=(StepSpec(
67
- id="ocr",
68
- adapter_class="picarones.app.services.legacy_runner_compat.IgnoredByPreset",
69
- adapter_kwargs={},
70
- input_types=(ArtifactType.IMAGE,),
71
- output_types=(ArtifactType.RAW_TEXT,),
72
- ),),
73
- )
74
-
75
-
76
- def run_via_orchestrator(
77
- corpus: "Corpus",
78
- engines: list[Any],
79
- *,
80
- char_exclude: Any | None = None,
81
- normalization_profile: Any | None = None,
82
- output_json: str | Path | None = None,
83
- code_version: str | None = None,
84
- show_progress: bool = True, # noqa: ARG001 — absorbé pour compat
85
- progress_callback: Callable[[str, int, str], None] | None = None,
86
- timeout_seconds: float = 60.0,
87
- cancel_event: Any | None = None,
88
- partial_dir: str | Path | None = None,
89
- entity_extractor: Callable[[str], list[dict]] | str | None = None,
90
- profile: str = "standard",
91
- ) -> "BenchmarkResult":
92
- """Drop-in remplacement de ``run_benchmark_via_service`` via
93
- ``RunOrchestrator.execute_preset()``.
94
-
95
- Préserve la signature legacy pour permettre la migration mécanique
96
- des call sites (CLI, web, tests). Retourne un ``BenchmarkResult``
97
- construit via le converter ``run_result_to_benchmark_result``.
98
-
99
- Parameters
100
- ----------
101
- corpus, engines:
102
- Identiques à ``run_benchmark_via_service``.
103
- char_exclude, normalization_profile, output_json, code_version,
104
- show_progress, progress_callback, timeout_seconds, cancel_event,
105
- partial_dir, entity_extractor, profile:
106
- Identiques à ``run_benchmark_via_service``.
107
-
108
- Notes
109
- -----
110
- Quelques différences subtiles vs le legacy :
111
-
112
- - ``entity_extractor`` accepte un callable direct (legacy) OU un
113
- dotted path string (RunSpec). Si callable, on l'invoque
114
- directement en post-process sur le ``BenchmarkResult``.
115
- - Le workspace temporaire est nettoyé automatiquement via
116
- ``TemporaryDirectory`` — ne pas s'attendre à des fichiers
117
- résiduels après l'appel.
118
- - ``normalization_profile`` accepte un objet ``NormalizationProfile``
119
- (legacy) OU un nom string (RunSpec). Conversion automatique.
120
- """
121
- from picarones.app.schemas.run_spec import RunSpec
122
- from picarones.app.services._benchmark_adapter_resolver import (
123
- build_adapter_resolver,
124
- engine_to_pipeline_spec,
125
- )
126
- from picarones.app.services._benchmark_converter import (
127
- run_result_to_benchmark_result,
128
- )
129
- from picarones.app.services._benchmark_conversions import (
130
- corpus_to_corpus_spec,
131
- )
132
- from picarones.app.services.run_orchestrator import RunOrchestrator
133
-
134
- # Résolution code_version (cohérent avec run_benchmark_via_service:219).
135
- if code_version is None:
136
- import importlib
137
- try:
138
- code_version = importlib.import_module("picarones").__version__
139
- except (ImportError, AttributeError):
140
- code_version = "unknown"
141
-
142
- # ``normalization_profile`` legacy accepte un objet
143
- # NormalizationProfile. RunSpec attend une string. On convertit.
144
- norm_profile_str = normalization_profile
145
- if normalization_profile is not None and not isinstance(
146
- normalization_profile, str,
147
- ):
148
- norm_profile_str = getattr(normalization_profile, "name", None)
149
-
150
- # ``entity_extractor`` legacy accepte un callable direct. RunSpec
151
- # attend un dotted path. Si callable, on le traite post-process
152
- # comme run_benchmark_via_service le fait.
153
- entity_extractor_dotted: str | None = None
154
- entity_extractor_callable: Callable | None = None
155
- if entity_extractor is not None:
156
- if isinstance(entity_extractor, str):
157
- entity_extractor_dotted = entity_extractor
158
- elif callable(entity_extractor):
159
- entity_extractor_callable = entity_extractor
160
-
161
- with tempfile.TemporaryDirectory(prefix="picarones_compat_") as ws:
162
- ws_path = Path(ws)
163
- gt_dir = ws_path / "gt"
164
- gt_dir.mkdir()
165
- run_dir = ws_path / "run"
166
- run_dir.mkdir()
167
-
168
- corpus_spec = corpus_to_corpus_spec(corpus, workspace_dir=gt_dir)
169
- pipeline_specs = [engine_to_pipeline_spec(e) for e in engines]
170
- adapter_resolver = build_adapter_resolver(engines)
171
- pipeline_to_engine_name = {
172
- spec.name: engine.name
173
- for spec, engine in zip(pipeline_specs, engines)
174
- }
175
-
176
- # ``char_exclude`` peut être frozenset (legacy parsed) ou string
177
- # (RunSpec format). RunSpec attend une string ; on convertit.
178
- char_exclude_str: str | None = None
179
- if char_exclude is not None:
180
- if isinstance(char_exclude, str):
181
- char_exclude_str = char_exclude
182
- else:
183
- char_exclude_str = "".join(sorted(char_exclude))
184
-
185
- spec = RunSpec(
186
- corpus_dir=str(ws_path), # ignoré par execute_preset
187
- pipelines=(_dummy_pipeline_yaml(),), # ignoré, juste pour validator
188
- views=("text_final",),
189
- output_dir=str(run_dir),
190
- char_exclude=char_exclude_str,
191
- normalization_profile=norm_profile_str,
192
- partial_dir=str(partial_dir) if partial_dir else None,
193
- entity_extractor=entity_extractor_dotted,
194
- profile=profile,
195
- output_json=str(output_json) if output_json else None,
196
- code_version=code_version,
197
- timeout_seconds_per_doc=timeout_seconds,
198
- )
199
-
200
- # Tag des engines avec le nom pour la map pipeline_to_engine
201
- # (utilisé par le progress_callback wrapper).
202
- wrapped_callback = None
203
- if progress_callback is not None:
204
- def wrapped_callback(
205
- pipeline_name: str, doc_idx: int, doc_id: str,
206
- ) -> None:
207
- engine_name = pipeline_to_engine_name.get(
208
- pipeline_name, pipeline_name,
209
- )
210
- progress_callback(engine_name, doc_idx, doc_id)
211
-
212
- orch = RunOrchestrator(run_dir)
213
- orch_result = orch.execute_preset(
214
- spec,
215
- corpus_spec=corpus_spec,
216
- extracted_dir=gt_dir,
217
- pipeline_specs=pipeline_specs,
218
- adapter_resolver=adapter_resolver,
219
- adapter_kwargs={},
220
- progress_callback=wrapped_callback,
221
- cancel_event=cancel_event,
222
- )
223
-
224
- # Converti RunResult → BenchmarkResult via le converter
225
- # canonique (utilisé aussi par output_json en Phase B2.7).
226
- benchmark_result = run_result_to_benchmark_result(
227
- orch_result.run_result,
228
- corpus=corpus,
229
- engines=engines,
230
- char_exclude=char_exclude, # passe la valeur originale
231
- normalization_profile=normalization_profile,
232
- profile=profile,
233
- )
234
-
235
- # NER attach post-process si entity_extractor callable fourni.
236
- # Cohérent avec run_benchmark_via_service:261-264.
237
- if entity_extractor_callable is not None:
238
- from picarones.app.services._benchmark_ner import (
239
- attach_ner_metrics_to_benchmark,
240
- )
241
- attach_ner_metrics_to_benchmark(
242
- benchmark_result, corpus, entity_extractor_callable,
243
- )
244
-
245
- # Sérialisation output_json si demandée (legacy comportement).
246
- if output_json is not None:
247
- from picarones.app.services._benchmark_persistence import (
248
- persist_benchmark_result_json,
249
- )
250
- persist_benchmark_result_json(
251
- benchmark_result, Path(output_json),
252
- )
253
-
254
- return benchmark_result
255
-
256
-
257
- __all__ = ["run_via_orchestrator"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
picarones/interfaces/web/benchmark_utils.py CHANGED
@@ -380,8 +380,7 @@ def run_benchmark_thread_v2(job: BenchmarkJob, req: BenchmarkRunRequest) -> None
380
  # 1. prepare_preset_args (conversion vers domain)
381
  # 2. execute_preset (run du benchmark)
382
  # 3. run_result_to_benchmark_result (BenchmarkResult legacy)
383
- # Plus de shim ``run_via_orchestrator`` la mécanique est
384
- # visible et chaque étape est unitairement testable.
385
  with tempfile.TemporaryDirectory(prefix="picarones_web_") as _ws:
386
  _ws_path = Path(_ws)
387
  _run_dir = _ws_path / "run"
 
380
  # 1. prepare_preset_args (conversion vers domain)
381
  # 2. execute_preset (run du benchmark)
382
  # 3. run_result_to_benchmark_result (BenchmarkResult legacy)
383
+ # Pattern explicitechaque étape est unitairement testable.
 
384
  with tempfile.TemporaryDirectory(prefix="picarones_web_") as _ws:
385
  _ws_path = Path(_ws)
386
  _run_dir = _ws_path / "run"
tests/_migration_helpers.py CHANGED
@@ -1,10 +1,9 @@
1
  """Helpers tests — pattern ``RunOrchestrator`` pour les tests B4.
2
 
3
- Phase B3-final (mai 2026) — ce module **n'est plus un re-export**
4
- du shim de production ``legacy_runner_compat``. Il implémente
5
- directement le pattern 3 étapes (``prepare_preset_args``
6
- ``execute_preset`` ``run_result_to_benchmark_result``) pour servir
7
- les 6 fichiers de tests catégorie A migrés en Phase B4.
8
 
9
  Pourquoi un helper test dédié plutôt qu'inline dans chaque test ?
10
  -----------------------------------------------------------------
@@ -13,16 +12,11 @@ que l'ancien ``run_benchmark_via_service``. Le mettre inline dans
13
  chaque test ajouterait ~10 lignes de boilerplate par cas, noyant
14
  l'intention du test.
15
 
16
- Différence vs le shim de production
17
- -----------------------------------
18
- Le shim ``legacy_runner_compat`` exposait ``run_via_orchestrator``
19
- comme API publique pour CLI/Web — il a été supprimé en Phase
20
- B3-final commit 5 au profit du pattern 3 étapes explicite dans
21
- chaque call site.
22
-
23
  Ce helper ``run_via_orchestrator`` est un **outil de test**
24
  (préfixe ``_`` du module + dossier ``tests/``). Son existence ne
25
- constitue pas de la dette technique en production.
 
 
26
  """
27
 
28
  from __future__ import annotations
 
1
  """Helpers tests — pattern ``RunOrchestrator`` pour les tests B4.
2
 
3
+ Phase B3-final (mai 2026) — implémente directement le pattern 3
4
+ étapes ``prepare_preset_args`` ``execute_preset``
5
+ ``run_result_to_benchmark_result`` pour servir les 6 fichiers de
6
+ tests catégorie A migrés en Phase B4.
 
7
 
8
  Pourquoi un helper test dédié plutôt qu'inline dans chaque test ?
9
  -----------------------------------------------------------------
 
12
  chaque test ajouterait ~10 lignes de boilerplate par cas, noyant
13
  l'intention du test.
14
 
 
 
 
 
 
 
 
15
  Ce helper ``run_via_orchestrator`` est un **outil de test**
16
  (préfixe ``_`` du module + dossier ``tests/``). Son existence ne
17
+ constitue pas de la dette technique en production : il n'y a pas
18
+ de shim équivalent dans ``picarones/`` (les call sites CLI/Web
19
+ font le pattern 3 étapes explicitement).
20
  """
21
 
22
  from __future__ import annotations