still fixing regressions

This commit is contained in:
2026-03-06 20:27:03 -05:00
parent 4c92817928
commit 13453a0a14
6 changed files with 241 additions and 160 deletions

View File

@@ -640,6 +640,7 @@ class AppController:
"Operations Hub": True,
"Message": False,
"Response": False,
"Tool Calls": False,
"Theme": True,
"Log Management": False,
"Diagnostics": False,
@@ -1714,6 +1715,7 @@ class AppController:
"show_windows": self.show_windows,
"separate_message_panel": getattr(self, "ui_separate_message_panel", False),
"separate_response_panel": getattr(self, "ui_separate_response_panel", False),
"separate_tool_calls_panel": getattr(self, "ui_separate_tool_calls_panel", False),
}
theme.save_to_config(self.config)

View File

@@ -107,11 +107,13 @@ class App:
gui_cfg = self.config.get("gui", {})
self.ui_separate_message_panel = gui_cfg.get("separate_message_panel", False)
self.ui_separate_response_panel = gui_cfg.get("separate_response_panel", False)
self.ui_separate_tool_calls_panel = gui_cfg.get("separate_tool_calls_panel", False)
self._comms_log_cache: list[dict[str, Any]] = []
self._comms_log_dirty: bool = True
self._tool_log_cache: list[dict[str, Any]] = []
self._tool_log_dirty: bool = True
self._last_ui_focus_agent: Optional[str] = None
self._log_registry: Optional[log_registry.LogRegistry] = None
def _handle_approve_tool(self, user_data=None) -> None:
"""UI-level wrapper for approving a pending tool execution ask."""
@@ -426,15 +428,22 @@ class App:
if imgui.button("x##clear_focus"):
self.ui_focus_agent = None
if exp:
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
ch, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
if ch: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
imgui.pop_style_var()
show_tc_tab = not self.ui_separate_tool_calls_panel
if imgui.begin_tab_bar("ops_tabs"):
if imgui.begin_tab_item("Comms History")[0]:
self._render_comms_history_panel()
imgui.end_tab_item()
if imgui.begin_tab_item("Tool Calls")[0]:
self._render_tool_calls_panel()
imgui.end_tab_item()
if show_tc_tab:
if imgui.begin_tab_item("Tool Calls")[0]:
self._render_tool_calls_panel()
imgui.end_tab_item()
imgui.end_tab_bar()
imgui.end()
if self.ui_separate_message_panel and self.show_windows.get("Message", False):
@@ -451,6 +460,13 @@ class App:
self._render_response_panel()
imgui.end()
if self.ui_separate_tool_calls_panel and self.show_windows.get("Tool Calls", False):
exp, opened = imgui.begin("Tool Calls", self.show_windows["Tool Calls"])
self.show_windows["Tool Calls"] = bool(opened)
if exp:
self._render_tool_calls_panel()
imgui.end()
if self.show_windows.get("Log Management", False):
self._render_log_management()
if self.show_windows["Diagnostics"]:
@@ -863,7 +879,18 @@ class App:
if not exp:
imgui.end()
return
registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml"))
if self._log_registry is None:
self._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml"))
else:
# Refresh data occasionally or on demand? For now let's just use the cached object.
# The LogRegistry object loads data into self.data upon __init__.
# We might want a refresh button or to reload every few seconds.
if imgui.button("Refresh Registry"):
self._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml"))
imgui.same_line()
registry = self._log_registry
sessions = registry.data
if imgui.begin_table("sessions_table", 7, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
imgui.table_setup_column("Session ID")
@@ -1145,6 +1172,8 @@ class App:
if imgui.button("+" if collapsed else "-"):
entry["collapsed"] = not collapsed
imgui.same_line()
self._render_text_viewer(f"Entry #{i+1}", entry["content"])
imgui.same_line()
imgui.set_next_item_width(120)
if imgui.begin_combo("##role", entry["role"]):
for r in self.disc_roles:
@@ -1164,8 +1193,6 @@ class App:
if imgui.button("Ins"):
self.disc_entries.insert(i, {"role": "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()})
imgui.same_line()
self._render_text_viewer(f"Entry #{i+1}", entry["content"])
imgui.same_line()
if imgui.button("Del"):
self.disc_entries.pop(i)
imgui.pop_id()
@@ -1405,113 +1432,88 @@ class App:
self.ai_status = "idle"
imgui.separator()
imgui.text_colored(C_OUT, "OUT")
imgui.same_line()
imgui.text_colored(C_REQ, "request")
imgui.same_line()
imgui.text_colored(C_TC, "tool_call")
imgui.same_line()
imgui.text(" ")
imgui.same_line()
imgui.text_colored(C_IN, "IN")
imgui.same_line()
imgui.text_colored(C_RES, "response")
imgui.same_line()
imgui.text_colored(C_TR, "tool_result")
imgui.separator()
# Use tinted background for prior session
if self.is_viewing_prior_session:
imgui.push_style_color(imgui.Col_.child_bg, vec4(40, 30, 20))
imgui.begin_child("comms_scroll", imgui.ImVec2(0, 0), False, imgui.WindowFlags_.horizontal_scrollbar)
log_to_render = self._comms_log_cache
flags = imgui.TableFlags_.resizable | imgui.TableFlags_.hideable | imgui.TableFlags_.borders_inner_v | imgui.TableFlags_.row_bg | imgui.TableFlags_.scroll_y
# Set a max height for the table or use available region
avail = imgui.get_content_region_avail()
if imgui.begin_table("comms_table", 6, flags, imgui.ImVec2(0, avail.y)):
imgui.table_setup_column("#", imgui.TableColumnFlags_.width_fixed, 40)
imgui.table_setup_column("Tier", imgui.TableColumnFlags_.width_fixed, 60)
imgui.table_setup_column("Type", imgui.TableColumnFlags_.width_fixed, 80)
imgui.table_setup_column("!", imgui.TableColumnFlags_.width_fixed, 20)
imgui.table_setup_column("Content", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Action", imgui.TableColumnFlags_.width_fixed, 50)
imgui.table_headers_row()
clipper = imgui.ListClipper()
clipper.begin(len(log_to_render))
while clipper.step():
for i in range(clipper.display_start, clipper.display_end):
entry = log_to_render[i]
imgui.push_id(f"comms_entry_{i}")
i_display = i + 1
ts = entry.get("ts", "00:00:00")
direction = entry.get("direction", "??")
kind = entry.get("kind", entry.get("type", "??"))
provider = entry.get("provider", "?")
model = entry.get("model", "?")
tier = entry.get("source_tier", "main")
payload = entry.get("payload", {})
if not payload and kind not in ("request", "response", "tool_call", "tool_result"):
payload = entry # legacy
def payload_to_str(msg_kind, payload):
if msg_kind == "request":
return payload.get("message", "")
elif msg_kind == "response":
# Row 1: #Idx TS DIR KIND Provider/Model [Tier]
imgui.text_colored(C_LBL, f"#{i_display}")
imgui.same_line()
imgui.text_colored(vec4(160, 160, 160), ts)
imgui.same_line()
d_col = DIR_COLORS.get(direction, C_VAL)
imgui.text_colored(d_col, direction)
imgui.same_line()
k_col = KIND_COLORS.get(kind, C_VAL)
imgui.text_colored(k_col, kind)
imgui.same_line()
imgui.text_colored(C_LBL, f"{provider}/{model}")
imgui.same_line()
imgui.text_colored(C_SUB, f"[{tier}]")
# Optimized content rendering using _render_heavy_text logic
if kind == "request":
self._render_heavy_text("message", payload.get("message", ""))
elif kind == "response":
r = payload.get("round", 0)
sr = payload.get("stop_reason", "STOP")
text = payload.get("text", "")
imgui.text_colored(C_LBL, f"round: {r} stop_reason: {sr}")
self._render_heavy_text("text", payload.get("text", ""))
tcs = payload.get("tool_calls", [])
tc_str = f" ({len(tcs)} tool calls)" if tcs else ""
return f"[R{r}] [{sr}]{tc_str} {text}"
elif msg_kind == "tool_call":
content = f"Call: {payload.get('name', '?')}"
if "script" in payload:
content += f" | Script: {payload['script']}"
elif "args" in payload:
content += f" | Args: {json.dumps(payload['args'])}"
return content
elif msg_kind == "tool_result":
return f"Result: {payload.get('name', '?')} | {payload.get('output', '')}"
return str(payload)
if tcs:
self._render_heavy_text("tool_calls", json.dumps(tcs, indent=1))
elif kind == "tool_call":
self._render_heavy_text(payload.get("name", "call"), payload.get("script") or json.dumps(payload.get("args", {}), indent=1))
elif kind == "tool_result":
self._render_heavy_text(payload.get("name", "result"), payload.get("output", ""))
else:
self._render_heavy_text("data", str(payload))
clipper = imgui.ListClipper()
clipper.begin(len(log_to_render))
while clipper.step():
for i in range(clipper.display_start, clipper.display_end):
entry = log_to_render[i]
imgui.table_next_row()
i_display = i + 1
source = entry.get("source_tier", "main")
msg_kind = entry.get("kind", entry.get("type", "?")) # Handle legacy 'type'
payload = entry.get("payload", {})
if not payload and msg_kind not in ("request", "response", "tool_call", "tool_result"):
payload = entry # Legacy format where entry IS the payload
full_content = payload_to_str(msg_kind, payload)
content_preview = full_content
if len(content_preview) > 500: # Increased clamp
content_preview = content_preview[:500] + "..."
# FG Color based on kind
fg = C_VAL
if msg_kind == "request": fg = C_REQ
elif msg_kind == "response": fg = C_RES
elif msg_kind == "tool_call": fg = C_TC
elif msg_kind == "tool_result": fg = C_TR
elif msg_kind == "error": fg = vec4(255, 80, 80)
imgui.table_next_column()
imgui.text_colored(C_LBL, f"#{i_display}")
imgui.table_next_column()
imgui.text_colored(C_SUB, f"[{source}]")
imgui.table_next_column()
imgui.text_colored(fg, msg_kind)
imgui.table_next_column()
elapsed = time.time() - entry.get("local_ts", 0)
if elapsed < 3.0:
blink = (math.sin(elapsed * 15) * 0.5 + 0.5)
imgui.text_colored(vec4(255, 255, 0, blink), "*")
else:
imgui.text("")
imgui.table_next_column()
if self.ui_word_wrap:
imgui.push_text_wrap_pos(0.0)
imgui.text_unformatted(content_preview)
imgui.pop_text_wrap_pos()
else:
imgui.text_unformatted(content_preview)
imgui.table_next_column()
if imgui.button(f"View##{i}"):
self.text_viewer_title = f"Log Entry #{i_display} ({msg_kind})"
self.text_viewer_content = full_content
self.show_text_viewer = True
imgui.end_table()
imgui.separator()
imgui.pop_id()
if self._scroll_comms_to_bottom:
# Table with scroll_y handles its own scrolling
# We might need ed.set_scroll_y or similar if we wanted precise control
# but usually imgui.set_scroll_here_y works inside the table
pass
imgui.set_scroll_here_y(1.0)
self._scroll_comms_to_bottom = False
imgui.end_child()
if self.is_viewing_prior_session:
imgui.pop_style_color()
@@ -1993,9 +1995,10 @@ class App:
imgui.separator()
ch1, self.ui_separate_message_panel = imgui.checkbox("Separate Message Panel", self.ui_separate_message_panel)
ch2, self.ui_separate_response_panel = imgui.checkbox("Separate Response Panel", self.ui_separate_response_panel)
ch3, self.ui_separate_tool_calls_panel = imgui.checkbox("Separate Tool Calls Panel", self.ui_separate_tool_calls_panel)
if ch1: self.show_windows["Message"] = self.ui_separate_message_panel
if ch2: self.show_windows["Response"] = self.ui_separate_response_panel
if ch3: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
imgui.separator()
imgui.text("Font")
imgui.push_item_width(-150)