File size: 9,119 Bytes
fe6661c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979f3c3
fe6661c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
"""Vue pipeline composée — chantier 3 post-Sprint 97.

Regroupe les renderers spécifiques aux benchmarks de **pipelines
composées** (axe B du plan d'évolution 2026, Sprints 63-68, 94-97) :

- :func:`picarones.report.pipeline_render.build_pipeline_summary_html`
  — résumé corpus-wide (taux de succès, durée, métriques aux jonctions).
- :func:`picarones.report.pipeline_render.build_pipeline_steps_table_html`
  — tableau par étape (Sprint 67).
- :func:`picarones.report.pipeline_dag_render.build_pipeline_dag_html`
  — visualisation SVG du DAG avec couleur des arêtes selon la métrique.
- :func:`picarones.report.error_absorption_render.build_error_absorption_html`
  — corrections vs introductions à chaque jonction (Sprint 94).
- :func:`picarones.report.incremental_comparison_render.build_incremental_comparison_html`
  — effet isolé d'un slot (LLM, reconstructeur, etc.) en contrôlant
  les autres (Sprint 96).
- :func:`picarones.report.module_audit_render.build_module_audit_html`
  — audit de conformité des modules contribués (Sprint 97).

Cette vue ne s'applique pas au rapport standard (mono-moteur OCR
classique). Elle est appelée explicitement par le workflow
``picarones pipeline run`` (CLI Sprint 70) et par tout outil
extérieur qui consomme un ``PipelineBenchmarkResult``.

Sources de données
------------------
Toutes les sous-sections consomment des structures opt-in passées
en ``opts``. Aucune n'est calculée à partir de ``report_data`` —
c'est par construction (un rapport classique n'a pas de DAG).

- ``opts["pipeline_benchmark"]`` : ``PipelineBenchmarkResult`` (Sprint 64).
- ``opts["dag_nodes"]`` / ``opts["dag_labels"]`` / ``opts["dag_edges"]``
  / ``opts["dag_thresholds"]`` / ``opts["dag_higher_is_better"]`` :
  arguments directs de :func:`build_pipeline_dag_html`.
- ``opts["junctions"]`` : liste de jonctions avec leurs paires
  ``before/after`` pour :func:`build_error_absorption_html`.
- ``opts["incremental_runs"]`` + ``opts["incremental_varying_slot"]`` :
  arguments de :func:`build_incremental_comparison_html`.
- ``opts["module_audits"]`` : liste de ``(manifest, audit_result)``.
"""

from __future__ import annotations

import logging
from typing import Any, Optional

logger = logging.getLogger(__name__)


def build_pipeline_view_html(
    report_data: Optional[dict] = None,
    labels: Optional[dict[str, str]] = None,
    *,
    pipeline_benchmark: Optional[Any] = None,
    dag_nodes: Optional[list] = None,
    dag_labels: Optional[dict[str, str]] = None,
    dag_edges: Optional[list] = None,
    dag_thresholds: Optional[tuple[float, float]] = None,
    dag_higher_is_better: bool = False,
    junctions: Optional[list[dict]] = None,
    incremental_runs: Optional[list] = None,
    incremental_varying_slot: Optional[str] = None,
    incremental_higher_is_better: bool = False,
    module_audits: Optional[list[tuple]] = None,
) -> str:
    """Compose la vue pipeline.

    Parameters
    ----------
    report_data:
        Inutilisé pour cette vue (la pipeline composée a sa propre
        structure de données via ``PipelineBenchmarkResult``).
        Présent dans la signature pour homogénéité avec les autres
        vues du chantier 3.
    labels:
        Dict i18n complet.
    pipeline_benchmark:
        ``PipelineBenchmarkResult`` (Sprint 64) — active les sections
        ``summary`` et ``steps_table`` du :mod:`pipeline_render`.
    dag_nodes, dag_labels, dag_edges, dag_thresholds, dag_higher_is_better:
        Arguments de :func:`build_pipeline_dag_html` (Sprint 95).
    junctions:
        Liste de dicts ``{junction_name, before, after, ...}`` pour
        :func:`build_error_absorption_html` (Sprint 94).
    incremental_runs, incremental_varying_slot, incremental_higher_is_better:
        Arguments de :func:`build_incremental_comparison_html`
        (Sprint 96).
    module_audits:
        Liste de tuples ``(ModuleManifest, AuditResult)`` pour
        :func:`build_module_audit_html` (Sprint 97).

    Returns
    -------
    str
        HTML de la vue ou ``""`` si aucune sous-section opt-in
        n'est fournie.
    """
    labels = labels or {}
    blocks: list[tuple[str, str]] = []

    # Sous-section 1 : résumé + steps table
    if pipeline_benchmark is not None:
        try:
            from picarones.report.pipeline_render import (
                build_pipeline_steps_table_html,
                build_pipeline_summary_html,
            )
            summary = build_pipeline_summary_html(pipeline_benchmark)
            steps = build_pipeline_steps_table_html(pipeline_benchmark)
            combined = "\n".join(filter(None, [summary, steps]))
            if combined:
                blocks.append((
                    labels.get(
                        "pipeline_summary_title",
                        "Résumé de la pipeline",
                    ),
                    combined,
                ))
        except Exception as exc:  # noqa: BLE001
            logger.warning(
                "[pipeline_view.summary] dégradé : %s", exc,
            )

    # Sous-section 2 : DAG visualization
    if dag_nodes:
        try:
            from picarones.report.pipeline_dag_render import (
                build_pipeline_dag_html,
            )
            html = build_pipeline_dag_html(
                nodes=dag_nodes,
                labels=dag_labels or {},
                edges=dag_edges,
                thresholds=dag_thresholds or (0.05, 0.15),
                higher_is_better=dag_higher_is_better,
            )
            if html:
                blocks.append((
                    labels.get(
                        "pipeline_dag_title",
                        "Visualisation du DAG",
                    ),
                    html,
                ))
        except Exception as exc:  # noqa: BLE001
            logger.warning("[pipeline_view.dag] dégradé : %s", exc)

    # Sous-section 3 : absorption d'erreur par jonction
    if junctions:
        try:
            from picarones.report.error_absorption_render import (
                build_error_absorption_html,
            )
            html = build_error_absorption_html(junctions, labels=labels)
            if html:
                blocks.append((
                    labels.get(
                        "pipeline_absorption_title",
                        "Absorption d'erreur par jonction",
                    ),
                    html,
                ))
        except Exception as exc:  # noqa: BLE001
            logger.warning(
                "[pipeline_view.error_absorption] dégradé : %s", exc,
            )

    # Sous-section 4 : comparaison incrémentale (effet d'un slot)
    if incremental_runs and incremental_varying_slot:
        try:
            from picarones.measurements.incremental_comparison import (
                compare_isolated_effect,
            )
            from picarones.report.incremental_comparison_render import (
                build_incremental_comparison_html,
            )
            comparison = compare_isolated_effect(
                incremental_runs,
                incremental_varying_slot,
                higher_is_better=incremental_higher_is_better,
            )
            html = build_incremental_comparison_html(
                comparison,
                varying_slot=incremental_varying_slot,
                labels=labels,
            )
            if html:
                blocks.append((
                    labels.get(
                        "pipeline_incremental_title",
                        "Comparaison incrémentale",
                    ),
                    html,
                ))
        except Exception as exc:  # noqa: BLE001
            logger.warning(
                "[pipeline_view.incremental] dégradé : %s", exc,
            )

    # Sous-section 5 : audit des modules contribués
    if module_audits:
        try:
            from picarones.report.module_audit_render import (
                build_module_audit_html,
            )
            html = build_module_audit_html(module_audits, labels=labels)
            if html:
                blocks.append((
                    labels.get(
                        "pipeline_audit_title",
                        "Audit des modules contribués",
                    ),
                    html,
                ))
        except Exception as exc:  # noqa: BLE001
            logger.warning("[pipeline_view.audit] dégradé : %s", exc)

    if not blocks:
        return ""

    from picarones.report.views.economics import _render_view_shell

    return _render_view_shell(
        view_title=labels.get(
            "pipeline_view_title", "Banc d'essai de pipeline composée",
        ),
        view_note=labels.get(
            "pipeline_view_note",
            "Vue spécifique aux pipelines composées (axe B) : "
            "métriques aux jonctions, absorption d'erreur, comparaison "
            "incrémentale par slot, audit des modules contribués.",
        ),
        blocks=blocks,
    )


__all__ = ["build_pipeline_view_html"]