diff --git a/headroom/cli/proxy.py b/headroom/cli/proxy.py index 5f7fa0f63..eeaaa4714 100644 --- a/headroom/cli/proxy.py +++ b/headroom/cli/proxy.py @@ -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 diff --git a/headroom/proxy/models.py b/headroom/proxy/models.py index 7a76bc9f9..eafaed538 100644 --- a/headroom/proxy/models.py +++ b/headroom/proxy/models.py @@ -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). diff --git a/headroom/proxy/server.py b/headroom/proxy/server.py index 610a2a817..fb8181af7 100644 --- a/headroom/proxy/server.py +++ b/headroom/proxy/server.py @@ -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 diff --git a/tests/test_cli_proxy_env.py b/tests/test_cli_proxy_env.py index 1ae78b5aa..0e5034586 100644 --- a/tests/test_cli_proxy_env.py +++ b/tests/test_cli_proxy_env.py @@ -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 diff --git a/tests/test_proxy_disable_kompress.py b/tests/test_proxy_disable_kompress.py index d608b0fbc..c1f3da46e 100644 --- a/tests/test_proxy_disable_kompress.py +++ b/tests/test_proxy_disable_kompress.py @@ -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