Source code for klea_utils.ui.web.nicegui.components.inspector
#!/usr/bin/env python3
"""
Inspector panel component: streamed info/debug entries for the active chat.
File: klea_utils/ui/web/nicegui/components/inspector.py
Copyright 2026 Ankur Sinha
Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
"""
import json
import logging
from nicegui import ui
from klea_utils.ui.web.nicegui.components.context import PageContext
from klea_utils.ui.web.nicegui.state import chats
logger = logging.getLogger(__name__)
def _toggle_inspector_entry(ctx: PageContext, idx: int) -> None:
"""Toggle the expanded/collapsed state of an inspector entry."""
current_chat = chats.get(f"{ctx.user_id}:{ctx.chat_id}")
if not current_chat:
return
expanded = current_chat.setdefault("inspector_expanded", set())
if idx in expanded:
expanded.discard(idx)
else:
expanded.add(idx)
[docs]
def attach_inspector_panel(ctx: PageContext) -> None:
"""Build the inspector panel for the active chat in the inspect tab.
Must be called with the inspect tab panel as the ambient NiceGUI
context. Registers ``refresh_inspector`` and ``reset_center_tab``
on the context and renders the (empty) initial state.
:param ctx: The shared page context.
"""
@ui.refreshable
def _render_inspector_panel() -> None:
"""Render the inspector entries for the active chat in the inspect tab."""
with ui.column().classes("w-full px-2 gap-0"):
current_chat = chats.get(f"{ctx.user_id}:{ctx.chat_id}")
if not current_chat or not current_chat.get("inspector_entries"):
ui.label("No inspection data yet").classes("text-sm text-grey-5 py-8")
ui.label(
"Inspector entries will appear here after a query completes."
).classes("text-xs text-grey-5")
return
entries = list(current_chat["inspector_entries"])
logger.debug(
"Rendering inspector panel for chat %s (entries=%d)",
ctx.chat_id,
len(entries),
)
for idx, entry in enumerate(entries):
heading = entry.get("heading", "")
timing = entry.get("timing_seconds", None)
with (
ui.element("details")
.props("open")
.classes("inspector-entry mb-2 w-full")
):
with ( # noqa: SIM117
ui.element("summary")
.classes("text-xs font-bold cursor-pointer w-full")
.on("click", lambda i=idx: _toggle_inspector_entry(ctx, i))
):
with ui.row().classes("w-full flex-nowrap items-center"):
ui.label(heading)
if timing:
ui.label(f"({timing:.1f}s)").classes(
"text-xs text-grey-5"
)
ui.label(entry.get("summary", "")).classes(
"text-xs text-grey-6 mb-1 w-full"
)
details = entry.get("details", {})
if details:
with ui.element("details").classes(
"inspector-details text-xs text-grey-5 cursor-pointer w-full"
):
with ui.element("summary").classes("text-xs w-full"):
ui.label("View details")
ui.code(
json.dumps(details, indent=2), language="json"
).classes("text-xs")
ctx.refresh_inspector = _render_inspector_panel.refresh
ctx.reset_center_tab = lambda: (
ctx.center_panels.set_value("chat") if ctx.center_panels is not None else None
)
_render_inspector_panel()