This commit is contained in:
2026-02-21 16:22:33 -05:00
parent d2568cd616
commit 9272fd42d6
5 changed files with 325 additions and 91 deletions

139
gui.py
View File

@@ -54,13 +54,19 @@ _KIND_COLORS = {
_HEAVY_KEYS = {"message", "text", "script", "output", "content"}
# Label colours used in rich rendering
_LABEL_COLOR = (180, 180, 180)
_VALUE_COLOR = (220, 220, 220)
_KEY_COLOR = (140, 200, 255) # dict key / call index
_NUM_COLOR = (180, 255, 180) # numbers / token counts
_SUBHDR_COLOR = (220, 200, 120) # sub-section header
def _add_comms_field(parent: str, label: str, value: str, heavy: bool):
"""Add a labelled field inside parent. Heavy fields get a clamped input_text box."""
def _add_text_field(parent: str, label: str, value: str):
"""Render a labelled text value; long values get a scrollable box."""
with dpg.group(horizontal=False, parent=parent):
dpg.add_text(f"{label}:", color=(200, 200, 200))
if heavy and len(value) > COMMS_CLAMP_CHARS:
# Show clamped scrollable box
dpg.add_text(f"{label}:", color=_LABEL_COLOR)
if len(value) > COMMS_CLAMP_CHARS:
dpg.add_input_text(
default_value=value,
multiline=True,
@@ -69,7 +75,116 @@ def _add_comms_field(parent: str, label: str, value: str, heavy: bool):
height=80,
)
else:
dpg.add_text(value if value else "(empty)", wrap=460)
dpg.add_text(value if value else "(empty)", wrap=460, color=_VALUE_COLOR)
def _add_kv_row(parent: str, key: str, val, val_color=None):
"""Single key: value row, horizontally laid out."""
vc = val_color or _VALUE_COLOR
with dpg.group(horizontal=True, parent=parent):
dpg.add_text(f"{key}:", color=_LABEL_COLOR)
dpg.add_text(str(val), color=vc)
def _render_usage(parent: str, usage: dict):
"""Render Anthropic usage dict as a compact token table."""
if not usage:
return
dpg.add_text("usage:", color=_SUBHDR_COLOR, parent=parent)
order = [
"input_tokens",
"cache_read_input_tokens",
"cache_creation_input_tokens",
"output_tokens",
]
shown: set = set()
for key in order:
if key in usage:
shown.add(key)
_add_kv_row(parent, f" {key.replace('_', ' ')}", usage[key], _NUM_COLOR)
for key, val in usage.items():
if key not in shown:
_add_kv_row(parent, f" {key}", val, _NUM_COLOR)
def _render_tool_calls_list(parent: str, tool_calls: list):
"""Render a list of tool_call dicts inline."""
if not tool_calls:
dpg.add_text(" (none)", color=_VALUE_COLOR, parent=parent)
return
for i, tc in enumerate(tool_calls):
dpg.add_text(f" call[{i}] {tc.get('name', '?')}", color=_KEY_COLOR, parent=parent)
if "id" in tc:
_add_kv_row(parent, " id", tc["id"])
args = tc.get("args") or tc.get("input") or {}
if isinstance(args, dict):
for ak, av in args.items():
_add_text_field(parent, f" {ak}", str(av))
elif args:
_add_text_field(parent, " args", str(args))
# ---- kind-specific renderers ------------------------------------------------
def _render_payload_request(parent: str, payload: dict):
_add_text_field(parent, "message", payload.get("message", ""))
def _render_payload_response(parent: str, payload: dict):
_add_kv_row(parent, "round", payload.get("round", ""))
_add_kv_row(parent, "stop_reason", payload.get("stop_reason", ""), (255, 200, 120))
text = payload.get("text", "")
if text:
_add_text_field(parent, "text", text)
dpg.add_text("tool_calls:", color=_LABEL_COLOR, parent=parent)
_render_tool_calls_list(parent, payload.get("tool_calls", []))
usage = payload.get("usage")
if usage:
_render_usage(parent, usage)
def _render_payload_tool_call(parent: str, payload: dict):
_add_kv_row(parent, "name", payload.get("name", ""))
if "id" in payload:
_add_kv_row(parent, "id", payload["id"])
_add_text_field(parent, "script", payload.get("script", ""))
def _render_payload_tool_result(parent: str, payload: dict):
_add_kv_row(parent, "name", payload.get("name", ""))
if "id" in payload:
_add_kv_row(parent, "id", payload["id"])
_add_text_field(parent, "output", payload.get("output", ""))
def _render_payload_tool_result_send(parent: str, payload: dict):
for i, r in enumerate(payload.get("results", [])):
dpg.add_text(f"result[{i}]", color=_KEY_COLOR, parent=parent)
_add_kv_row(parent, " tool_use_id", r.get("tool_use_id", ""))
_add_text_field(parent, " content", str(r.get("content", "")))
def _render_payload_generic(parent: str, payload: dict):
"""Fallback: render any unknown payload kind as labelled fields."""
import json
for key, val in payload.items():
if isinstance(val, (dict, list)):
val_str = json.dumps(val, ensure_ascii=False, indent=2)
else:
val_str = str(val)
if key in _HEAVY_KEYS:
_add_text_field(parent, key, val_str)
else:
_add_kv_row(parent, key, val_str)
_KIND_RENDERERS = {
"request": _render_payload_request,
"response": _render_payload_response,
"tool_call": _render_payload_tool_call,
"tool_result": _render_payload_tool_result,
"tool_result_send": _render_payload_tool_result_send,
}
def _render_comms_entry(parent: str, entry: dict, idx: int):
@@ -92,15 +207,9 @@ def _render_comms_entry(parent: str, entry: dict, idx: int):
dpg.add_text(kind, color=kind_color)
dpg.add_text(f"{provider}/{model}", color=(180, 180, 180))
# Payload fields
for key, val in payload.items():
is_heavy = key in _HEAVY_KEYS
if isinstance(val, (dict, list)):
import json
val_str = json.dumps(val, ensure_ascii=False, indent=2)
else:
val_str = str(val)
_add_comms_field(parent, key, val_str, is_heavy)
# Payload - use rich renderer if available, else generic fallback
renderer = _KIND_RENDERERS.get(kind, _render_payload_generic)
renderer(parent, payload)
dpg.add_separator()