From 814ffa36a4d1bb40165a630f96a855452037735e Mon Sep 17 00:00:00 2001 From: gglucass Date: Thu, 2 Jul 2026 06:19:48 +0200 Subject: [PATCH] fix(proxy): wire --compression-max-workers / HEADROOM_COMPRESSION_MAX_WORKERS (#1632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description `ProxyConfig.compression_max_workers` is documented as settable via `--compression-max-workers` / `HEADROOM_COMPRESSION_MAX_WORKERS` and is consumed by `HeadroomProxy.__init__` to bound the dedicated compression threadpool. But the proxy CLI never defined the option and never passed the value into `ProxyConfig`, so the field was permanently `None` and always resolved to the `min(32, (cpu_count or 1) * 4)` default. Neither the flag nor the env var had any effect. This matters under concurrent sessions: the compression pool runs CPU-bound Kompress work that releases the GIL, so `cpu*4` oversubscribes cores and there was no way to cap it despite the docs promising one. Closes # ## Type of Change - [x] 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 - Added the `--compression-max-workers` click option (with `envvar="HEADROOM_COMPRESSION_MAX_WORKERS"`) to the `proxy` command, mirroring the existing `--anthropic-pre-upstream-concurrency` wiring. - Added the `compression_max_workers` parameter to the `proxy()` signature and passed it into the `ProxyConfig(...)` construction. - No change to `HeadroomProxy` — it already reads `config.compression_max_workers` and clamps `< 1` to 1. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] Type checking passes (`mypy headroom`) - [x] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text $ pytest tests/test_cli_proxy_improvements.py::TestCompressionMaxWorkers -q 3 passed in 1.50s $ pytest tests/test_cli_proxy_improvements.py -q 48 passed in 5.04s $ ruff check headroom/cli/proxy.py tests/test_cli_proxy_improvements.py All checks passed! $ mypy headroom/cli/proxy.py Success: no issues found in 1 source file ``` ## Real Behavior Proof - Environment: macOS, Python 3.10.18, branch off upstream/main @ 0.28.0 - Exact command / steps: new tests assert the value reaches `ProxyConfig` via both `--compression-max-workers 3` (flag) and `HEADROOM_COMPRESSION_MAX_WORKERS=5` (env), and that it stays `None` when unset. - Observed result: flag -> `config.compression_max_workers == 3`; env -> `== 5`; unset -> `is None`. - Not tested: end-to-end proxy run under real concurrent load (the pool-sizing effect itself is already covered by existing `test_proxy_compression_executor.py`). ## 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 - [x] 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 ## Additional Notes CHANGELOG left untouched: this makes existing documented behavior actually work rather than adding new surface. N/A: manual testing (covered by unit tests + existing executor tests). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 Co-authored-by: JerrettDavis --- headroom/cli/proxy.py | 14 +++++++++++++ tests/test_cli_proxy_improvements.py | 31 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/headroom/cli/proxy.py b/headroom/cli/proxy.py index 88e4c0c26..229693374 100644 --- a/headroom/cli/proxy.py +++ b/headroom/cli/proxy.py @@ -417,6 +417,18 @@ def dashboard(port: int, no_open: bool) -> None: "Env: HEADROOM_ANTHROPIC_PRE_UPSTREAM_MEMORY_CONTEXT_TIMEOUT_SECONDS." ), ) +@click.option( + "--compression-max-workers", + type=int, + default=None, + envvar="HEADROOM_COMPRESSION_MAX_WORKERS", + help=( + "Bound the dedicated compression threadpool (CPU-bound Kompress work). " + "Default (unset): min(32, (cpu_count or 1) * 4). Lower it to reduce CPU " + "oversubscription under concurrent sessions; a value < 1 is clamped to 1. " + "Env: HEADROOM_COMPRESSION_MAX_WORKERS." + ), +) @click.option( "--log-file", default=None, @@ -843,6 +855,7 @@ def proxy( anthropic_pre_upstream_concurrency: int | None, anthropic_pre_upstream_acquire_timeout_seconds: float | None, anthropic_pre_upstream_memory_context_timeout_seconds: float | None, + compression_max_workers: int | None, log_file: str | None, log_messages: bool, codex_wire_debug: bool, @@ -1167,6 +1180,7 @@ def proxy( # Precedence: CLI > env > auto-compute (click's ``envvar`` # handles the env-var fallback). anthropic_pre_upstream_concurrency=anthropic_pre_upstream_concurrency, + compression_max_workers=compression_max_workers, anthropic_pre_upstream_acquire_timeout_seconds=( anthropic_pre_upstream_acquire_timeout_seconds if anthropic_pre_upstream_acquire_timeout_seconds is not None diff --git a/tests/test_cli_proxy_improvements.py b/tests/test_cli_proxy_improvements.py index ae1e1b6e1..3230fc2ec 100644 --- a/tests/test_cli_proxy_improvements.py +++ b/tests/test_cli_proxy_improvements.py @@ -448,3 +448,34 @@ class TestHelpTextCompleteness: result = runner.invoke(main, ["proxy", "--mode", "bogus_mode_xyz"]) assert result.exit_code != 0 assert "invalid" in result.output.lower() or "choice" in result.output.lower() + + +class TestCompressionMaxWorkers: + """--compression-max-workers / HEADROOM_COMPRESSION_MAX_WORKERS must reach ProxyConfig. + + Regression: the field was documented in ProxyConfig and consumed by the + server, but the CLI never defined the option or passed it through, so it + was permanently None (always resolving to the min(32, cpu*4) default). + """ + + def test_flag_reaches_config(self, runner: CliRunner, mock_run_server: dict) -> None: + result = runner.invoke( + main, ["proxy", "--compression-max-workers", "3"], catch_exceptions=False + ) + assert result.exit_code == 0, result.output + assert mock_run_server["config"].compression_max_workers == 3 + + def test_env_reaches_config(self, runner: CliRunner, mock_run_server: dict) -> None: + result = runner.invoke( + main, + ["proxy"], + env={"HEADROOM_COMPRESSION_MAX_WORKERS": "5"}, + catch_exceptions=False, + ) + assert result.exit_code == 0, result.output + assert mock_run_server["config"].compression_max_workers == 5 + + def test_default_is_none(self, runner: CliRunner, mock_run_server: dict) -> None: + result = runner.invoke(main, ["proxy"], catch_exceptions=False) + assert result.exit_code == 0, result.output + assert mock_run_server["config"].compression_max_workers is None