diff --git a/src/app_controller.py b/src/app_controller.py index 39178b9..f1cf68a 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -147,6 +147,8 @@ class AppController: 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._cached_cache_stats: Dict[str, Any] = {} # Pre-computed cache stats for GUI + self._token_history: List[Dict[str, Any]] = [] # Token usage over time [{"time": t, "input": n, "output": n, "model": s}, ...] + self._session_start_time: float = time.time() # For calculating burn rate self._comms_log: List[Dict[str, Any]] = [] self.session_usage: Dict[str, Any] = { "input_tokens": 0, @@ -961,6 +963,15 @@ class AppController: for k in ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens", "total_tokens"]: if k in u: self.session_usage[k] += u.get(k, 0) or 0 + input_t = u.get("input_tokens", 0) + output_t = u.get("output_tokens", 0) + model = payload.get("model", "unknown") + self._token_history.append({ + "time": time.time(), + "input": input_t, + "output": output_t, + "model": model + }) if kind in ("tool_result", "tool_call"): role = "Tool" if kind == "tool_result" else "Vendor API" @@ -1753,6 +1764,28 @@ class AppController: ai_client.cleanup() self._update_cached_stats() + def get_session_insights(self) -> Dict[str, Any]: + import cost_tracker + total_input = sum(e["input"] for e in self._token_history) + total_output = sum(e["output"] for e in self._token_history) + total_tokens = total_input + total_output + elapsed_min = (time.time() - self._session_start_time) / 60.0 if self._token_history else 0 + burn_rate = total_tokens / elapsed_min if elapsed_min > 0 else 0 + session_cost = cost_tracker.estimate_cost("gemini-2.5-flash", total_input, total_output) + completed = sum(1 for t in self.active_tickets if t.get("status") == "complete") + efficiency = total_tokens / completed if completed > 0 else 0 + return { + "total_tokens": total_tokens, + "total_input": total_input, + "total_output": total_output, + "elapsed_min": elapsed_min, + "burn_rate": burn_rate, + "session_cost": session_cost, + "completed_tickets": completed, + "efficiency": efficiency, + "call_count": len(self._token_history) + } + def _flush_to_project(self) -> None: proj = self.project proj.setdefault("output", {})["output_dir"] = self.ui_output_dir diff --git a/src/gui_2.py b/src/gui_2.py index 684f644..d721fdc 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -333,6 +333,7 @@ class App: self._render_token_budget_panel() self._render_cache_panel() self._render_tool_analytics_panel() + self._render_session_insights_panel() if imgui.collapsing_header("System Prompts"): self._render_system_prompts_panel() @@ -1509,6 +1510,19 @@ class App: imgui.text("0%") imgui.end_table() + def _render_session_insights_panel(self) -> None: + if not imgui.collapsing_header("Session Insights"): + return + insights = self.controller.get_session_insights() + imgui.text(f"Total Tokens: {insights.get('total_tokens', 0):,}") + imgui.text(f"API Calls: {insights.get('call_count', 0)}") + imgui.text(f"Burn Rate: {insights.get('burn_rate', 0):.0f} tokens/min") + imgui.text(f"Session Cost: ${insights.get('session_cost', 0):.4f}") + completed = insights.get('completed_tickets', 0) + efficiency = insights.get('efficiency', 0) + imgui.text(f"Completed: {completed}") + imgui.text(f"Tokens/Ticket: {efficiency:.0f}" if efficiency > 0 else "Tokens/Ticket: N/A") + def _render_message_panel(self) -> None: # LIVE indicator is_live = self.ai_status in ["running powershell...", "fetching url...", "searching web...", "powershell done, awaiting AI..."]