still fixing regressions
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
205
src/gui_2.py
205
src/gui_2.py
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user