From 68676daa5076286e2992e81a5cd7d583d82a71b9 Mon Sep 17 00:00:00 2001 From: Tejas Chopra Date: Thu, 9 Jul 2026 10:49:54 -0400 Subject: [PATCH] feat: ship the coding profile as Headroom's out-of-box default posture (#1893) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make a bare `headroom proxy` (and the uvicorn factory / argparse main) default to the cache-mode coding posture instead of requiring users to set a dozen env vars. Profile (agent_savings.py): * "coding" is now the DEFAULT profile (DEFAULT_PROFILE), and it is rewritten for cache mode: proxy_mode="cache" and compress_user_messages=True (cache mode compresses the newest OBSERVATION delta — a user/tool turn — so compress_user must be on or there is nothing to compress; prefix stability is preserved by the delta engine, not by refusing to touch user turns). * AgentSavingsProfile carries the standalone router/handler toggles too (tool_search, cross_turn_dedup, lossless_then_lossy, protect_reads, code_aware, effort_router, lossless, min_chars_for_block); proxy_env() emits them. Defaults preserve current behavior for the other profiles. * coding sets: tool_search=1, dedupe=1, lossless_then_lossy=1, protect_reads=1, code_aware=1, effort_router=0, lossless=0, min_chars_for_block=25. CCR stays ON (no HEADROOM_NO_CCR) so any lossy loss is recoverable. * apply_agent_savings_env_defaults() now honors an explicit HEADROOM_SAVINGS_PROFILE already in the env before falling back to the default. Delivery (pollution-free by construction): * MODE and savings_profile default via INLINE defaults in the config builders (cache / coding) — no global env mutation, so unit tests that build config directly keep clean defaults. * The request-time toggles are seeded into os.environ (setdefault) via seed_proxy_env_defaults() ONLY at the executable/deployment entries — run_server() (before serving) and create_app_from_env() (uvicorn factory) — NOT in the CLI command or any library builder, so CliRunner tests never leak coding defaults into os.environ across tests. * CLI code_aware now defaults ON, matching the argparse server path (degrades to a no-op without tree-sitter). All explicit user env vars / CLI flags still win (setdefault + `or` fallbacks). HEADROOM_LOSSLESS was already 0 by default; unchanged. Tests: coding-profile + CLI-proxy-env tests updated to the new defaults; 1047 passed across the touched areas (only pre-existing memory/env failures remain). ## Description Closes # ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - ## Testing - [ ] Unit tests pass (`pytest`) - [ ] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text # Paste relevant command output or artifact links here ``` ## Real Behavior Proof - Environment: - Exact command / steps: - Observed result: - Not tested: ## Review Readiness - [ ] I have performed a self-review - [ ] This PR is ready for human review ## Checklist - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) Add screenshots to help explain your changes. ## Additional Notes --------- Co-authored-by: Claude Opus 4.8 (1M context) --- headroom/agent_savings.py | 83 ++++++++++++++++++++++++++++--- headroom/cli/proxy.py | 13 +++-- headroom/proxy/server.py | 28 +++++++++-- tests/test_agent_savings.py | 24 +++++++-- tests/test_cli/test_wrap_codex.py | 11 ++++ tests/test_cli_proxy_env.py | 8 +-- 6 files changed, 145 insertions(+), 22 deletions(-) diff --git a/headroom/agent_savings.py b/headroom/agent_savings.py index d998e5af1..aa90bc437 100644 --- a/headroom/agent_savings.py +++ b/headroom/agent_savings.py @@ -4,6 +4,7 @@ from __future__ import annotations import logging import os +from collections.abc import MutableMapping from dataclasses import dataclass, replace from typing import Protocol @@ -11,6 +12,10 @@ logger = logging.getLogger(__name__) AGENT_90_PROFILE = "agent-90" FALLBACK_PROFILE = "balanced" +# Out-of-the-box default profile when none is requested. Headroom's primary +# workload is coding agents, and the "coding" profile encodes the cache-mode +# delta posture (see below). +DEFAULT_PROFILE = "coding" class CompressConfigLike(Protocol): @@ -42,6 +47,17 @@ class AgentSavingsProfile: force_kompress: bool proxy_mode: str accuracy_guard: str + # Standalone router/handler toggles carried through the profile so a single + # named profile seeds Headroom's full posture. Defaults below preserve the + # current global behavior, so only a profile that opts in changes anything. + tool_search: bool = False + cross_turn_dedup: bool = False + lossless_then_lossy: bool = False + protect_reads: bool = False + code_aware: bool = True + effort_router: bool = True + lossless: bool = False + min_chars_for_block: int | None = None @property def savings_percent(self) -> int: @@ -65,14 +81,25 @@ class AgentSavingsProfile: ), "HEADROOM_FORCE_KOMPRESS": "1" if self.force_kompress else "0", "HEADROOM_ACCURACY_GUARD": self.accuracy_guard, + # Standalone router/handler toggles. + "HEADROOM_TOOL_SEARCH": "1" if self.tool_search else "0", + "HEADROOM_DEDUPE": "1" if self.cross_turn_dedup else "0", + "HEADROOM_LOSSLESS_THEN_LOSSY": "1" if self.lossless_then_lossy else "0", + "HEADROOM_PROTECT_READS": "1" if self.protect_reads else "0", + "HEADROOM_CODE_AWARE_ENABLED": "1" if self.code_aware else "0", + "HEADROOM_EFFORT_ROUTER": "1" if self.effort_router else "0", + "HEADROOM_LOSSLESS": "1" if self.lossless else "0", } # Only pin a keep-ratio when the profile sets one; workload personas # leave it unset so Kompress decides and the ambient default applies. if self.target_ratio is not None: env["HEADROOM_TARGET_RATIO"] = f"{self.target_ratio:.2f}" + # Block-compression char floor: only emit when the profile pins one. + if self.min_chars_for_block is not None: + env["HEADROOM_MIN_CHARS_FOR_BLOCK"] = str(self.min_chars_for_block) return env - def apply_proxy_env_defaults(self, env: dict[str, str]) -> dict[str, str]: + def apply_proxy_env_defaults(self, env: MutableMapping[str, str]) -> MutableMapping[str, str]: """Seed proxy env defaults without overriding explicit user settings.""" for key, value in self.proxy_env().items(): @@ -125,7 +152,11 @@ _PROFILES: dict[str, AgentSavingsProfile] = { name="coding", target_savings=0.50, # nominal (display only); savings are emergent target_ratio=None, - compress_user_messages=False, # no prompt mutation / cache bust + # Cache mode compresses only the newest delta — a tool/user OBSERVATION — + # so compress_user must be ON or there is nothing to compress. Prefix + # stability (no bust) is preserved by the delta engine (frozen prefix + + # append-only forwarding), not by refusing to touch user turns. + compress_user_messages=True, compress_system_messages=False, # system prompt is the hottest cache protect_recent=2, # keep the active code working set verbatim protect_analysis_context=True, @@ -133,8 +164,21 @@ _PROFILES: dict[str, AgentSavingsProfile] = { max_items_after_crush=15, smart_crusher_with_compaction=True, force_kompress=False, # don't override diff/log lossless with lossy ML - proxy_mode="token", + proxy_mode="cache", # delta-only compression at ~0 prefix-cache busts accuracy_guard="strict", + # Coding posture: defer non-core tool schemas, dedupe re-reads, extend + # lossy coverage when lossless found nothing, and NEVER lossy-compress a + # file read (the agent patches exact bytes). CCR stays ON (unset) so any + # lossy loss is recoverable. Effort router off; low block floor so modest + # deltas are eligible. + tool_search=True, + cross_turn_dedup=True, + lossless_then_lossy=True, + protect_reads=True, + code_aware=True, + effort_router=False, + lossless=False, + min_chars_for_block=25, ), "general": AgentSavingsProfile( name="general", @@ -166,7 +210,7 @@ def get_agent_savings_profile(name: str | None = None) -> AgentSavingsProfile: to ``balanced`` rather than leaving the user with no proxy at all. """ - key = (name or AGENT_90_PROFILE).strip().lower() + key = (name or DEFAULT_PROFILE).strip().lower() profile = _PROFILES.get(key) if profile is not None: return profile @@ -181,11 +225,18 @@ def get_agent_savings_profile(name: str | None = None) -> AgentSavingsProfile: def apply_agent_savings_env_defaults( - env: dict[str, str], + env: MutableMapping[str, str], profile: AgentSavingsProfile | str | None = None, -) -> dict[str, str]: - """Apply agent savings env defaults to a proxy subprocess environment.""" +) -> MutableMapping[str, str]: + """Apply agent savings env defaults to a proxy subprocess environment. + When ``profile`` is not given, an explicit ``HEADROOM_SAVINGS_PROFILE`` already + in ``env`` is honored; only when that too is absent do we fall back to the + out-of-box default (:data:`DEFAULT_PROFILE`, i.e. ``coding``). + """ + + if profile is None: + profile = env.get("HEADROOM_SAVINGS_PROFILE") resolved = ( get_agent_savings_profile(profile) if isinstance(profile, str) or profile is None @@ -295,6 +346,24 @@ def proxy_pipeline_kwargs(config: object) -> dict[str, object]: return kwargs +def seed_proxy_env_defaults(env: MutableMapping[str, str] | None = None) -> None: + """Seed the process env with the savings-profile defaults (default: coding). + + Call at proxy EXECUTABLE entry points (the ``headroom proxy`` command and the + uvicorn ``create_app_from_env`` factory) BEFORE building config from env. Uses + ``setdefault`` semantics via :func:`apply_agent_savings_env_defaults`, so any + explicit user setting wins and the call is idempotent. + + Deliberately NOT called from ``_proxy_config_from_env`` / ``ContentRouter`` or + any other library-level builder that unit tests construct directly, so those + keep clean (unseeded) defaults and test isolation is preserved. + """ + target = os.environ if env is None else env + # apply_agent_savings_env_defaults honors an explicit HEADROOM_SAVINGS_PROFILE + # already in the env and otherwise falls back to DEFAULT_PROFILE (coding). + apply_agent_savings_env_defaults(target) + + def with_target_savings( profile: AgentSavingsProfile, target_savings: float, diff --git a/headroom/cli/proxy.py b/headroom/cli/proxy.py index fbce35372..e5693862d 100644 --- a/headroom/cli/proxy.py +++ b/headroom/cli/proxy.py @@ -10,7 +10,7 @@ import click from headroom import paths as _paths from headroom.providers.registry import resolve_api_overrides, resolve_api_targets -from headroom.proxy.modes import PROXY_MODE_TOKEN, normalize_proxy_mode +from headroom.proxy.modes import PROXY_MODE_CACHE, normalize_proxy_mode from .main import main @@ -1033,9 +1033,10 @@ def proxy( # Resolve anyllm provider: env var takes precedence over CLI default (matches argparse path) effective_anyllm_provider = os.environ.get("HEADROOM_ANYLLM_PROVIDER") or anyllm_provider - # Resolve mode: CLI flag > env var > default + # Resolve mode: CLI flag > env var > default. Default is CACHE (Headroom's + # coding posture): delta-only compression at ~0 prefix-cache busts. effective_mode: str = normalize_proxy_mode( - mode or os.environ.get("HEADROOM_MODE") or PROXY_MODE_TOKEN + mode or os.environ.get("HEADROOM_MODE") or PROXY_MODE_CACHE ) # Stateless mode: CLI flag or env var @@ -1102,7 +1103,7 @@ def proxy( else frozenset(), tool_profiles=_parse_tool_profiles([]) or None, smart_crusher_with_compaction=_get_env_bool_optional("HEADROOM_SMART_CRUSHER_COMPACTION"), - savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or None, + savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or "coding", target_ratio=target_ratio, compress_system_messages=_get_env_bool_optional("HEADROOM_COMPRESS_SYSTEM_MESSAGES"), protect_recent=_get_env_int_optional("HEADROOM_PROTECT_RECENT"), @@ -1154,10 +1155,12 @@ def proxy( # 2. Otherwise read HEADROOM_CODE_AWARE_ENABLED (truthy = on). # 3. Otherwise default off — matches the prior cli/proxy.py behavior so # existing users see no change unless they opt in. + # Default ON (coding posture; consistent with the argparse server path). + # Degrades gracefully to a no-op when tree-sitter isn't installed. code_aware_enabled=( bool(code_aware_flag) if code_aware_flag is not None - else os.environ.get("HEADROOM_CODE_AWARE_ENABLED", "").strip().lower() + else os.environ.get("HEADROOM_CODE_AWARE_ENABLED", "1").strip().lower() in ("true", "1", "yes", "on") ), disable_kompress=disable_kompress, diff --git a/headroom/proxy/server.py b/headroom/proxy/server.py index 3d69e5ff6..ca7127f69 100644 --- a/headroom/proxy/server.py +++ b/headroom/proxy/server.py @@ -4313,7 +4313,13 @@ def _proxy_config_from_env() -> ProxyConfig: periodic_toin_stats_enabled=_get_env_bool("HEADROOM_PERIODIC_TOIN_STATS", True), proxy_token=os.environ.get("HEADROOM_PROXY_TOKEN") or None, offline=_get_env_bool("HEADROOM_OFFLINE", False), - mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_TOKEN)), + # Default mode is CACHE (Headroom's coding posture): delta-only compression + # at ~0 prefix-cache busts. HEADROOM_MODE overrides. + mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_CACHE)), + # Default savings profile is "coding" so proxy_pipeline_kwargs applies its + # posture (compress_user, protect_recent, min_tokens). HEADROOM_SAVINGS_PROFILE + # overrides. + savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or "coding", read_maturation=_get_env_bool("HEADROOM_READ_MATURATION", False), read_maturation_quiesce_turns=_get_env_int("HEADROOM_READ_MATURATION_QUIESCE_TURNS", 5), read_maturation_max_hold_turns=_get_env_int("HEADROOM_READ_MATURATION_MAX_HOLD_TURNS", 25), @@ -4324,6 +4330,12 @@ def _proxy_config_from_env() -> ProxyConfig: def create_app_from_env() -> FastAPI: + # Seed the coding-profile defaults into the process env BEFORE reading config, + # so the uvicorn factory launch gets Headroom's out-of-box posture (cache mode, + # tool-search, dedupe, read protection, …). setdefault → explicit env wins. + from headroom.agent_savings import seed_proxy_env_defaults + + seed_proxy_env_defaults() return create_app(_proxy_config_from_env()) @@ -4362,6 +4374,16 @@ def run_server( print("ERROR: FastAPI required. Install: pip install fastapi uvicorn httpx") sys.exit(1) + # Seed the request-time coding-profile toggles (tool-search, dedupe, read + # protection, lossless→lossy, effort-router, block-char floor) into the + # process env before serving, so downstream per-request readers pick them up. + # Done here (not in the CLI command) so unit tests that mock run_server never + # mutate os.environ. setdefault → explicit env still wins. MODE / profile are + # already resolved into `config` above via their inline defaults. + from headroom.agent_savings import seed_proxy_env_defaults + + seed_proxy_env_defaults() + config = config or ProxyConfig() code_aware_status = _get_code_aware_banner_status(config) @@ -4969,10 +4991,10 @@ if __name__ == "__main__": protect_tool_results=frozenset(protect_tool_results) if protect_tool_results else frozenset(), - mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_TOKEN)), + mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_CACHE)), compress_user_messages=args.compress_user_messages or _get_env_bool("HEADROOM_COMPRESS_USER_MESSAGES", False), - savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or None, + savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or "coding", # Default 0.4 keep-ratio so the Kompress text (prose/code) path compresses # meaningfully out of the box; HEADROOM_TARGET_RATIO overrides. target_ratio=( diff --git a/tests/test_agent_savings.py b/tests/test_agent_savings.py index 589eaf1eb..165949b7a 100644 --- a/tests/test_agent_savings.py +++ b/tests/test_agent_savings.py @@ -70,12 +70,23 @@ def test_coding_persona_protects_working_set_and_stays_visible() -> None: env = profile.proxy_env() assert env["HEADROOM_SAVINGS_PROFILE"] == "coding" + assert env["HEADROOM_MODE"] == "cache" # delta-only compression at ~0 prefix-cache busts assert env["HEADROOM_PROTECT_RECENT"] == "2" # keep the active code working set verbatim assert env["HEADROOM_MIN_TOKENS"] == "25" # low → compression is actually visible - assert env["HEADROOM_COMPRESS_USER_MESSAGES"] == "0" # no prompt mutation / cache bust - assert env["HEADROOM_COMPRESS_SYSTEM_MESSAGES"] == "0" + # Cache mode compresses the newest observation delta → compress_user must be ON. + assert env["HEADROOM_COMPRESS_USER_MESSAGES"] == "1" + assert env["HEADROOM_COMPRESS_SYSTEM_MESSAGES"] == "0" # system prompt is the hottest cache assert env["HEADROOM_ACCURACY_GUARD"] == "strict" assert "HEADROOM_TARGET_RATIO" not in env # unset → Kompress / ambient default decides + # Coding posture toggles seeded through the profile. + assert env["HEADROOM_TOOL_SEARCH"] == "1" + assert env["HEADROOM_DEDUPE"] == "1" + assert env["HEADROOM_LOSSLESS_THEN_LOSSY"] == "1" + assert env["HEADROOM_PROTECT_READS"] == "1" + assert env["HEADROOM_CODE_AWARE_ENABLED"] == "1" + assert env["HEADROOM_EFFORT_ROUTER"] == "0" + assert env["HEADROOM_LOSSLESS"] == "0" # lossy enabled (CCR keeps it recoverable) + assert env["HEADROOM_MIN_CHARS_FOR_BLOCK"] == "25" def test_general_persona_has_no_positional_code_protection() -> None: @@ -89,13 +100,18 @@ def test_general_persona_has_no_positional_code_protection() -> None: def test_personas_omit_target_ratio_in_pipeline_kwargs() -> None: - for name, expected_protect in (("coding", 2), ("general", 0)): + # coding compresses the delta observation (cache mode) → compress_user True; + # general has no positional code working set and leaves user turns intact. + for name, expected_protect, expected_compress_user in ( + ("coding", 2, True), + ("general", 0, False), + ): kwargs = proxy_pipeline_kwargs(ProxyConfig(savings_profile=name)) assert kwargs["protect_recent"] == expected_protect assert kwargs["read_protection_window"] == expected_protect assert kwargs["min_tokens_to_compress"] == 25 - assert kwargs["compress_user_messages"] is False + assert kwargs["compress_user_messages"] is expected_compress_user assert kwargs["compress_system_messages"] is False assert kwargs["force_kompress"] is False assert "target_ratio" not in kwargs # persona never pins a keep-ratio diff --git a/tests/test_cli/test_wrap_codex.py b/tests/test_cli/test_wrap_codex.py index b380ca75b..7a3122a55 100644 --- a/tests/test_cli/test_wrap_codex.py +++ b/tests/test_cli/test_wrap_codex.py @@ -1005,6 +1005,17 @@ def test_start_proxy_does_not_apply_agent_90_defaults( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, agent_type: str ) -> None: """Wrapped coding agents keep agent-savings opt-in by default.""" + # Clean baseline: the proxy's out-of-box coding profile seeds these into the + # process env at startup (``seed_proxy_env_defaults``), which another test in + # the shard can leave behind in ``os.environ``. This test is about what the + # WRAPPER adds, so start from an unset env rather than inheriting pollution. + for _var in ( + "HEADROOM_SAVINGS_PROFILE", + "HEADROOM_TARGET_RATIO", + "HEADROOM_MAX_ITEMS", + "HEADROOM_SMART_CRUSHER_COMPACTION", + ): + monkeypatch.delenv(_var, raising=False) popen_kwargs: dict[str, object] = {} class FakeProc: diff --git a/tests/test_cli_proxy_env.py b/tests/test_cli_proxy_env.py index ae1758b32..8904c2e54 100644 --- a/tests/test_cli_proxy_env.py +++ b/tests/test_cli_proxy_env.py @@ -338,8 +338,10 @@ class TestCLIProxyEnvVars: assert result.exit_code == 0, result.output assert captured_config["config"].code_aware_enabled is True - def test_code_aware_enabled_defaults_false(self, runner): - """Without HEADROOM_CODE_AWARE_ENABLED, code-aware stays disabled in the wrapper.""" + def test_code_aware_enabled_defaults_true(self, runner): + """Without HEADROOM_CODE_AWARE_ENABLED, code-aware defaults ON (coding + posture; consistent with the argparse server path). It degrades to a no-op + when tree-sitter isn't installed, so defaulting it on is safe.""" captured_config = {} def mock_run_server(config, **kwargs): @@ -358,7 +360,7 @@ class TestCLIProxyEnvVars: ) assert result.exit_code == 0, result.output - assert captured_config["config"].code_aware_enabled is False + assert captured_config["config"].code_aware_enabled is True def test_code_aware_enabled_from_cli_flag(self, runner): """--code-aware should enable code-aware compression in the wrapper."""