mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
PR-A4 of the Realignment Phase A lockdown
(REALIGNMENT/03-phase-A-lockdown.md). Eliminates P0-3 (Rust proxy
ignores customer cache_control markers) and P0-5 (numeric precision
lost via serde_json::Value round-trip) at the library level; Phase B
PR-B2 wires the helper into the live-zone block dispatcher.
Cargo.toml — add `arbitrary_precision` and `raw_value` to
`serde_json` workspace features. `arbitrary_precision` keeps `1.0`
from collapsing to `1` and preserves >2^53 integers; `raw_value`
exposes `&RawValue` so PR-B2 can forward unmodified `messages[*]`
entries as exact byte copies.
crates/headroom-core/src/cache_control.rs (new) — `compute_frozen_count`
walks `messages[i].content[*].cache_control` via serde_json
accessors only (no regex) and returns the smallest N such that
`messages[i]` is frozen for every i < N. Markers in `system` or
`tools[*]` log at debug! but never bump the floor (those fields are
unconditionally cache-hot per invariant I2). TTL ordering violations
(5m before 1h, guide §2.19) emit `tracing::warn!` but the function
computes the correct count regardless — the customer's request, not
ours to reject.
crates/headroom-core/src/lib.rs — re-export `compute_frozen_count` at
crate root so the proxy crate has a stable import path.
crates/headroom-proxy/src/compression/anthropic.rs — add
`resolve_frozen_count` thin wrapper that consults the
`cache_control_auto_frozen` config flag. When `disabled`, returns 0
regardless of body content (operator opt-out for benchmarking).
crates/headroom-proxy/src/config.rs — add `CacheControlAutoFrozen`
enum and the matching CLI flag `--cache-control-auto-frozen` /
env var `HEADROOM_PROXY_CACHE_CONTROL_AUTO_FROZEN`. Default is
`enabled`. Documented in the doc comments.
Tests
- crates/headroom-core/src/cache_control.rs (inline): 11 unit tests
covering marker detection, system/tools negative cases, ordering
state machine, defensive (missing fields, non-array messages,
non-object content blocks).
- crates/headroom-core/tests/cache_control.rs: 11 unit + 3 property
tests (monotonic non-decrease as markers are added; system/tools
markers don't change count; empty messages → 0).
- crates/headroom-proxy/tests/integration_cache_control.rs: 8 tests
exercising the proxy wrapper (configurability gate; tracing
capture for the 5m-before-1h warn path).
Acceptance gates: `cargo build --workspace`, `cargo test --workspace`
(33 new tests green), `cargo clippy --workspace -- -D warnings`,
`cargo fmt --all --check` all clean. No new `regex::` imports;
`git grep -n 'regex::' crates/{headroom-core/src/cache_control.rs,
headroom-core/tests/cache_control.rs, headroom-proxy/tests/
integration_cache_control.rs}` empty.
Honors the realignment build constraints: configurable (CLI + env),
no hardcodes (TTL strings live as const), no regex (serde_json
accessor walk), no fallbacks (one impl), structured logging
(debug!/warn! with field/index/ttl/rule context), tests
comprehensive (unit + property + integration + tracing capture).
58 lines
2.5 KiB
TOML
58 lines
2.5 KiB
TOML
[workspace]
|
|
resolver = "2"
|
|
members = [
|
|
"crates/headroom-core",
|
|
"crates/headroom-proxy",
|
|
"crates/headroom-py",
|
|
"crates/headroom-parity",
|
|
]
|
|
# headroom-py is a Python extension module — it must be built via maturin, not
|
|
# plain cargo (the "extension-module" feature tells pyo3 not to link libpython,
|
|
# which is required for `import` to work). `cargo build --workspace` without
|
|
# explicit members skips it; `cargo test --workspace` still runs its tests
|
|
# because pyo3 can dynamically link here for the cdylib used by tests.
|
|
default-members = [
|
|
"crates/headroom-core",
|
|
"crates/headroom-proxy",
|
|
"crates/headroom-parity",
|
|
]
|
|
|
|
[workspace.package]
|
|
edition = "2021"
|
|
rust-version = "1.80"
|
|
license = "Apache-2.0"
|
|
repository = "https://github.com/chopratejas/headroom"
|
|
authors = ["Headroom Maintainers"]
|
|
|
|
[workspace.dependencies]
|
|
serde = { version = "1", features = ["derive"] }
|
|
# `preserve_order` makes `serde_json::Value::Object` use IndexMap so JSON
|
|
# parse order is preserved through Value→string→Value round-trips. The
|
|
# smart_crusher port relies on this to match Python's `str(dict)` output,
|
|
# which preserves insertion order; otherwise BTreeMap's sorted-key default
|
|
# would diverge from Python on every multi-key object.
|
|
#
|
|
# `arbitrary_precision` keeps the literal numeric token from the source
|
|
# JSON intact: `Value::Number` becomes a wrapper around the original
|
|
# digit string, so `1.0` does NOT collapse to `1`, and `12345678901234567`
|
|
# does NOT lose precision through f64. Required by Realignment invariant
|
|
# I1 (byte-faithful passthrough on unmutated bytes; see REALIGNMENT/02-
|
|
# architecture.md §2.2) and PR-A4 (see REALIGNMENT/03-phase-A-lockdown.md).
|
|
#
|
|
# `raw_value` exposes `serde_json::value::RawValue`, the unparsed JSON
|
|
# fragment type. Phase B PR-B2 uses this to forward unmodified
|
|
# `messages[*]` entries as exact byte copies — the parser captures the
|
|
# original byte slice, so byte-for-byte round-trips work even with
|
|
# whitespace, key order, or escape preferences the producer chose.
|
|
# Enabled here in Phase A so PR-B2 can land as a pure consumer change.
|
|
serde_json = { version = "1", features = ["preserve_order", "arbitrary_precision", "raw_value"] }
|
|
bytes = "1"
|
|
thiserror = "1"
|
|
tracing = "0.1"
|
|
anyhow = "1"
|
|
clap = { version = "4", features = ["derive"] }
|
|
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }
|
|
axum = "0.7"
|
|
tower = "0.5"
|
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
|
pyo3 = "0.22"
|