4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
139 lines
5.0 KiB
Python
139 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from rich.text import Text
|
|
|
|
from platform.observability.trace.redaction import format_json_preview
|
|
from platform.terminal.theme import BRAND, DIM, HIGHLIGHT, SECONDARY, TEXT
|
|
from surfaces.interactive_shell.ui.components.time_format import _elapsed_hms, _fmt_timing
|
|
from surfaces.shared.tool_labels import tool_short_label, tool_source_label
|
|
|
|
__all__ = [
|
|
"build_live_tool_detail_rows",
|
|
"build_tool_call_line",
|
|
"build_tool_detail_text",
|
|
"format_tool_summary",
|
|
"make_tool_detail_record",
|
|
"record_tool_summary",
|
|
"tool_detail_body",
|
|
"tool_short_label",
|
|
"tool_source_label",
|
|
]
|
|
|
|
|
|
def record_tool_summary(
|
|
tool_name: str,
|
|
summary_counts: dict[str, dict[str, int]],
|
|
summary_order: list[tuple[str, str]],
|
|
) -> None:
|
|
source = tool_source_label(tool_name)
|
|
label = tool_short_label(tool_name, source)
|
|
source_counts = summary_counts.setdefault(source, {})
|
|
if label not in source_counts:
|
|
summary_order.append((source, label))
|
|
source_counts[label] = source_counts.get(label, 0) + 1
|
|
|
|
|
|
def format_tool_summary(
|
|
summary_counts: dict[str, dict[str, int]],
|
|
summary_order: list[tuple[str, str]],
|
|
) -> str:
|
|
source_labels: dict[str, list[str]] = {}
|
|
for source, label in summary_order:
|
|
count = summary_counts.get(source, {}).get(label, 0)
|
|
if count <= 0:
|
|
continue
|
|
rendered = f"{label} x{count}" if count > 1 else label
|
|
source_labels.setdefault(source, []).append(rendered)
|
|
parts = [
|
|
f"{source}: {', '.join(labels[:4])}{', ...' if len(labels) > 4 else ''}"
|
|
for source, labels in source_labels.items()
|
|
]
|
|
summary = " | ".join(parts[:2])
|
|
return summary[:117] + "..." if len(summary) > 120 else summary
|
|
|
|
|
|
def build_tool_call_line(tool_name: str, elapsed_ms: int, elapsed_total: float) -> Text:
|
|
source = tool_source_label(tool_name)
|
|
label = tool_short_label(tool_name, source)
|
|
call_display = f"{source} · {label}" if label else source
|
|
t = Text()
|
|
t.append(f"{_elapsed_hms(elapsed_total)} ", style=SECONDARY)
|
|
t.append(" ↳ ", style=DIM)
|
|
t.append(call_display, style=BRAND)
|
|
t.append(f" {_fmt_timing(elapsed_ms)}", style=SECONDARY)
|
|
return t
|
|
|
|
|
|
def make_tool_detail_record(
|
|
display: str,
|
|
tool_input: Any,
|
|
output: Any,
|
|
*,
|
|
elapsed: str = "",
|
|
) -> dict[str, Any] | None:
|
|
if tool_input in ({}, None) and output in ({}, None, ""):
|
|
return None
|
|
return {"display": display, "input": tool_input, "output": output, "elapsed": elapsed}
|
|
|
|
|
|
def build_tool_detail_text(record: dict[str, Any]) -> Text:
|
|
display = str(record.get("display") or "tool")
|
|
elapsed = str(record.get("elapsed") or "")
|
|
suffix = f" {elapsed}" if elapsed else ""
|
|
detail = Text()
|
|
detail.append(f" Tool details: {display}{suffix}\n", style=f"bold {TEXT}")
|
|
for line in tool_detail_body(record).splitlines():
|
|
detail.append(f" {line}\n", style=DIM)
|
|
return detail
|
|
|
|
|
|
def tool_detail_body(record: dict[str, Any]) -> str:
|
|
body_parts: list[str] = []
|
|
if (tool_input := record.get("input")) not in ({}, None):
|
|
body_parts.append(f"Input:\n{format_json_preview(tool_input, max_chars=1600)}")
|
|
if (output := record.get("output")) not in ({}, None, ""):
|
|
body_parts.append(f"Output:\n{format_json_preview(output, max_chars=3000)}")
|
|
return "\n\n".join(body_parts)
|
|
|
|
|
|
def build_live_tool_detail_rows(
|
|
records: list[dict[str, Any]], max_width: int, now: float
|
|
) -> list[Text]:
|
|
rows: list[Text] = []
|
|
rows.append(Text(" Tool Details", style=f"bold {TEXT}"))
|
|
hidden_count = max(0, len(records) - 6)
|
|
if hidden_count:
|
|
rows.append(Text(f" {hidden_count} older tool call(s) hidden", style=DIM))
|
|
if not records:
|
|
rows.append(Text(" No tool calls have finished yet.", style=DIM))
|
|
for record in records[-6:]:
|
|
elapsed = str(record.get("elapsed") or "")
|
|
suffix = f" {elapsed}" if elapsed else ""
|
|
row = Text()
|
|
row.append(" ● ", style=f"bold {HIGHLIGHT}")
|
|
row.append(str(record.get("display") or "tool"), style=f"bold {TEXT}")
|
|
row.append(suffix, style=SECONDARY)
|
|
rows.extend([row, *_detail_preview_rows(record), Text("")])
|
|
rows.append(Text("┄" * (max_width - 1), style=DIM))
|
|
rows.append(Text(f" ● TOOL DETAILS {_elapsed_hms(now)}", style=SECONDARY))
|
|
return rows
|
|
|
|
|
|
def _detail_preview_rows(record: dict[str, Any]) -> list[Text]:
|
|
rows: list[Text] = []
|
|
if (tool_input := record.get("input")) not in ({}, None):
|
|
rows.append(Text(" Input:", style=SECONDARY))
|
|
rows.extend(
|
|
Text(f" {line}", style=DIM)
|
|
for line in format_json_preview(tool_input, max_chars=1200).splitlines()
|
|
)
|
|
if (output := record.get("output")) not in ({}, None, ""):
|
|
rows.append(Text(" Output:", style=SECONDARY))
|
|
rows.extend(
|
|
Text(f" {line}", style=DIM)
|
|
for line in format_json_preview(output, max_chars=2200).splitlines()
|
|
)
|
|
return rows
|