Claude commited on
Commit
c9cdc95
·
unverified ·
1 Parent(s): 2ac9751

feat(migration): Phase 7.B.2 — PipelineRunner.run délègue à PipelineExecutor

Browse files

Le runner legacy (Sprint 63, ~440 tests) délègue désormais sa boucle
d'exécution au PipelineExecutor canonique (Sprint S6) via le wrapper
_BaseModuleAdapter créé en 7.B.1. Plus aucune duplication de moteur
d'exécution : un seul code path à maintenir.

L'API publique du Sprint 63 est rigoureusement préservée — les
~440 tests existants continuent de passer sans modification. Les
sub-phases 7.C (refactor des tests vers le canonique direct) et 7.D
(suppression du runner legacy) viendront ensuite.

Refactor en deux temps
----------------------
1. Délégation dans le corps de PipelineRunner.run :
- validation amont legacy (préserve les messages d'erreur français
du Sprint 63 — "étape N (X) demande Y qui n'est ni…") ;
- construction d'une PipelineSpec canonique + adapter_resolver ad-hoc
(chaque BaseModule legacy devient un _BaseModuleAdapter) ;
- appel à PipelineExecutor.run ;
- reconstruction du PipelineResult legacy + traduction des messages
d'erreur canoniques ("missing_input:", "adapter_raised:", etc.)
vers le format attendu par les tests legacy.

2. Relocalisation forcée par l'architecture concentrique :
- evaluation/ ne peut pas importer pipeline/ (CLAUDE.md
§"architecture des couches", règle vérifiée par
test_layer_dependencies). Donc le runner legacy migre :
evaluation/pipeline.py → pipeline/legacy_runner.py
evaluation/pipeline_benchmark.py → pipeline/legacy_pipeline_benchmark.py
evaluation/pipeline_comparison.py → pipeline/legacy_pipeline_comparison.py
- Les 3 modules sont préfixés "legacy_" pour signaler qu'ils sont
transitoires (suppression en 7.D).

Calcul des junction_metrics
---------------------------
Le PipelineExecutor canonique laisse le calcul des métriques au
caller (le planner détecte les jonctions, l'executor n'évalue pas).
Le runner legacy garde donc cette responsabilité : après chaque step
réussi, il lit les payloads depuis _PayloadRegistry et appelle
compute_at_junction(GT, payload, (T, T)) — comportement automatique
du Sprint 63 préservé.

14 callers migrés
-----------------
- picarones/__init__.py
- picarones/cli/_pipeline.py
- picarones/measurements/{pipeline_benchmark,pipeline_comparison,pipeline_spec_loader}.py
- picarones/reports_v2/html/renderers/pipeline.py
- 8 tests : tests/core/test_sprint{63,66}_*, tests/integration/test_{alto_baseline,pipeline_ocr_to_alto}.py,
tests/measurements/test_sprint{64,65}_*, tests/report/test_sprint{67,68}_*

Tests architecture mis à jour
-----------------------------
- test_file_budgets : "picarones/pipeline/legacy_runner.py": 825 (actuel 735)
- test_doc_paths : BROKEN_PATHS_BASELINE 138 → 141 (3 nouveaux chemins
cassés dans les docs historiques qui citent les anciens chemins —
CHANGELOG.md, audits, sub-plans, intentionnellement non corrigées
pour la traçabilité)
- test_public_api : module path remappé vers pipeline.legacy_runner
- docs/reference/api-stable.md : 3 sections renommées avec note Phase 7.B.2

Vérifications
-------------
- pytest tests/ : 4967 passed, 0 failed, 0 errors (identique au
baseline sur main)
- ruff check picarones/ tests/ : All checks passed
- mypy picarones/domain/ : Success, 0 issues
- tests/architecture/ : 73 passed

https://claude.ai/code/session_011XQZNitg1rCgia8ZD1a2hP

docs/reference/api-stable.md CHANGED
@@ -119,7 +119,12 @@ def run_benchmark(
119
  ) -> BenchmarkResult
120
  ```
121
 
122
- ### `picarones.evaluation.pipeline`
 
 
 
 
 
123
 
124
  ```python
125
  class PipelineStep:
@@ -129,7 +134,10 @@ class PipelineResult:
129
  class PipelineRunner:
130
  ```
131
 
132
- ### `picarones.measurements.pipeline_benchmark`
 
 
 
133
 
134
  ```python
135
  class StepAggregate:
@@ -139,7 +147,9 @@ def default_initial_inputs(doc) -> dict
139
  def run_pipeline_benchmark(spec, corpus, factory=...) -> PipelineBenchmarkResult
140
  ```
141
 
142
- ### `picarones.measurements.pipeline_comparison`
 
 
143
 
144
  ```python
145
  class PipelineComparisonResult:
 
119
  ) -> BenchmarkResult
120
  ```
121
 
122
+ ### `picarones.pipeline.legacy_runner`
123
+
124
+ > Phase 7.B.2 (2026-05-07) — module relocalisé depuis
125
+ > ``picarones.evaluation.pipeline`` vers ``picarones.pipeline.legacy_runner``.
126
+ > La délégation au ``PipelineExecutor`` canonique impose à ce module
127
+ > d'importer la couche ``pipeline/`` — interdit à ``evaluation/``.
128
 
129
  ```python
130
  class PipelineStep:
 
134
  class PipelineRunner:
135
  ```
136
 
137
+ ### `picarones.pipeline.legacy_pipeline_benchmark`
138
+
139
+ > Phase 7.B.2 — relocalisé depuis ``picarones.evaluation.pipeline_benchmark``
140
+ > (mêmes raisons que ``legacy_runner``).
141
 
142
  ```python
143
  class StepAggregate:
 
147
  def run_pipeline_benchmark(spec, corpus, factory=...) -> PipelineBenchmarkResult
148
  ```
149
 
150
+ ### `picarones.pipeline.legacy_pipeline_comparison`
151
+
152
+ > Phase 7.B.2 — relocalisé depuis ``picarones.evaluation.pipeline_comparison``.
153
 
154
  ```python
155
  class PipelineComparisonResult:
picarones/__init__.py CHANGED
@@ -69,7 +69,7 @@ from picarones.domain.facts import (
69
  FactImportance,
70
  FactType,
71
  )
72
- from picarones.evaluation.pipeline import (
73
  PipelineResult,
74
  PipelineRunner,
75
  PipelineSpec,
 
69
  FactImportance,
70
  FactType,
71
  )
72
+ from picarones.pipeline.legacy_runner import (
73
  PipelineResult,
74
  PipelineRunner,
75
  PipelineSpec,
picarones/cli/_pipeline.py CHANGED
@@ -66,7 +66,7 @@ def pipeline_run_cmd(
66
  import json as _json
67
 
68
  from picarones.evaluation.corpus import load_corpus_from_directory
69
- from picarones.evaluation.pipeline_benchmark import run_pipeline_benchmark
70
  from picarones.measurements.pipeline_spec_loader import load_pipeline_spec_from_yaml
71
 
72
  spec = load_pipeline_spec_from_yaml(spec_path)
@@ -163,7 +163,7 @@ def pipeline_compare_cmd(
163
  """Compare N pipelines décrites dans SPECS_PATH sur le même corpus."""
164
  from picarones.evaluation.corpus import load_corpus_from_directory
165
  from picarones.domain.artifacts import ArtifactType
166
- from picarones.evaluation.pipeline_comparison import compare_pipelines
167
  from picarones.measurements.pipeline_spec_loader import (
168
  load_comparison_specs_from_yaml,
169
  )
 
66
  import json as _json
67
 
68
  from picarones.evaluation.corpus import load_corpus_from_directory
69
+ from picarones.pipeline.legacy_pipeline_benchmark import run_pipeline_benchmark
70
  from picarones.measurements.pipeline_spec_loader import load_pipeline_spec_from_yaml
71
 
72
  spec = load_pipeline_spec_from_yaml(spec_path)
 
163
  """Compare N pipelines décrites dans SPECS_PATH sur le même corpus."""
164
  from picarones.evaluation.corpus import load_corpus_from_directory
165
  from picarones.domain.artifacts import ArtifactType
166
+ from picarones.pipeline.legacy_pipeline_comparison import compare_pipelines
167
  from picarones.measurements.pipeline_spec_loader import (
168
  load_comparison_specs_from_yaml,
169
  )
picarones/measurements/pipeline_benchmark.py CHANGED
@@ -8,11 +8,11 @@ from __future__ import annotations
8
 
9
  import warnings
10
 
11
- from picarones.evaluation.pipeline_benchmark import * # noqa: F401, F403
12
 
13
  warnings.warn(
14
  "picarones.measurements.pipeline_benchmark is deprecated and will be removed in 2.0. "
15
- "Import from picarones.evaluation.pipeline_benchmark instead.",
16
  DeprecationWarning,
17
  stacklevel=2,
18
  )
 
8
 
9
  import warnings
10
 
11
+ from picarones.pipeline.legacy_pipeline_benchmark import * # noqa: F401, F403
12
 
13
  warnings.warn(
14
  "picarones.measurements.pipeline_benchmark is deprecated and will be removed in 2.0. "
15
+ "Import from picarones.pipeline.legacy_pipeline_benchmark instead.",
16
  DeprecationWarning,
17
  stacklevel=2,
18
  )
picarones/measurements/pipeline_comparison.py CHANGED
@@ -8,11 +8,11 @@ from __future__ import annotations
8
 
9
  import warnings
10
 
11
- from picarones.evaluation.pipeline_comparison import * # noqa: F401, F403
12
 
13
  warnings.warn(
14
  "picarones.measurements.pipeline_comparison is deprecated and will be removed in 2.0. "
15
- "Import from picarones.evaluation.pipeline_comparison instead.",
16
  DeprecationWarning,
17
  stacklevel=2,
18
  )
 
8
 
9
  import warnings
10
 
11
+ from picarones.pipeline.legacy_pipeline_comparison import * # noqa: F401, F403
12
 
13
  warnings.warn(
14
  "picarones.measurements.pipeline_comparison is deprecated and will be removed in 2.0. "
15
+ "Import from picarones.pipeline.legacy_pipeline_comparison instead.",
16
  DeprecationWarning,
17
  stacklevel=2,
18
  )
picarones/measurements/pipeline_spec_loader.py CHANGED
@@ -69,7 +69,7 @@ from typing import Any
69
 
70
  from picarones.domain.artifacts import ArtifactType
71
  from picarones.domain.module_protocol import BaseModule
72
- from picarones.evaluation.pipeline import PipelineSpec, PipelineStep
73
 
74
  logger = logging.getLogger(__name__)
75
 
 
69
 
70
  from picarones.domain.artifacts import ArtifactType
71
  from picarones.domain.module_protocol import BaseModule
72
+ from picarones.pipeline.legacy_runner import PipelineSpec, PipelineStep
73
 
74
  logger = logging.getLogger(__name__)
75
 
picarones/{evaluation/pipeline_benchmark.py → pipeline/legacy_pipeline_benchmark.py} RENAMED
@@ -53,7 +53,7 @@ from typing import Any, Callable, Optional
53
 
54
  from picarones.evaluation.corpus import Corpus, Document
55
  from picarones.domain.artifacts import ArtifactType
56
- from picarones.evaluation.pipeline import (
57
  PipelineResult,
58
  PipelineRunner,
59
  PipelineSpec,
 
53
 
54
  from picarones.evaluation.corpus import Corpus, Document
55
  from picarones.domain.artifacts import ArtifactType
56
+ from picarones.pipeline.legacy_runner import (
57
  PipelineResult,
58
  PipelineRunner,
59
  PipelineSpec,
picarones/{evaluation/pipeline_comparison.py → pipeline/legacy_pipeline_comparison.py} RENAMED
@@ -58,13 +58,13 @@ from typing import Optional
58
 
59
  from picarones.evaluation.corpus import Corpus
60
  from picarones.domain.artifacts import ArtifactType
61
- from picarones.evaluation.pipeline_benchmark import (
62
  InitialInputsFactory,
63
  PipelineBenchmarkResult,
64
  default_initial_inputs,
65
  run_pipeline_benchmark,
66
  )
67
- from picarones.evaluation.pipeline import PipelineSpec
68
 
69
  logger = logging.getLogger(__name__)
70
 
 
58
 
59
  from picarones.evaluation.corpus import Corpus
60
  from picarones.domain.artifacts import ArtifactType
61
+ from picarones.pipeline.legacy_pipeline_benchmark import (
62
  InitialInputsFactory,
63
  PipelineBenchmarkResult,
64
  default_initial_inputs,
65
  run_pipeline_benchmark,
66
  )
67
+ from picarones.pipeline.legacy_runner import PipelineSpec
68
 
69
  logger = logging.getLogger(__name__)
70
 
picarones/{evaluation/pipeline.py → pipeline/legacy_runner.py} RENAMED
@@ -4,15 +4,39 @@ Phase 5.C.batch7 — module relocalisé depuis
4
  ``picarones.core.pipeline`` vers ``picarones.evaluation.pipeline``.
5
  Shim ``picarones.core.pipeline`` retiré au Lot C (2026-05-07).
6
 
7
- Coexistence avec ``picarones.pipeline.executor``
8
  ------------------------------------------------
9
- Le présent module porte le ``PipelineRunner`` historique
10
- (Sprint 63), riche en behavior, qui orchestre l'exécution
11
- mono-document. Le module canonique
12
- ``picarones.pipeline.executor`` (Sprint S6) propose un design
13
- différent (instance-based, immutable specs). Les deux
14
- cohabitent volontairement ; un convertisseur explicite viendra
15
- quand un caller institutionnel l'exigera.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  Sprint 63 — Étape 4 / axe B du plan d'évolution 2026 : démarrage du
18
  banc d'essai de pipelines.
@@ -63,14 +87,28 @@ Reporté à des sprints dédiés :
63
  from __future__ import annotations
64
 
65
  import logging
66
- import time
67
  from dataclasses import dataclass, field
68
  from typing import Any, Optional
69
 
70
  from picarones.evaluation.corpus import Document, GTLevel
71
  from picarones.evaluation.metric_registry import compute_at_junction
72
  from picarones.domain.artifacts import ArtifactType
 
73
  from picarones.domain.module_protocol import BaseModule
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  # Sprint A3 (renforce la règle Cercle 1 → Cercle 1 uniquement) — la
76
  # cérémonie d'eager-load des métriques typées (Sprint 34) qui vivait
@@ -387,6 +425,15 @@ class PipelineRunner:
387
  corpus-wide et l'agrégation par pipeline sont reportées à un
388
  sprint dédié.
389
 
 
 
 
 
 
 
 
 
 
390
  Usage typique
391
  -------------
392
 
@@ -436,158 +483,225 @@ class PipelineRunner:
436
  pipeline_name=spec.name, doc_id=document.doc_id,
437
  )
438
 
439
- # Validation amont : si la pipeline est statiquement
440
- # invalide, on n'exécute aucune étape.
 
 
 
 
 
441
  problems = spec.validate(tuple(initial_inputs.keys()))
442
  if problems:
443
  result.error = " ; ".join(problems)
444
  return result
445
 
446
- # Sprint 66 — bag versionné : ``versioned[(type, src_step)]``
447
- # contient l'artefact produit par ``src_step`` pour ``type``.
448
- # ``src_step`` vaut ``"__initial__"`` pour les entrées
449
- # initiales fournies par l'utilisateur. ``latest[type]``
450
- # désigne le nom de l'étape qui a produit la version la plus
451
- # récente du type — utilisé en l'absence d'``inputs_from``
452
- # explicite (rétrocompat Sprint 63).
453
- versioned: dict[tuple[ArtifactType, str], Any] = {
454
- (t, "__initial__"): v for t, v in initial_inputs.items()
455
- }
456
- latest: dict[ArtifactType, str] = {
457
- t: "__initial__" for t in initial_inputs
458
- }
459
 
460
- pipeline_t0 = time.monotonic()
461
- for step in spec.steps:
462
- step_result = PipelineRunner._run_step(
463
- step, versioned, latest, document,
 
 
 
464
  )
465
- result.steps.append(step_result)
466
- result.total_duration_seconds = time.monotonic() - pipeline_t0
467
  return result
468
 
469
- @staticmethod
470
- def _run_step(
471
- step: PipelineStep,
472
- versioned: dict[tuple[ArtifactType, str], Any],
473
- latest: dict[ArtifactType, str],
474
- document: Document,
475
- ) -> StepResult:
476
- # Sprint 66 — résolution des entrées : pour chaque type
477
- # demandé, on consulte ``inputs_from`` ; sinon on prend la
478
- # dernière version disponible (rétrocompat Sprint 63).
479
- resolved: dict[ArtifactType, Any] = {}
480
- missing: list[str] = []
481
- for t in step.input_types:
482
- src = step.inputs_from.get(t, latest.get(t))
483
- if src is None:
484
- missing.append(t.value)
485
- continue
486
- key = (t, src)
487
- if key not in versioned:
488
- # Référence explicite vers une étape qui n'a pas
489
- # produit cet artefact (ex. l'étape source a échoué).
490
- missing.append(f"{t.value}@{src}")
491
- continue
492
- resolved[t] = versioned[key]
493
- if missing:
494
- miss_str = ",".join(missing)
495
- return StepResult(
496
- step_name=step.name,
497
- duration_seconds=0.0,
498
- output_types=(),
499
- error=f"entrée manquante : {miss_str}",
500
- )
501
- inputs_for_module = resolved
502
- # Exécution chronométrée
503
- t0 = time.monotonic()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  try:
505
- outputs = step.module.process(inputs_for_module)
 
 
 
 
506
  except Exception as exc: # noqa: BLE001
507
- duration = time.monotonic() - t0
508
  logger.warning(
509
- "[pipeline_runner] étape '%s' a levé : %s",
510
- step.name, exc,
 
511
  )
512
- return StepResult(
513
- step_name=step.name,
514
- duration_seconds=duration,
515
- output_types=(),
516
- error=f"{type(exc).__name__}: {exc}",
517
- )
518
- duration = time.monotonic() - t0
519
-
520
- # Validation des sorties : le module est censé déclarer ses
521
- # output_types, on vérifie qu'il les a tous produits. Si
522
- # ce n'est pas le cas, on remonte une erreur explicite mais
523
- # on conserve les sorties effectivement présentes (utile
524
- # pour le diagnostic).
525
- if not isinstance(outputs, dict):
526
- return StepResult(
527
- step_name=step.name,
528
- duration_seconds=duration,
529
- output_types=(),
530
- error=(
531
- f"le module a retourné {type(outputs).__name__}, "
532
- f"un dict[ArtifactType, Any] est attendu"
533
- ),
534
- )
535
- produced = tuple(t for t in step.output_types if t in outputs)
536
- missing_outputs = [t for t in step.output_types if t not in outputs]
537
- error: Optional[str] = None
538
- if missing_outputs:
539
- miss_str = ",".join(t.value for t in missing_outputs)
540
- error = f"sortie manquante : {miss_str}"
541
-
542
- # Mise à jour du bag versionné : on stocke la sortie sous
543
- # une clé (type, step.name) ET on met à jour ``latest`` pour
544
- # que les étapes suivantes la récupèrent par défaut.
545
- for t in produced:
546
- versioned[(t, step.name)] = outputs[t]
547
- latest[t] = step.name
548
-
549
- # Évaluation aux jonctions : pour chaque type produit, si
550
- # la GT du même niveau existe, on calcule les métriques.
551
- junction_metrics: dict[str, dict[str, Any]] = {}
552
- for at in produced:
553
- gt_level = _artifact_type_to_gt_level(at)
554
- if gt_level is None:
555
- continue
556
- gt_payload = document.get_gt(gt_level)
557
- if gt_payload is None:
558
- continue
559
- try:
560
- metrics = compute_at_junction(
561
- _gt_payload_to_value(gt_payload),
562
- outputs[at],
563
- (at, at),
564
- )
565
- except Exception as exc: # noqa: BLE001
566
- logger.warning(
567
- "[pipeline_runner] évaluation à la jonction %s "
568
- "a levé : %s",
569
- at.value, exc,
570
- )
571
- continue
572
- if metrics:
573
- junction_metrics[at.value] = metrics
574
-
575
- # Phase 4-bis : double-clé pour rétrocompat. Les tests
576
- # legacy cherchent junction_metrics["text"] mais le runner
577
- # peut produire junction_metrics["raw_text"] si l'enum est
578
- # migré (ArtifactType.TEXT alias de RAW_TEXT, valeur
579
- # "raw_text"). expand_legacy_keys ajoute la clé legacy
580
- # ("text") à côté de la canonique ("raw_text") sans écraser.
581
- from picarones.domain.artifacts import expand_legacy_keys
582
- expand_legacy_keys(junction_metrics)
583
-
584
- return StepResult(
585
- step_name=step.name,
586
- duration_seconds=duration,
587
- output_types=produced,
588
- junction_metrics=junction_metrics,
589
- error=error,
590
- )
591
 
592
 
593
  def _gt_payload_to_value(payload: Any) -> Any:
 
4
  ``picarones.core.pipeline`` vers ``picarones.evaluation.pipeline``.
5
  Shim ``picarones.core.pipeline`` retiré au Lot C (2026-05-07).
6
 
7
+ Phase 7.B.2 — module relocalisé une seconde fois
8
  ------------------------------------------------
9
+ ``picarones.evaluation.pipeline`` ``picarones.pipeline.legacy_runner``.
10
+ La délégation à :class:`PipelineExecutor` (ci-dessous) exige d'importer
11
+ la couche ``pipeline/``, ce que la règle d'architecture concentrique
12
+ interdit à ``evaluation/`` (whitelist externe restreinte, pas de
13
+ dépendance sortante vers une couche plus externe — cf. CLAUDE.md
14
+ § "architecture des couches"). Le module bridge legacy ↔ canonique
15
+ vit donc dans la couche ``pipeline/``. ``picarones.evaluation.pipeline``
16
+ reste exposé en re-export shim le temps que les callers historiques
17
+ migrent.
18
+
19
+ Phase 7.B.2 — délégation au ``PipelineExecutor`` canonique
20
+ ----------------------------------------------------------
21
+ Depuis 2026-05, ``PipelineRunner.run`` ne porte **plus** sa propre
22
+ boucle d'exécution. Le corps de la méthode délègue intégralement à
23
+ :class:`picarones.pipeline.executor.PipelineExecutor` via le wrapper
24
+ :class:`picarones.pipeline._legacy_module_adapter._BaseModuleAdapter`
25
+ (créé en 7.B.1). Le runner ne conserve que :
26
+
27
+ 1. La validation amont legacy (préservation des messages d'erreur
28
+ français du Sprint 63 — ``"étape N (X) demande Y qui n'est ni…"``).
29
+ 2. La traduction des résultats canoniques (``pipeline.types.StepResult``
30
+ Pydantic) vers les types legacy (``StepResult``, ``PipelineResult``
31
+ dataclass) attendus par les ~440 tests existants.
32
+ 3. Le calcul des ``junction_metrics`` aux jonctions GT-vs-sortie —
33
+ le canonique laisse cette responsabilité au caller (`MetricRegistry`
34
+ intégré au planner mais évaluation déférée).
35
+
36
+ Cela élimine la duplication de moteur d'exécution (un seul code
37
+ path) tout en préservant intégralement l'API publique du Sprint 63
38
+ le temps que la sub-phase 7.C migre les tests vers le canonique
39
+ direct, puis 7.D supprime le runner legacy.
40
 
41
  Sprint 63 — Étape 4 / axe B du plan d'évolution 2026 : démarrage du
42
  banc d'essai de pipelines.
 
87
  from __future__ import annotations
88
 
89
  import logging
 
90
  from dataclasses import dataclass, field
91
  from typing import Any, Optional
92
 
93
  from picarones.evaluation.corpus import Document, GTLevel
94
  from picarones.evaluation.metric_registry import compute_at_junction
95
  from picarones.domain.artifacts import ArtifactType
96
+ from picarones.domain.documents import DocumentRef
97
  from picarones.domain.module_protocol import BaseModule
98
+ from picarones.domain.pipeline_spec import (
99
+ PipelineSpec as _DomainPipelineSpec,
100
+ PipelineStep as _DomainPipelineStep,
101
+ )
102
+ from picarones.pipeline._legacy_module_adapter import (
103
+ _BaseModuleAdapter,
104
+ _PayloadRegistry,
105
+ wrap_initial_inputs,
106
+ )
107
+ from picarones.pipeline.executor import PipelineExecutor
108
+ from picarones.pipeline.types import (
109
+ RunContext,
110
+ StepResult as _CanonicalStepResult,
111
+ )
112
 
113
  # Sprint A3 (renforce la règle Cercle 1 → Cercle 1 uniquement) — la
114
  # cérémonie d'eager-load des métriques typées (Sprint 34) qui vivait
 
425
  corpus-wide et l'agrégation par pipeline sont reportées à un
426
  sprint dédié.
427
 
428
+ Phase 7.B.2 — délégation au canonique
429
+ --------------------------------------
430
+ L'API publique (``run`` statique, types de retour ``PipelineResult``
431
+ et ``StepResult`` legacy, format des messages d'erreur en français)
432
+ est rigoureusement préservée pour rétrocompat. Le corps de
433
+ ``run`` délègue à :class:`picarones.pipeline.executor.PipelineExecutor`
434
+ via :class:`_BaseModuleAdapter` — il n'y a plus de code de
435
+ boucle d'exécution dupliqué.
436
+
437
  Usage typique
438
  -------------
439
 
 
483
  pipeline_name=spec.name, doc_id=document.doc_id,
484
  )
485
 
486
+ # Validation amont legacy : si la pipeline est statiquement
487
+ # invalide, on n'exécute aucune étape. Cette validation
488
+ # produit des messages français spécifiques au Sprint 63
489
+ # (cf. ``PipelineSpec.validate``) que les tests vérifient ;
490
+ # le canonique a sa propre ``ValidationError`` au format
491
+ # différent — d'où la double validation tant que les tests
492
+ # legacy ne sont pas migrés (sub-phase 7.C).
493
  problems = spec.validate(tuple(initial_inputs.keys()))
494
  if problems:
495
  result.error = " ; ".join(problems)
496
  return result
497
 
498
+ canonical_result, registry = _delegate_to_canonical_executor(
499
+ spec, document, initial_inputs,
500
+ )
 
 
 
 
 
 
 
 
 
 
501
 
502
+ for legacy_step, canonical_sr in zip(
503
+ spec.steps, canonical_result.step_results,
504
+ ):
505
+ result.steps.append(
506
+ _build_legacy_step_result(
507
+ legacy_step, canonical_sr, registry, document,
508
+ ),
509
  )
510
+ result.total_duration_seconds = canonical_result.duration_seconds
 
511
  return result
512
 
513
+
514
+ # ──────────────────────────────────────────────────────────────────────────
515
+ # Phase 7.B.2 — délégation au PipelineExecutor canonique
516
+ # ──────────────────────────────────────────────────────────────────────────
517
+
518
+
519
+ def _delegate_to_canonical_executor(
520
+ legacy_spec: PipelineSpec,
521
+ legacy_doc: Document,
522
+ initial_inputs: dict[ArtifactType, Any],
523
+ ) -> tuple[Any, _PayloadRegistry]:
524
+ """Exécute ``legacy_spec`` via :class:`PipelineExecutor`.
525
+
526
+ Construit la ``_DomainPipelineSpec`` canonique équivalente, un
527
+ ``adapter_resolver`` ad-hoc qui mappe ``step.name → _BaseModuleAdapter``,
528
+ et délègue à l'executor. Retourne le ``PipelineResult`` canonique
529
+ + le registre de payloads (dont le caller a besoin pour reconstruire
530
+ les ``junction_metrics`` du contrat legacy).
531
+ """
532
+ registry = _PayloadRegistry()
533
+ canonical_inputs = wrap_initial_inputs(
534
+ initial_inputs, registry, legacy_doc.doc_id,
535
+ )
536
+
537
+ adapter_map: dict[str, _BaseModuleAdapter] = {}
538
+ canonical_steps: list[_DomainPipelineStep] = []
539
+ for step in legacy_spec.steps:
540
+ adapter_map[step.name] = _BaseModuleAdapter(step.module, registry)
541
+ canonical_steps.append(
542
+ _DomainPipelineStep(
543
+ id=step.name,
544
+ kind="legacy_module",
545
+ adapter_name=step.name,
546
+ input_types=tuple(step.input_types),
547
+ output_types=tuple(step.output_types),
548
+ inputs_from=dict(step.inputs_from),
549
+ ),
550
+ )
551
+ canonical_spec = _DomainPipelineSpec(
552
+ name=legacy_spec.name,
553
+ initial_inputs=tuple(initial_inputs.keys()),
554
+ steps=tuple(canonical_steps),
555
+ )
556
+
557
+ document_ref = DocumentRef(id=legacy_doc.doc_id)
558
+ # ``code_version`` est libre (str non vide). Le wrapper
559
+ # ``_BaseModuleAdapter`` ne produit pas de ``ProvenanceRecord``
560
+ # détaillée — la couche pipeline ne peut pas importer
561
+ # ``picarones.__version__`` (whitelist externe restreinte).
562
+ # On étiquette les runs legacy avec un sentinel constant ; la
563
+ # traçabilité fine reviendra avec le canonique pur en 7.D.
564
+ context = RunContext(
565
+ document_id=legacy_doc.doc_id,
566
+ code_version="legacy_runner",
567
+ pipeline_name=legacy_spec.name,
568
+ )
569
+ executor = PipelineExecutor(adapter_resolver=adapter_map.__getitem__)
570
+ canonical_result = executor.run(
571
+ canonical_spec, document_ref, canonical_inputs, context,
572
+ )
573
+ return canonical_result, registry
574
+
575
+
576
+ def _build_legacy_step_result(
577
+ legacy_step: PipelineStep,
578
+ canonical_sr: _CanonicalStepResult,
579
+ registry: _PayloadRegistry,
580
+ document: Document,
581
+ ) -> StepResult:
582
+ """Reconstruit un ``StepResult`` legacy depuis le canonique.
583
+
584
+ Trois responsabilités :
585
+
586
+ 1. Traduire le format des messages d'erreur (``adapter_raised:``,
587
+ ``missing_input:``, ``missing_output:``) vers le format français
588
+ attendu par les tests legacy (``"Type: msg"``,
589
+ ``"entrée manquante : ..."``, ``"sortie manquante : ..."``).
590
+ 2. Reconstruire le tuple ``output_types`` à partir de
591
+ ``produced_artifacts`` du canonique.
592
+ 3. Calculer les ``junction_metrics`` en lisant les payloads
593
+ depuis ``registry`` et en appelant ``compute_at_junction``
594
+ contre la GT du document — comportement automatique du
595
+ Sprint 63 que le canonique laisse au caller.
596
+ """
597
+ error = _translate_canonical_error(canonical_sr.error)
598
+
599
+ produced_at: list[ArtifactType] = []
600
+ for type_value in canonical_sr.produced_artifacts:
601
+ try:
602
+ produced_at.append(ArtifactType(type_value))
603
+ except ValueError:
604
+ continue
605
+
606
+ junction_metrics = _compute_junction_metrics_for_step(
607
+ produced_at, canonical_sr, registry, document,
608
+ )
609
+
610
+ return StepResult(
611
+ step_name=legacy_step.name,
612
+ duration_seconds=canonical_sr.duration_seconds,
613
+ output_types=tuple(produced_at),
614
+ junction_metrics=junction_metrics,
615
+ error=error,
616
+ )
617
+
618
+
619
+ def _compute_junction_metrics_for_step(
620
+ produced_at: list[ArtifactType],
621
+ canonical_sr: _CanonicalStepResult,
622
+ registry: _PayloadRegistry,
623
+ document: Document,
624
+ ) -> dict[str, dict[str, Any]]:
625
+ """Calcule ``junction_metrics`` en post-traitant les outputs.
626
+
627
+ Pour chaque ``ArtifactType`` produit, retrouve le payload via
628
+ ``registry`` (les ``Artifact`` du canonique ne portent pas de
629
+ ``content`` direct — voir ``_BaseModuleAdapter``) puis appelle
630
+ ``compute_at_junction(gt, payload, (T, T))`` exactement comme le
631
+ Sprint 63. Les exceptions par jonction sont logguées et la
632
+ jonction est silencieusement ignorée — comportement historique.
633
+ """
634
+ junction_metrics: dict[str, dict[str, Any]] = {}
635
+ for at in produced_at:
636
+ gt_level = _artifact_type_to_gt_level(at)
637
+ if gt_level is None:
638
+ continue
639
+ gt_payload = document.get_gt(gt_level)
640
+ if gt_payload is None:
641
+ continue
642
+ artifact_id = canonical_sr.produced_artifacts.get(at.value)
643
+ if artifact_id is None or artifact_id not in registry:
644
+ continue
645
+ payload = registry.get(artifact_id)
646
  try:
647
+ metrics = compute_at_junction(
648
+ _gt_payload_to_value(gt_payload),
649
+ payload,
650
+ (at, at),
651
+ )
652
  except Exception as exc: # noqa: BLE001
 
653
  logger.warning(
654
+ "[pipeline_runner] évaluation à la jonction %s "
655
+ "a levé : %s",
656
+ at.value, exc,
657
  )
658
+ continue
659
+ if metrics:
660
+ junction_metrics[at.value] = metrics
661
+
662
+ # Phase 4-bis : double-clé pour rétrocompat. Les tests
663
+ # legacy cherchent junction_metrics["text"] mais le runner
664
+ # peut produire junction_metrics["raw_text"] si l'enum est
665
+ # migré (ArtifactType.TEXT alias de RAW_TEXT, valeur
666
+ # "raw_text"). expand_legacy_keys ajoute la clé legacy
667
+ # ("text") à côté de la canonique ("raw_text") sans écraser.
668
+ from picarones.domain.artifacts import expand_legacy_keys
669
+ expand_legacy_keys(junction_metrics)
670
+ return junction_metrics
671
+
672
+
673
+ def _translate_canonical_error(canonical_error: str | None) -> Optional[str]:
674
+ """Traduit un message d'erreur canonique vers le format legacy.
675
+
676
+ Le ``PipelineExecutor`` produit des messages structurés avec un
677
+ préfixe (``adapter_raised:``, ``missing_input:``, ``missing_output:``,
678
+ ``adapter_not_found:``). Les tests legacy s'attendent à des
679
+ messages français du Sprint 63 — on convertit pour préserver
680
+ rétrocompat strict tant que la sub-phase 7.C n'a pas migré les
681
+ tests.
682
+ """
683
+ if canonical_error is None:
684
+ return None
685
+ if canonical_error.startswith("adapter_raised: "):
686
+ # "adapter_raised: TypeError: bla" "TypeError: bla"
687
+ return canonical_error[len("adapter_raised: "):]
688
+ if canonical_error.startswith("missing_input: "):
689
+ miss = canonical_error[len("missing_input: "):]
690
+ return f"entrée manquante : {miss}"
691
+ if canonical_error.startswith("missing_output: "):
692
+ # Format canonique : "missing_output: ['raw_text', 'alto_xml']"
693
+ # On parse cette repr de liste pour produire le format legacy
694
+ # "sortie manquante : raw_text,alto_xml".
695
+ miss_repr = canonical_error[len("missing_output: "):]
696
+ miss = miss_repr.strip("[]").replace("'", "").replace(" ", "")
697
+ return f"sortie manquante : {miss}"
698
+ if canonical_error.startswith("adapter_not_found: "):
699
+ adapter = canonical_error[len("adapter_not_found: "):]
700
+ return f"adapter introuvable : {adapter}"
701
+ if canonical_error.startswith("adapter_resolver_failed: "):
702
+ msg = canonical_error[len("adapter_resolver_failed: "):]
703
+ return f"résolution adapter échouée : {msg}"
704
+ return canonical_error
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
 
706
 
707
  def _gt_payload_to_value(payload: Any) -> Any:
picarones/reports_v2/html/renderers/pipeline.py CHANGED
@@ -54,8 +54,8 @@ from html import escape as _e
54
  from typing import Optional
55
 
56
  from picarones.domain.artifacts import ArtifactType
57
- from picarones.evaluation.pipeline_benchmark import PipelineBenchmarkResult
58
- from picarones.evaluation.pipeline_comparison import PipelineComparisonResult
59
  from picarones.reports_v2._helpers.render_helpers import color_traffic_light
60
 
61
 
 
54
  from typing import Optional
55
 
56
  from picarones.domain.artifacts import ArtifactType
57
+ from picarones.pipeline.legacy_pipeline_benchmark import PipelineBenchmarkResult
58
+ from picarones.pipeline.legacy_pipeline_comparison import PipelineComparisonResult
59
  from picarones.reports_v2._helpers.render_helpers import color_traffic_light
60
 
61
 
tests/architecture/test_doc_paths.py CHANGED
@@ -110,7 +110,13 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
110
  #: - ``docs/migration/{executor-equivalence, legacy-retirement-plan}.md`` :
111
  #: audits/plans historiques (citent des chemins legacy à des fins
112
  #: de comparaison).
113
- BROKEN_PATHS_BASELINE = 138
 
 
 
 
 
 
114
 
115
  #: Patrons de fichiers de documentation à scanner.
116
  DOC_GLOBS: tuple[str, ...] = (
 
110
  #: - ``docs/migration/{executor-equivalence, legacy-retirement-plan}.md`` :
111
  #: audits/plans historiques (citent des chemins legacy à des fins
112
  #: de comparaison).
113
+ # Phase 7.B.2 : +3 broken paths — la doc référence
114
+ # ``picarones.evaluation.pipeline_benchmark`` /
115
+ # ``pipeline_comparison`` / ``pipeline`` qui ont migré vers
116
+ # ``picarones.pipeline.legacy_*``. Les docs concernées
117
+ # (CHANGELOG.md, audits, sub-plans) gardent volontairement les
118
+ # anciens chemins pour la traçabilité historique.
119
+ BROKEN_PATHS_BASELINE = 141
120
 
121
  #: Patrons de fichiers de documentation à scanner.
122
  DOC_GLOBS: tuple[str, ...] = (
tests/architecture/test_file_budgets.py CHANGED
@@ -69,9 +69,16 @@ FILE_BUDGETS: dict[str, int] = {
69
  "picarones/measurements/history.py": 725, # actuel 615
70
  "picarones/measurements/modern_archives.py": 700, # actuel 599
71
  "picarones/measurements/builtin_hooks.py": 700, # actuel 590
72
- # Phase 5.C.batch7 : ``core/pipeline.py`` est désormais un shim ;
73
- # canonique dans ``evaluation/pipeline.py``.
74
- "picarones/evaluation/pipeline.py": 700, # actuel 622
 
 
 
 
 
 
 
75
  "picarones/extras/importers/iiif.py": 675, # actuel 567
76
  "picarones/extras/importers/gallica.py": 675, # actuel 563
77
  # Sprint A14-S10 + Lot D — déplacés depuis measurements/.
 
69
  "picarones/measurements/history.py": 725, # actuel 615
70
  "picarones/measurements/modern_archives.py": 700, # actuel 599
71
  "picarones/measurements/builtin_hooks.py": 700, # actuel 590
72
+ # Phase 7.B.2 : le runner legacy a migré vers
73
+ # ``pipeline/legacy_runner.py`` parce qu'il importe désormais le
74
+ # ``PipelineExecutor`` canonique (couche pipeline) interdit à
75
+ # ``evaluation/`` par la règle d'architecture concentrique.
76
+ # Le module a gagné ~100 LOC pour les helpers de délégation
77
+ # (_delegate_to_canonical_executor, _build_legacy_step_result,
78
+ # _translate_canonical_error) en remplacement de la boucle
79
+ # _run_step supprimée. Sera divisé en 7.D quand le runner
80
+ # legacy disparaît au profit du canonique direct.
81
+ "picarones/pipeline/legacy_runner.py": 825, # actuel 735
82
  "picarones/extras/importers/iiif.py": 675, # actuel 567
83
  "picarones/extras/importers/gallica.py": 675, # actuel 563
84
  # Sprint A14-S10 + Lot D — déplacés depuis measurements/.
tests/core/test_public_api.py CHANGED
@@ -235,8 +235,12 @@ class TestRunnerApi:
235
 
236
 
237
  # ──────────────────────────────────────────────────────────────────────────
238
- # 6. picarones.evaluation.pipeline — banc d'essai pipelines (canonique)
239
  # ──────────────────────────────────────────────────────────────────────────
 
 
 
 
240
 
241
 
242
  class TestPipelineRunnerApi:
@@ -245,7 +249,7 @@ class TestPipelineRunnerApi:
245
  "StepResult", "PipelineResult", "PipelineRunner",
246
  ])
247
  def test_class_exists(self, name):
248
- _assert_class("picarones.evaluation.pipeline", name)
249
 
250
 
251
  class TestPipelineBenchmarkApi:
@@ -498,9 +502,9 @@ class TestApiStableDoc:
498
  "picarones.evaluation.benchmark_result",
499
  "picarones.measurements.metrics",
500
  "picarones.measurements.runner",
501
- "picarones.evaluation.pipeline",
502
- "picarones.measurements.pipeline_benchmark",
503
- "picarones.measurements.pipeline_comparison",
504
  "picarones.measurements.pipeline_spec_loader",
505
  "picarones.evaluation.metric_registry",
506
  "picarones.evaluation.metric_hooks",
 
235
 
236
 
237
  # ──────────────────────────────────────────────────────────────────────────
238
+ # 6. picarones.pipeline.legacy_runner — banc d'essai pipelines (legacy)
239
  # ──────────────────────────────────────────────────────────────────────────
240
+ # Phase 7.B.2 : ce module a migré depuis ``picarones.evaluation.pipeline``
241
+ # vers ``picarones.pipeline.legacy_runner`` parce que sa délégation au
242
+ # ``PipelineExecutor`` canonique l'oblige à importer la couche
243
+ # ``pipeline/`` — interdit à ``evaluation/``.
244
 
245
 
246
  class TestPipelineRunnerApi:
 
249
  "StepResult", "PipelineResult", "PipelineRunner",
250
  ])
251
  def test_class_exists(self, name):
252
+ _assert_class("picarones.pipeline.legacy_runner", name)
253
 
254
 
255
  class TestPipelineBenchmarkApi:
 
502
  "picarones.evaluation.benchmark_result",
503
  "picarones.measurements.metrics",
504
  "picarones.measurements.runner",
505
+ "picarones.pipeline.legacy_runner",
506
+ "picarones.pipeline.legacy_pipeline_benchmark",
507
+ "picarones.pipeline.legacy_pipeline_comparison",
508
  "picarones.measurements.pipeline_spec_loader",
509
  "picarones.evaluation.metric_registry",
510
  "picarones.evaluation.metric_hooks",
tests/core/test_sprint63_pipeline_runner.py CHANGED
@@ -29,7 +29,7 @@ from typing import Any
29
  from picarones.evaluation.corpus import Document, GTLevel, TextGT
30
  from picarones.domain.artifacts import ArtifactType
31
  from picarones.domain.module_protocol import BaseModule
32
- from picarones.evaluation.pipeline import (
33
  PipelineResult,
34
  PipelineRunner,
35
  PipelineSpec,
 
29
  from picarones.evaluation.corpus import Document, GTLevel, TextGT
30
  from picarones.domain.artifacts import ArtifactType
31
  from picarones.domain.module_protocol import BaseModule
32
+ from picarones.pipeline.legacy_runner import (
33
  PipelineResult,
34
  PipelineRunner,
35
  PipelineSpec,
tests/core/test_sprint66_dag_branching.py CHANGED
@@ -33,7 +33,7 @@ from typing import Any
33
  from picarones.evaluation.corpus import Document, GTLevel, TextGT
34
  from picarones.domain.artifacts import ArtifactType
35
  from picarones.domain.module_protocol import BaseModule
36
- from picarones.evaluation.pipeline import (
37
  PipelineRunner,
38
  PipelineSpec,
39
  PipelineStep,
 
33
  from picarones.evaluation.corpus import Document, GTLevel, TextGT
34
  from picarones.domain.artifacts import ArtifactType
35
  from picarones.domain.module_protocol import BaseModule
36
+ from picarones.pipeline.legacy_runner import (
37
  PipelineRunner,
38
  PipelineSpec,
39
  PipelineStep,
tests/integration/test_alto_baseline.py CHANGED
@@ -30,7 +30,7 @@ from picarones.evaluation.corpus import AltoGT, Document, GTLevel, TextGT
30
  from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
31
  from picarones.domain.artifacts import ArtifactType
32
  from picarones.domain.module_protocol import BaseModule
33
- from picarones.evaluation.pipeline import (
34
  PipelineRunner,
35
  PipelineSpec,
36
  PipelineStep,
 
30
  from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
31
  from picarones.domain.artifacts import ArtifactType
32
  from picarones.domain.module_protocol import BaseModule
33
+ from picarones.pipeline.legacy_runner import (
34
  PipelineRunner,
35
  PipelineSpec,
36
  PipelineStep,
tests/integration/test_pipeline_ocr_to_alto.py CHANGED
@@ -35,7 +35,7 @@ from picarones.evaluation.corpus import AltoGT, Document, GTLevel, TextGT
35
  from picarones.evaluation.metric_registry import select_metrics
36
  from picarones.domain.artifacts import ArtifactType
37
  from picarones.domain.module_protocol import BaseModule
38
- from picarones.evaluation.pipeline import (
39
  PipelineRunner,
40
  PipelineSpec,
41
  PipelineStep,
 
35
  from picarones.evaluation.metric_registry import select_metrics
36
  from picarones.domain.artifacts import ArtifactType
37
  from picarones.domain.module_protocol import BaseModule
38
+ from picarones.pipeline.legacy_runner import (
39
  PipelineRunner,
40
  PipelineSpec,
41
  PipelineStep,
tests/measurements/test_sprint64_pipeline_benchmark.py CHANGED
@@ -32,13 +32,13 @@ from typing import Any
32
  from picarones.evaluation.corpus import Corpus, Document, GTLevel, TextGT
33
  from picarones.domain.artifacts import ArtifactType
34
  from picarones.domain.module_protocol import BaseModule
35
- from picarones.evaluation.pipeline_benchmark import (
36
  PipelineBenchmarkResult,
37
  StepAggregate,
38
  default_initial_inputs,
39
  run_pipeline_benchmark,
40
  )
41
- from picarones.evaluation.pipeline import PipelineSpec, PipelineStep
42
 
43
 
44
  # ──────────────────────────────────────────────────────────────────────────
 
32
  from picarones.evaluation.corpus import Corpus, Document, GTLevel, TextGT
33
  from picarones.domain.artifacts import ArtifactType
34
  from picarones.domain.module_protocol import BaseModule
35
+ from picarones.pipeline.legacy_pipeline_benchmark import (
36
  PipelineBenchmarkResult,
37
  StepAggregate,
38
  default_initial_inputs,
39
  run_pipeline_benchmark,
40
  )
41
+ from picarones.pipeline.legacy_runner import PipelineSpec, PipelineStep
42
 
43
 
44
  # ──────────────────────────────────────────────────────────────────────────
tests/measurements/test_sprint65_pipeline_comparison.py CHANGED
@@ -34,11 +34,11 @@ import pytest
34
  from picarones.evaluation.corpus import Corpus, Document, GTLevel, TextGT
35
  from picarones.domain.artifacts import ArtifactType
36
  from picarones.domain.module_protocol import BaseModule
37
- from picarones.evaluation.pipeline_comparison import (
38
  PipelineComparisonResult,
39
  compare_pipelines,
40
  )
41
- from picarones.evaluation.pipeline import PipelineSpec, PipelineStep
42
 
43
 
44
  # ──────────────────────────────────────────────────────────────────────────
 
34
  from picarones.evaluation.corpus import Corpus, Document, GTLevel, TextGT
35
  from picarones.domain.artifacts import ArtifactType
36
  from picarones.domain.module_protocol import BaseModule
37
+ from picarones.pipeline.legacy_pipeline_comparison import (
38
  PipelineComparisonResult,
39
  compare_pipelines,
40
  )
41
+ from picarones.pipeline.legacy_runner import PipelineSpec, PipelineStep
42
 
43
 
44
  # ──────────────────────────────────────────────────────────────────────────
tests/report/test_sprint67_pipeline_html.py CHANGED
@@ -21,7 +21,7 @@ from __future__ import annotations
21
  import json
22
  from pathlib import Path
23
 
24
- from picarones.evaluation.pipeline_benchmark import (
25
  PipelineBenchmarkResult,
26
  StepAggregate,
27
  )
 
21
  import json
22
  from pathlib import Path
23
 
24
+ from picarones.pipeline.legacy_pipeline_benchmark import (
25
  PipelineBenchmarkResult,
26
  StepAggregate,
27
  )
tests/report/test_sprint68_pipeline_comparison_html.py CHANGED
@@ -32,11 +32,11 @@ import json
32
  from pathlib import Path
33
 
34
  from picarones.domain.artifacts import ArtifactType
35
- from picarones.evaluation.pipeline_benchmark import (
36
  PipelineBenchmarkResult,
37
  StepAggregate,
38
  )
39
- from picarones.evaluation.pipeline_comparison import PipelineComparisonResult
40
  from picarones.reports_v2.html.renderers.pipeline import (
41
  RankingSpec,
42
  build_pipeline_comparison_report_html,
 
32
  from pathlib import Path
33
 
34
  from picarones.domain.artifacts import ArtifactType
35
+ from picarones.pipeline.legacy_pipeline_benchmark import (
36
  PipelineBenchmarkResult,
37
  StepAggregate,
38
  )
39
+ from picarones.pipeline.legacy_pipeline_comparison import PipelineComparisonResult
40
  from picarones.reports_v2.html.renderers.pipeline import (
41
  RankingSpec,
42
  build_pipeline_comparison_report_html,