headroom/tests/test_proxy_per_provider_kompress.py
gglucass dee3500db6
feat(proxy): per-provider Kompress enable/disable (#1119)
## 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>
2026-06-22 15:07:10 -05:00

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