mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
feat: ship the coding profile as Headroom's out-of-box default posture (#1893)
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 <!-- Briefly explain the change and why it is needed. --> 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 <!-- Check what you actually ran, then paste the real command output below. --> - [ ] 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 <!-- Mention any N/A checklist items, tradeoffs, follow-ups, or maintainer context. --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c9217856d3
commit
68676daa50
6 changed files with 145 additions and 22 deletions
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from collections.abc import MutableMapping
|
||||||
from dataclasses import dataclass, replace
|
from dataclasses import dataclass, replace
|
||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
|
|
||||||
|
|
@ -11,6 +12,10 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
AGENT_90_PROFILE = "agent-90"
|
AGENT_90_PROFILE = "agent-90"
|
||||||
FALLBACK_PROFILE = "balanced"
|
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):
|
class CompressConfigLike(Protocol):
|
||||||
|
|
@ -42,6 +47,17 @@ class AgentSavingsProfile:
|
||||||
force_kompress: bool
|
force_kompress: bool
|
||||||
proxy_mode: str
|
proxy_mode: str
|
||||||
accuracy_guard: 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
|
@property
|
||||||
def savings_percent(self) -> int:
|
def savings_percent(self) -> int:
|
||||||
|
|
@ -65,14 +81,25 @@ class AgentSavingsProfile:
|
||||||
),
|
),
|
||||||
"HEADROOM_FORCE_KOMPRESS": "1" if self.force_kompress else "0",
|
"HEADROOM_FORCE_KOMPRESS": "1" if self.force_kompress else "0",
|
||||||
"HEADROOM_ACCURACY_GUARD": self.accuracy_guard,
|
"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
|
# Only pin a keep-ratio when the profile sets one; workload personas
|
||||||
# leave it unset so Kompress decides and the ambient default applies.
|
# leave it unset so Kompress decides and the ambient default applies.
|
||||||
if self.target_ratio is not None:
|
if self.target_ratio is not None:
|
||||||
env["HEADROOM_TARGET_RATIO"] = f"{self.target_ratio:.2f}"
|
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
|
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."""
|
"""Seed proxy env defaults without overriding explicit user settings."""
|
||||||
|
|
||||||
for key, value in self.proxy_env().items():
|
for key, value in self.proxy_env().items():
|
||||||
|
|
@ -125,7 +152,11 @@ _PROFILES: dict[str, AgentSavingsProfile] = {
|
||||||
name="coding",
|
name="coding",
|
||||||
target_savings=0.50, # nominal (display only); savings are emergent
|
target_savings=0.50, # nominal (display only); savings are emergent
|
||||||
target_ratio=None,
|
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
|
compress_system_messages=False, # system prompt is the hottest cache
|
||||||
protect_recent=2, # keep the active code working set verbatim
|
protect_recent=2, # keep the active code working set verbatim
|
||||||
protect_analysis_context=True,
|
protect_analysis_context=True,
|
||||||
|
|
@ -133,8 +164,21 @@ _PROFILES: dict[str, AgentSavingsProfile] = {
|
||||||
max_items_after_crush=15,
|
max_items_after_crush=15,
|
||||||
smart_crusher_with_compaction=True,
|
smart_crusher_with_compaction=True,
|
||||||
force_kompress=False, # don't override diff/log lossless with lossy ML
|
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",
|
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(
|
"general": AgentSavingsProfile(
|
||||||
name="general",
|
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.
|
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)
|
profile = _PROFILES.get(key)
|
||||||
if profile is not None:
|
if profile is not None:
|
||||||
return profile
|
return profile
|
||||||
|
|
@ -181,11 +225,18 @@ def get_agent_savings_profile(name: str | None = None) -> AgentSavingsProfile:
|
||||||
|
|
||||||
|
|
||||||
def apply_agent_savings_env_defaults(
|
def apply_agent_savings_env_defaults(
|
||||||
env: dict[str, str],
|
env: MutableMapping[str, str],
|
||||||
profile: AgentSavingsProfile | str | None = None,
|
profile: AgentSavingsProfile | str | None = None,
|
||||||
) -> dict[str, str]:
|
) -> MutableMapping[str, str]:
|
||||||
"""Apply agent savings env defaults to a proxy subprocess environment."""
|
"""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 = (
|
resolved = (
|
||||||
get_agent_savings_profile(profile)
|
get_agent_savings_profile(profile)
|
||||||
if isinstance(profile, str) or profile is None
|
if isinstance(profile, str) or profile is None
|
||||||
|
|
@ -295,6 +346,24 @@ def proxy_pipeline_kwargs(config: object) -> dict[str, object]:
|
||||||
return kwargs
|
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(
|
def with_target_savings(
|
||||||
profile: AgentSavingsProfile,
|
profile: AgentSavingsProfile,
|
||||||
target_savings: float,
|
target_savings: float,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import click
|
||||||
|
|
||||||
from headroom import paths as _paths
|
from headroom import paths as _paths
|
||||||
from headroom.providers.registry import resolve_api_overrides, resolve_api_targets
|
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
|
from .main import main
|
||||||
|
|
||||||
|
|
@ -1033,9 +1033,10 @@ def proxy(
|
||||||
# Resolve anyllm provider: env var takes precedence over CLI default (matches argparse path)
|
# 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
|
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(
|
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
|
# Stateless mode: CLI flag or env var
|
||||||
|
|
@ -1102,7 +1103,7 @@ def proxy(
|
||||||
else frozenset(),
|
else frozenset(),
|
||||||
tool_profiles=_parse_tool_profiles([]) or None,
|
tool_profiles=_parse_tool_profiles([]) or None,
|
||||||
smart_crusher_with_compaction=_get_env_bool_optional("HEADROOM_SMART_CRUSHER_COMPACTION"),
|
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,
|
target_ratio=target_ratio,
|
||||||
compress_system_messages=_get_env_bool_optional("HEADROOM_COMPRESS_SYSTEM_MESSAGES"),
|
compress_system_messages=_get_env_bool_optional("HEADROOM_COMPRESS_SYSTEM_MESSAGES"),
|
||||||
protect_recent=_get_env_int_optional("HEADROOM_PROTECT_RECENT"),
|
protect_recent=_get_env_int_optional("HEADROOM_PROTECT_RECENT"),
|
||||||
|
|
@ -1154,10 +1155,12 @@ def proxy(
|
||||||
# 2. Otherwise read HEADROOM_CODE_AWARE_ENABLED (truthy = on).
|
# 2. Otherwise read HEADROOM_CODE_AWARE_ENABLED (truthy = on).
|
||||||
# 3. Otherwise default off — matches the prior cli/proxy.py behavior so
|
# 3. Otherwise default off — matches the prior cli/proxy.py behavior so
|
||||||
# existing users see no change unless they opt in.
|
# 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=(
|
code_aware_enabled=(
|
||||||
bool(code_aware_flag)
|
bool(code_aware_flag)
|
||||||
if code_aware_flag is not None
|
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")
|
in ("true", "1", "yes", "on")
|
||||||
),
|
),
|
||||||
disable_kompress=disable_kompress,
|
disable_kompress=disable_kompress,
|
||||||
|
|
|
||||||
|
|
@ -4313,7 +4313,13 @@ def _proxy_config_from_env() -> ProxyConfig:
|
||||||
periodic_toin_stats_enabled=_get_env_bool("HEADROOM_PERIODIC_TOIN_STATS", True),
|
periodic_toin_stats_enabled=_get_env_bool("HEADROOM_PERIODIC_TOIN_STATS", True),
|
||||||
proxy_token=os.environ.get("HEADROOM_PROXY_TOKEN") or None,
|
proxy_token=os.environ.get("HEADROOM_PROXY_TOKEN") or None,
|
||||||
offline=_get_env_bool("HEADROOM_OFFLINE", False),
|
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=_get_env_bool("HEADROOM_READ_MATURATION", False),
|
||||||
read_maturation_quiesce_turns=_get_env_int("HEADROOM_READ_MATURATION_QUIESCE_TURNS", 5),
|
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),
|
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:
|
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())
|
return create_app(_proxy_config_from_env())
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4362,6 +4374,16 @@ def run_server(
|
||||||
print("ERROR: FastAPI required. Install: pip install fastapi uvicorn httpx")
|
print("ERROR: FastAPI required. Install: pip install fastapi uvicorn httpx")
|
||||||
sys.exit(1)
|
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()
|
config = config or ProxyConfig()
|
||||||
code_aware_status = _get_code_aware_banner_status(config)
|
code_aware_status = _get_code_aware_banner_status(config)
|
||||||
|
|
||||||
|
|
@ -4969,10 +4991,10 @@ if __name__ == "__main__":
|
||||||
protect_tool_results=frozenset(protect_tool_results)
|
protect_tool_results=frozenset(protect_tool_results)
|
||||||
if protect_tool_results
|
if protect_tool_results
|
||||||
else frozenset(),
|
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
|
compress_user_messages=args.compress_user_messages
|
||||||
or _get_env_bool("HEADROOM_COMPRESS_USER_MESSAGES", False),
|
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
|
# Default 0.4 keep-ratio so the Kompress text (prose/code) path compresses
|
||||||
# meaningfully out of the box; HEADROOM_TARGET_RATIO overrides.
|
# meaningfully out of the box; HEADROOM_TARGET_RATIO overrides.
|
||||||
target_ratio=(
|
target_ratio=(
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,23 @@ def test_coding_persona_protects_working_set_and_stays_visible() -> None:
|
||||||
env = profile.proxy_env()
|
env = profile.proxy_env()
|
||||||
|
|
||||||
assert env["HEADROOM_SAVINGS_PROFILE"] == "coding"
|
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_PROTECT_RECENT"] == "2" # keep the active code working set verbatim
|
||||||
assert env["HEADROOM_MIN_TOKENS"] == "25" # low → compression is actually visible
|
assert env["HEADROOM_MIN_TOKENS"] == "25" # low → compression is actually visible
|
||||||
assert env["HEADROOM_COMPRESS_USER_MESSAGES"] == "0" # no prompt mutation / cache bust
|
# Cache mode compresses the newest observation delta → compress_user must be ON.
|
||||||
assert env["HEADROOM_COMPRESS_SYSTEM_MESSAGES"] == "0"
|
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 env["HEADROOM_ACCURACY_GUARD"] == "strict"
|
||||||
assert "HEADROOM_TARGET_RATIO" not in env # unset → Kompress / ambient default decides
|
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:
|
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:
|
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))
|
kwargs = proxy_pipeline_kwargs(ProxyConfig(savings_profile=name))
|
||||||
|
|
||||||
assert kwargs["protect_recent"] == expected_protect
|
assert kwargs["protect_recent"] == expected_protect
|
||||||
assert kwargs["read_protection_window"] == expected_protect
|
assert kwargs["read_protection_window"] == expected_protect
|
||||||
assert kwargs["min_tokens_to_compress"] == 25
|
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["compress_system_messages"] is False
|
||||||
assert kwargs["force_kompress"] is False
|
assert kwargs["force_kompress"] is False
|
||||||
assert "target_ratio" not in kwargs # persona never pins a keep-ratio
|
assert "target_ratio" not in kwargs # persona never pins a keep-ratio
|
||||||
|
|
|
||||||
|
|
@ -1005,6 +1005,17 @@ def test_start_proxy_does_not_apply_agent_90_defaults(
|
||||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, agent_type: str
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, agent_type: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Wrapped coding agents keep agent-savings opt-in by default."""
|
"""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] = {}
|
popen_kwargs: dict[str, object] = {}
|
||||||
|
|
||||||
class FakeProc:
|
class FakeProc:
|
||||||
|
|
|
||||||
|
|
@ -338,8 +338,10 @@ class TestCLIProxyEnvVars:
|
||||||
assert result.exit_code == 0, result.output
|
assert result.exit_code == 0, result.output
|
||||||
assert captured_config["config"].code_aware_enabled is True
|
assert captured_config["config"].code_aware_enabled is True
|
||||||
|
|
||||||
def test_code_aware_enabled_defaults_false(self, runner):
|
def test_code_aware_enabled_defaults_true(self, runner):
|
||||||
"""Without HEADROOM_CODE_AWARE_ENABLED, code-aware stays disabled in the wrapper."""
|
"""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 = {}
|
captured_config = {}
|
||||||
|
|
||||||
def mock_run_server(config, **kwargs):
|
def mock_run_server(config, **kwargs):
|
||||||
|
|
@ -358,7 +360,7 @@ class TestCLIProxyEnvVars:
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result.exit_code == 0, result.output
|
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):
|
def test_code_aware_enabled_from_cli_flag(self, runner):
|
||||||
"""--code-aware should enable code-aware compression in the wrapper."""
|
"""--code-aware should enable code-aware compression in the wrapper."""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue