Add wrap commands for Codex/Cursor/Aider with rtk instructions, fix savings metrics

- headroom wrap codex: injects rtk instructions into AGENTS.md
- headroom wrap cursor: injects into .cursorrules, prints config steps
- headroom wrap aider: injects into CONVENTIONS.md, sets both env vars
- All with --no-rtk flag to skip rtk setup

Unified savings metrics across three layers:
- cli_filtering: tokens avoided by rtk before reaching context
- compression: tokens removed by proxy (SmartCrusher, etc.)
- prefix_cache: provider cache discount with honest attribution

Fix flaky test_process_stats_collected when psutil not installed.
This commit is contained in:
chopratejas 2026-03-13 22:02:47 -07:00
parent 0e09097735
commit e2aac4863a
3 changed files with 527 additions and 69 deletions

View file

@ -2,6 +2,9 @@
Usage:
headroom wrap claude # Start proxy + rtk + claude
headroom wrap codex # Start proxy + OpenAI Codex CLI
headroom wrap aider # Start proxy + aider
headroom wrap cursor # Start proxy + print Cursor config instructions
headroom wrap claude --no-rtk # Without rtk hooks
headroom wrap claude --port 9999 # Custom proxy port
headroom wrap claude -- --model opus # Pass args to claude
@ -112,6 +115,184 @@ def _setup_rtk(verbose: bool = False) -> Path | None:
return rtk_path
# rtk instructions for tools without hook support (Codex, Cursor, Aider).
# These get injected into AGENTS.md / .cursorrules so the LLM voluntarily
# uses rtk-prefixed commands. Kept concise to minimize instruction overhead.
RTK_INSTRUCTIONS_BLOCK = """\
<!-- headroom:rtk-instructions -->
# RTK (Rust Token Killer) - Token-Optimized Commands
When running shell commands, **always prefix with `rtk`**. This reduces context
usage by 60-90% with zero behavior change. If rtk has no filter for a command,
it passes through unchanged so it is always safe to use.
## Key Commands
```bash
# Git (59-80% savings)
rtk git status rtk git diff rtk git log
# Files & Search (60-75% savings)
rtk ls <path> rtk read <file> rtk grep <pattern>
rtk find <pattern> rtk diff <file>
# Test (90-99% savings) — shows failures only
rtk pytest tests/ rtk cargo test rtk test <cmd>
# Build & Lint (80-90% savings) — shows errors only
rtk tsc rtk lint rtk cargo build
rtk prettier --check rtk mypy rtk ruff check
# Analysis (70-90% savings)
rtk err <cmd> rtk log <file> rtk json <file>
rtk summary <cmd> rtk deps rtk env
# GitHub (26-87% savings)
rtk gh pr view <n> rtk gh run list rtk gh issue list
# Infrastructure (85% savings)
rtk docker ps rtk kubectl get rtk docker logs <c>
# Package managers (70-90% savings)
rtk pip list rtk pnpm install rtk npm run <script>
```
## Rules
- In command chains, prefix each segment: `rtk git add . && rtk git commit -m "msg"`
- For debugging, use raw command without rtk prefix
- `rtk proxy <cmd>` runs command without filtering but tracks usage
<!-- /headroom:rtk-instructions -->
"""
# Marker used to detect if instructions are already injected
_RTK_MARKER = "<!-- headroom:rtk-instructions -->"
def _ensure_rtk_binary(verbose: bool = False) -> Path | None:
"""Ensure rtk binary is installed (download if needed). No hook registration."""
from headroom.rtk import get_rtk_path
from headroom.rtk.installer import ensure_rtk
rtk_path = get_rtk_path()
if rtk_path:
if verbose:
click.echo(f" rtk found at {rtk_path}")
return rtk_path
click.echo(" Downloading rtk (Rust Token Killer)...")
rtk_path = ensure_rtk()
if rtk_path:
click.echo(f" rtk installed at {rtk_path}")
return rtk_path
click.echo(" rtk download failed — continuing without it")
return None
def _inject_rtk_instructions(file_path: Path, verbose: bool = False) -> bool:
"""Inject rtk instructions into a file (AGENTS.md, .cursorrules, etc.).
Idempotent skips if marker already present. Appends to existing content.
Returns True if instructions were written.
"""
if file_path.exists():
existing = file_path.read_text()
if _RTK_MARKER in existing:
if verbose:
click.echo(f" rtk instructions already in {file_path.name}")
return True
# Append to existing file
with open(file_path, "a") as f:
f.write("\n\n" + RTK_INSTRUCTIONS_BLOCK)
else:
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(RTK_INSTRUCTIONS_BLOCK)
click.echo(f" rtk instructions injected into {file_path}")
return True
def _ensure_proxy(port: int, no_proxy: bool) -> subprocess.Popen | None:
"""Start or verify proxy. Returns process handle if we started it."""
if not no_proxy:
if _check_proxy(port):
click.echo(f" Proxy already running on port {port}")
return None
else:
click.echo(f" Starting Headroom proxy on port {port}...")
try:
proc = _start_proxy(port)
click.echo(f" Proxy ready on http://127.0.0.1:{port}")
return proc
except RuntimeError as e:
click.echo(f" Error: {e}")
raise SystemExit(1) from e
else:
if not _check_proxy(port):
click.echo(f" Warning: No proxy detected on port {port}")
return None
def _make_cleanup(proxy_proc_holder: list) -> Any:
"""Create a cleanup function that terminates the proxy on exit."""
def cleanup(signum: int | None = None, frame: Any = None) -> None:
proc = proxy_proc_holder[0] if proxy_proc_holder else None
if proc and proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
return cleanup
def _launch_tool(
binary: str,
args: tuple,
env: dict[str, str],
port: int,
no_proxy: bool,
tool_label: str,
env_vars_display: list[str],
) -> None:
"""Common logic: start proxy, launch tool, clean up."""
proxy_holder: list[subprocess.Popen | None] = [None]
cleanup = _make_cleanup(proxy_holder)
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
try:
click.echo()
padded = f"HEADROOM WRAP: {tool_label}".center(47)
click.echo(" ╔═══════════════════════════════════════════════╗")
click.echo(f"{padded}")
click.echo(" ╚═══════════════════════════════════════════════╝")
click.echo()
proxy_holder[0] = _ensure_proxy(port, no_proxy)
click.echo()
click.echo(f" Launching {tool_label} (API routed through Headroom)...")
for var in env_vars_display:
click.echo(f" {var}")
if args:
click.echo(f" Extra args: {' '.join(args)}")
click.echo()
result = subprocess.run([binary, *args], env=env)
raise SystemExit(result.returncode)
except SystemExit:
raise
except Exception as e:
click.echo(f" Error: {e}")
raise SystemExit(1) from e
finally:
cleanup()
@main.group()
def wrap() -> None:
"""Wrap CLI tools to run through Headroom.
@ -121,13 +302,19 @@ def wrap() -> None:
the target tool so all API calls route through Headroom automatically.
\b
Example:
headroom wrap claude # Proxy + rtk + Claude Code
headroom wrap claude --no-rtk # Proxy only, no rtk hooks
headroom wrap claude -- -p # Pass --print flag to claude
Supported tools:
headroom wrap claude # Claude Code (Anthropic)
headroom wrap codex # OpenAI Codex CLI
headroom wrap aider # Aider
headroom wrap cursor # Cursor (prints config instructions)
"""
# =============================================================================
# Claude Code
# =============================================================================
@wrap.command()
@click.option("--port", "-p", default=8787, type=int, help="Proxy port (default: 8787)")
@click.option("--no-rtk", is_flag=True, help="Skip rtk installation and hook registration")
@ -138,17 +325,7 @@ def claude(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, claude_args:
"""Launch Claude Code through Headroom proxy.
\b
This command:
1. Starts a Headroom optimization proxy on localhost
2. Installs rtk and registers Claude Code hooks (compresses CLI output)
3. Sets ANTHROPIC_API_URL to route through the proxy
4. Launches 'claude' with your arguments
\b
All API calls from Claude Code flow through Headroom, which:
- Compresses tool outputs (SmartCrusher, Kompress, CodeCompressor)
- Preserves prefix cache (frozen message optimization)
- Tracks token savings and cache hit rates
Sets ANTHROPIC_BASE_URL to route all Anthropic API calls through Headroom.
\b
Examples:
@ -157,24 +334,15 @@ def claude(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, claude_args:
headroom wrap claude --port 9999 # Custom proxy port
headroom wrap claude --no-rtk # Skip rtk (proxy only)
"""
# Check that claude CLI is available
claude_bin = shutil.which("claude")
if not claude_bin:
click.echo("Error: 'claude' not found in PATH.")
click.echo("Install Claude Code: https://docs.anthropic.com/en/docs/claude-code")
raise SystemExit(1)
proxy_proc: subprocess.Popen | None = None
def cleanup(signum: int | None = None, frame: Any = None) -> None:
"""Clean up proxy on exit."""
if proxy_proc and proxy_proc.poll() is None:
proxy_proc.terminate()
try:
proxy_proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proxy_proc.kill()
# Setup rtk before launching (Claude-specific)
proxy_holder: list[subprocess.Popen | None] = [None]
cleanup = _make_cleanup(proxy_holder)
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
@ -185,31 +353,14 @@ def claude(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, claude_args:
click.echo(" ╚═══════════════════════════════════════════════╝")
click.echo()
# Step 1: Start proxy
if not no_proxy:
if _check_proxy(port):
click.echo(f" Proxy already running on port {port}")
else:
click.echo(f" Starting Headroom proxy on port {port}...")
try:
proxy_proc = _start_proxy(port)
click.echo(f" Proxy ready on http://127.0.0.1:{port}")
except RuntimeError as e:
click.echo(f" Error: {e}")
raise SystemExit(1) from e
else:
if not _check_proxy(port):
click.echo(f" Warning: No proxy detected on port {port}")
proxy_holder[0] = _ensure_proxy(port, no_proxy)
# Step 2: Setup rtk
if not no_rtk:
click.echo(" Setting up rtk...")
_setup_rtk(verbose=verbose)
else:
if verbose:
click.echo(" Skipping rtk (--no-rtk)")
elif verbose:
click.echo(" Skipping rtk (--no-rtk)")
# Step 3: Launch claude
click.echo()
click.echo(" Launching Claude Code (API routed through Headroom)...")
click.echo(f" ANTHROPIC_BASE_URL=http://127.0.0.1:{port}")
@ -220,12 +371,7 @@ def claude(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, claude_args:
env = os.environ.copy()
env["ANTHROPIC_BASE_URL"] = f"http://127.0.0.1:{port}"
# Run claude — this blocks until claude exits
result = subprocess.run(
[claude_bin, *claude_args],
env=env,
)
result = subprocess.run([claude_bin, *claude_args], env=env)
raise SystemExit(result.returncode)
except SystemExit:
@ -235,3 +381,213 @@ def claude(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, claude_args:
raise SystemExit(1) from e
finally:
cleanup()
# =============================================================================
# OpenAI Codex CLI
# =============================================================================
@wrap.command()
@click.option("--port", "-p", default=8787, type=int, help="Proxy port (default: 8787)")
@click.option("--no-rtk", is_flag=True, help="Skip rtk installation and AGENTS.md injection")
@click.option("--no-proxy", is_flag=True, help="Skip proxy startup (use existing proxy)")
@click.option("--verbose", "-v", is_flag=True, help="Verbose output")
@click.argument("codex_args", nargs=-1, type=click.UNPROCESSED)
def codex(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, codex_args: tuple) -> None:
"""Launch OpenAI Codex CLI through Headroom proxy.
\b
Sets OPENAI_BASE_URL to route all OpenAI API calls through Headroom.
Installs rtk and injects instructions into AGENTS.md so Codex uses
token-optimized commands (60-90% savings on shell output).
\b
Examples:
headroom wrap codex # Start proxy + rtk + codex
headroom wrap codex -- "fix the bug" # Pass prompt to codex
headroom wrap codex --no-rtk # Skip rtk setup
headroom wrap codex --port 9999 # Custom proxy port
"""
codex_bin = shutil.which("codex")
if not codex_bin:
click.echo("Error: 'codex' not found in PATH.")
click.echo("Install Codex CLI: npm install -g @openai/codex")
raise SystemExit(1)
# Setup rtk for Codex (binary + AGENTS.md instructions, no hooks)
if not no_rtk:
click.echo(" Setting up rtk for Codex...")
rtk_path = _ensure_rtk_binary(verbose=verbose)
if rtk_path:
# Inject into project AGENTS.md (Codex reads this automatically)
agents_md = Path.cwd() / "AGENTS.md"
_inject_rtk_instructions(agents_md, verbose=verbose)
# Also inject into global ~/.codex/AGENTS.md
global_agents = Path.home() / ".codex" / "AGENTS.md"
_inject_rtk_instructions(global_agents, verbose=verbose)
env = os.environ.copy()
env["OPENAI_BASE_URL"] = f"http://127.0.0.1:{port}/v1"
_launch_tool(
binary=codex_bin,
args=codex_args,
env=env,
port=port,
no_proxy=no_proxy,
tool_label="CODEX",
env_vars_display=[f"OPENAI_BASE_URL=http://127.0.0.1:{port}/v1"],
)
# =============================================================================
# Aider
# =============================================================================
@wrap.command()
@click.option("--port", "-p", default=8787, type=int, help="Proxy port (default: 8787)")
@click.option("--no-rtk", is_flag=True, help="Skip rtk installation and conventions injection")
@click.option("--no-proxy", is_flag=True, help="Skip proxy startup (use existing proxy)")
@click.option("--verbose", "-v", is_flag=True, help="Verbose output")
@click.argument("aider_args", nargs=-1, type=click.UNPROCESSED)
def aider(port: int, no_rtk: bool, no_proxy: bool, verbose: bool, aider_args: tuple) -> None:
"""Launch aider through Headroom proxy.
\b
Sets OPENAI_API_BASE to route all API calls through Headroom.
Installs rtk and injects instructions into .aider.conf.yml conventions
so aider uses token-optimized commands.
\b
Examples:
headroom wrap aider # Start proxy + rtk + aider
headroom wrap aider -- --model gpt-4o # Use GPT-4o
headroom wrap aider -- --model claude-sonnet-4 # Use Claude
headroom wrap aider --no-rtk # Skip rtk setup
"""
aider_bin = shutil.which("aider")
if not aider_bin:
click.echo("Error: 'aider' not found in PATH.")
click.echo("Install aider: pip install aider-chat")
raise SystemExit(1)
# Setup rtk for aider (binary + CONVENTIONS.md instructions)
if not no_rtk:
click.echo(" Setting up rtk for aider...")
rtk_path = _ensure_rtk_binary(verbose=verbose)
if rtk_path:
# aider reads CONVENTIONS.md from project root
conventions = Path.cwd() / "CONVENTIONS.md"
_inject_rtk_instructions(conventions, verbose=verbose)
env = os.environ.copy()
env["OPENAI_API_BASE"] = f"http://127.0.0.1:{port}/v1"
env["ANTHROPIC_BASE_URL"] = f"http://127.0.0.1:{port}"
_launch_tool(
binary=aider_bin,
args=aider_args,
env=env,
port=port,
no_proxy=no_proxy,
tool_label="AIDER",
env_vars_display=[
f"OPENAI_API_BASE=http://127.0.0.1:{port}/v1",
f"ANTHROPIC_BASE_URL=http://127.0.0.1:{port}",
],
)
# =============================================================================
# Cursor
# =============================================================================
@wrap.command()
@click.option("--port", "-p", default=8787, type=int, help="Proxy port (default: 8787)")
@click.option("--no-rtk", is_flag=True, help="Skip rtk installation and .cursorrules injection")
@click.option("--no-proxy", is_flag=True, help="Skip proxy startup (use existing proxy)")
@click.option("--verbose", "-v", is_flag=True, help="Verbose output")
def cursor(port: int, no_rtk: bool, no_proxy: bool, verbose: bool) -> None:
"""Start Headroom proxy for use with Cursor.
\b
Cursor reads its API configuration from its settings UI, not from
environment variables. This command starts the proxy, installs rtk
with .cursorrules instructions, and prints the Cursor settings.
\b
After running this command, open Cursor and configure:
Settings > Models > OpenAI API Key > Advanced > Override Base URL
\b
Example:
headroom wrap cursor # Start proxy + rtk + instructions
headroom wrap cursor --no-rtk # Proxy only, no rtk
headroom wrap cursor --port 9999 # Custom proxy port
"""
proxy_holder: list[subprocess.Popen | None] = [None]
cleanup = _make_cleanup(proxy_holder)
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
try:
click.echo()
click.echo(" ╔═══════════════════════════════════════════════╗")
click.echo(" ║ HEADROOM WRAP: CURSOR ║")
click.echo(" ╚═══════════════════════════════════════════════╝")
click.echo()
proxy_holder[0] = _ensure_proxy(port, no_proxy)
# Setup rtk for Cursor (binary + .cursorrules instructions)
if not no_rtk:
click.echo(" Setting up rtk for Cursor...")
rtk_path = _ensure_rtk_binary(verbose=verbose)
if rtk_path:
cursorrules = Path.cwd() / ".cursorrules"
_inject_rtk_instructions(cursorrules, verbose=verbose)
click.echo()
click.echo(" Headroom proxy is running. Configure Cursor:")
click.echo()
click.echo(" For OpenAI models:")
click.echo(f" Base URL: http://127.0.0.1:{port}/v1")
click.echo(" API Key: your-openai-api-key")
click.echo()
click.echo(" For Anthropic models:")
click.echo(f" Base URL: http://127.0.0.1:{port}")
click.echo(" API Key: your-anthropic-api-key")
click.echo()
click.echo(" In Cursor:")
click.echo(" Settings > Models > OpenAI API Key > Override OpenAI Base URL")
click.echo(f" Set to: http://127.0.0.1:{port}/v1")
if not no_rtk:
click.echo()
click.echo(" rtk instructions injected into .cursorrules")
click.echo(" Cursor will use token-optimized commands automatically.")
click.echo()
click.echo(" Press Ctrl+C to stop the proxy.")
click.echo()
# Block until Ctrl+C
try:
while True:
time.sleep(1)
proc = proxy_holder[0]
if proc and proc.poll() is not None:
click.echo(" Proxy process exited unexpectedly.")
raise SystemExit(1)
except KeyboardInterrupt:
click.echo("\n Shutting down...")
except SystemExit:
raise
except Exception as e:
click.echo(f" Error: {e}")
raise SystemExit(1) from e
finally:
cleanup()

View file

@ -217,6 +217,46 @@ _CACHE_ECONOMICS = {
}
def _get_rtk_stats() -> dict[str, Any] | None:
"""Get rtk (Rust Token Killer) savings stats if rtk is installed.
Reads from rtk's tracking database via `rtk gain --format json`.
Returns None if rtk is not installed.
"""
import shutil
import subprocess as _sp
rtk_bin = shutil.which("rtk")
if not rtk_bin:
# Check headroom-managed install
rtk_managed = Path.home() / ".headroom" / "bin" / "rtk"
if rtk_managed.exists():
rtk_bin = str(rtk_managed)
else:
return None
try:
result = _sp.run(
[rtk_bin, "gain", "--format", "json"],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode == 0 and result.stdout.strip():
data = json.loads(result.stdout)
summary = data.get("summary", {})
return {
"installed": True,
"total_commands": summary.get("total_commands", 0),
"tokens_saved": summary.get("total_saved", 0),
"avg_savings_pct": summary.get("avg_savings_pct", 0.0),
}
except Exception:
pass
return {"installed": True, "total_commands": 0, "tokens_saved": 0, "avg_savings_pct": 0.0}
def _build_prefix_cache_stats(
metrics: PrometheusMetrics,
cost_tracker: CostTracker | None,
@ -322,24 +362,27 @@ def _build_prefix_cache_stats(
metrics.prefix_freeze_tokens_preserved - metrics.prefix_freeze_compression_foregone
),
},
"attribution": (
"Prefix caching is performed by the LLM provider (Anthropic, OpenAI). "
"Headroom reports cache stats as observed from API responses. "
"CacheAligner and prefix freeze improve cache hit rates by stabilizing "
"the message prefix, but baseline caching happens without Headroom."
),
}
def _merge_cost_stats(
cost_stats: dict | None,
cache_stats: dict,
cli_tokens_avoided: int = 0,
) -> dict | None:
"""Add prefix cache savings to overall cost stats.
"""Add prefix cache and CLI filtering savings to overall cost stats.
Compression savings and cache savings are computed on different token
scopes (Headroom tracks user-message tokens; Anthropic's cache metrics
cover the entire prompt including system/tools). We keep both as
additive line items without cross-contaminating the counterfactual.
- compression_savings_usd: from removing tokens (CostTracker scope)
- cache_savings_usd: from prefix cache discounts (full-prompt scope)
- savings_usd: sum of both (hero metric)
- cost_with/without_headroom_usd: unchanged (compression scope only)
Three savings layers, each on a different scope:
- compression_savings_usd: tokens removed by proxy (SmartCrusher, etc.)
- cache_savings_usd: prefix cache discount from provider
- cli_filtering_savings_usd: tokens avoided by rtk before reaching context
- savings_usd: sum of all (hero metric)
"""
if cost_stats is None:
return None
@ -347,11 +390,24 @@ def _merge_cost_stats(
cache_net = cache_stats.get("totals", {}).get("net_savings_usd", 0.0)
compression_savings = cost_stats.get("savings_usd", 0.0)
# Estimate CLI filtering savings: tokens_avoided * avg input price
# Use the average input price from the cost tracker if available
cli_savings_usd = 0.0
if cli_tokens_avoided > 0 and LITELLM_AVAILABLE:
# Use a conservative estimate: average across models seen
total_input_cost = cost_stats.get("total_input_cost_usd", 0.0)
total_input_tokens = cost_stats.get("total_input_tokens", 0)
if total_input_tokens > 0:
avg_price_per_token = total_input_cost / total_input_tokens
cli_savings_usd = cli_tokens_avoided * avg_price_per_token
return {
**cost_stats,
"savings_usd": round(compression_savings + cache_net, 4),
"savings_usd": round(compression_savings + cache_net + cli_savings_usd, 4),
"compression_savings_usd": round(compression_savings, 4),
"cache_savings_usd": round(cache_net, 4),
"cli_filtering_savings_usd": round(cli_savings_usd, 4),
"cli_tokens_avoided": cli_tokens_avoided,
}
@ -6240,10 +6296,42 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI:
# Build prefix cache stats once (used in both prefix_cache and cost)
prefix_cache_stats = _build_prefix_cache_stats(m, proxy.cost_tracker)
# Fetch CLI filtering savings (rtk — tokens avoided before reaching context)
cli_filtering_stats = _get_rtk_stats()
cli_tokens_avoided = (
cli_filtering_stats.get("tokens_saved", 0) if cli_filtering_stats else 0
)
# Calculate total tokens before compression
total_tokens_before = m.tokens_input_total + m.tokens_saved_total
# Build unified savings summary (all layers)
compression_tokens = m.tokens_saved_total
cache_net_usd = prefix_cache_stats.get("totals", {}).get("net_savings_usd", 0.0)
total_tokens_all_layers = compression_tokens + cli_tokens_avoided
return {
"savings": {
"total_tokens": total_tokens_all_layers,
"by_layer": {
"cli_filtering": {
"tokens": cli_tokens_avoided,
"description": "Tokens avoided by CLI output filtering (rtk) before reaching context",
},
"compression": {
"tokens": compression_tokens,
"description": "Tokens removed by proxy compression (SmartCrusher, ContentRouter, etc.)",
},
"prefix_cache": {
"discount_usd": round(cache_net_usd, 4),
"description": (
"Cost discount from provider prefix caching. "
"Headroom's CacheAligner improves hit rates; "
"baseline caching is provider-native."
),
},
},
},
"requests": {
"total": m.requests_total,
"cached": m.requests_cached,
@ -6256,6 +6344,7 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI:
"input": m.tokens_input_total,
"output": m.tokens_output_total,
"saved": m.tokens_saved_total,
"cli_tokens_avoided": cli_tokens_avoided,
"total_before_compression": total_tokens_before,
"savings_percent": round(
(m.tokens_saved_total / total_tokens_before * 100)
@ -6298,6 +6387,7 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI:
"cost": _merge_cost_stats(
proxy.cost_tracker.stats() if proxy.cost_tracker else None,
prefix_cache_stats,
cli_tokens_avoided=cli_tokens_avoided,
),
"compression": {
"ccr_entries": compression_stats.get("entry_count", 0),
@ -6327,6 +6417,7 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI:
),
},
"toin": get_toin().get_stats(),
"cli_filtering": cli_filtering_stats,
"cache": await proxy.cache.stats() if proxy.cache else None,
"rate_limiter": await proxy.rate_limiter.stats() if proxy.rate_limiter else None,
"recent_requests": proxy.logger.get_recent(10) if proxy.logger else [],

View file

@ -445,9 +445,14 @@ class TestProcessStatsCollection:
stats = tracker.get_process_stats()
# Should have real values from the current process
assert stats.rss_bytes > 0 # Process must use some memory
assert stats.vms_bytes > 0
# psutil is optional — without it, stats are all zeros (graceful degradation)
try:
import psutil # noqa: F401
assert stats.rss_bytes > 0 # Process must use some memory
assert stats.vms_bytes > 0
except ImportError:
assert stats.rss_bytes == 0 # No psutil → zeros expected
assert stats.percent >= 0 # Could be 0 on some systems
def test_process_stats_in_report(self):
@ -456,5 +461,11 @@ class TestProcessStatsCollection:
report = tracker.get_report()
assert report.process.rss_bytes > 0
assert report.process.rss_mb > 0
try:
import psutil # noqa: F401
assert report.process.rss_bytes > 0
assert report.process.rss_mb > 0
except ImportError:
# Without psutil, process stats are zeros — that's expected
assert report.process.rss_bytes == 0