Headroom

Configuration

All configuration options for the Headroom Python and TypeScript SDKs, proxy server, and per-request overrides.

Headroom can be configured via the SDK constructor, proxy command line, environment variables, or per-request overrides.

Runtime Rollout Channels

Headroom uses rollout channels to control which behaviors an already-installed artifact may expose. They do not select a package or released version.

VariableDefaultPurpose
HEADROOM_ROLLOUT_CHANNELstableSelects stable, beta, canary, or dev.
HEADROOM_FEATURESunsetComma-separated feature names to request explicitly.
HEADROOM_DISABLE_FEATURESunsetComma-separated feature names to force off. Disable wins over every enable path.
HEADROOM_UNSAFE_ALLOW_UNSTABLE_FEATURESunsetBreak-glass override for emergency mitigation only.

See Runtime Rollouts for policy, provenance, and contributor rules. If Codex history disappeared after using an older wrapper, see Recover Codex State before wrapping Codex again.

SDK Modes (default_mode / headroom_mode)

These modes apply to SDK usage via HeadroomClient(default_mode=...) or per-request headroom_mode=.... They are not the same as the proxy --mode flag.

ModeBehaviorUse Case
auditObserves and logs, no modificationsProduction monitoring, baseline measurement
optimizeApplies safe, deterministic transformsProduction optimization
simulateReturns plan without API callTesting, cost estimation

Proxy --mode is a separate axis: headroom proxy --mode token (maximize compression) or --mode cache (freeze prior turns for prefix-cache stability). The proxy does not accept audit, optimize, or simulate.

SDK Configuration

import {  } from 'headroom-ai';

// Reads from HEADROOM_BASE_URL and HEADROOM_API_KEY automatically
const  = new ();

// Or configure explicitly
const  = new ({
  : 'http://localhost:8787',
  : 'your-api-key',
  : 30_000,
  : true,
  : 2,
});
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",
)

Per-Request Overrides

Override configuration for individual requests:

import {  } from 'headroom-ai';

const  = await (messages, {
  : 'gpt-4o',
  : 100_000,
  : 15_000,
});
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}
    },
)

Proxy upstream override (x-headroom-base-url)

When using the proxy, send the x-headroom-base-url request header to route a single request to a different upstream instead of the configured provider URL. This lets a client that speaks a provider's wire format authenticate against a compatible gateway (for example an OpenAI-compatible endpoint, or an Anthropic-Messages gateway such as OpenCode Zen) without changing the proxy configuration.

The header is honored by the OpenAI-compatible routes, the Anthropic Messages route (POST /v1/messages), and the generic passthrough route. The proxy forwards the request to <x-headroom-base-url> + the original request path (e.g. /v1/messages). An empty or whitespace-only value is ignored and the configured upstream is used.

curl http://127.0.0.1:8787/v1/messages \
  -H "content-type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -H "x-headroom-base-url: https://opencode.ai/zen/go" \
  -H "x-api-key: <gateway-api-key>" \
  -d '{"model":"glm-5.2","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'

When HEADROOM_STRIP_INTERNAL_HEADERS is enabled (the default), the proxy reads this header for routing and then strips it before forwarding upstream.

Configured secret headers are not sent to arbitrary upstreams

ANTHROPIC_TARGET_API_HEADERS / OPENAI_TARGET_API_HEADERS hold operator secrets. Because x-headroom-base-url is chosen by the client, those headers are only attached when the destination is one the operator designated:

  • a host in the configured provider targets (ANTHROPIC_TARGET_API_URL, OPENAI_TARGET_API_URL, and the Gemini/Vertex/Cloud Code equivalents), or
  • a host listed in HEADROOM_UPSTREAM_ALLOWED_HOSTS (comma-separated).

A request to any other upstream is still proxied — it just does not carry your configured headers, and the proxy logs upstream_extra_headers_withheld host=<host> once per host. If you route to a gateway via this header and need your configured headers to reach it, add its host to HEADROOM_UPSTREAM_ALLOWED_HOSTS:

export HEADROOM_UPSTREAM_ALLOWED_HOSTS="gateway.internal,api.example-gateway.ai"

Matching is on the parsed hostname and is exact — no wildcards — so api.anthropic.com.evil.example and https://api.anthropic.com@evil.example do not match api.anthropic.com.

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,

    # Fraction of items always kept from the start/end
    first_fraction=0.3,
    last_fraction=0.15,

    # Variance threshold for statistical analysis
    variance_threshold=2.0,
)

CacheAligner Configuration

Control prefix stabilization for provider cache hit rates:

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 Window Management

Context management is now automatic. Use per-request overrides to control behavior:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    # Reserve tokens for model output
    headroom_output_buffer_tokens=4000,
    # Keep last N turns uncompressed
    headroom_keep_turns=3,
)

The RollingWindowConfig, IntelligentContextConfig, and ScoringWeights classes are no longer part of Headroom. Context management now happens automatically inside the pipeline (live-zone-only compression).

Claude 1M context window (headroom wrap claude --1m)

headroom wrap claude --1m opts a Claude Code session into Anthropic's 1M-token context window by selecting a [1m]-suffixed model id, which makes Claude Code send the context-1m beta header. The model that --1m targets is resolved in this order:

  1. an explicit --model / ANTHROPIC_MODEL value (used as-is, with a [1m] suffix appended when missing),
  2. otherwise HEADROOM_1M_MODEL, when set,
  3. otherwise the built-in default (currently claude-opus-5).

Set HEADROOM_1M_MODEL to point --1m at a specific model without pinning ANTHROPIC_MODEL globally, so the default can follow a new Opus generation without a code change:

# Route --1m at a specific model for this shell / session
export HEADROOM_1M_MODEL=claude-opus-5
headroom wrap claude --1m

HEADROOM_1M_MODEL is a fallback only: an explicit --model or ANTHROPIC_MODEL always wins. The value may be given with or without the [1m] suffix; both claude-opus-5 and claude-opus-5[1m] are accepted, and the suffix is added when absent.

Pipeline Extensions

Use a headroom.pipeline_extension entry point when you need to normalize or annotate requests before they leave Headroom. The PRE_SEND stage is the right place for provider-specific request cleanup, such as turning content: null into content: "" for upstreams that reject OpenAI-spec tool-call messages.

from headroom.pipeline import PipelineEvent, PipelineStage


class NormalizeNullContent:
    def on_pipeline_event(self, event: PipelineEvent) -> PipelineEvent:
        if event.stage is not PipelineStage.PRE_SEND or not event.messages:
            return event

        for message in event.messages:
            if (
                message.get("role") == "assistant"
                and message.get("content") is None
                and message.get("tool_calls")
            ):
                message["content"] = ""

        return event

Register it in pyproject.toml:

[project.entry-points."headroom.pipeline_extension"]
normalize_null_content = "my_pkg.normalize:NormalizeNullContent"

If the upstream base URL itself must vary per request, use the x-headroom-base-url override header in addition to the normalization hook.

Proxy Configuration

Command Line Options

headroom proxy \
  --port 8787 \              # Port to listen on
  --host 0.0.0.0 \           # Host to bind to
  --mode token \             # token compression mode; use cache for prefix-cache stability
  --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

# Preserve provider prefix-cache stability instead of maximizing token removal
headroom proxy --mode cache

# Enable memory and live learning
headroom proxy --memory
headroom proxy --learn --min-evidence 3

Environment Variables

VariableDescriptionDefault
HEADROOM_HOSTProxy bind host127.0.0.1
HEADROOM_PORTProxy bind port8787
HEADROOM_MODEProxy optimization mode: token or cachecache
HEADROOM_WORKERSUvicorn worker count1
HEADROOM_LIMIT_CONCURRENCYMaximum concurrent connections before 5031000
HEADROOM_MAX_CONNECTIONSMaximum upstream HTTP connections500
HEADROOM_MAX_KEEPALIVEMaximum upstream keep-alive connections100
HEADROOM_KEEPALIVE_EXPIRYSeconds an idle upstream keep-alive connection is kept open90
HEADROOM_HTTP_PROXYHTTP proxy URL for upstream provider requests only; HTTPS provider APIs use CONNECT--
HEADROOM_BUDGETDaily budget limit in USD--
HEADROOM_TELEMETRYSet to on for local-only usage stats (powers your own /stats and dashboard; nothing is sent externally)off
HEADROOM_STATELESSSet to true to disable filesystem writesfalse
HEADROOM_MODEL_LIMITSCustom model config (JSON string or file path)--
HEADROOM_BASE_URLBase URL of the Headroom proxy (TypeScript SDK)http://localhost:8787
HEADROOM_API_KEYOptional API key for authenticated Headroom endpoints (TypeScript SDK)--
HEADROOM_CONFIG_DIRCanonical config (read-mostly) root. Derives models.json and per-plugin config paths when set.~/.headroom/config
HEADROOM_WORKSPACE_DIRCanonical workspace (read-write state) root. Derives savings, memory DB, logs, TOIN, subscription state, and more when set.~/.headroom
HEADROOM_SAVINGS_PATHOverride persistent savings file location. Always wins when set.derived from ${HEADROOM_WORKSPACE_DIR}
HEADROOM_TOIN_PATHOverride TOIN telemetry file location. Always wins when set.derived from ${HEADROOM_WORKSPACE_DIR}
HEADROOM_SUBSCRIPTION_STATE_PATHOverride subscription tracker state file. Always wins when set.derived from ${HEADROOM_WORKSPACE_DIR}
HEADROOM_PERIODIC_TOIN_STATSControls periodic TOIN stats logging in long-lived proxy workers. Set to 0, false, off, or no to disable the 5-minute stats loop without disabling TOIN learning or request-time feedback.true
HEADROOM_MEMORY_INJECTION_MODEMemory-context routing mode: live_zone_tail (default) or disabled. The legacy system_prompt mode was retired by PR-A2; supplying it raises.live_zone_tail
HEADROOM_PROXY_PYTHON_FORWARDER_MODEPython forwarder serialization mode. byte_faithful (default) forwards original request bytes verbatim when no transform mutated the body and re-serializes canonically only when needed — keeps Anthropic prompt-cache hit-rate intact. legacy_json_kwarg is an explicit operator opt-in for emergency rollback to the historical httpx ... json=body behavior. NOT a fallback — only flip on explicit operator decision.byte_faithful
HEADROOM_STRIP_INTERNAL_HEADERSPython proxy: whether to strip internal x-headroom-* request headers (e.g. x-headroom-bypass, x-headroom-mode, x-headroom-user-id, x-headroom-stack, x-headroom-base-url) before every upstream forwarder call (PR-A5, fixes P5-49). enabled (default) stops fingerprinting / leakage. disabled is an explicit operator opt-in for diagnostic shadow tracing — NOT a fallback. Inbound reads of these headers (bypass gating, memory user-id resolution) are unaffected because they read request.headers directly.enabled
HEADROOM_PROXY_STRIP_INTERNAL_HEADERSRust proxy: same policy as HEADROOM_STRIP_INTERNAL_HEADERS but for the Rust transparent proxy. Stripping happens inside build_forward_request_headers so both HTTP and WebSocket upstream calls are gated by one flag. enabled default; disabled operator opt-in for diagnostic shadow tracing. Response-side X-Headroom-* injection (e.g. x-headroom-tokens-saved) is unrelated and stays.enabled
HEADROOM_EMBEDDER_RUNTIMESet 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
ORT_DYLIB_PATHPath to the ONNX Runtime shared library loaded by the Rust core (magika detection, fastembed embeddings), which loads ORT dynamically on every platform. Auto-pinned at import headroom to the library inside the onnxruntime pip package (onnxruntime.dll / libonnxruntime.so* / libonnxruntime*.dylib); set it yourself to override. Without a pin, ML detection degrades to the non-ONNX tiers — and on Windows the bare DLL search can resolve to the Windows ML System32 build (1.17.x on Win11 24H2+), which deadlocks ONNX session init — see Troubleshooting.auto-pinned
HEADROOM_MAGIKA_INIT_TIMEOUT_SECSUpper bound (integer seconds, > 0) on magika's one-time ONNX session init in the Rust detection chain. On timeout the init error is cached and detection uses the non-ML fallback tiers for the rest of the process; a warning is logged. Safety net for environments where the dylib pin above does not apply.5
HEADROOM_REQUEST_TIMEOUTRequest timeout in seconds300
HEADROOM_BETA_HEADER_STICKYControls 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_SESSIONSLRU capacity of the in-memory session beta tracker. Once full, the oldest session entry is evicted.1000
HEADROOM_PROXY_BETA_HEADER_STICKYRust proxy: same per-conversation beta-token union as HEADROOM_BETA_HEADER_STICKY, applied to anthropic-beta / openai-beta on the intercepted /v1/messages, /v1/chat/completions, and /v1/responses routes. Requires the compression interceptor (HEADROOM_PROXY_COMPRESSION=1) — with it off the Rust proxy is a strict byte-pipe and this flag has no effect (startup warns). Unlike the Python tracker (keyed on model + system prompt), sessions are keyed per conversation, shared with the cache-drift detector — parallel conversations never inherit each other's tokens. enabled default; disabled forwards the client value verbatim and keeps no state. Tracker capacity is fixed at 1000 sessions.enabled
HEADROOM_MODEL_ROUTER_ENABLEDEnable cost-aware model routing. 1/true/yes/on/enabled turns it on and requires HEADROOM_MODEL_ROUTES. See Cost-aware model routing.off
HEADROOM_MODEL_ROUTESJSON array of ordered routing rules for cost-aware model routing (schema below).--
HEADROOM_THINKING_COMPACTCompact plain-text reasoning that models re-send every turn (Kimi/GLM/DeepSeek reasoning_content / inline <think>): Kompress it on warm turns, drop it on cold turns. No-op for Claude/Codex/OpenAI (encrypted reasoning). See Cold-prefix hook.off
HEADROOM_THINKING_COMPACT_KEEP_LASTMost-recent assistant turns whose reasoning is left intact (the active reasoning the model still uses). Only applies with HEADROOM_THINKING_COMPACT.1
HEADROOM_COLD_RECOMPACTOn a confirmed-cold turn (idle past the real cache TTL), recompact the whole prefix — cross-turn dedupe + superseded-read drop + lossless folds — instead of forwarding a byte-identical prefix to a dead cache. Warm turns unchanged. Pair with HEADROOM_DEDUPE. See Cold-prefix hook.off
HEADROOM_DEDUPEWhole-conversation verbatim cross-turn dedup in the router (cache-safe, information-preserving via retrieval markers). Superseded-read drop + lossless folds run without it; this adds verbatim dedup.off
HEADROOM_CACHE_TTL_LEARNAppend per-turn cache-outcome observations (provider, model, idle, hit/miss) to cache_ttl_observations.jsonl for the offline headroom-cache-ttl learner. Observation-only (no request-behavior change); respects HEADROOM_STATELESS; the log is size-bounded.off
HEADROOM_KOMPRESS_ENDPOINT / HEADROOM_KOMPRESS_ENDPOINT_TOKENOffload ML compression (Kompress) to a remote endpoint instead of the local ONNX model — used by reasoning compaction and the router when set.--
HEADROOM_1M_MODELFallback model that headroom wrap claude --1m targets when neither --model nor ANTHROPIC_MODEL is set. Accepts the id with or without the [1m] suffix (added when absent); an explicit --model / ANTHROPIC_MODEL always wins. See Claude 1M context window.claude-opus-5

For provider-only proxying, prefer HEADROOM_HTTP_PROXY over process-wide variables such as HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, or NO_PROXY. HTTPX reads those global variables, but Headroom also passes them through to tool executions.

Cold-prefix hook & reasoning compaction

Two related optimizations target tokens that agent harnesses re-send every turn: model reasoning (re-billed as input on Kimi/GLM/DeepSeek and on Claude 4.6+), and the prefix itself once its prompt cache has lapsed. All flags below are off by default — the base proxy is unchanged until you opt in.

What to set for what:

GoalSetNotes
Shrink Kimi/GLM/DeepSeek reasoning re-sent each turnHEADROOM_THINKING_COMPACT=1 (+ HEADROOM_KOMPRESS_ENDPOINT for the best ratio)Warm turns Kompress the reasoning; cold turns drop it. No-op for Claude/Codex/OpenAI (their reasoning is an encrypted handle, already cheap on resend).
Recompact a dead-cache prefix instead of re-sending it wholeHEADROOM_COLD_RECOMPACT=1 (+ HEADROOM_DEDUPE=1)Fires only when the cache has genuinely lapsed. Works in both cache and token mode.
Learn each provider's real cache TTLHEADROOM_CACHE_TTL_LEARN=1, then run the headroom-cache-ttl estimator periodicallySharpens cold detection for providers that don't expose a TTL (Kimi/OpenAI).

How cold detection knows the TTL. A wrong "cold" call would recompact a warm prefix and bust the cache, so the TTL must be right:

  • Claude Code: read exactly, from the request's cache_control.ttl plus CC's own controls — ENABLE_PROMPT_CACHING_1H (1h), FORCE_PROMPT_CACHING_5M (5m), and DISABLE_PROMPT_CACHING (+ per-model DISABLE_PROMPT_CACHING_<FAMILY>), which turns caching off and makes every turn a free recompaction candidate.
  • Kimi / OpenAI / Codex: they don't expose a TTL, so detection uses a conservative default until the learner (HEADROOM_CACHE_TTL_LEARN + the headroom-cache-ttl plugin) fills in the real value from observed hits/misses.

Can these be on by default?

  • HEADROOM_THINKING_COMPACTstays opt-in. It rewrites model inputs (reasoning the model actively uses) and depends on Kompress, so its quality / latency trade should be a deliberate choice.
  • HEADROOM_COLD_RECOMPACTopt-in today; a candidate to default for Claude Code once TTL detection is field-validated. It only fires on confirmed-cold turns and recompacts losslessly, but a mis-read TTL would bust a warm cache (expensive), so it waits for confidence. For Kimi/OpenAI it should stay opt-in until the learner has data.
  • HEADROOM_CACHE_TTL_LEARNthe safest to default on: observation-only, a size-bounded local log, and it respects HEADROOM_STATELESS. Kept opt-in for now so nothing is written to disk unasked.

Cost-aware model routing

Complementary to content compression, Headroom can rewrite the upstream model per request to stretch quota and control spend, for example by sending small, tool-free requests to a cheaper model. Routing is opt-in and disabled by default, so behavior is unchanged unless you configure it.

Enable it with HEADROOM_MODEL_ROUTER_ENABLED=1 and declare ordered rules in HEADROOM_MODEL_ROUTES (a JSON array). The router evaluates rules top to bottom and the first rule whose conditions all match wins; every decision is logged with a reason so routing stays observable. Each rule object supports:

FieldTypeMeaning
to_modelstring (required)Model to route to when the rule matches.
max_input_tokensintegerMatch only when the estimated input size is at or below this.
min_input_tokensintegerMatch only when the estimated input size is at or above this.
require_no_toolsbooleanMatch only when the request declares no tools (a proxy for low-risk work).
from_modelslist of stringsRestrict the rule to these source models. Omit for any source model.
namestringLabel surfaced in the decision log.
export HEADROOM_MODEL_ROUTER_ENABLED=1
export HEADROOM_MODEL_ROUTES='[
  {"name": "small-no-tools", "max_input_tokens": 4000, "require_no_tools": true,
   "from_models": ["claude-sonnet-4-6"], "to_model": "claude-haiku-4-5"}
]'

Notes:

  • Input size is a fast, tokenizer-free estimate over the messages, tools, and top-level system prompt, meant for tier selection rather than exact accounting.
  • A malformed rule fails open: it is skipped (never silently widened), and the rest of the rules still apply.
  • Routing is skipped for byte-faithful passthrough requests (x-headroom-bypass: true or x-headroom-mode: passthrough), so those are never model-rewritten.
  • Routing currently applies on the Anthropic /v1/messages path.

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 + HEADROOM_WORKSPACE_DIR) with additive precedence rules: explicit argument > per-resource env var > derived from canonical root > default. Every legacy env var continues to work unchanged.

See the Filesystem Contract page 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:

{
  "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
    }
  }
}

Save as ${HEADROOM_CONFIG_DIR}/models.json (defaults to ~/.headroom/config/models.json), or set HEADROOM_MODEL_LIMITS to a JSON string or file path. Installs that still have ~/.headroom/models.json (the legacy location) continue to work.

Settings are resolved in this order (later overrides earlier):

  1. Built-in defaults
  2. ${HEADROOM_CONFIG_DIR}/models.json (new canonical location); falls back to ~/.headroom/models.json (legacy) when the canonical file is absent
  3. HEADROOM_MODEL_LIMITS environment variable
  4. SDK constructor arguments

Pattern-Based Inference

Unknown models are automatically inferred from naming patterns:

PatternInferred 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

Provider-Specific Settings

from headroom import OpenAIProvider

provider = OpenAIProvider(
    enable_prefix_caching=True,
)
from headroom import AnthropicProvider

provider = AnthropicProvider(
    enable_cache_control=True,
)
from headroom.providers import GoogleProvider

provider = GoogleProvider(
    enable_context_caching=True,
)

Tool Profiles

Skip or customize compression for specific tools:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    headroom_tool_profiles={
        "important_tool": {"skip_compression": True},
        "search_tool": {"max_items_after_crush": 25},
    },
)

Configuration Precedence

Settings are applied in this order (later overrides earlier):

  1. Default values
  2. Environment variables
  3. SDK constructor arguments
  4. Per-request overrides

Validation

Validate your configuration at startup:

result = client.validate_setup()

if not result["valid"]:
    print("Configuration issues:")
    for issue in result["issues"]:
        print(f"  - {issue}")

On this page