mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-10 14:27:00 -04:00
feat: add --disable-kompress-fallback to restore legacy PASSTHROUGH fallback (#1185)
## Description Follow-up to #1046. That PR stopped `--disable-kompress` from forcing `fallback_strategy = CompressionStrategy.PASSTHROUGH`, so ContentRouter's rule-based passes keep running when the ML model is off. As noted in review, that is a behaviour change for callers who relied on the old passthrough-everything fallback. This adds an opt-in `--disable-kompress-fallback` flag (env `HEADROOM_DISABLE_KOMPRESS_FALLBACK`) that, together with `--disable-kompress`, restores the previous behaviour by routing fall-through content to `PASSTHROUGH`. It defaults to off, so the corrected behaviour from #1046 is unchanged unless a caller explicitly opts back in. The flag is a no-op unless `--disable-kompress` is also set. ## 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 - `headroom/proxy/models.py`: added `disable_kompress_fallback: bool = False` to `ProxyConfig`. - `headroom/proxy/server.py`: when `disable_kompress` and `disable_kompress_fallback` are both set, restore `router_config.fallback_strategy = CompressionStrategy.PASSTHROUGH` (re-adding the `CompressionStrategy` import); wired the new field through the env factory, the `__main__` argparse path (`--disable-kompress-fallback`), and the `/health` config payload. - `headroom/cli/proxy.py`: added the `--disable-kompress-fallback` Click option (with `HEADROOM_DISABLE_KOMPRESS_FALLBACK` envvar) and passed it into `ProxyConfig`. - `tests/test_proxy_disable_kompress.py`: added tests for the flag restoring `PASSTHROUGH`, for it being a no-op without `--disable-kompress`, and for the `/health` config payload exposing the field. - `tests/test_cli_proxy_env.py`: added a test that the env factory honours `HEADROOM_DISABLE_KOMPRESS_FALLBACK`. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [x] New tests added for new functionality - [x] Manual testing performed ### Test Output ```text $ pytest tests/test_proxy_disable_kompress.py -v collected 5 items tests/test_proxy_disable_kompress.py::test_disable_kompress_config_keeps_optimization_but_disables_ml_fallback PASSED [ 20%] tests/test_proxy_disable_kompress.py::test_disable_kompress_defaults_to_existing_kompress_behavior PASSED [ 40%] tests/test_proxy_disable_kompress.py::test_health_config_reports_disable_kompress_fallback PASSED [ 60%] tests/test_proxy_disable_kompress.py::test_disable_kompress_fallback_restores_passthrough PASSED [ 80%] tests/test_proxy_disable_kompress.py::test_disable_kompress_fallback_without_disable_kompress_is_noop PASSED [100%] 5 passed $ ruff check headroom/proxy/server.py headroom/proxy/models.py headroom/cli/proxy.py tests/ All checks passed! ``` ## Real Behavior Proof - Environment: local clone, Python 3.13.7 venv, headroom core deps + fastapi/uvicorn/httpx[http2]. - Exact command / steps: booted the app in-process with FastAPI `TestClient` across four flag combinations and inspected both the live `ContentRouter` config and the `/health` config payload. - Observed result: both flags -> enable_kompress=False and fallback_strategy=PASSTHROUGH (/health reports disable_kompress_fallback=true); --disable-kompress alone -> fallback_strategy stays KOMPRESS (the #1046 default, /health reports false); --disable-kompress-fallback alone -> no-op (enable_kompress=True, KOMPRESS); neither flag -> defaults (enable_kompress=True, KOMPRESS). - Not tested: full live-proxy `/stats` run against a real LLM backend. ## 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 - [ ] 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 ## Additional Notes The flag is intentionally a no-op unless `--disable-kompress` is also set, mirroring where the original override lived. Happy to add a short note to the docs/README flag list if you'd like it documented there. Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
This commit is contained in:
parent
359004646b
commit
f309244a77
5 changed files with 103 additions and 0 deletions
|
|
@ -426,6 +426,16 @@ def dashboard(port: int, no_open: bool) -> None:
|
|||
"Env: HEADROOM_DISABLE_KOMPRESS=1."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--disable-kompress-fallback",
|
||||
is_flag=True,
|
||||
envvar="HEADROOM_DISABLE_KOMPRESS_FALLBACK",
|
||||
help=(
|
||||
"With --disable-kompress, route fall-through content to PASSTHROUGH instead of "
|
||||
"the default KOMPRESS fallback (restores legacy --disable-kompress behaviour). "
|
||||
"Env: HEADROOM_DISABLE_KOMPRESS_FALLBACK=1."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--disable-kompress-anthropic/--enable-kompress-anthropic",
|
||||
"disable_kompress_anthropic",
|
||||
|
|
@ -731,6 +741,7 @@ def proxy(
|
|||
budget_period: str,
|
||||
code_aware_flag: bool | None,
|
||||
disable_kompress: bool,
|
||||
disable_kompress_fallback: bool,
|
||||
disable_kompress_anthropic: bool | None,
|
||||
disable_kompress_openai: bool | None,
|
||||
code_graph: bool,
|
||||
|
|
@ -959,6 +970,7 @@ def proxy(
|
|||
in ("true", "1", "yes", "on")
|
||||
),
|
||||
disable_kompress=disable_kompress,
|
||||
disable_kompress_fallback=disable_kompress_fallback,
|
||||
disable_kompress_anthropic=disable_kompress_anthropic,
|
||||
disable_kompress_openai=disable_kompress_openai,
|
||||
# Code graph: live file watcher for incremental reindexing
|
||||
|
|
|
|||
|
|
@ -154,6 +154,13 @@ class ProxyConfig:
|
|||
# CLI: --disable-kompress; env: HEADROOM_DISABLE_KOMPRESS=1.
|
||||
disable_kompress: bool = False
|
||||
|
||||
# With disable_kompress, route fall-through content to PASSTHROUGH instead
|
||||
# of the default KOMPRESS fallback strategy. Restores the legacy
|
||||
# --disable-kompress behaviour for callers that relied on it. No effect
|
||||
# unless disable_kompress is also set.
|
||||
# CLI: --disable-kompress-fallback; env: HEADROOM_DISABLE_KOMPRESS_FALLBACK=1.
|
||||
disable_kompress_fallback: bool = False
|
||||
|
||||
# Per-provider overrides for `disable_kompress`. None inherits the global
|
||||
# value above; True/False force-disable/enable Kompress for that provider's
|
||||
# pipeline only (other compressors and all routing/exclusion are unaffected).
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ from headroom.transforms import (
|
|||
CacheAligner,
|
||||
CodeAwareCompressor,
|
||||
CodeCompressorConfig,
|
||||
CompressionStrategy,
|
||||
ContentRouter,
|
||||
ContentRouterConfig,
|
||||
TransformPipeline,
|
||||
|
|
@ -626,6 +627,10 @@ class HeadroomProxy(
|
|||
)
|
||||
if config.disable_kompress:
|
||||
router_config.enable_kompress = False
|
||||
# Opt-in restore of the legacy behaviour: send fall-through content
|
||||
# to PASSTHROUGH instead of the default KOMPRESS fallback strategy.
|
||||
if config.disable_kompress_fallback:
|
||||
router_config.fallback_strategy = CompressionStrategy.PASSTHROUGH
|
||||
# A non-None exclude_tools replaces DEFAULT_EXCLUDE_TOOLS in
|
||||
# ContentRouter, so merge rather than assign.
|
||||
if config.exclude_tools:
|
||||
|
|
@ -2068,6 +2073,7 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI:
|
|||
"cache": config.cache_enabled,
|
||||
"rate_limit": config.rate_limit_enabled,
|
||||
"disable_kompress": config.disable_kompress,
|
||||
"disable_kompress_fallback": config.disable_kompress_fallback,
|
||||
"disable_kompress_anthropic": config.disable_kompress_anthropic,
|
||||
"disable_kompress_openai": config.disable_kompress_openai,
|
||||
"memory": config.memory_enabled,
|
||||
|
|
@ -3770,6 +3776,7 @@ def _proxy_config_from_env() -> ProxyConfig:
|
|||
bedrock_api_url=os.environ.get("BEDROCK_TARGET_API_URL"),
|
||||
anyllm_provider=_get_env_str("HEADROOM_ANYLLM_PROVIDER", "openai"),
|
||||
disable_kompress=_get_env_bool("HEADROOM_DISABLE_KOMPRESS", False),
|
||||
disable_kompress_fallback=_get_env_bool("HEADROOM_DISABLE_KOMPRESS_FALLBACK", False),
|
||||
disable_kompress_anthropic=_get_env_optional_bool("HEADROOM_DISABLE_KOMPRESS_ANTHROPIC"),
|
||||
disable_kompress_openai=_get_env_optional_bool("HEADROOM_DISABLE_KOMPRESS_OPENAI"),
|
||||
max_connections=_get_env_int("HEADROOM_MAX_CONNECTIONS", 500),
|
||||
|
|
@ -4165,6 +4172,15 @@ if __name__ == "__main__":
|
|||
"Also settable via HEADROOM_DISABLE_KOMPRESS=1."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-kompress-fallback",
|
||||
action="store_true",
|
||||
help=(
|
||||
"With --disable-kompress, route fall-through content to PASSTHROUGH instead of "
|
||||
"the default KOMPRESS fallback (restores legacy --disable-kompress behaviour). "
|
||||
"Also settable via HEADROOM_DISABLE_KOMPRESS_FALLBACK=1."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-kompress-anthropic",
|
||||
dest="disable_kompress_anthropic",
|
||||
|
|
@ -4259,6 +4275,9 @@ if __name__ == "__main__":
|
|||
cache_enabled = env_cache if not args.no_cache else False
|
||||
rate_limit_enabled = env_rate_limit if not args.no_rate_limit else False
|
||||
disable_kompress = args.disable_kompress or _get_env_bool("HEADROOM_DISABLE_KOMPRESS", False)
|
||||
disable_kompress_fallback = args.disable_kompress_fallback or _get_env_bool(
|
||||
"HEADROOM_DISABLE_KOMPRESS_FALLBACK", False
|
||||
)
|
||||
disable_kompress_anthropic = (
|
||||
args.disable_kompress_anthropic
|
||||
if args.disable_kompress_anthropic is not None
|
||||
|
|
@ -4312,6 +4331,7 @@ if __name__ == "__main__":
|
|||
log_full_messages=args.log_messages or _get_env_bool("HEADROOM_LOG_MESSAGES", False),
|
||||
code_aware_enabled=code_aware_enabled,
|
||||
disable_kompress=disable_kompress,
|
||||
disable_kompress_fallback=disable_kompress_fallback,
|
||||
disable_kompress_anthropic=disable_kompress_anthropic,
|
||||
disable_kompress_openai=disable_kompress_openai,
|
||||
# Connection pool settings
|
||||
|
|
|
|||
|
|
@ -927,6 +927,15 @@ class TestArgparseBackendValidation:
|
|||
|
||||
assert config.disable_kompress is True
|
||||
|
||||
def test_proxy_config_from_env_reads_disable_kompress_fallback(self):
|
||||
"""The direct server env path should honor HEADROOM_DISABLE_KOMPRESS_FALLBACK."""
|
||||
from headroom.proxy.server import _proxy_config_from_env
|
||||
|
||||
with patch.dict(os.environ, {"HEADROOM_DISABLE_KOMPRESS_FALLBACK": "1"}):
|
||||
config = _proxy_config_from_env()
|
||||
|
||||
assert config.disable_kompress_fallback is True
|
||||
|
||||
def test_argparse_registers_keepalive_expiry_flag(self):
|
||||
"""The argparse path (python -m headroom.proxy.server) must register
|
||||
--keepalive-expiry as a float flag, so it can override the
|
||||
|
|
|
|||
|
|
@ -49,3 +49,58 @@ def test_disable_kompress_defaults_to_existing_kompress_behavior() -> None:
|
|||
|
||||
assert router.config.enable_kompress is True
|
||||
assert router.config.fallback_strategy == CompressionStrategy.KOMPRESS
|
||||
|
||||
|
||||
def test_health_config_reports_disable_kompress_fallback(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
monkeypatch.setenv("HEADROOM_SKIP_UPSTREAM_CHECK", "1")
|
||||
app = create_app(
|
||||
ProxyConfig(
|
||||
optimize=True,
|
||||
disable_kompress=True,
|
||||
disable_kompress_fallback=True,
|
||||
cache_enabled=False,
|
||||
rate_limit_enabled=False,
|
||||
cost_tracking_enabled=False,
|
||||
log_requests=False,
|
||||
)
|
||||
)
|
||||
with TestClient(app, base_url="http://127.0.0.1", client=("127.0.0.1", 12345)) as client:
|
||||
config = client.get("/health").json()["config"]
|
||||
|
||||
assert config["disable_kompress"] is True
|
||||
assert config["disable_kompress_fallback"] is True
|
||||
|
||||
|
||||
def test_disable_kompress_fallback_restores_passthrough() -> None:
|
||||
router = _proxy_router(
|
||||
ProxyConfig(
|
||||
optimize=True,
|
||||
disable_kompress=True,
|
||||
disable_kompress_fallback=True,
|
||||
cache_enabled=False,
|
||||
rate_limit_enabled=False,
|
||||
cost_tracking_enabled=False,
|
||||
log_requests=False,
|
||||
)
|
||||
)
|
||||
|
||||
assert router.config.enable_kompress is False
|
||||
assert router.config.fallback_strategy == CompressionStrategy.PASSTHROUGH
|
||||
|
||||
|
||||
def test_disable_kompress_fallback_without_disable_kompress_is_noop() -> None:
|
||||
router = _proxy_router(
|
||||
ProxyConfig(
|
||||
optimize=True,
|
||||
disable_kompress_fallback=True,
|
||||
cache_enabled=False,
|
||||
rate_limit_enabled=False,
|
||||
cost_tracking_enabled=False,
|
||||
log_requests=False,
|
||||
)
|
||||
)
|
||||
|
||||
assert router.config.enable_kompress is True
|
||||
assert router.config.fallback_strategy == CompressionStrategy.KOMPRESS
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue