mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
feat(proxy): expose retry delay configuration (#2077)
## Description Expose Headroom's existing retry-delay configuration through the proxy CLI and environment so operators can tune upstream backoff without changing code. Existing 1000 ms / 30000 ms defaults remain unchanged. Closes #2030 ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [x] New feature (non-breaking change that adds functionality) - [ ] Breaking change - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring ## Changes Made - add CLI flags for initial and maximum upstream retry delays - support `HEADROOM_RETRY_BASE_DELAY_MS` and `HEADROOM_RETRY_MAX_DELAY_MS` - validate non-negative values and forward them into `ProxyConfig` - cover explicit CLI values and environment-variable wiring ## Testing - [x] Focused unit tests pass (`pytest`) - [x] Touched-file linting passes (`ruff check`) - [ ] Type checking passes (`mypy headroom`) — not run - [x] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_cli_proxy_improvements.py::TestRetryDelayValidation tests/test_cli_proxy_improvements.py::TestNewEnvVarWiring::test_headroom_retry_delays_from_env -q 4 passed uv run ruff check headroom/cli/proxy.py tests/test_cli_proxy_improvements.py All checks passed! uv run ruff format --check headroom/cli/proxy.py tests/test_cli_proxy_improvements.py 2 files already formatted ``` ## Real Behavior Proof - Environment: local Python test environment - Exact command / steps: invoke the focused Click CLI tests with explicit delay flags and `HEADROOM_RETRY_*` environment variables - Observed result: all four cases passed; parsed values reached `ProxyConfig`, while invalid negative values were rejected - Not tested: live upstream retry timing or the full repository test suite ## 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] My changes generate no new warnings in the scoped checks - [x] I have added tests that prove the feature works - [x] Relevant existing and new unit tests pass locally - [ ] Documentation and changelog updates — not applicable for these self-documenting CLI options ## Additional Notes The existing runtime backoff helper still caps the base delay against the maximum. This change only exposes values already supported by `ProxyConfig`. Co-authored-by: Tejas Chopra <chopratejas@gmail.com>
This commit is contained in:
parent
f53f720eb5
commit
099c66432b
2 changed files with 55 additions and 0 deletions
|
|
@ -373,6 +373,26 @@ def dashboard(port: int, no_open: bool) -> None:
|
|||
"Env: HEADROOM_RETRY_MAX_ATTEMPTS."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--retry-base-delay-ms",
|
||||
type=click.IntRange(min=0),
|
||||
default=None,
|
||||
envvar="HEADROOM_RETRY_BASE_DELAY_MS",
|
||||
help=(
|
||||
"Initial upstream retry delay in milliseconds (minimum: 0, default: 1000). "
|
||||
"Env: HEADROOM_RETRY_BASE_DELAY_MS."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--retry-max-delay-ms",
|
||||
type=click.IntRange(min=0),
|
||||
default=None,
|
||||
envvar="HEADROOM_RETRY_MAX_DELAY_MS",
|
||||
help=(
|
||||
"Maximum upstream retry delay in milliseconds (minimum: 0, default: 30000). "
|
||||
"Env: HEADROOM_RETRY_MAX_DELAY_MS."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--request-timeout-seconds",
|
||||
type=int,
|
||||
|
|
@ -876,6 +896,8 @@ def proxy(
|
|||
no_subscription_tracking: bool,
|
||||
subscription_poll_interval: int | None,
|
||||
retry_max_attempts: int | None,
|
||||
retry_base_delay_ms: int | None,
|
||||
retry_max_delay_ms: int | None,
|
||||
request_timeout_seconds: int | None,
|
||||
connect_timeout_seconds: int | None,
|
||||
anthropic_buffered_request_timeout_seconds: int | None,
|
||||
|
|
@ -1129,6 +1151,8 @@ def proxy(
|
|||
subscription_poll_interval if subscription_poll_interval is not None else 300
|
||||
),
|
||||
retry_max_attempts=retry_max_attempts if retry_max_attempts is not None else 3,
|
||||
retry_base_delay_ms=retry_base_delay_ms if retry_base_delay_ms is not None else 1000,
|
||||
retry_max_delay_ms=retry_max_delay_ms if retry_max_delay_ms is not None else 30000,
|
||||
request_timeout_seconds=request_timeout_seconds
|
||||
if request_timeout_seconds is not None and request_timeout_seconds > 0
|
||||
else 300,
|
||||
|
|
|
|||
|
|
@ -193,6 +193,23 @@ class TestRetryMaxAttemptsValidation:
|
|||
assert result.exit_code != 0
|
||||
|
||||
|
||||
class TestRetryDelayValidation:
|
||||
def test_retry_delays_are_forwarded(self, runner: CliRunner, mock_run_server: dict) -> None:
|
||||
result = runner.invoke(
|
||||
main,
|
||||
["proxy", "--retry-base-delay-ms", "250", "--retry-max-delay-ms", "5000"],
|
||||
catch_exceptions=False,
|
||||
)
|
||||
assert result.exit_code == 0, result.output
|
||||
assert mock_run_server["config"].retry_base_delay_ms == 250
|
||||
assert mock_run_server["config"].retry_max_delay_ms == 5000
|
||||
|
||||
@pytest.mark.parametrize("option", ["--retry-base-delay-ms", "--retry-max-delay-ms"])
|
||||
def test_negative_delay_is_rejected(self, runner: CliRunner, option: str) -> None:
|
||||
result = runner.invoke(main, ["proxy", option, "-1"])
|
||||
assert result.exit_code != 0
|
||||
|
||||
|
||||
class TestConnectTimeoutSecondsValidation:
|
||||
"""--connect-timeout-seconds should accept 1-300, reject outside that range."""
|
||||
|
||||
|
|
@ -350,6 +367,20 @@ class TestNewEnvVarWiring:
|
|||
assert result.exit_code == 0, result.output
|
||||
assert mock_run_server["config"].retry_max_attempts == 5
|
||||
|
||||
def test_headroom_retry_delays_from_env(self, runner: CliRunner, mock_run_server: dict) -> None:
|
||||
result = runner.invoke(
|
||||
main,
|
||||
["proxy"],
|
||||
env={
|
||||
"HEADROOM_RETRY_BASE_DELAY_MS": "125",
|
||||
"HEADROOM_RETRY_MAX_DELAY_MS": "8000",
|
||||
},
|
||||
catch_exceptions=False,
|
||||
)
|
||||
assert result.exit_code == 0, result.output
|
||||
assert mock_run_server["config"].retry_base_delay_ms == 125
|
||||
assert mock_run_server["config"].retry_max_delay_ms == 8000
|
||||
|
||||
def test_headroom_connect_timeout_from_env(
|
||||
self, runner: CliRunner, mock_run_server: dict
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue