mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
feat(proxy): add CLI opt-outs for CCR injection (compression-only mode) (#823)
## What & why Streaming / non-MCP clients can't resolve the injected `headroom_retrieve` (CCR) tool, so CCR injection turns into unresolvable tool calls that error and inflate turn count. Today there's no proxy CLI flag to run **compression-only** — `ccr_inject_tool`, `ccr_inject_marker`, and `ccr_proactive_expansion` are hardcoded `True` defaults — so a faithful compression-only eval requires patching the image. This adds three opt-in `--no-*` flags (with env vars), **all defaulting to current behavior (CCR fully on)**: | flag | env var | effect | |---|---|---| | `--no-ccr-inject-tool` | `HEADROOM_NO_CCR_INJECT_TOOL` | don't inject the retrieve tool | | `--no-ccr-marker` | `HEADROOM_NO_CCR_MARKER` | don't add retrieval markers to compressed content | | `--no-ccr-proactive-expansion` | `HEADROOM_NO_CCR_PROACTIVE_EXPANSION` | disable proactive expansion | `ccr_inject_tool` and `ccr_proactive_expansion` already existed on `ProxyConfig`. `ccr_inject_marker` is added to `ProxyConfig` and threaded into `ContentRouterConfig` in `server.py` (previously it was only ever the router's own default). Per CONTRIBUTING I raised this in #645 first; you accepted the patch offer there. ## Changes to existing behavior None unless a flag is passed. With no flags, all three toggles stay `True` (test `test_ccr_defaults_on`). ## Test plan - `tests/test_cli_proxy_env.py::TestCLICompressionOnlyFlags` — defaults-on, `--no-ccr-inject-tool` in isolation, all three combined, and the `HEADROOM_NO_CCR_MARKER` env path. - `pytest tests/test_cli_proxy_env.py` → 26 passed; `tests/test_proxy_modes.py tests/test_proxy_pipeline_lifecycle.py tests/test_cli_proxy_env.py` → 34 passed. - `ruff check` + `ruff format --check` clean on all changed files. ## Real behavior proof - **Setup:** Linux, Python 3.13.5, `python -m venv .venv && .venv/bin/pip install -e ".[dev]"` at this branch; OpenAI-compatible upstream. - **Ran:** - `headroom proxy --help` → all three flags appear with help text. - Instantiated the live proxy: ```python from headroom.proxy.server import ProxyConfig, HeadroomProxy from headroom.transforms.content_router import ContentRouter cfg = ProxyConfig(host="127.0.0.1", port=1, ccr_inject_tool=False, ccr_inject_marker=False, ccr_proactive_expansion=False) p = HeadroomProxy(cfg) router = [t for t in p.anthropic_pipeline.transforms if isinstance(t, ContentRouter)][0] print(router.config.ccr_inject_marker) # -> False ``` - **Observed:** `router.config.ccr_inject_marker == False`; `cfg.ccr_inject_tool == False`; `cfg.ccr_proactive_expansion == False`. With no flags, all three are `True`. - **Not tested here:** a full live agentic run on this branch. The motivating field evidence (a compression-only run with zero `headroom_retrieve` calls, compression intact) was collected on the v0.23.0 image with these same three defaults flipped — this PR replaces that image patch with first-class flags. Refs #645.
This commit is contained in:
parent
929698af10
commit
693d9d20e2
4 changed files with 123 additions and 0 deletions
|
|
@ -159,6 +159,31 @@ def _selected_context_tool() -> str:
|
|||
@click.option("--no-optimize", is_flag=True, help="Disable optimization (passthrough mode)")
|
||||
@click.option("--no-cache", is_flag=True, help="Disable semantic caching")
|
||||
@click.option("--no-rate-limit", is_flag=True, help="Disable rate limiting")
|
||||
@click.option(
|
||||
"--no-ccr-inject-tool",
|
||||
is_flag=True,
|
||||
envvar="HEADROOM_NO_CCR_INJECT_TOOL",
|
||||
help=(
|
||||
"Don't inject the CCR headroom_retrieve tool. Run compression-only — "
|
||||
"for streaming / non-MCP clients that can't resolve the retrieve tool "
|
||||
"and would otherwise error on it. Env: HEADROOM_NO_CCR_INJECT_TOOL."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--no-ccr-marker",
|
||||
is_flag=True,
|
||||
envvar="HEADROOM_NO_CCR_MARKER",
|
||||
help=("Don't add CCR retrieval markers to compressed content. Env: HEADROOM_NO_CCR_MARKER."),
|
||||
)
|
||||
@click.option(
|
||||
"--no-ccr-proactive-expansion",
|
||||
is_flag=True,
|
||||
envvar="HEADROOM_NO_CCR_PROACTIVE_EXPANSION",
|
||||
help=(
|
||||
"Disable proactive expansion of previously compressed content. "
|
||||
"Env: HEADROOM_NO_CCR_PROACTIVE_EXPANSION."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--proxy-extension",
|
||||
"proxy_extension",
|
||||
|
|
@ -555,6 +580,9 @@ def proxy(
|
|||
no_optimize: bool,
|
||||
no_cache: bool,
|
||||
no_rate_limit: bool,
|
||||
no_ccr_inject_tool: bool,
|
||||
no_ccr_marker: bool,
|
||||
no_ccr_proactive_expansion: bool,
|
||||
proxy_extension: tuple[str, ...],
|
||||
no_subscription_tracking: bool,
|
||||
subscription_poll_interval: int | None,
|
||||
|
|
@ -729,6 +757,12 @@ def proxy(
|
|||
optimize=not no_optimize,
|
||||
cache_enabled=not no_cache,
|
||||
rate_limit_enabled=not no_rate_limit,
|
||||
# CCR opt-outs for compression-only deployments (streaming / non-MCP
|
||||
# clients that can't resolve the injected retrieve tool). Defaults keep
|
||||
# CCR fully on; each flag flips one dataclass default to False.
|
||||
ccr_inject_tool=not no_ccr_inject_tool,
|
||||
ccr_inject_marker=not no_ccr_marker,
|
||||
ccr_proactive_expansion=not no_ccr_proactive_expansion,
|
||||
# Flatten repeat-flag tuple AND any comma-separated values inside it.
|
||||
# `--proxy-extension a,b --proxy-extension c` and `HEADROOM_PROXY_EXTENSIONS=a,b,c`
|
||||
# both yield ["a", "b", "c"]. None when nothing was supplied.
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@ class ProxyConfig:
|
|||
# CCR Tool Injection
|
||||
ccr_inject_tool: bool = True
|
||||
ccr_inject_system_instructions: bool = False
|
||||
# Proxy-level mirror of ContentRouterConfig.ccr_inject_marker, so retrieval
|
||||
# markers can be toggled from the CLI (--no-ccr-marker). Threaded into the
|
||||
# router in server.py; default preserves current behavior.
|
||||
ccr_inject_marker: bool = True
|
||||
|
||||
# CCR Response Handling
|
||||
ccr_handle_responses: bool = True
|
||||
|
|
|
|||
|
|
@ -364,6 +364,7 @@ class HeadroomProxy(
|
|||
enable_code_aware=config.code_aware_enabled,
|
||||
tool_profiles=config.tool_profiles,
|
||||
read_lifecycle=ReadLifecycleConfig(enabled=config.read_lifecycle),
|
||||
ccr_inject_marker=config.ccr_inject_marker,
|
||||
)
|
||||
# A non-None exclude_tools replaces DEFAULT_EXCLUDE_TOOLS in
|
||||
# ContentRouter, so merge rather than assign.
|
||||
|
|
|
|||
|
|
@ -596,6 +596,90 @@ class TestCLIAnyllmProviderEnv:
|
|||
assert captured_config["config"].anyllm_provider == "groq"
|
||||
|
||||
|
||||
class TestCLICompressionOnlyFlags:
|
||||
"""The CCR opt-out flags must flip the corresponding ProxyConfig fields.
|
||||
|
||||
These enable a compression-only deployment for streaming / non-MCP clients
|
||||
that can't resolve the injected headroom_retrieve tool (issue #645).
|
||||
"""
|
||||
|
||||
def test_ccr_defaults_on(self, runner):
|
||||
"""Without flags, all three CCR toggles stay enabled (no behavior change)."""
|
||||
captured_config = {}
|
||||
|
||||
def mock_run_server(config, **kwargs):
|
||||
captured_config["config"] = config
|
||||
|
||||
with patch("headroom.proxy.server.run_server", mock_run_server):
|
||||
result = runner.invoke(main, ["proxy"], catch_exceptions=False)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
cfg = captured_config["config"]
|
||||
assert cfg.ccr_inject_tool is True
|
||||
assert cfg.ccr_inject_marker is True
|
||||
assert cfg.ccr_proactive_expansion is True
|
||||
|
||||
def test_no_ccr_inject_tool_flag(self, runner):
|
||||
"""--no-ccr-inject-tool disables retrieve-tool injection only."""
|
||||
captured_config = {}
|
||||
|
||||
def mock_run_server(config, **kwargs):
|
||||
captured_config["config"] = config
|
||||
|
||||
with patch("headroom.proxy.server.run_server", mock_run_server):
|
||||
result = runner.invoke(main, ["proxy", "--no-ccr-inject-tool"], catch_exceptions=False)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
cfg = captured_config["config"]
|
||||
assert cfg.ccr_inject_tool is False
|
||||
# Untouched flags remain on.
|
||||
assert cfg.ccr_inject_marker is True
|
||||
assert cfg.ccr_proactive_expansion is True
|
||||
|
||||
def test_compression_only_all_flags(self, runner):
|
||||
"""All three flags together yield a compression-only config."""
|
||||
captured_config = {}
|
||||
|
||||
def mock_run_server(config, **kwargs):
|
||||
captured_config["config"] = config
|
||||
|
||||
with patch("headroom.proxy.server.run_server", mock_run_server):
|
||||
result = runner.invoke(
|
||||
main,
|
||||
[
|
||||
"proxy",
|
||||
"--no-ccr-inject-tool",
|
||||
"--no-ccr-marker",
|
||||
"--no-ccr-proactive-expansion",
|
||||
],
|
||||
catch_exceptions=False,
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
cfg = captured_config["config"]
|
||||
assert cfg.ccr_inject_tool is False
|
||||
assert cfg.ccr_inject_marker is False
|
||||
assert cfg.ccr_proactive_expansion is False
|
||||
|
||||
def test_no_ccr_marker_from_env(self, runner):
|
||||
"""HEADROOM_NO_CCR_MARKER env var disables marker injection."""
|
||||
captured_config = {}
|
||||
|
||||
def mock_run_server(config, **kwargs):
|
||||
captured_config["config"] = config
|
||||
|
||||
with patch("headroom.proxy.server.run_server", mock_run_server):
|
||||
result = runner.invoke(
|
||||
main,
|
||||
["proxy"],
|
||||
env={"HEADROOM_NO_CCR_MARKER": "1"},
|
||||
catch_exceptions=False,
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert captured_config["config"].ccr_inject_marker is False
|
||||
|
||||
|
||||
class TestArgparseBackendValidation:
|
||||
"""Test that the argparse path (python -m headroom.proxy.server) accepts litellm-* backends."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue