mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
fix(proxy): plumb CompressionPolicy through proxy + dispatchers (F2.1 c2/6)
Phase F2.1, commit 2 of 6. No behaviour change — wiring only. Three things land: 1. **Proxy entry derives the policy alongside auth_mode** (proxy.rs). Both go into `req.extensions_mut()` so downstream stages can read either without re-classifying. New structured log event `policy_selected` fires once per request with auth_mode + live_zone_only + cache_aligner_enabled — gives F2.2 the bake-time data it'll need to tune. Pure addition; no existing log was removed. 2. **OpenAI live-zone dispatchers stop hard-coding `AuthMode::Payg`.** `compress_openai_chat_request` and `compress_openai_responses_request` already received `auth_mode` from the proxy caller (F1's plumbing), but the dispatcher invocation hard-coded `AuthMode::Payg` and ignored the parameter — c.f. the `_auth_mode` underscore-prefixed identifier in `compress_openai_chat_live_zone` upstream. Now the classified mode is forwarded via `auth_mode.into()` (uses the `From<>` impl added in c1/6 to bridge the two `AuthMode` enums). 3. **Anthropic live-zone dispatcher gets the same fix.** `compress_anthropic_request` was the closest to right — it threaded auth_mode all the way to `compress_anthropic_live_zone` already — but the inner call still had `AuthMode::Payg` hard-coded. Now it uses `auth_mode.into()` for symmetry and so the F2.1 dispatcher- level gate (added in c4/6) reads the real mode. The dispatcher is unchanged in F2.1: it still runs the same compression for every mode. The point of c2/6 is that *if* a future commit (c4/6 in this PR, or anything post-F2.1) gates on the mode-aware policy, the wiring is already in place. PAYG behaviour is byte-for-byte identical because every mode currently dispatches identically. Two tiny cleanups: drop the `AuthMode` import from the three live-zone dispatcher files now that the value flows in via `.into()` (the `RequestAuthMode` alias remains, since it's still in the function signatures). Behaviour-change risk in c2/6: zero. Verified by running the full `cargo test -p headroom-proxy` suite; existing tests pass without modification because they're already shape-compatible (tests pass `AuthMode::Payg` directly into the inner dispatcher; this commit only changes how the *outer* compress_* fns invoke that dispatcher).
This commit is contained in:
parent
8376630062
commit
948c8f2069
4 changed files with 43 additions and 6 deletions
|
|
@ -42,7 +42,7 @@ use bytes::Bytes;
|
|||
use headroom_core::auth_mode::AuthMode as RequestAuthMode;
|
||||
use headroom_core::transforms::live_zone::DEFAULT_MODEL;
|
||||
use headroom_core::transforms::{
|
||||
compress_anthropic_live_zone, AuthMode, BlockAction, ExclusionReason, LiveZoneError,
|
||||
compress_anthropic_live_zone, BlockAction, ExclusionReason, LiveZoneError,
|
||||
LiveZoneOutcome,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
|
@ -326,7 +326,14 @@ pub fn compress_anthropic_request(
|
|||
// `NoChange` otherwise (live zone empty, every compressor
|
||||
// declined, or every compressor produced output whose token
|
||||
// count was not strictly less than the input's).
|
||||
match compress_anthropic_live_zone(&dispatch_body, frozen_count, AuthMode::Payg, model) {
|
||||
// F2.1 c2/6: forward F1's classified auth_mode into the dispatcher
|
||||
// instead of the hard-coded `Payg`. The dispatcher itself doesn't
|
||||
// change behaviour by mode in F2.1 (live-zone compression runs for
|
||||
// every mode — closing #327/#388 means subscription users keep
|
||||
// getting compression, not losing it). The plumbing here lets
|
||||
// F2.2 vary per-block thresholds by mode without touching this
|
||||
// call site again.
|
||||
match compress_anthropic_live_zone(&dispatch_body, frozen_count, auth_mode.into(), model) {
|
||||
Ok(LiveZoneOutcome::NoChange { manifest }) => {
|
||||
let block_count = manifest.block_outcomes.len();
|
||||
let blocks_excluded = manifest
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use bytes::Bytes;
|
|||
use headroom_core::auth_mode::AuthMode as RequestAuthMode;
|
||||
use headroom_core::transforms::live_zone::DEFAULT_MODEL;
|
||||
use headroom_core::transforms::{
|
||||
compress_openai_chat_live_zone, AuthMode, BlockAction, LiveZoneError, LiveZoneOutcome,
|
||||
compress_openai_chat_live_zone, BlockAction, LiveZoneError, LiveZoneOutcome,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
@ -132,7 +132,10 @@ pub fn compress_openai_chat_request(
|
|||
let (dispatch_body, normalization_applied) =
|
||||
normalize_tool_definitions_openai_chat(body, &parsed, auth_mode, request_id);
|
||||
|
||||
match compress_openai_chat_live_zone(&dispatch_body, AuthMode::Payg, model) {
|
||||
// F2.1 c2/6: forward F1's classified auth_mode into the dispatcher
|
||||
// instead of the hard-coded `Payg`. See live_zone_anthropic.rs for
|
||||
// the rationale — same wiring on the OpenAI chat path.
|
||||
match compress_openai_chat_live_zone(&dispatch_body, auth_mode.into(), model) {
|
||||
Ok(LiveZoneOutcome::NoChange { manifest }) => {
|
||||
tracing::info!(
|
||||
event = "compression_decision",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ use bytes::Bytes;
|
|||
use headroom_core::auth_mode::AuthMode as RequestAuthMode;
|
||||
use headroom_core::transforms::live_zone::DEFAULT_MODEL;
|
||||
use headroom_core::transforms::{
|
||||
compress_openai_responses_live_zone, AuthMode, BlockAction, LiveZoneError, LiveZoneOutcome,
|
||||
compress_openai_responses_live_zone, BlockAction, LiveZoneError, LiveZoneOutcome,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
@ -141,7 +141,10 @@ pub fn compress_openai_responses_request(
|
|||
let (dispatch_body, normalization_applied) =
|
||||
normalize_tool_definitions_responses(body, &parsed, auth_mode, request_id);
|
||||
|
||||
match compress_openai_responses_live_zone(&dispatch_body, AuthMode::Payg, model) {
|
||||
// F2.1 c2/6: forward F1's classified auth_mode into the dispatcher
|
||||
// instead of the hard-coded `Payg`. See live_zone_anthropic.rs for
|
||||
// the rationale — same wiring on the OpenAI Responses path.
|
||||
match compress_openai_responses_live_zone(&dispatch_body, auth_mode.into(), model) {
|
||||
Ok(LiveZoneOutcome::NoChange { manifest }) => {
|
||||
tracing::info!(
|
||||
event = "compression_decision",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ use crate::websocket::ws_handler;
|
|||
// path for downstream handlers that read the value back out of
|
||||
// `req.extensions()` (Phase F PR-F2/F3/F4).
|
||||
use headroom_core::auth_mode::{classify as classify_auth_mode, AuthMode};
|
||||
use headroom_core::compression_policy::CompressionPolicy;
|
||||
|
||||
/// Shared state passed to every handler.
|
||||
///
|
||||
|
|
@ -390,6 +391,17 @@ pub(crate) async fn forward_http(
|
|||
let auth_mode = classify_auth_mode(req.headers());
|
||||
req.extensions_mut().insert(auth_mode);
|
||||
|
||||
// Phase F PR-F2.1, c2/6: derive the per-mode CompressionPolicy at
|
||||
// request entry and stash alongside auth_mode. Storing the policy
|
||||
// (not just auth_mode) in extensions lets downstream stages read
|
||||
// the gate they need directly — no per-stage `for_mode` call.
|
||||
// F2.1 c2/6 only writes the policy; the dispatcher gates on
|
||||
// `policy.live_zone_compression_enabled()` (currently always true,
|
||||
// so no behaviour change). c4/6 flips the gate to read
|
||||
// `live_zone_only` and `cache_aligner_enabled` for real.
|
||||
let policy = CompressionPolicy::for_mode(auth_mode);
|
||||
req.extensions_mut().insert(policy);
|
||||
|
||||
// Per PR-A1: structured entry log. The `auth_mode` field is now
|
||||
// populated with the real classification result (Phase F PR-F1
|
||||
// replaces the prior `auth_mode_placeholder = "unknown"`). Body
|
||||
|
|
@ -406,6 +418,18 @@ pub(crate) async fn forward_http(
|
|||
"request received"
|
||||
);
|
||||
|
||||
// F2.1 c2/6: emit the policy that the request will run under so
|
||||
// F2.2 has bake-time data to tune from. One log per request,
|
||||
// structured fields so it joins on auth_mode + request_id.
|
||||
tracing::debug!(
|
||||
event = "policy_selected",
|
||||
request_id = %request_id,
|
||||
auth_mode = auth_mode.as_str(),
|
||||
live_zone_only = policy.live_zone_only,
|
||||
cache_aligner_enabled = policy.cache_aligner_enabled,
|
||||
"compression policy resolved"
|
||||
);
|
||||
|
||||
let upstream_url = build_upstream_url(&state.config.upstream, &uri)?;
|
||||
|
||||
// Forwarded-Host: prefer client's Host. Forwarded-Proto: assume http for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue