Bumps the pip-minor-patch group with 1 update in the / directory: [ruff](https://github.com/astral-sh/ruff). Updates `ruff` from 0.15.22 to 0.16.2 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ruff/releases">ruff's releases</a>.</em></p> <blockquote> <h2>0.16.2</h2> <h2>Release Notes</h2> <p>Released on 2026-08-06.</p> <h3>Bug fixes</h3> <ul> <li>[<code>flake8-pyi</code>] Avoid false positives on <code>singledispatch</code> functions (<code>PYI041</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27335">#27335</a>)</li> </ul> <h3>Server</h3> <ul> <li>Register formatting capabilities dynamically to exclude TOML files (<a href="https://redirect.github.com/astral-sh/ruff/pull/27332">#27332</a>)</li> </ul> <h3>Contributors</h3> <ul> <li><a href="https://github.com/MeGaGiGaGon"><code>@MeGaGiGaGon</code></a></li> <li><a href="https://github.com/charliermarsh"><code>@charliermarsh</code></a></li> <li><a href="https://github.com/epage"><code>@epage</code></a></li> <li><a href="https://github.com/sharkdp"><code>@sharkdp</code></a></li> <li><a href="https://github.com/ntBre"><code>@ntBre</code></a></li> </ul> <h2>Install ruff 0.16.2</h2> <h3>Install prebuilt binaries via shell script</h3> <pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-installer.sh | sh </code></pre> <h3>Install prebuilt binaries via powershell script</h3> <pre lang="sh"><code>powershell -ExecutionPolicy Bypass -c "irm https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-installer.ps1 | iex" </code></pre> <h2>Download ruff 0.16.2</h2> <table> <thead> <tr> <th>File</th> <th>Platform</th> <th>Checksum</th> </tr> </thead> <tbody> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-aarch64-apple-darwin.tar.gz">ruff-aarch64-apple-darwin.tar.gz</a></td> <td>Apple Silicon macOS</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-x86_64-apple-darwin.tar.gz">ruff-x86_64-apple-darwin.tar.gz</a></td> <td>Intel macOS</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-aarch64-pc-windows-msvc.zip">ruff-aarch64-pc-windows-msvc.zip</a></td> <td>ARM64 Windows</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-aarch64-pc-windows-msvc.zip.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-i686-pc-windows-msvc.zip">ruff-i686-pc-windows-msvc.zip</a></td> <td>x86 Windows</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-i686-pc-windows-msvc.zip.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-x86_64-pc-windows-msvc.zip">ruff-x86_64-pc-windows-msvc.zip</a></td> <td>x64 Windows</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-x86_64-pc-windows-msvc.zip.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-aarch64-unknown-linux-gnu.tar.gz">ruff-aarch64-unknown-linux-gnu.tar.gz</a></td> <td>ARM64 Linux</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-aarch64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-i686-unknown-linux-gnu.tar.gz">ruff-i686-unknown-linux-gnu.tar.gz</a></td> <td>x86 Linux</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-i686-unknown-linux-gnu.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-powerpc64-unknown-linux-gnu.tar.gz">ruff-powerpc64-unknown-linux-gnu.tar.gz</a></td> <td>PPC64 Linux</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-powerpc64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-powerpc64le-unknown-linux-gnu.tar.gz">ruff-powerpc64le-unknown-linux-gnu.tar.gz</a></td> <td>PPC64LE Linux</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-powerpc64le-unknown-linux-gnu.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-riscv64gc-unknown-linux-gnu.tar.gz">ruff-riscv64gc-unknown-linux-gnu.tar.gz</a></td> <td>RISCV Linux</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-riscv64gc-unknown-linux-gnu.tar.gz.sha256">checksum</a></td> </tr> <tr> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-s390x-unknown-linux-gnu.tar.gz">ruff-s390x-unknown-linux-gnu.tar.gz</a></td> <td>S390x Linux</td> <td><a href="https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-s390x-unknown-linux-gnu.tar.gz.sha256">checksum</a></td> </tr> </tbody> </table> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md">ruff's changelog</a>.</em></p> <blockquote> <h2>0.16.2</h2> <p>Released on 2026-08-06.</p> <h3>Bug fixes</h3> <ul> <li>[<code>flake8-pyi</code>] Avoid false positives on <code>singledispatch</code> functions (<code>PYI041</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27335">#27335</a>)</li> </ul> <h3>Server</h3> <ul> <li>Register formatting capabilities dynamically to exclude TOML files (<a href="https://redirect.github.com/astral-sh/ruff/pull/27332">#27332</a>)</li> </ul> <h3>Contributors</h3> <ul> <li><a href="https://github.com/MeGaGiGaGon"><code>@MeGaGiGaGon</code></a></li> <li><a href="https://github.com/charliermarsh"><code>@charliermarsh</code></a></li> <li><a href="https://github.com/epage"><code>@epage</code></a></li> <li><a href="https://github.com/sharkdp"><code>@sharkdp</code></a></li> <li><a href="https://github.com/ntBre"><code>@ntBre</code></a></li> </ul> <h2>0.16.1</h2> <p>Released on 2026-07-30.</p> <h3>Preview features</h3> <ul> <li>Add an option to opt out of human-readable names (<a href="https://redirect.github.com/astral-sh/ruff/pull/27160">#27160</a>)</li> <li>[<code>flake8-pytest-style</code>] Make fixes safe by default and unsafe only when comments are present (<code>PT018</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27201">#27201</a>)</li> <li>[<code>pyupgrade</code>] Skip fix when a defaulted <code>TypeVar</code> precedes a non-defaulted one (<code>UP040</code>, <code>UP046</code>, <code>UP047</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27133">#27133</a>)</li> <li>[<code>ruff</code>] Fix false positive with unpacked arguments (<code>RUF065</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/26959">#26959</a>)</li> </ul> <h3>Bug fixes</h3> <ul> <li>Bump <code>gen-lsp-types</code> to gracefully handle unknown enumeration values in LSP messages (<a href="https://redirect.github.com/astral-sh/ruff/pull/27230">#27230</a>)</li> <li>[<code>flake8-bugbear</code>] Mark <code>range</code> as immutable (<code>B008</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27247">#27247</a>)</li> <li>[<code>flake8-comprehensions</code>] NFKC-normalize keyword names in <code>C408</code> fix (<a href="https://redirect.github.com/astral-sh/ruff/pull/26813">#26813</a>)</li> <li>[<code>flake8-return</code>] Fix false positive when variable is read in <code>finally</code> clause (<code>RET504</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/25441">#25441</a>)</li> <li>[<code>pydocstyle</code>] Skip section detection inside RST directive bodies (<code>D214</code>, <code>D405</code>, <code>D413</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/23635">#23635</a>)</li> <li>[<code>refurb</code>] Parenthesize <code>yield</code> arguments in the <code>FURB192</code> fix (<a href="https://redirect.github.com/astral-sh/ruff/pull/27192">#27192</a>)</li> </ul> <h3>Rule changes</h3> <ul> <li>[<code>flake8-pytest-style</code>] Mark <code>PT022</code> fixes as unsafe (<a href="https://redirect.github.com/astral-sh/ruff/pull/26440">#26440</a>)</li> <li>[<code>refurb</code>] Mark fixes that remove unknown separators as unsafe (<code>FURB105</code>) (<a href="https://redirect.github.com/astral-sh/ruff/pull/27200">#27200</a>)</li> </ul> <h3>Server</h3> <ul> <li>Fix indexing of excluded nested Ruff workspaces (<a href="https://redirect.github.com/astral-sh/ruff/pull/27303">#27303</a>)</li> <li>Lint TOML files in the LSP (<a href="https://redirect.github.com/astral-sh/ruff/pull/26862">#26862</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="5b48a04097"><code>5b48a04</code></a> Bump 0.16.2 (<a href="https://redirect.github.com/astral-sh/ruff/issues/27555">#27555</a>)</li> <li><a href="1b9e5fc483"><code>1b9e5fc</code></a> Update Swatinem/rust-cache action to v2.9.2 (<a href="https://redirect.github.com/astral-sh/ruff/issues/27568">#27568</a>)</li> <li><a href="c4e86fc039"><code>c4e86fc</code></a> [ty] Add helper extension methods for half-range and equality constraints (<a href="https://redirect.github.com/astral-sh/ruff/issues/2">#2</a>...</li> <li><a href="17a00de2e2"><code>17a00de</code></a> [ty] Reuse primer commands in memory reports (<a href="https://redirect.github.com/astral-sh/ruff/issues/27553">#27553</a>)</li> <li><a href="6ea296b969"><code>6ea296b</code></a> [ty] Normalize type labels in structured docstrings (<a href="https://redirect.github.com/astral-sh/ruff/issues/26923">#26923</a>)</li> <li><a href="2fc445f005"><code>2fc445f</code></a> [ty] Diagnose invalid <strong>getattr</strong> calls (<a href="https://redirect.github.com/astral-sh/ruff/issues/27502">#27502</a>)</li> <li><a href="22c7823c4e"><code>22c7823</code></a> [ty] Enable (but downrank) auto-import completion suggestions from stub-only ...</li> <li><a href="05160d507f"><code>05160d5</code></a> [ty] Diagnose invalid descriptor <code>__get__</code> calls (<a href="https://redirect.github.com/astral-sh/ruff/issues/27400">#27400</a>)</li> <li><a href="baea3d0dce"><code>baea3d0</code></a> [ty] Expose strict analysis options in the playground (<a href="https://redirect.github.com/astral-sh/ruff/issues/27543">#27543</a>)</li> <li><a href="c88946ebeb"><code>c88946e</code></a> [ty] Bump ecosystem-analyzer for strict project settings (<a href="https://redirect.github.com/astral-sh/ruff/issues/27542">#27542</a>)</li> <li>Additional commits viewable in <a href="https://github.com/astral-sh/ruff/compare/0.15.22...0.16.2">compare view</a></li> </ul> </details> <br /> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
17 KiB
Configuration
Headroom can be configured via the SDK, proxy command line, or per-request overrides.
Runtime Rollout Channels
Rollout channels control behaviors in an already-installed artifact. They do not install or select a Headroom release/version.
| Variable | Default | Purpose |
|---|---|---|
HEADROOM_ROLLOUT_CHANNEL |
stable |
Selects stable, beta, canary, or dev. |
HEADROOM_FEATURES |
unset | Comma-separated feature names to request explicitly. |
HEADROOM_DISABLE_FEATURES |
unset | Comma-separated feature names to force off. Disable wins over every enable path. |
HEADROOM_UNSAFE_ALLOW_UNSTABLE_FEATURES |
unset | Break-glass override for emergency mitigation only. |
Example:
export HEADROOM_ROLLOUT_CHANNEL=canary
export HEADROOM_FEATURES=tool_result_interceptors
headroom proxy --intercept-tool-results
SDK Configuration
from headroom import HeadroomClient, OpenAIProvider
from openai import OpenAI
client = HeadroomClient(
original_client=OpenAI(),
provider=OpenAIProvider(),
# Mode: "audit" (observe only) or "optimize" (apply transforms)
default_mode="optimize",
# Enable provider-specific cache optimization
enable_cache_optimizer=True,
# Enable query-level semantic caching
enable_semantic_cache=False,
# Override default context limits per model
model_context_limits={
"gpt-4o": 128000,
"gpt-4o-mini": 128000,
},
# Database location (defaults to temp directory)
# store_url="sqlite:////absolute/path/to/headroom.db",
)
Proxy Configuration
Command Line Options
headroom proxy \
--port 8787 \ # Port to listen on
--host 0.0.0.0 \ # Host to bind to
--budget 10.00 \ # Daily budget limit in USD
--log-file headroom.jsonl # Log file path
Feature Flags
# Disable optimization (passthrough mode)
headroom proxy --no-optimize
# Disable semantic caching
headroom proxy --no-cache
# Disable CCR entirely (no retrieval markers and no injected retrieve tool)
headroom proxy --no-ccr
# Disable proactive CCR expansion
headroom proxy --no-ccr-proactive-expansion
# (The earlier --llmlingua flag was retired in 0.9.x and replaced by
# Kompress (ModernBERT). See `wiki/transforms.md` for the current
# opt-in path via the `[ml]` extra.)
All Options
headroom proxy --help
Kompress backend selection
Kompress (the model-based compressor) can run on two engines:
- ONNX Runtime — lightweight, CPU-first. Installed with
pip install headroom-ai[proxy]. Optionally uses the CoreML execution provider on macOS. - PyTorch — heavier, supports CUDA and Apple-Silicon MPS
acceleration. Installed with
pip install headroom-ai[ml]. Withdevice=autoit selectscuda, thenmps, thencpu.
Select the backend via the HEADROOM_KOMPRESS_BACKEND environment
variable:
| Value | Behavior |
|---|---|
auto |
Default. ONNX CPU first (stable, lightweight), PyTorch as fallback. |
onnx / onnx_cpu |
Force ONNX Runtime on CPU. |
onnx_coreml |
Force ONNX Runtime with the CoreML provider (CPU fallback). |
pytorch |
Force PyTorch with automatic device selection (CUDA → MPS → CPU). |
pytorch_mps |
Force PyTorch on Apple-Silicon MPS; falls back to ONNX CPU on failure. |
Values are case-insensitive and hyphens are accepted (onnx-cpu ==
onnx_cpu). Shorthand aliases: cpu → onnx_cpu, coreml →
onnx_coreml, mps / torch_mps → pytorch_mps, torch →
pytorch. Unrecognized values log a warning and fall back to auto.
Example — opt in to MPS on an Apple-Silicon machine:
export HEADROOM_KOMPRESS_BACKEND=mps
headroom proxy ...
The default deliberately stays on ONNX CPU so existing installs keep their compression quality and performance characteristics; accelerator backends are opt-in.
Per-Request Overrides
Override configuration for specific requests:
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
# Override mode for this request
headroom_mode="audit",
# Reserve more tokens for output
headroom_output_buffer_tokens=8000,
# Keep last N turns (don't compress)
headroom_keep_turns=5,
# Skip compression for specific tools
headroom_tool_profiles={"important_tool": {"skip_compression": True}},
)
Modes
| Mode | Behavior | Use Case |
|---|---|---|
audit |
Observes and logs, no modifications | Production monitoring, baseline measurement |
optimize |
Applies safe, deterministic transforms | Production optimization |
simulate |
Returns plan without API call | Testing, cost estimation |
Simulate Mode
Preview what would happen without making an API call:
plan = client.chat.completions.simulate(
model="gpt-4o",
messages=large_conversation,
)
print(f"Would save {plan.tokens_saved} tokens")
print(f"Transforms: {plan.transforms}")
print(f"Estimated savings: {plan.estimated_savings}")
SmartCrusher Configuration
Fine-tune JSON compression behavior:
from headroom.transforms import SmartCrusherConfig
config = SmartCrusherConfig(
# Maximum items to keep after compression
max_items_after_crush=15,
# Minimum tokens before applying compression
min_tokens_to_crush=200,
# Relevance scoring tier: "bm25" (fast) or "embedding" (accurate)
relevance_tier="bm25",
# Always keep items with these field values
preserve_fields=["error", "warning", "failure"],
)
Cache Aligner Configuration
Control prefix stabilization:
from headroom.transforms import CacheAlignerConfig
config = CacheAlignerConfig(
# Enable/disable cache alignment
enabled=True,
# Patterns to extract from system prompt
dynamic_patterns=[
r"Today is \w+ \d+, \d{4}",
r"Current time: .*",
],
)
Context Management
Context management is handled automatically inside the pipeline (live-zone-only compression) — there is nothing to configure. Headroom never drops messages from the conversation history and does not do position-based or score-based context management. It compresses only the newest content blocks (the latest user message and the latest tool result / tool output), type-aware and reversible via CCR. The cache hot zone — system prompt, tools, and older turns — is never mutated, which preserves provider prompt caching.
The earlier
RollingWindowConfig,IntelligentContextConfig, andScoringWeightsconfiguration classes (and the position-/score-based context managers they configured) have been removed and are no longer part of Headroom.
Environment Variables
Some settings can be configured via environment variables:
| Variable | Description | Default |
|---|---|---|
HEADROOM_MODEL_LIMITS |
Custom model config (JSON string or file path) | - |
HEADROOM_CONFIG_DIR |
Canonical config (read-mostly) root. Derives models.json and per-plugin config paths when set. |
~/.headroom/config |
HEADROOM_WORKSPACE_DIR |
Canonical workspace (read-write state) root. Derives savings ledger, memory DB, logs, TOIN, subscription state, and more when set. | ~/.headroom |
HEADROOM_SAVINGS_PATH |
Full path to the proxy savings JSON ledger. Always wins when set. | derived from ${HEADROOM_WORKSPACE_DIR} |
HEADROOM_TOIN_PATH |
Full path to the TOIN telemetry JSON file. Always wins when set. | derived from ${HEADROOM_WORKSPACE_DIR} |
HEADROOM_SUBSCRIPTION_STATE_PATH |
Full path to the subscription tracker state. Always wins when set. | derived from ${HEADROOM_WORKSPACE_DIR} |
HEADROOM_EMBEDDER_RUNTIME |
Set to pytorch_mps to run the memory embedder via the torch sentence-transformers backend on the Apple GPU (MPS). Only engages when Apple MPS is actually available; otherwise it logs a warning and uses the existing default embedder selection path. pytorch_mps is the only accepted value. Requires the [pytorch-mps] extra. See Memory. |
default embedder selection |
HEADROOM_BETA_HEADER_STICKY |
Controls per-session anthropic-beta / OpenAI-Beta re-echo. enabled (default): the proxy unions beta tokens across turns within a session — if the client sends a token in turn N and omits it in turn N+1, the proxy re-injects it to preserve prefix-cache stability. disabled: the client's value is forwarded verbatim with no accumulation. Any other value raises at request time. See Session Beta Header Tracking. |
enabled |
HEADROOM_BETA_TRACKER_MAX_SESSIONS |
LRU capacity of the in-memory session beta tracker. Once full, the oldest session entry is evicted. | 1000 |
Settings GUI
A web-based settings interface is available at http://127.0.0.1:<port>/dashboard/settings for configuring every safe HEADROOM_* proxy knob without hand-exporting environment variables, plus an Endpoints group for custom Anthropic/OpenAI upstream base URLs (ANTHROPIC_TARGET_API_URL / OPENAI_TARGET_API_URL) and extra headers merged into (and overriding) forwarded requests -- e.g. for a corporate gateway or Azure Foundry deployment that needs a different endpoint plus one extra auth header. Fields are split into a Settings tab (commonly-tuned: compression ratio, budget, rate limits, verbosity) and an Advanced tab (everything else, including Endpoints). Third-party credentials such as OPENAI_API_KEY/AWS_* are never exposed here; the two extra-headers fields are the only secret-typed fields in the panel and render masked once set, with a "Clear stored value" action to remove them -- resaving the page without touching a masked field never overwrites the real stored value.
- Persistence: Settings are saved to
~/.headroom/settings.json(merged with existing values, not replaced) and loaded into the process environment at startup. - Precedence (highest to lowest):
- Explicit shell export (
export HEADROOM_FOO=bar) - Settings from
~/.headroom/settings.json - Code default
- Explicit shell export (
- Activation: Click "Save" to persist without restarting, or "Apply & Restart" to persist and take effect immediately. Apply & Restart behavior depends on how the proxy is running:
- Service (supervised launchd/systemd install): self-restarts in one click.
- Docker: cannot self-restart from inside the container; the GUI surfaces the host-side
headroom install restart --profile <p>command to run instead. - Task (Windows Task Scheduler / cron-managed install):
headroom installdoes not support lifecycle operations for task deployments; the GUI shows an instruction to restart via the OS task scheduler or by stopping the process so it relaunches on its next trigger. - Foreground (plain
headroom proxy): shows a manual-restart instruction.
- Provenance / locking: a field currently shadowed by an explicit environment variable export is rendered read-only with a tooltip, since editing it here would have no effect until the env var is unset. Manifest-baked settings (
HEADROOM_PORT,HEADROOM_HOST) are similarly locked on supervised (Docker/Service) installs — managed by the install manifest, not the settings interface. - CSRF protection:
/settingsand/settings/applyreject requests whoseOriginheader (when present) doesn't resolve to a loopback host, in addition to the existing loopback-only + Host-header DNS-rebinding guard shared by all admin endpoints.
Session Beta Header Tracking
When running as a proxy, Headroom maintains a per-session union of anthropic-beta (and OpenAI-Beta) tokens via SessionBetaTracker. The session key is derived from the x-headroom-session-id header if present, otherwise from md5(model + system_prompt[:500])[:16] — stable across turns of the same conversation.
Why: clients such as Claude Code and Codex CLI may drop a beta token between consecutive turns. Because anthropic-beta is part of the request bytes that determine the upstream prefix-cache key, a dropped token would bust the cache mid-conversation. The tracker re-injects any token seen earlier in the session so the cache key stays stable.
Trade-off: once the proxy has seen a beta token in a session it will continue re-sending it for the rest of that session, even if the client stops including it. Stopping the token on the client side alone is not sufficient — the proxy re-injects it. Set HEADROOM_BETA_HEADER_STICKY=disabled to pass the client's anthropic-beta value verbatim and bypass this accumulation.
# Disable sticky beta re-echo
export HEADROOM_BETA_HEADER_STICKY=disabled
headroom proxy ...
Note: disabling sticky mode may reduce prefix-cache hit rates for clients that legitimately drop-and-re-add beta tokens across turns.
Filesystem Contract
Headroom resolves every on-disk resource through a two-root model:
HEADROOM_CONFIG_DIR(default~/.headroom/config) — read-mostly configurationHEADROOM_WORKSPACE_DIR(default~/.headroom) — read-write state
Precedence for each resource is: explicit argument > per-resource env var > derived from canonical root > default. Every legacy env var continues to work unchanged.
See Filesystem Contract for the full
bucket table, plugin-author guidance, and the Docker naming overlap
note (HEADROOM_WORKSPACE is not the same as HEADROOM_WORKSPACE_DIR).
Custom Model Configuration
Configure context limits and pricing for new or custom models. Useful when:
- A new model is released before Headroom is updated
- You're using fine-tuned or custom models
- You want to override built-in limits
Configuration Methods
Settings are resolved in this order (later overrides earlier):
- Built-in defaults
${HEADROOM_CONFIG_DIR}/models.json(defaults to~/.headroom/config/models.json); falls back to the legacy location~/.headroom/models.jsonwhen the canonical file is absentHEADROOM_MODEL_LIMITSenvironment variable- SDK constructor arguments
Config File Format
Create ~/.headroom/models.json:
{
"anthropic": {
"context_limits": {
"claude-4-opus-20250301": 200000,
"claude-custom-finetune": 128000
},
"pricing": {
"claude-4-opus-20250301": {
"input": 15.00,
"output": 75.00,
"cached_input": 1.50
}
}
},
"openai": {
"context_limits": {
"gpt-5": 256000,
"ft:gpt-4o:my-org": 128000
},
"pricing": {
"gpt-5": [5.00, 15.00]
}
}
}
Environment Variable
Set HEADROOM_MODEL_LIMITS as a JSON string or file path:
# JSON string
export HEADROOM_MODEL_LIMITS='{"anthropic":{"context_limits":{"claude-new":200000}}}'
# File path
export HEADROOM_MODEL_LIMITS=/path/to/models.json
Pattern-Based Inference
Unknown models are automatically inferred from naming patterns:
| Pattern | Inferred Settings |
|---|---|
*opus* |
200K context, Opus-tier pricing |
*sonnet* |
200K context, Sonnet-tier pricing |
*haiku* |
200K context, Haiku-tier pricing |
gpt-4o* |
128K context, GPT-4o pricing |
o1*, o3* |
200K context, reasoning model pricing |
This means new models like claude-4-sonnet-20251201 will work automatically with Sonnet-tier defaults.
SDK Override
Override in code for specific models:
from headroom import HeadroomClient, AnthropicProvider
client = HeadroomClient(
original_client=Anthropic(),
provider=AnthropicProvider(
context_limits={
"claude-new-model": 300000,
}
),
)
Provider-Specific Settings
OpenAI
from headroom import OpenAIProvider
provider = OpenAIProvider(
# Enable automatic prefix caching
enable_prefix_caching=True,
)
Anthropic
from headroom import AnthropicProvider
provider = AnthropicProvider(
# Enable cache_control blocks
enable_cache_control=True,
)
from headroom import GoogleProvider
provider = GoogleProvider(
# Enable context caching
enable_context_caching=True,
)
Configuration Precedence
Settings are applied in this order (later overrides earlier):
- Default values
- Environment variables
- SDK constructor arguments
- Per-request overrides
Validation
Validate your configuration:
result = client.validate_setup()
if not result["valid"]:
print("Configuration issues:")
for issue in result["issues"]:
print(f" - {issue}")
TypeScript SDK Configuration
The TypeScript SDK is configured via environment variables or constructor options.
Environment Variables
| Variable | Description | Default |
|---|---|---|
HEADROOM_BASE_URL |
Base URL of the Headroom proxy | http://localhost:8787 |
HEADROOM_API_KEY |
Optional API key for authenticated Headroom endpoints | - |
Usage
export HEADROOM_BASE_URL=http://localhost:8787
export HEADROOM_API_KEY=your-api-key
import { HeadroomClient } from 'headroom-ai';
// Reads from HEADROOM_BASE_URL and HEADROOM_API_KEY automatically
const client = new HeadroomClient();
// Or configure explicitly
const client = new HeadroomClient({
baseUrl: 'http://localhost:8787',
apiKey: 'your-api-key',
});
See the TypeScript SDK Guide for full configuration options.