Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 77 additions & 24 deletions sdks/opik_optimizer/src/opik_optimizer/reporting_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,68 @@
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

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."""

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.
Expand All @@ -34,9 +86,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
Expand Down Expand Up @@ -113,6 +163,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"),
Expand All @@ -123,17 +175,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)
renderable: RenderableType = panel
if prefix:
renderable = PrefixedRenderable(panel, prefix)

# Retrieve the rendered string (with ANSI)
rendered_panel = capture.get()

# 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:
Expand Down Expand Up @@ -186,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(
Expand All @@ -214,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)

Expand Down