Commit graph

1 commit

Author SHA1 Message Date
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