Source code for ralph.pipeline._phase_transition_summary

"""Completion-summary helpers for pipeline phase transitions."""

from __future__ import annotations

from importlib import import_module
from typing import TYPE_CHECKING, Protocol, cast

from loguru import logger

from ralph.display.completion_summary import CompletionSummaryOptions
from ralph.display.parallel_display import (
    ParallelDisplay,
    get_display_context,
    resolve_active_display,
)

if TYPE_CHECKING:
    from pathlib import Path

    from ralph.display.context import DisplayContext
    from ralph.display.snapshot import PipelineSnapshot, SnapshotContext
    from ralph.display.subscriber import PipelineSubscriber
    from ralph.pipeline.state import PipelineState


if TYPE_CHECKING:

    class _ParallelDisplayModule(Protocol):
        ParallelDisplay: type[ParallelDisplay]

    class _SnapshotFromStateFn(Protocol):
        def __call__(
            self,
            state: PipelineState,
            context: SnapshotContext = ...,
        ) -> PipelineSnapshot: ...

    class _SnapshotModule(Protocol):
        snapshot_from_state: _SnapshotFromStateFn


def _snapshot_from_state_func() -> _SnapshotFromStateFn:
    module = cast("_SnapshotModule", import_module("ralph.display.snapshot"))
    return module.snapshot_from_state


[docs] def emit_final_summary( state: PipelineState, workspace_root: Path, *, subscriber: PipelineSubscriber | None = None, display: ParallelDisplay | None = None, display_context: DisplayContext, ) -> None: """Emit an end-of-run completion summary panel.""" try: snapshot_from_state = _snapshot_from_state_func() dropped_count = 0 snapshot: PipelineSnapshot | None = None if subscriber is not None: try: dropped_count = subscriber.dropped_count snapshot = subscriber.build_snapshot(state) except Exception: logger.debug( "subscriber.build_snapshot failed; falling back to raw snapshot", exc_info=True, ) if snapshot is None: snapshot = snapshot_from_state(state) pipeline_policy = subscriber.pipeline_policy if subscriber is not None else None ctx = get_display_context(display, display_context) if isinstance(display, ParallelDisplay): content_block_count: int = display.content_blocks_count thinking_block_count: int = display.thinking_blocks_count tool_call_count: int = display.tool_calls_count error_count: int = display.errors_count elapsed_seconds: float | None = display.run_elapsed_seconds else: content_block_count = 0 thinking_block_count = 0 tool_call_count = 0 error_count = 0 elapsed_seconds = None cs_opts = CompletionSummaryOptions( workspace_root=workspace_root, dropped_count=dropped_count, include_context_sections=not state.interrupted_by_user, content_block_count=content_block_count, thinking_block_count=thinking_block_count, tool_call_count=tool_call_count, error_count=error_count, elapsed_seconds=elapsed_seconds, pipeline_policy=pipeline_policy, ) # Route the end-of-run completion panel through the consolidated # ParallelDisplay surface (37th emit_* method). When a ParallelDisplay # is in scope we call directly; otherwise we resolve one from the # DisplayContext so the panel is rendered through the same console # and theme as the rest of the transcript. target_display = display if isinstance(display, ParallelDisplay) else None if target_display is None: target_display = resolve_active_display(None, ctx) target_display.emit_completion_summary_panel(snapshot, options=cs_opts) except Exception: logger.debug("Failed to emit completion summary", exc_info=True)