diff --git a/headroom/proxy/cost.py b/headroom/proxy/cost.py index ce91bb04a..45a6937f5 100644 --- a/headroom/proxy/cost.py +++ b/headroom/proxy/cost.py @@ -575,6 +575,21 @@ def build_session_summary( # dropping info the model actually needs). summary["mcp"] = _aggregate_mcp_events() + # Codex WS sessions compress per-unit on the long-lived /responses socket, + # but turn-level records (which feed tokens_saved_total above) only land + # when a response.completed frame carries usage. Surface the live per-unit + # counters so a WS-only session doesn't read as "no activity" mid-turn. + # Kept as a separate block rather than summed into the compression totals: + # turns that DID record already contributed the same savings there, so + # adding the unit sums on top would double-count. + ws_units = getattr(metrics, "codex_ws_units_total", 0) + if ws_units: + summary["codex_ws"] = { + "units_total": ws_units, + "units_modified": getattr(metrics, "codex_ws_units_modified_total", 0), + "tokens_saved": getattr(metrics, "codex_ws_unit_tokens_saved_sum", 0), + } + # Add tip if token mode would help if proxy.config.mode == PROXY_MODE_CACHE and uncompressed_reasons["prefix_frozen"] > 10: summary["tip"] = ( diff --git a/tests/test_proxy_dashboard_stats_cache.py b/tests/test_proxy_dashboard_stats_cache.py index 92056e1ce..ba0cde706 100644 --- a/tests/test_proxy_dashboard_stats_cache.py +++ b/tests/test_proxy_dashboard_stats_cache.py @@ -495,6 +495,39 @@ def test_session_summary_uses_generic_cli_filtering_keys() -> None: assert payload["compression"]["rtk_tokens_avoided"] == 7 assert payload["cost"]["breakdown"]["cli_filtering_savings_usd"] is None assert payload["cost"]["breakdown"]["rtk_savings_usd"] is None + # Metrics fixture has no codex_ws counters -> no codex_ws block. + assert "codex_ws" not in payload + + +def test_session_summary_surfaces_codex_ws_counters() -> None: + from headroom.proxy.cost import build_session_summary + + proxy = SimpleNamespace( + config=SimpleNamespace(mode="token"), + logger=SimpleNamespace(_logs=[]), + cost_tracker=SimpleNamespace(stats=lambda: {}), + ) + metrics = SimpleNamespace( + requests_by_model={}, + tokens_saved_total=0, + codex_ws_units_total=12, + codex_ws_units_modified_total=9, + codex_ws_unit_tokens_saved_sum=4321, + ) + + payload = build_session_summary( + proxy, + metrics, + {}, + cli_tokens_avoided=0, + total_tokens_before=0, + ) + + assert payload["codex_ws"] == { + "units_total": 12, + "units_modified": 9, + "tokens_saved": 4321, + } def test_stats_reset_clears_runtime_proxy_counters(monkeypatch: pytest.MonkeyPatch) -> None: