feat(gui): add Tool Usage Analytics panel with stats tracking

This commit is contained in:
2026-03-07 10:58:23 -05:00
parent 5075a82fe4
commit 1d87ad3566
2 changed files with 75 additions and 1 deletions

View File

@@ -145,6 +145,7 @@ class AppController:
self._patch_error_message: Optional[str] = None
self.mma_status: str = "idle"
self._tool_log: List[Dict[str, Any]] = []
self._tool_stats: Dict[str, Dict[str, Any]] = {} # {tool_name: {"count": 0, "total_time_ms": 0.0, "failures": 0}}
self._comms_log: List[Dict[str, Any]] = []
self.session_usage: Dict[str, Any] = {
"input_tokens": 0,
@@ -1062,8 +1063,17 @@ class AppController:
self._set_status("powershell done, awaiting AI...")
return output
def _append_tool_log(self, script: str, result: str, source_tier: str | None = None) -> None:
def _append_tool_log(self, script: str, result: str, source_tier: str | None = None, elapsed_ms: float = 0.0) -> None:
self._tool_log.append({"script": script, "result": result, "ts": time.time(), "source_tier": source_tier})
tool_name = self._extract_tool_name(script)
is_failure = "REJECTED" in result or "Error" in result or "error" in result.lower()
if tool_name:
if tool_name not in self._tool_stats:
self._tool_stats[tool_name] = {"count": 0, "total_time_ms": 0.0, "failures": 0}
self._tool_stats[tool_name]["count"] += 1
self._tool_stats[tool_name]["total_time_ms"] += elapsed_ms
if is_failure:
self._tool_stats[tool_name]["failures"] += 1
self.ui_last_script_text = script
self.ui_last_script_output = result
self._trigger_script_blink = True
@@ -1071,6 +1081,28 @@ class AppController:
if self.ui_auto_scroll_tool_calls:
self._scroll_tool_calls_to_bottom = True
def _extract_tool_name(self, script: str) -> str:
if not script:
return "unknown"
script_lower = script.lower()
if "powershell" in script_lower or "run_powershell" in script_lower:
return "run_powershell"
if "read_file" in script_lower:
return "read_file"
if "write_file" in script_lower or "write" in script_lower:
return "write_file"
if "list_directory" in script_lower or "ls" in script_lower:
return "list_directory"
if "search_files" in script_lower or "glob" in script_lower:
return "search_files"
if "web_search" in script_lower:
return "web_search"
if "fetch_url" in script_lower:
return "fetch_url"
if "py_get" in script_lower:
return "py_get_skeleton"
return "other"
def resolve_pending_action(self, action_id: str, approved: bool) -> bool:
with self._pending_dialog_lock:
if action_id in self._pending_actions:
@@ -1160,6 +1192,7 @@ class AppController:
"ai_status": self.ai_status,
"mma_streams": self.mma_streams,
"worker_status": self._worker_status,
"tool_stats": self._tool_stats,
"active_tier": self.active_tier,
"active_tickets": self.active_tickets,
"proposed_tracks": self.proposed_tracks
@@ -1608,6 +1641,7 @@ class AppController:
ai_client.reset_session()
ai_client.clear_comms_log()
self._tool_log.clear()
self._tool_stats.clear()
self._comms_log.clear()
self.disc_entries.clear()
# Clear history in ALL discussions to be safe