From 8a83d229672b0de40515399348d35c7e00e48ce4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 9 Mar 2026 23:25:06 -0400 Subject: [PATCH] feat(ops): Consolidate usage analytics into Operations Hub with popout option --- config.toml | 2 + manualslop_layout.ini | 112 +++++++++++------------ scripts/tasks/popout_usage.toml | 13 +++ src/app_controller.py | 9 +- src/gui_2.py | 31 +++++-- tests/test_usage_analytics_popout_sim.py | 30 ++++++ 6 files changed, 132 insertions(+), 65 deletions(-) create mode 100644 scripts/tasks/popout_usage.toml create mode 100644 tests/test_usage_analytics_popout_sim.py diff --git a/config.toml b/config.toml index 20f7200..78f1c81 100644 --- a/config.toml +++ b/config.toml @@ -24,12 +24,14 @@ separate_response_panel = false separate_tool_calls_panel = false bg_shader_enabled = true crt_filter_enabled = false +separate_task_dag = false [gui.show_windows] "Context Hub" = true "Files & Media" = true "AI Settings" = true "MMA Dashboard" = true +"Task DAG" = false "Tier 1: Strategy" = true "Tier 2: Tech Lead" = true "Tier 3: Workers" = true diff --git a/manualslop_layout.ini b/manualslop_layout.ini index 11d1e94..8d5931c 100644 --- a/manualslop_layout.ini +++ b/manualslop_layout.ini @@ -73,10 +73,10 @@ Collapsed=0 DockId=0xAFC85805,2 [Window][Theme] -Pos=0,29 -Size=762,736 +Pos=0,1344 +Size=760,793 Collapsed=0 -DockId=0x00000005,1 +DockId=0x00000002,2 [Window][Text Viewer - Entry #7] Pos=379,324 @@ -84,16 +84,16 @@ Size=900,700 Collapsed=0 [Window][Diagnostics] -Pos=2674,26 -Size=1166,1678 +Pos=2732,32 +Size=1108,1683 Collapsed=0 DockId=0x0000000C,2 [Window][Context Hub] -Pos=0,29 -Size=762,736 +Pos=0,1344 +Size=760,793 Collapsed=0 -DockId=0x00000005,0 +DockId=0x00000002,1 [Window][AI Settings Hub] Pos=406,17 @@ -102,26 +102,26 @@ Collapsed=0 DockId=0x0000000D,0 [Window][Discussion Hub] -Pos=1668,29 -Size=1004,2108 +Pos=1691,32 +Size=1039,2105 Collapsed=0 DockId=0x00000013,0 [Window][Operations Hub] -Pos=764,29 -Size=902,2108 +Pos=762,32 +Size=927,2105 Collapsed=0 DockId=0x00000012,0 [Window][Files & Media] -Pos=0,1520 -Size=762,617 +Pos=0,1344 +Size=760,793 Collapsed=0 DockId=0x00000002,0 [Window][AI Settings] -Pos=0,767 -Size=762,751 +Pos=0,32 +Size=760,1310 Collapsed=0 DockId=0x00000001,0 @@ -131,14 +131,14 @@ Size=416,325 Collapsed=0 [Window][MMA Dashboard] -Pos=2674,29 -Size=1166,1676 +Pos=2732,32 +Size=1108,1683 Collapsed=0 DockId=0x0000000C,0 [Window][Log Management] -Pos=2674,29 -Size=1166,1676 +Pos=2732,32 +Size=1108,1683 Collapsed=0 DockId=0x0000000C,1 @@ -148,26 +148,26 @@ Size=262,209 Collapsed=0 [Window][Tier 1: Strategy] -Pos=2674,1707 -Size=1166,430 +Pos=2732,1717 +Size=1108,420 Collapsed=0 DockId=0x0000000F,0 [Window][Tier 2: Tech Lead] -Pos=2674,1707 -Size=1166,430 +Pos=2732,1717 +Size=1108,420 Collapsed=0 DockId=0x0000000F,1 [Window][Tier 4: QA] -Pos=2674,1707 -Size=1166,430 +Pos=2732,1717 +Size=1108,420 Collapsed=0 DockId=0x0000000F,2 [Window][Tier 3: Workers] -Pos=764,29 -Size=902,2108 +Pos=762,32 +Size=927,2105 Collapsed=0 DockId=0x00000012,1 @@ -357,11 +357,11 @@ Column 3 Width=20 Column 4 Weight=1.0000 [Table][0x2A6000B6,4] -RefScale=18 -Column 0 Width=54 -Column 1 Width=82 +RefScale=22 +Column 0 Width=66 +Column 1 Width=100 Column 2 Weight=1.0000 -Column 3 Width=137 +Column 3 Width=167 [Table][0x8BCC69C7,6] RefScale=13 @@ -380,11 +380,11 @@ Column 2 Weight=1.0000 Column 3 Width=106 [Table][0x2C515046,4] -RefScale=20 -Column 0 Width=64 +RefScale=24 +Column 0 Width=76 Column 1 Weight=1.0000 -Column 2 Width=153 -Column 3 Width=60 +Column 2 Width=183 +Column 3 Width=72 [Table][0xD99F45C5,4] Column 0 Sort=0v @@ -405,30 +405,28 @@ Column 1 Width=100 Column 2 Weight=1.0000 [Table][0xA02D8C87,3] -RefScale=16 -Column 0 Width=182 -Column 1 Width=120 +RefScale=22 +Column 0 Width=250 +Column 1 Width=165 Column 2 Weight=1.0000 [Docking][Data] -DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y - DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A - DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,29 Size=3840,2108 Split=X - DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2672,1183 Split=X - DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 - DockNode ID=0x00000007 Parent=0x0000000B SizeRef=762,858 Split=Y Selected=0x8CA2375C - DockNode ID=0x00000005 Parent=0x00000007 SizeRef=295,736 Selected=0xF4139CA2 - DockNode ID=0x00000006 Parent=0x00000007 SizeRef=295,1369 Split=Y Selected=0x7BD57D6A - DockNode ID=0x00000001 Parent=0x00000006 SizeRef=824,750 CentralNode=1 Selected=0x7BD57D6A - DockNode ID=0x00000002 Parent=0x00000006 SizeRef=824,617 Selected=0x1DCB2623 - DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1908,858 Split=X Selected=0x418C7449 - DockNode ID=0x00000012 Parent=0x0000000E SizeRef=902,402 Selected=0x418C7449 - DockNode ID=0x00000013 Parent=0x0000000E SizeRef=1004,402 Selected=0x6F2B5B04 - DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 - DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1166,1183 Split=Y Selected=0x3AEC3498 - DockNode ID=0x0000000C Parent=0x00000004 SizeRef=1074,1679 Selected=0x2C0206CE - DockNode ID=0x0000000F Parent=0x00000004 SizeRef=1074,431 Selected=0x5CDB7A4B +DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y + DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A + DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,32 Size=3840,2105 Split=X + DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2730,1183 Split=X + DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 + DockNode ID=0x00000007 Parent=0x0000000B SizeRef=760,858 Split=Y Selected=0x8CA2375C + DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,1310 CentralNode=1 Selected=0x7BD57D6A + DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,793 Selected=0x1DCB2623 + DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1968,858 Split=X Selected=0x418C7449 + DockNode ID=0x00000012 Parent=0x0000000E SizeRef=927,402 Selected=0x418C7449 + DockNode ID=0x00000013 Parent=0x0000000E SizeRef=1039,402 Selected=0x6F2B5B04 + DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 + DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1108,1183 Split=Y Selected=0x3AEC3498 + DockNode ID=0x0000000C Parent=0x00000004 SizeRef=1074,1681 Selected=0x3AEC3498 + DockNode ID=0x0000000F Parent=0x00000004 SizeRef=1074,420 Selected=0xBB346584 ;;;<<>>;;; ;;;<<>>;;; diff --git a/scripts/tasks/popout_usage.toml b/scripts/tasks/popout_usage.toml new file mode 100644 index 0000000..13185d7 --- /dev/null +++ b/scripts/tasks/popout_usage.toml @@ -0,0 +1,13 @@ +prompt = """ +In src/gui_2.py: +1. In '__init__', initialize 'self.ui_separate_usage_analytics = gui_cfg.get("separate_usage_analytics", False)'. +2. Implement 'App._render_usage_analytics_panel(self)': + - Call '_render_token_budget_panel()' (optionally inside a collapsing header if you want, but user asked to consolidate headers). + - Call '_render_tool_analytics_panel()'. + - Call '_render_session_insights_panel()'. +3. In '_gui_func', remove the original calls to these three panels from the 'AI Settings' block (around line 410). +4. In '_gui_func', add logic to render the standalone 'Usage Analytics' window if 'self.ui_separate_usage_analytics' and 'self.show_windows.get("Usage Analytics", False)' are true. +5. In 'Operations Hub' rendering (around line 530), add a 'Pop Out Usage Analytics' checkbox. If changed, update 'self.show_windows["Usage Analytics"]'. +6. In 'Operations Hub' tab bar, add a 'Usage Analytics' tab that calls '_render_usage_analytics_panel()' if 'not self.ui_separate_usage_analytics'. +Use 1-space indentation. +""" diff --git a/src/app_controller.py b/src/app_controller.py index e29a507..ef85f38 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -344,7 +344,8 @@ class AppController: '_editing_preset_max_output_tokens': '_editing_preset_max_output_tokens', '_editing_preset_scope': '_editing_preset_scope', 'show_windows': 'show_windows', - 'ui_separate_task_dag': 'ui_separate_task_dag' + 'ui_separate_task_dag': 'ui_separate_task_dag', + 'ui_separate_usage_analytics': 'ui_separate_usage_analytics' } self._gettable_fields = dict(self._settable_fields) self._gettable_fields.update({ @@ -382,7 +383,8 @@ class AppController: '_editing_preset_top_p': '_editing_preset_top_p', '_editing_preset_max_output_tokens': '_editing_preset_max_output_tokens', '_editing_preset_scope': '_editing_preset_scope', - 'ui_separate_task_dag': 'ui_separate_task_dag' + 'ui_separate_task_dag': 'ui_separate_task_dag', + 'ui_separate_usage_analytics': 'ui_separate_usage_analytics' }) self.perf_monitor = performance_monitor.get_monitor() self._perf_profiling_enabled = False @@ -783,6 +785,7 @@ class AppController: def init_state(self): """Initializes the application state from configurations.""" self.ui_separate_task_dag = False + self.ui_separate_usage_analytics = False self.config = models.load_config() theme.load_from_config(self.config) ai_cfg = self.config.get("ai", {}) @@ -842,6 +845,7 @@ class AppController: "AI Settings": True, "MMA Dashboard": True, "Task DAG": False, + "Usage Analytics": False, "Tier 1: Strategy": True, "Tier 2: Tech Lead": True, "Tier 3: Workers": True, @@ -2184,6 +2188,7 @@ class AppController: "separate_response_panel": getattr(self, "ui_separate_response_panel", False), "separate_tool_calls_panel": getattr(self, "ui_separate_tool_calls_panel", False), "separate_task_dag": self.ui_separate_task_dag, + "separate_usage_analytics": self.ui_separate_usage_analytics, "bg_shader_enabled": bg_shader.get_bg().enabled }) self.config["gui"] = gui_cfg diff --git a/src/gui_2.py b/src/gui_2.py index b8e7485..03dc2ee 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -127,6 +127,8 @@ class App: 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.ui_separate_task_dag = gui_cfg.get("separate_task_dag", False) + self.ui_separate_usage_analytics = gui_cfg.get("separate_usage_analytics", False) + self.show_windows.setdefault("Usage Analytics", False) self.ui_multi_viewport = gui_cfg.get("multi_viewport", False) self.layout_presets = self.config.get("layout_presets", {}) self._new_preset_name = "" @@ -412,13 +414,15 @@ class App: self._render_provider_panel() if imgui.collapsing_header("System Prompts"): self._render_system_prompts_panel() - if imgui.collapsing_header("Token Budget"): - self._render_token_budget_panel() self._render_cache_panel() - self._render_tool_analytics_panel() - self._render_session_insights_panel() imgui.end() + if self.ui_separate_usage_analytics and self.show_windows.get("Usage Analytics", False): + exp, opened = imgui.begin("Usage Analytics", self.show_windows["Usage Analytics"]) + self.show_windows["Usage Analytics"] = bool(opened) + if exp: + self._render_usage_analytics_panel() + imgui.end() if self.show_windows.get("MMA Dashboard", False): exp, opened = imgui.begin("MMA Dashboard", self.show_windows["MMA Dashboard"]) self.show_windows["MMA Dashboard"] = bool(opened) @@ -525,11 +529,15 @@ class App: 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 + ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel) + if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel + imgui.same_line() + ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics) + if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics imgui.pop_style_var() show_tc_tab = not self.ui_separate_tool_calls_panel + show_usage_tab = not self.ui_separate_usage_analytics if imgui.begin_tab_bar("ops_tabs"): if imgui.begin_tab_item("Comms History")[0]: @@ -539,6 +547,10 @@ class App: if imgui.begin_tab_item("Tool Calls")[0]: self._render_tool_calls_panel() imgui.end_tab_item() + if show_usage_tab: + if imgui.begin_tab_item("Usage Analytics")[0]: + self._render_usage_analytics_panel() + imgui.end_tab_item() imgui.end_tab_bar() imgui.end() @@ -1972,6 +1984,13 @@ def hello(): imgui.text(f"Tokens/Ticket: {efficiency:.0f}" if efficiency > 0 else "Tokens/Ticket: N/A") if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_session_insights_panel") + def _render_usage_analytics_panel(self) -> None: + if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_usage_analytics_panel") + self._render_token_budget_panel() + self._render_tool_analytics_panel() + self._render_session_insights_panel() + if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_usage_analytics_panel") + def _render_message_panel(self) -> None: if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_message_panel") # LIVE indicator diff --git a/tests/test_usage_analytics_popout_sim.py b/tests/test_usage_analytics_popout_sim.py new file mode 100644 index 0000000..f98336f --- /dev/null +++ b/tests/test_usage_analytics_popout_sim.py @@ -0,0 +1,30 @@ +import pytest +import time +from src.api_hook_client import ApiHookClient + +def test_usage_analytics_popout(live_gui): + client = ApiHookClient() + + # 1. Check initial state + state = client.get_gui_state() + assert state.get("ui_separate_usage_analytics") is False + + # 2. Enable popout and ensure window is shown + client.set_value("ui_separate_usage_analytics", True) + # Manually set show_windows["Usage Analytics"] as well + show_windows = state.get("show_windows", {}) + show_windows["Usage Analytics"] = True + client.set_value("show_windows", show_windows) + time.sleep(1) + + # 3. Verify state + state = client.get_gui_state() + assert state.get("ui_separate_usage_analytics") is True + assert state.get("show_windows", {}).get("Usage Analytics") is True + + # 4. Disable popout + client.set_value("ui_separate_usage_analytics", False) + time.sleep(1) + + state = client.get_gui_state() + assert state.get("ui_separate_usage_analytics") is False