mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description
Adds per-provider control of Kompress (the lossy ML text compressor) via
two new `ProxyConfig` fields, `disable_kompress_anthropic` and
`disable_kompress_openai`. The global `--disable-kompress` /
`HEADROOM_DISABLE_KOMPRESS` remains the baseline for **all** providers;
the per-provider flags optionally override it for one provider (`None`
inherits the global; `True`/`False` force-disable/enable).
**Motivation.** In token mode, older excluded-tool results
(`Read`/`Bash`/`Grep`/...) fall outside the recent-read protection
window and become Kompress-eligible. On Anthropic that content is
typically already in the cached prefix (0.1x cache-read discount), so
recompressing it saves little, risks busting the prefix cache (1.25x
writes), and lossily corrupts exact command/file output. This makes it
possible to disable Kompress for the Anthropic pipeline while keeping it
for OpenAI/Codex — **without changing any routing, tool-exclusion, or
read-protection logic**. Structural compressors (SmartCrusher,
log/search/diff, schema compaction) keep running for the disabled
provider.
Closes # N/A
## Type of Change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [x] 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
- `ProxyConfig`: add `disable_kompress_anthropic: bool | None` and
`disable_kompress_openai: bool | None` (default `None` = inherit global
`disable_kompress`).
- `HeadroomProxy.__init__`: resolve Kompress on/off per provider and
build each pipeline's `ContentRouter` accordingly. When both providers
resolve identically, **one `ContentRouter` instance is reused** so the
Kompress model still loads once (startup warmup dedupes by `id()`); a
second instance is created only when they differ.
- Wiring: Click CLI
(`--disable-kompress-anthropic/--enable-kompress-anthropic`, and
`-openai`), argparse entrypoint, env builder
(`HEADROOM_DISABLE_KOMPRESS_ANTHROPIC` / `_OPENAI`, tristate via new
`_get_env_optional_bool`), and the `/config` debug payload.
- Tests: `tests/test_proxy_per_provider_kompress.py`.
## Testing
- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed
### Test Output
```text
$ uv run --extra dev pytest tests/test_proxy_warmup.py tests/test_proxy_disable_kompress.py \
tests/test_cli_proxy_env.py tests/test_proxy_per_provider_kompress.py -q
collected 60 items
tests/test_proxy_warmup.py ......... [ 15%]
tests/test_proxy_disable_kompress.py .. [ 18%]
tests/test_cli_proxy_env.py ............................................ [ 91%]
tests/test_proxy_per_provider_kompress.py ..... [100%]
============================== 60 passed in 6.05s ==============================
$ uvx ruff check headroom/proxy/server.py headroom/proxy/models.py headroom/cli/proxy.py
All checks passed!
$ uvx ruff format --check headroom/proxy/server.py headroom/proxy/models.py headroom/cli/proxy.py \
tests/test_proxy_per_provider_kompress.py
4 files already formatted
$ uv run --extra dev mypy headroom/proxy/models.py headroom/proxy/server.py headroom/cli/proxy.py
Success: no issues found in 3 source files
```
## Real Behavior Proof
- Environment: fresh clone of `chopratejas/headroom` @ `main`
(8894ee0c); `uv run --extra dev`; Python 3.10.18 on macOS.
- Exact command / steps: set `HEADROOM_DISABLE_KOMPRESS_ANTHROPIC=1`,
build the proxy from env, and inspect each pipeline's `ContentRouter`:
```
HEADROOM_DISABLE_KOMPRESS_ANTHROPIC=1 uv run --extra dev python - <<'PY'
import dataclasses
from headroom.proxy.server import _proxy_config_from_env, HeadroomProxy
cfg = _proxy_config_from_env()
print("config.disable_kompress_anthropic =",
cfg.disable_kompress_anthropic)
print("config.disable_kompress_openai =", cfg.disable_kompress_openai)
cfg = dataclasses.replace(cfg, optimize=False, cache_enabled=False,
rate_limit_enabled=False, cost_tracking_enabled=False,
code_aware_enabled=False)
p = HeadroomProxy(cfg)
a = p.anthropic_pipeline.transforms[-1]; o =
p.openai_pipeline.transforms[-1]
print("anthropic enable_kompress =", a.config.enable_kompress)
print("openai enable_kompress =", o.config.enable_kompress)
print("separate router instances =", a is not o)
PY
```
- Observed result: `config.disable_kompress_anthropic = True`,
`config.disable_kompress_openai = None`; `anthropic enable_kompress =
False`, `openai enable_kompress = True`, `separate router instances =
True`. `headroom proxy --help` lists `--disable-kompress-anthropic /
--enable-kompress-anthropic` and `--disable-kompress-openai /
--enable-kompress-openai`.
- Not tested: end-to-end token-savings impact on live Anthropic traffic
(reasoned from the 0.9 cache-read discount, not measured here); a
follow-up could instrument per-provider/per-transform savings.
## Review Readiness
- [x] I have performed a self-review
- [x] This PR is ready for human review
## Checklist
- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable
## Screenshots (if applicable)
N/A
## Additional Notes
- Opened as a draft pending maintainer review.
- Backward compatible: with no per-provider override set, behavior is
identical to today (both pipelines follow the global `disable_kompress`,
sharing a single `ContentRouter`).
- Tradeoff: disabling Kompress for a provider forgoes only *lossy text*
savings; structural compressors still run. On Anthropic the forgone
slice is low-value due to the 0.9 cache-read discount and avoids
cache-bust risk on already-cached tool output.
- CHANGELOG.md / docs not updated here — happy to add if desired.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
139 lines
4.7 KiB
Python
139 lines
4.7 KiB
Python
"""Per-provider Kompress enable/disable (disable_kompress_{anthropic,openai}).
|
|
|
|
The global ``disable_kompress`` is the baseline for both providers; a per-provider
|
|
override wins when set. Only ``enable_kompress`` differs between the two pipelines,
|
|
so when both resolve identically they reuse ONE ContentRouter instance (keeping the
|
|
single Kompress model load).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
from click.testing import CliRunner
|
|
|
|
from headroom.cli.main import main
|
|
from headroom.proxy.server import (
|
|
HeadroomProxy,
|
|
ProxyConfig,
|
|
_get_env_optional_bool,
|
|
_proxy_config_from_env,
|
|
)
|
|
|
|
|
|
def _build(**overrides: object) -> HeadroomProxy:
|
|
config = ProxyConfig(
|
|
optimize=False,
|
|
cache_enabled=False,
|
|
rate_limit_enabled=False,
|
|
cost_tracking_enabled=False,
|
|
code_aware_enabled=False,
|
|
**overrides,
|
|
)
|
|
return HeadroomProxy(config)
|
|
|
|
|
|
def _routers(proxy: HeadroomProxy):
|
|
# ContentRouter is the last transform in each pipeline.
|
|
return (
|
|
proxy.anthropic_pipeline.transforms[-1],
|
|
proxy.openai_pipeline.transforms[-1],
|
|
)
|
|
|
|
|
|
def test_default_enables_kompress_and_shares_one_router() -> None:
|
|
anthropic, openai = _routers(_build())
|
|
assert anthropic.config.enable_kompress is True
|
|
assert openai.config.enable_kompress is True
|
|
# Identical resolution -> one shared instance (Kompress model loads once).
|
|
assert anthropic is openai
|
|
|
|
|
|
def test_global_disable_respected_by_both() -> None:
|
|
anthropic, openai = _routers(_build(disable_kompress=True))
|
|
assert anthropic.config.enable_kompress is False
|
|
assert openai.config.enable_kompress is False
|
|
assert anthropic is openai
|
|
|
|
|
|
def test_disable_for_anthropic_only() -> None:
|
|
anthropic, openai = _routers(_build(disable_kompress_anthropic=True))
|
|
assert anthropic.config.enable_kompress is False
|
|
assert openai.config.enable_kompress is True
|
|
assert anthropic is not openai
|
|
|
|
|
|
def test_disable_for_openai_only() -> None:
|
|
anthropic, openai = _routers(_build(disable_kompress_openai=True))
|
|
assert anthropic.config.enable_kompress is True
|
|
assert openai.config.enable_kompress is False
|
|
assert anthropic is not openai
|
|
|
|
|
|
def test_per_provider_override_beats_global() -> None:
|
|
# Global disables Kompress; Anthropic override force-enables it, OpenAI inherits.
|
|
anthropic, openai = _routers(_build(disable_kompress=True, disable_kompress_anthropic=False))
|
|
assert anthropic.config.enable_kompress is True
|
|
assert openai.config.enable_kompress is False
|
|
assert anthropic is not openai
|
|
|
|
|
|
def test_get_env_optional_bool_tristate() -> None:
|
|
os.environ.pop("HRD_KOMPRESS_TEST", None)
|
|
assert _get_env_optional_bool("HRD_KOMPRESS_TEST") is None # unset
|
|
with patch.dict(os.environ, {"HRD_KOMPRESS_TEST": ""}):
|
|
assert _get_env_optional_bool("HRD_KOMPRESS_TEST") is None # empty
|
|
for truthy in ("1", "true", "yes", "on"):
|
|
with patch.dict(os.environ, {"HRD_KOMPRESS_TEST": truthy}):
|
|
assert _get_env_optional_bool("HRD_KOMPRESS_TEST") is True
|
|
for falsy in ("0", "false", "no", "off"):
|
|
with patch.dict(os.environ, {"HRD_KOMPRESS_TEST": falsy}):
|
|
assert _get_env_optional_bool("HRD_KOMPRESS_TEST") is False
|
|
|
|
|
|
def test_proxy_config_from_env_reads_per_provider_kompress() -> None:
|
|
with patch.dict(
|
|
os.environ,
|
|
{
|
|
"HEADROOM_DISABLE_KOMPRESS_ANTHROPIC": "1",
|
|
"HEADROOM_DISABLE_KOMPRESS_OPENAI": "0",
|
|
},
|
|
):
|
|
config = _proxy_config_from_env()
|
|
assert config.disable_kompress_anthropic is True
|
|
assert config.disable_kompress_openai is False
|
|
|
|
|
|
def test_cli_disable_kompress_anthropic_only() -> None:
|
|
captured: dict = {}
|
|
|
|
def mock_run_server(config, **kwargs):
|
|
captured["config"] = config
|
|
|
|
with patch("headroom.proxy.server.run_server", mock_run_server):
|
|
result = CliRunner().invoke(
|
|
main,
|
|
["proxy", "--disable-kompress-anthropic"],
|
|
catch_exceptions=False,
|
|
)
|
|
assert result.exit_code == 0, result.output
|
|
assert captured["config"].disable_kompress_anthropic is True
|
|
assert captured["config"].disable_kompress_openai is None
|
|
|
|
|
|
def test_cli_enable_kompress_openai_from_env() -> None:
|
|
captured: dict = {}
|
|
|
|
def mock_run_server(config, **kwargs):
|
|
captured["config"] = config
|
|
|
|
with patch("headroom.proxy.server.run_server", mock_run_server):
|
|
result = CliRunner().invoke(
|
|
main,
|
|
["proxy"],
|
|
env={"HEADROOM_DISABLE_KOMPRESS_OPENAI": "0"},
|
|
catch_exceptions=False,
|
|
)
|
|
assert result.exit_code == 0, result.output
|
|
assert captured["config"].disable_kompress_openai is False
|