Skip to content
Merged
Show file tree
Hide file tree
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
41 changes: 41 additions & 0 deletions tests/test_tui_info_formatting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import json

from verifiers.scripts.tui import format_info_for_details


def test_format_info_for_details_handles_dict() -> None:
info = {"status": "ok", "attempt": 2}

rendered = format_info_for_details(info)

assert rendered == json.dumps(info, ensure_ascii=False, indent=2)


def test_format_info_for_details_parses_json_string() -> None:
info = '{"status":"ok","nested":{"value":1}}'

rendered = format_info_for_details(info)

assert rendered == json.dumps(
{"status": "ok", "nested": {"value": 1}},
ensure_ascii=False,
indent=2,
)


def test_format_info_for_details_preserves_large_content() -> None:
info = {"payload": [f"line-{i}" for i in range(200)]}

rendered = format_info_for_details(info)

assert "line-199" in rendered
assert "(truncated;" not in rendered


def test_format_info_for_details_handles_non_serializable_data() -> None:
info: dict[str, object] = {"callback": lambda: "x"}

rendered = format_info_for_details(info)

assert "callback" in rendered
assert "function" in rendered
46 changes: 39 additions & 7 deletions verifiers/scripts/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,32 @@ def format_prompt_or_completion(prompt_or_completion) -> Text:
return out


def _coerce_info_value(info: Any) -> Any:
"""Return parsed JSON when info is a JSON string, otherwise original value."""
if not isinstance(info, str):
return info
stripped = info.strip()
if not stripped:
return ""
if stripped[0] not in "[{":
return info
try:
return json.loads(stripped)
except json.JSONDecodeError:
return info


def format_info_for_details(info: Any) -> str:
"""Format record info for the details panel in rollout view."""
info_value = _coerce_info_value(info)
if isinstance(info_value, (dict, list)):
try:
return json.dumps(info_value, ensure_ascii=False, indent=2)
except (TypeError, ValueError):
return str(info_value)
return str(info_value)


# ----------------------------
# Custom Panel Widget
# ----------------------------
Expand Down Expand Up @@ -650,8 +676,12 @@ def compose(self) -> ComposeResult:
id="completion-scroll",
)

# Details section (horizontal scroll)
yield Panel(Static("", id="details", markup=False), classes="details-panel")
# Details section
with Panel(classes="details-panel"):
yield VerticalScroll(
Static("", id="details", markup=False),
id="details-scroll",
)

yield Footer()

Expand Down Expand Up @@ -803,13 +833,12 @@ def update_display(self) -> None:
info = record.get("info", None)
if info not in (None, {}):
details_lines.append("Info: ", style="bold")
try:
details_lines.append(json.dumps(info, ensure_ascii=False, indent=2))
except Exception:
details_lines.append(str(info))
details_lines.append(format_info_for_details(info))

task = record.get("task", None)
if task not in (None, ""):
if details_lines.plain and not details_lines.plain.endswith("\n"):
details_lines.append("\n")
details_lines.append("Task: ", style="bold")
details_lines.append(str(task))

Expand All @@ -835,6 +864,7 @@ def action_prev_record(self) -> None:
# Reset scroll positions
self.query_one("#prompt-scroll").scroll_y = 0
self.query_one("#completion-scroll").scroll_y = 0
self.query_one("#details-scroll").scroll_y = 0

def action_next_record(self) -> None:
if self.records:
Expand All @@ -843,6 +873,7 @@ def action_next_record(self) -> None:
# Reset scroll positions
self.query_one("#prompt-scroll").scroll_y = 0
self.query_one("#completion-scroll").scroll_y = 0
self.query_one("#details-scroll").scroll_y = 0

def action_search(self) -> None:
if not self.records:
Expand Down Expand Up @@ -1063,7 +1094,7 @@ class VerifiersTUI(App):
text-style: bold;
}

#prompt-scroll, #completion-scroll {
#prompt-scroll, #completion-scroll, #details-scroll {
height: 1fr;
background: $surface;
padding: 0 1;
Expand All @@ -1077,6 +1108,7 @@ class VerifiersTUI(App):
min-height: 3;
max-height: 6;
}


.run-list-panel {
height: 1fr;
Expand Down
6 changes: 3 additions & 3 deletions verifiers/workers/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ class BaseResponse(BaseModel):


class HealthRequest(BaseRequest):
request_type: Literal["health"] = "health" # type: ignore[override]
request_type: Literal["health"] = "health"


class HealthResponse(BaseResponse): ...


class RunRolloutRequest(BaseRequest):
request_type: Literal["run_rollout"] = "run_rollout" # type: ignore[override]
request_type: Literal["run_rollout"] = "run_rollout"

# skip validation because multi-modal content type + tool calls validate weirdly
# (https://github.com/PrimeIntellect-ai/prime-rl/pull/1249)
Expand All @@ -54,7 +54,7 @@ class RunRolloutResponse(BaseResponse):


class RunGroupRequest(BaseRequest):
request_type: Literal["run_group"] = "run_group" # type: ignore[override]
request_type: Literal["run_group"] = "run_group"

# skip validation because multi-modal content type + tool calls validate weirdly
# (https://github.com/PrimeIntellect-ai/prime-rl/pull/1249)
Expand Down
Loading