From adc9fe84765647fcec334500772997af530f4629 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 31 Oct 2025 17:05:22 -0700 Subject: [PATCH 1/2] Update reporting_utils.py --- .../src/opik_optimizer/reporting_utils.py | 68 +++++++++++++++---- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py b/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py index d3e34a53856..6102e658cb6 100644 --- a/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py +++ b/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py @@ -5,9 +5,17 @@ from typing import Any from rich import box -from rich.console import Console, Group +from rich.console import ( + Console, + ConsoleOptions, + Group, + RenderResult, + RenderableType, +) +from rich.measure import Measurement from rich.panel import Panel from rich.progress import track +from rich.segment import Segment from rich.text import Text from .utils import get_optimization_run_url_by_id @@ -15,6 +23,44 @@ PANEL_WIDTH = 70 +class PrefixedRenderable: + """Render helper that preserves Rich metadata while prefixing each line.""" + + def __init__(self, renderable: RenderableType, prefix: str) -> None: + self._renderable = renderable + self._prefix = prefix + + def __rich_measure__( + self, console: Console, options: ConsoleOptions + ) -> Measurement: + if not self._prefix: + return Measurement.get(console, options, self._renderable) + + prefix_measure = Measurement.get(console, options, Text(self._prefix)) + body_measure = Measurement.get(console, options, self._renderable) + return Measurement( + body_measure.minimum + prefix_measure.minimum, + body_measure.maximum + prefix_measure.maximum, + ) + + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + if not self._prefix: + yield from console.render(self._renderable, options) + return + + prefix_segments = list(console.render(Text(self._prefix), options)) + lines = console.render_lines(self._renderable, options, pad=False) + line_count = len(lines) + + for index, line in enumerate(lines): + yield from prefix_segments + yield from line + if index < line_count - 1: + yield Segment.line() + + def safe_percentage_change(current: float, baseline: float) -> tuple[float, bool]: """ Calculate percentage change safely, handling division by zero. @@ -34,9 +80,7 @@ def safe_percentage_change(current: float, baseline: float) -> tuple[float, bool def get_console(*args: Any, **kwargs: Any) -> Console: - console = Console(*args, **kwargs) - console.is_jupyter = False - return console + return Console(*args, **kwargs) @contextmanager @@ -113,6 +157,8 @@ def format_prompt_snippet(text: str, max_length: int = 100) -> str: def display_messages(messages: list[dict[str, str]], prefix: str = "") -> None: + console = get_console() + for i, msg in enumerate(messages): panel = Panel( Text(msg.get("content", ""), overflow="fold"), @@ -123,17 +169,11 @@ def display_messages(messages: list[dict[str, str]], prefix: str = "") -> None: padding=(1, 2), ) - # Capture the panel as rendered text with ANSI styles - console = get_console() - with console.capture() as capture: - console.print(panel) - - # Retrieve the rendered string (with ANSI) - rendered_panel = capture.get() + renderable: RenderableType = panel + if prefix: + renderable = PrefixedRenderable(panel, prefix) - # Prefix each line with '| ', preserving ANSI styles - for line in rendered_panel.splitlines(): - console.print(Text(prefix) + Text.from_ansi(line)) + console.print(renderable) def _format_tool_panel(tool: dict[str, Any]) -> Panel: From 80f233e573e26a0e79418bfe469184697a95f444 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 31 Oct 2025 18:18:36 -0700 Subject: [PATCH 2/2] Update reporting_utils.py --- .../src/opik_optimizer/reporting_utils.py | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py b/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py index 6102e658cb6..dc731da2f54 100644 --- a/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py +++ b/sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py @@ -23,6 +23,12 @@ PANEL_WIDTH = 70 +def _supports_inline_link(console: Console) -> bool: + if getattr(console, "is_jupyter", False): + return True + return bool(getattr(console, "is_terminal", False) and console.color_system) + + class PrefixedRenderable: """Render helper that preserves Rich metadata while prefixing each line.""" @@ -226,16 +232,22 @@ def get_link_text( ) -> Text: if optimization_id is not None and dataset_id is not None: optimization_url = get_optimization_run_url_by_id( - optimization_id=optimization_id, dataset_id=dataset_id + optimization_id=optimization_id, + dataset_id=dataset_id, ) - - # Create a visually appealing panel with an icon and ensure link doesn't wrap - link_text = Text(pre_text + link_text) - link_text.stylize(f"link {optimization_url}", len(pre_text), len(link_text)) # type: ignore else: - link_text = Text("No optimization run link available", style="dim") + return Text("No optimization run link available", style="dim") - return link_text + console = Console() + rich_text = Text(pre_text + link_text) + if _supports_inline_link(console): + rich_text.stylize(f"link {optimization_url}", len(pre_text), len(rich_text)) + return rich_text + + return Text( + f"{pre_text}{link_text}: {optimization_url}", + style="cyan", + ) def display_header( @@ -254,9 +266,10 @@ def display_header( dataset_id=dataset_id, ) - content = Text.assemble( - ("● ", "green"), "Running Opik Evaluation - ", (algorithm, "blue"), "\n\n" - ).append(link_text) + header_text = Text.assemble( + ("● ", "green"), "Running Opik Evaluation - ", (algorithm, "blue"), "\n" + ) + content = Group(header_text, link_text) panel = Panel(content, box=box.ROUNDED, width=PANEL_WIDTH)