3 Commits

11 changed files with 268 additions and 112 deletions
@@ -44,6 +44,7 @@
- [x] Task: Bugfix: Correct `PresetManager` initialization to use project parent directory. - [x] Task: Bugfix: Correct `PresetManager` initialization to use project parent directory.
- [x] Task: Hardening: Wrap modal rendering in `try...finally` to prevent ImGui state corruption. - [x] Task: Hardening: Wrap modal rendering in `try...finally` to prevent ImGui state corruption.
- [x] Task: Hardening: Ensure `PresetManager._save_file` validates that parent is a directory. - [x] Task: Hardening: Ensure `PresetManager._save_file` validates that parent is a directory.
- [x] Task: Feature: Implement "Pop Out Task DAG" option in MMA Dashboard.
- [x] Task: Final UI polish (spacing, icons, tooltips). - [x] Task: Final UI polish (spacing, icons, tooltips).
- [x] Task: Run full suite of relevant tests. - [x] Task: Run full suite of relevant tests.
- [x] Task: Conductor - User Manual Verification 'Phase 4: Final Integration & Polish' (Protocol in workflow.md) - [x] Task: Conductor - User Manual Verification 'Phase 4: Final Integration & Polish' (Protocol in workflow.md)
+9 -5
View File
@@ -2,10 +2,10 @@
provider = "minimax" provider = "minimax"
model = "MiniMax-M2.5" model = "MiniMax-M2.5"
temperature = 0.0 temperature = 0.0
max_tokens = 4096 max_tokens = 32000
history_trunc_limit = 900000 history_trunc_limit = 900000
active_preset = "Default" active_preset = "Default"
system_prompt = "Not sure yet." system_prompt = ""
[projects] [projects]
paths = [ paths = [
@@ -24,12 +24,16 @@ separate_response_panel = false
separate_tool_calls_panel = false separate_tool_calls_panel = false
bg_shader_enabled = true bg_shader_enabled = true
crt_filter_enabled = false crt_filter_enabled = false
separate_task_dag = false
separate_usage_analytics = false
[gui.show_windows] [gui.show_windows]
"Context Hub" = true "Context Hub" = true
"Files & Media" = true "Files & Media" = true
"AI Settings" = true "AI Settings" = true
"MMA Dashboard" = true "MMA Dashboard" = true
"Task DAG" = false
"Usage Analytics" = false
"Tier 1: Strategy" = true "Tier 1: Strategy" = true
"Tier 2: Tech Lead" = true "Tier 2: Tech Lead" = true
"Tier 3: Workers" = true "Tier 3: Workers" = true
@@ -41,13 +45,13 @@ Response = false
"Tool Calls" = false "Tool Calls" = false
Theme = true Theme = true
"Log Management" = true "Log Management" = true
Diagnostics = false Diagnostics = true
[theme] [theme]
palette = "Nord Dark" palette = "Nord Dark"
font_path = "C:/projects/manual_slop/assets/fonts/Inter-Regular.ttf" font_path = "C:/projects/manual_slop/assets/fonts/Inter-Regular.ttf"
font_size = 12.0 font_size = 14.0
scale = 1.2999999523162842 scale = 1.2000000476837158
transparency = 0.550000011920929 transparency = 0.550000011920929
child_transparency = 0.6399999856948853 child_transparency = 0.6399999856948853
+60 -57
View File
@@ -73,10 +73,10 @@ Collapsed=0
DockId=0xAFC85805,2 DockId=0xAFC85805,2
[Window][Theme] [Window][Theme]
Pos=0,30 Pos=0,1164
Size=762,943 Size=760,973
Collapsed=0 Collapsed=0
DockId=0x00000005,1 DockId=0x00000002,2
[Window][Text Viewer - Entry #7] [Window][Text Viewer - Entry #7]
Pos=379,324 Pos=379,324
@@ -84,16 +84,16 @@ Size=900,700
Collapsed=0 Collapsed=0
[Window][Diagnostics] [Window][Diagnostics]
Pos=2674,26 Pos=2719,28
Size=1166,1678 Size=1121,1686
Collapsed=0 Collapsed=0
DockId=0x0000000C,2 DockId=0x0000000C,2
[Window][Context Hub] [Window][Context Hub]
Pos=0,30 Pos=0,1164
Size=762,943 Size=760,973
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000002,1
[Window][AI Settings Hub] [Window][AI Settings Hub]
Pos=406,17 Pos=406,17
@@ -102,26 +102,26 @@ Collapsed=0
DockId=0x0000000D,0 DockId=0x0000000D,0
[Window][Discussion Hub] [Window][Discussion Hub]
Pos=1668,30 Pos=1606,28
Size=1004,2107 Size=1111,2109
Collapsed=0 Collapsed=0
DockId=0x00000013,0 DockId=0x00000013,0
[Window][Operations Hub] [Window][Operations Hub]
Pos=764,30 Pos=762,28
Size=902,2107 Size=842,2109
Collapsed=0 Collapsed=0
DockId=0x00000012,0 DockId=0x00000012,0
[Window][Files & Media] [Window][Files & Media]
Pos=0,2013 Pos=0,1164
Size=762,124 Size=760,973
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Window][AI Settings] [Window][AI Settings]
Pos=0,975 Pos=0,28
Size=762,1036 Size=760,1134
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000001,0
@@ -131,14 +131,14 @@ Size=416,325
Collapsed=0 Collapsed=0
[Window][MMA Dashboard] [Window][MMA Dashboard]
Pos=2674,30 Pos=2719,28
Size=1166,1675 Size=1121,1686
Collapsed=0 Collapsed=0
DockId=0x0000000C,0 DockId=0x0000000C,0
[Window][Log Management] [Window][Log Management]
Pos=2674,30 Pos=2719,28
Size=1166,1675 Size=1121,1686
Collapsed=0 Collapsed=0
DockId=0x0000000C,1 DockId=0x0000000C,1
@@ -148,26 +148,26 @@ Size=262,209
Collapsed=0 Collapsed=0
[Window][Tier 1: Strategy] [Window][Tier 1: Strategy]
Pos=2674,1707 Pos=2719,1716
Size=1166,430 Size=1121,421
Collapsed=0 Collapsed=0
DockId=0x0000000F,0 DockId=0x0000000F,0
[Window][Tier 2: Tech Lead] [Window][Tier 2: Tech Lead]
Pos=2674,1707 Pos=2719,1716
Size=1166,430 Size=1121,421
Collapsed=0 Collapsed=0
DockId=0x0000000F,1 DockId=0x0000000F,1
[Window][Tier 4: QA] [Window][Tier 4: QA]
Pos=2674,1707 Pos=2719,1716
Size=1166,430 Size=1121,421
Collapsed=0 Collapsed=0
DockId=0x0000000F,2 DockId=0x0000000F,2
[Window][Tier 3: Workers] [Window][Tier 3: Workers]
Pos=764,30 Pos=762,28
Size=902,2107 Size=842,2109
Collapsed=0 Collapsed=0
DockId=0x00000012,1 DockId=0x00000012,1
@@ -323,7 +323,12 @@ Collapsed=0
[Window][Preset Manager] [Window][Preset Manager]
Pos=786,858 Pos=786,858
Size=780,650 Size=956,942
Collapsed=0
[Window][Task DAG]
Pos=1461,823
Size=1190,872
Collapsed=0 Collapsed=0
[Table][0xFB6E3870,4] [Table][0xFB6E3870,4]
@@ -357,11 +362,11 @@ Column 3 Width=20
Column 4 Weight=1.0000 Column 4 Weight=1.0000
[Table][0x2A6000B6,4] [Table][0x2A6000B6,4]
RefScale=18 RefScale=20
Column 0 Width=54 Column 0 Width=60
Column 1 Width=82 Column 1 Width=90
Column 2 Weight=1.0000 Column 2 Weight=1.0000
Column 3 Width=137 Column 3 Width=151
[Table][0x8BCC69C7,6] [Table][0x8BCC69C7,6]
RefScale=13 RefScale=13
@@ -373,18 +378,18 @@ Column 4 Weight=1.0000
Column 5 Width=50 Column 5 Width=50
[Table][0x3751446B,4] [Table][0x3751446B,4]
RefScale=14 RefScale=20
Column 0 Width=42 Column 0 Width=60
Column 1 Width=64 Column 1 Width=91
Column 2 Weight=1.0000 Column 2 Weight=1.0000
Column 3 Width=106 Column 3 Width=151
[Table][0x2C515046,4] [Table][0x2C515046,4]
RefScale=18 RefScale=20
Column 0 Width=58 Column 0 Width=63
Column 1 Weight=1.0000 Column 1 Weight=1.0000
Column 2 Width=138 Column 2 Width=152
Column 3 Width=54 Column 3 Width=60
[Table][0xD99F45C5,4] [Table][0xD99F45C5,4]
Column 0 Sort=0v Column 0 Sort=0v
@@ -405,30 +410,28 @@ Column 1 Width=100
Column 2 Weight=1.0000 Column 2 Weight=1.0000
[Table][0xA02D8C87,3] [Table][0xA02D8C87,3]
RefScale=16 RefScale=20
Column 0 Width=182 Column 0 Width=227
Column 1 Width=120 Column 1 Width=150
Column 2 Weight=1.0000 Column 2 Weight=1.0000
[Docking][Data] [Docking][Data]
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,30 Size=3840,2107 Split=X DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=3840,2109 Split=X
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2672,1183 Split=X DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2717,1183 Split=X
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 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=0x00000007 Parent=0x0000000B SizeRef=760,858 Split=Y Selected=0x8CA2375C
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=295,943 Selected=0xF4139CA2 DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,1134 CentralNode=1 Selected=0x7BD57D6A
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=295,1168 Split=Y Selected=0x7BD57D6A DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,973 Selected=0x1DCB2623
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=824,1036 CentralNode=1 Selected=0x7BD57D6A DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1955,858 Split=X Selected=0x418C7449
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=824,124 Selected=0x1DCB2623 DockNode ID=0x00000012 Parent=0x0000000E SizeRef=842,402 Selected=0x418C7449
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1908,858 Split=X Selected=0x418C7449 DockNode ID=0x00000013 Parent=0x0000000E SizeRef=1111,402 Selected=0x6F2B5B04
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=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1166,1183 Split=Y Selected=0x3AEC3498 DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1121,1183 Split=Y Selected=0x3AEC3498
DockNode ID=0x0000000C Parent=0x00000004 SizeRef=1074,1679 Selected=0x2C0206CE DockNode ID=0x0000000C Parent=0x00000004 SizeRef=1074,1681 Selected=0x3AEC3498
DockNode ID=0x0000000F Parent=0x00000004 SizeRef=1074,431 Selected=0x5CDB7A4B DockNode ID=0x0000000F Parent=0x00000004 SizeRef=1074,420 Selected=0xBB346584
;;;<<<Layout_655921752_Default>>>;;; ;;;<<<Layout_655921752_Default>>>;;;
;;;<<<HelloImGui_Misc>>>;;; ;;;<<<HelloImGui_Misc>>>;;;
+2 -2
View File
@@ -1,5 +1,5 @@
[presets.Default] [presets.Default]
system_prompt = "Not sure yet." system_prompt = ""
temperature = 0.0 temperature = 0.0
top_p = 1.0 top_p = 1.0
max_output_tokens = 4096 max_output_tokens = 32000
+15
View File
@@ -0,0 +1,15 @@
prompt = """
In src/gui_2.py:
1. In '__init__', initialize 'self.ui_separate_task_dag = gui_cfg.get("separate_task_dag", False)'.
2. In '_gui_func', add logic to render the 'Task DAG' window if 'self.ui_separate_task_dag' and 'self.show_windows.get("Task DAG", False)' are true. Use:
if self.ui_separate_task_dag and self.show_windows.get("Task DAG", False):
exp, opened = imgui.begin("Task DAG", self.show_windows["Task DAG"])
self.show_windows["Task DAG"] = bool(opened)
if exp:
self._render_task_dag_panel()
imgui.end()
3. In '_render_mma_dashboard', add 'ch, self.ui_separate_task_dag = imgui.checkbox("Pop Out Task DAG", self.ui_separate_task_dag)' before the '4. Task DAG Visualizer' section. If 'ch' is true, update 'self.show_windows["Task DAG"] = self.ui_separate_task_dag'.
4. Move the entire '4. Task DAG Visualizer' and '5. Add Ticket Form' sections into a new method '_render_task_dag_panel(self)'.
5. In '_render_mma_dashboard', call 'self._render_task_dag_panel()' only if 'not self.ui_separate_task_dag'.
Use 1-space indentation.
"""
+13
View File
@@ -0,0 +1,13 @@
In src/gui_2.py:
1. In '__init__', initialize 'self.ui_separate_task_dag = gui_cfg.get("separate_task_dag", False)'.
2. In '_gui_func', add logic to render the 'Task DAG' window if 'self.ui_separate_task_dag' and 'self.show_windows.get("Task DAG", False)' are true. Use:
if self.ui_separate_task_dag and self.show_windows.get("Task DAG", False):
exp, opened = imgui.begin("Task DAG", self.show_windows["Task DAG"])
self.show_windows["Task DAG"] = bool(opened)
if exp:
self._render_task_dag_panel()
imgui.end()
3. In '_render_mma_dashboard', add 'ch, self.ui_separate_task_dag = imgui.checkbox("Pop Out Task DAG", self.ui_separate_task_dag)' before the '4. Task DAG Visualizer' section. If 'ch' is true, update 'self.show_windows["Task DAG"] = self.ui_separate_task_dag'.
4. Move the entire '4. Task DAG Visualizer' and '5. Add Ticket Form' sections into a new method '_render_task_dag_panel(self)'.
5. In '_render_mma_dashboard', call 'self._render_task_dag_panel()' only if 'not self.ui_separate_task_dag'.
Use 1-space indentation.
+13
View File
@@ -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.
"""
+14 -2
View File
@@ -342,10 +342,14 @@ class AppController:
'_editing_preset_temperature': '_editing_preset_temperature', '_editing_preset_temperature': '_editing_preset_temperature',
'_editing_preset_top_p': '_editing_preset_top_p', '_editing_preset_top_p': '_editing_preset_top_p',
'_editing_preset_max_output_tokens': '_editing_preset_max_output_tokens', '_editing_preset_max_output_tokens': '_editing_preset_max_output_tokens',
'_editing_preset_scope': '_editing_preset_scope' '_editing_preset_scope': '_editing_preset_scope',
'show_windows': 'show_windows',
'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 = dict(self._settable_fields)
self._gettable_fields.update({ self._gettable_fields.update({
'show_windows': 'show_windows',
'ui_focus_agent': 'ui_focus_agent', 'ui_focus_agent': 'ui_focus_agent',
'active_discussion': 'active_discussion', 'active_discussion': 'active_discussion',
'_track_discussion_active': '_track_discussion_active', '_track_discussion_active': '_track_discussion_active',
@@ -378,7 +382,9 @@ class AppController:
'_editing_preset_temperature': '_editing_preset_temperature', '_editing_preset_temperature': '_editing_preset_temperature',
'_editing_preset_top_p': '_editing_preset_top_p', '_editing_preset_top_p': '_editing_preset_top_p',
'_editing_preset_max_output_tokens': '_editing_preset_max_output_tokens', '_editing_preset_max_output_tokens': '_editing_preset_max_output_tokens',
'_editing_preset_scope': '_editing_preset_scope' '_editing_preset_scope': '_editing_preset_scope',
'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_monitor = performance_monitor.get_monitor()
self._perf_profiling_enabled = False self._perf_profiling_enabled = False
@@ -778,6 +784,8 @@ class AppController:
def init_state(self): def init_state(self):
"""Initializes the application state from configurations.""" """Initializes the application state from configurations."""
self.ui_separate_task_dag = False
self.ui_separate_usage_analytics = False
self.config = models.load_config() self.config = models.load_config()
theme.load_from_config(self.config) theme.load_from_config(self.config)
ai_cfg = self.config.get("ai", {}) ai_cfg = self.config.get("ai", {})
@@ -836,6 +844,8 @@ class AppController:
"Files & Media": True, "Files & Media": True,
"AI Settings": True, "AI Settings": True,
"MMA Dashboard": True, "MMA Dashboard": True,
"Task DAG": False,
"Usage Analytics": False,
"Tier 1: Strategy": True, "Tier 1: Strategy": True,
"Tier 2: Tech Lead": True, "Tier 2: Tech Lead": True,
"Tier 3: Workers": True, "Tier 3: Workers": True,
@@ -2177,6 +2187,8 @@ class AppController:
"separate_message_panel": getattr(self, "ui_separate_message_panel", False), "separate_message_panel": getattr(self, "ui_separate_message_panel", False),
"separate_response_panel": getattr(self, "ui_separate_response_panel", False), "separate_response_panel": getattr(self, "ui_separate_response_panel", False),
"separate_tool_calls_panel": getattr(self, "ui_separate_tool_calls_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 "bg_shader_enabled": bg_shader.get_bg().enabled
}) })
self.config["gui"] = gui_cfg self.config["gui"] = gui_cfg
+76 -41
View File
@@ -126,6 +126,9 @@ class App:
self.ui_separate_message_panel = gui_cfg.get("separate_message_panel", False) 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_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_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.ui_multi_viewport = gui_cfg.get("multi_viewport", False)
self.layout_presets = self.config.get("layout_presets", {}) self.layout_presets = self.config.get("layout_presets", {})
self._new_preset_name = "" self._new_preset_name = ""
@@ -411,13 +414,15 @@ class App:
self._render_provider_panel() self._render_provider_panel()
if imgui.collapsing_header("System Prompts"): if imgui.collapsing_header("System Prompts"):
self._render_system_prompts_panel() self._render_system_prompts_panel()
if imgui.collapsing_header("Token Budget"):
self._render_token_budget_panel()
self._render_cache_panel() self._render_cache_panel()
self._render_tool_analytics_panel()
self._render_session_insights_panel()
imgui.end() 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): if self.show_windows.get("MMA Dashboard", False):
exp, opened = imgui.begin("MMA Dashboard", self.show_windows["MMA Dashboard"]) exp, opened = imgui.begin("MMA Dashboard", self.show_windows["MMA Dashboard"])
self.show_windows["MMA Dashboard"] = bool(opened) self.show_windows["MMA Dashboard"] = bool(opened)
@@ -426,6 +431,13 @@ class App:
self._render_mma_dashboard() self._render_mma_dashboard()
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard") if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
imgui.end() imgui.end()
if self.ui_separate_task_dag and self.show_windows.get("Task DAG", False):
exp, opened = imgui.begin("Task DAG", self.show_windows["Task DAG"])
self.show_windows["Task DAG"] = bool(opened)
if exp:
self._render_task_dag_panel()
imgui.end()
if self.show_windows.get("Tier 1: Strategy", False): if self.show_windows.get("Tier 1: Strategy", False):
exp, opened = imgui.begin("Tier 1: Strategy", self.show_windows["Tier 1: Strategy"]) exp, opened = imgui.begin("Tier 1: Strategy", self.show_windows["Tier 1: Strategy"])
self.show_windows["Tier 1: Strategy"] = bool(opened) self.show_windows["Tier 1: Strategy"] = bool(opened)
@@ -517,11 +529,15 @@ class App:
self.ui_focus_agent = None self.ui_focus_agent = None
if exp: if exp:
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4)) 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) ch1, 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 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() imgui.pop_style_var()
show_tc_tab = not self.ui_separate_tool_calls_panel 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_bar("ops_tabs"):
if imgui.begin_tab_item("Comms History")[0]: if imgui.begin_tab_item("Comms History")[0]:
@@ -531,6 +547,10 @@ class App:
if imgui.begin_tab_item("Tool Calls")[0]: if imgui.begin_tab_item("Tool Calls")[0]:
self._render_tool_calls_panel() self._render_tool_calls_panel()
imgui.end_tab_item() 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_tab_bar()
imgui.end() imgui.end()
@@ -1768,7 +1788,7 @@ def hello():
def _render_token_budget_panel(self) -> None: def _render_token_budget_panel(self) -> None:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_token_budget_panel") if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_token_budget_panel")
imgui.text("Session Telemetry") imgui.text_colored(C_LBL, 'Prompt Utilization')
usage = self.session_usage usage = self.session_usage
total = usage["input_tokens"] + usage["output_tokens"] total = usage["input_tokens"] + usage["output_tokens"]
if total == 0 and usage.get("total_tokens", 0) > 0: if total == 0 and usage.get("total_tokens", 0) > 0:
@@ -1909,9 +1929,8 @@ def hello():
def _render_tool_analytics_panel(self) -> None: def _render_tool_analytics_panel(self) -> None:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tool_analytics_panel") if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tool_analytics_panel")
if not imgui.collapsing_header("Tool Usage Analytics"): imgui.text_colored(C_LBL, 'Tool Usage')
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_tool_analytics_panel") imgui.separator()
return
now = time.time() now = time.time()
if not hasattr(self, '_tool_stats_cache_time') or now - self._tool_stats_cache_time > 1.0: if not hasattr(self, '_tool_stats_cache_time') or now - self._tool_stats_cache_time > 1.0:
self._cached_tool_stats = getattr(self.controller, '_tool_stats', {}) self._cached_tool_stats = getattr(self.controller, '_tool_stats', {})
@@ -1950,9 +1969,8 @@ def hello():
def _render_session_insights_panel(self) -> None: def _render_session_insights_panel(self) -> None:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_session_insights_panel") if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_session_insights_panel")
if not imgui.collapsing_header("Session Insights"): imgui.text_colored(C_LBL, 'Session Insights')
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_session_insights_panel") imgui.separator()
return
insights = self.controller.get_session_insights() insights = self.controller.get_session_insights()
imgui.text(f"Total Tokens: {insights.get('total_tokens', 0):,}") imgui.text(f"Total Tokens: {insights.get('total_tokens', 0):,}")
imgui.text(f"API Calls: {insights.get('call_count', 0)}") imgui.text(f"API Calls: {insights.get('call_count', 0)}")
@@ -1964,6 +1982,15 @@ def hello():
imgui.text(f"Tokens/Ticket: {efficiency:.0f}" if efficiency > 0 else "Tokens/Ticket: N/A") 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") 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()
imgui.separator()
self._render_tool_analytics_panel()
imgui.separator()
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: def _render_message_panel(self) -> None:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_message_panel") if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_message_panel")
# LIVE indicator # LIVE indicator
@@ -2709,6 +2736,42 @@ def hello():
imgui.separator() imgui.separator()
self._render_ticket_queue() self._render_ticket_queue()
imgui.separator() imgui.separator()
ch, self.ui_separate_task_dag = imgui.checkbox("Pop Out Task DAG", self.ui_separate_task_dag)
if ch:
self.show_windows["Task DAG"] = self.ui_separate_task_dag
if not self.ui_separate_task_dag:
self._render_task_dag_panel()
# 6. Edit Selected Ticket
if self.ui_selected_ticket_id:
imgui.separator()
imgui.text_colored(C_VAL, f"Editing: {self.ui_selected_ticket_id}")
ticket = next((t for t in self.active_tickets if str(t.get('id', '')) == self.ui_selected_ticket_id), None)
if ticket:
imgui.text(f"Status: {ticket.get('status', 'todo')}")
prio = ticket.get('priority', 'medium')
imgui.text("Priority:")
imgui.same_line()
if imgui.begin_combo(f"##edit_prio_{ticket.get('id')}", prio):
for p_opt in ['high', 'medium', 'low']:
if imgui.selectable(p_opt, p_opt == prio)[0]:
ticket['priority'] = p_opt
self._push_mma_state_update()
imgui.end_combo()
imgui.text(f"Target: {ticket.get('target_file', '')}")
deps = ticket.get('depends_on', [])
imgui.text(f"Depends on: {', '.join(deps)}")
if imgui.button(f"Mark Complete##{self.ui_selected_ticket_id}"):
ticket['status'] = 'done'
self._push_mma_state_update()
imgui.same_line()
if imgui.button(f"Delete##{self.ui_selected_ticket_id}"):
self.active_tickets = [t for t in self.active_tickets if str(t.get('id', '')) != self.ui_selected_ticket_id]
self.ui_selected_ticket_id = None
self._push_mma_state_update()
def _render_task_dag_panel(self) -> None:
# 4. Task DAG Visualizer # 4. Task DAG Visualizer
imgui.text("Task DAG") imgui.text("Task DAG")
if self.active_track and self.node_editor_ctx: if self.active_track and self.node_editor_ctx:
@@ -2850,34 +2913,6 @@ def hello():
else: else:
imgui.text_disabled("No active MMA track.") imgui.text_disabled("No active MMA track.")
# 6. Edit Selected Ticket
if self.ui_selected_ticket_id:
imgui.separator()
imgui.text_colored(C_VAL, f"Editing: {self.ui_selected_ticket_id}")
ticket = next((t for t in self.active_tickets if str(t.get('id', '')) == self.ui_selected_ticket_id), None)
if ticket:
imgui.text(f"Status: {ticket.get('status', 'todo')}")
prio = ticket.get('priority', 'medium')
imgui.text("Priority:")
imgui.same_line()
if imgui.begin_combo(f"##edit_prio_{ticket.get('id')}", prio):
for p_opt in ['high', 'medium', 'low']:
if imgui.selectable(p_opt, p_opt == prio)[0]:
ticket['priority'] = p_opt
self._push_mma_state_update()
imgui.end_combo()
imgui.text(f"Target: {ticket.get('target_file', '')}")
deps = ticket.get('depends_on', [])
imgui.text(f"Depends on: {', '.join(deps)}")
if imgui.button(f"Mark Complete##{self.ui_selected_ticket_id}"):
ticket['status'] = 'done'
self._push_mma_state_update()
imgui.same_line()
if imgui.button(f"Delete##{self.ui_selected_ticket_id}"):
self.active_tickets = [t for t in self.active_tickets if str(t.get('id', '')) != self.ui_selected_ticket_id]
self.ui_selected_ticket_id = None
self._push_mma_state_update()
def _render_tier_stream_panel(self, tier_key: str, stream_key: str | None) -> None: def _render_tier_stream_panel(self, tier_key: str, stream_key: str | None) -> None:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tier_stream_panel") if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tier_stream_panel")
if self.is_viewing_prior_session: if self.is_viewing_prior_session:
+30
View File
@@ -0,0 +1,30 @@
import pytest
import time
from src.api_hook_client import ApiHookClient
def test_task_dag_popout(live_gui):
client = ApiHookClient()
# 1. Check initial state
state = client.get_gui_state()
assert state.get("ui_separate_task_dag") is False
# 2. Enable popout and ensure window is shown
client.set_value("ui_separate_task_dag", True)
# We need to manually set show_windows["Task DAG"] as well since we are bypassing the checkbox logic
show_windows = state.get("show_windows", {})
show_windows["Task DAG"] = True
client.set_value("show_windows", show_windows)
time.sleep(1)
# 3. Verify state
state = client.get_gui_state()
assert state.get("ui_separate_task_dag") is True
assert state.get("show_windows", {}).get("Task DAG") is True
# 4. Disable popout
client.set_value("ui_separate_task_dag", False)
time.sleep(1)
state = client.get_gui_state()
assert state.get("ui_separate_task_dag") is False
+30
View File
@@ -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