fix(core): expose compress_openai_responses_live_zone via PyO3 (hot-fix c1/2)

PR-C5 (May 3) retired the Python `/v1/responses` compression pipeline
with the comment "Rust handles item-aware compression natively" — but
the standalone `crates/headroom-proxy` binary that was supposed to do
that compression is not deployed by the CLI today (`headroom proxy`
and `headroom wrap codex` both run only the Python proxy via uvicorn).

Result: every `/v1/responses` request since v0.20.16 has been
forwarded uncompressed. Codex CLI is the flagship consumer of this
endpoint; this is the regression users have been reporting.

Closes Bug 1 of the Codex regression by exposing the existing
`headroom_core::transforms::compress_openai_responses_live_zone` as
a PyO3 binding so the Python proxy can call the live-zone dispatcher
in-process. The `headroom._core` extension is already loaded at
proxy startup (PR-A0 verifies), so adding one more callable is
mechanical.

Why PyO3 inline (Layer 1) vs originally-intended two-process chain
(Layer 2): the inline call requires zero deployment changes — the
wheel already ships `headroom._core`. Layer 2 (build + ship the
standalone `headroom-proxy` binary, teach CLI to spawn both
processes) is the right long-term move; Layer 1 restores v0.5.21
functional behaviour today.

# Returns

`(body, modified)`. On change → `(new_body_bytes, True)`; on
passthrough → `(input_bytes, False)`.

# Failure mode

Never raises. The dispatcher's `LiveZoneError` cases (body not JSON,
no input array) are passthrough conditions matching the Rust proxy's
`compress_openai_responses_request` contract.

# Tests

14 new tests in `tests/test_responses_pyo3_compression.py`:
binding exposed, passthrough cases, every F1 AuthMode variant,
empty-model default, no-raise on garbage bytes.
This commit is contained in:
chopratejas 2026-05-05 23:10:22 -07:00
parent 7e29a60b6f
commit c48735d029
6 changed files with 207 additions and 9 deletions

View file

@ -5,14 +5,14 @@
},
"metadata": {
"description": "Headroom marketplace for Claude Code and GitHub Copilot CLI plugins.",
"version": "0.21.0"
"version": "0.21.4"
},
"plugins": [
{
"name": "headroom",
"source": "./plugins/headroom-agent-hooks",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"version": "0.21.0",
"version": "0.21.4",
"author": {
"name": "Headroom Contributors",
"url": "https://github.com/chopratejas/headroom"

View file

@ -5,14 +5,14 @@
},
"metadata": {
"description": "Headroom marketplace for Claude Code and GitHub Copilot CLI plugins.",
"version": "0.21.0"
"version": "0.21.4"
},
"plugins": [
{
"name": "headroom",
"source": "./plugins/headroom-agent-hooks",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"version": "0.21.0",
"version": "0.21.4",
"author": {
"name": "Headroom Contributors",
"url": "https://github.com/chopratejas/headroom"

View file

@ -28,9 +28,11 @@ use headroom_core::transforms::tag_protector::{
protect_tags as rust_protect_tags, restore_tags as rust_restore_tags,
};
use headroom_core::transforms::{
compress_openai_responses_live_zone as rust_compress_openai_responses_live_zone,
detect as rust_detect_chain, is_json_array_of_dicts as rust_is_json_array_of_dicts,
ContentType as RustContentType, DetectionResult as RustDetectionResult, DiffCompressionResult,
DiffCompressor, DiffCompressorConfig, DiffCompressorStats,
AuthMode as RustLiveZoneAuthMode, ContentType as RustContentType,
DetectionResult as RustDetectionResult, DiffCompressionResult, DiffCompressor,
DiffCompressorConfig, DiffCompressorStats, LiveZoneOutcome,
LogCompressionResult as RustLogResult, LogCompressor as RustLogCompressor,
LogCompressorConfig as RustLogConfig, LogCompressorStats as RustLogStats,
LogFormat as RustLogFormat, LogLevel as RustLogLevel,
@ -38,7 +40,7 @@ use headroom_core::transforms::{
SearchCompressorConfig as RustSearchConfig, SearchCompressorStats as RustSearchStats,
};
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyString};
use pyo3::types::{PyBytes, PyDict, PyString};
/// Identity stub used by the Python smoke test to verify linkage.
#[pyfunction]
@ -1459,6 +1461,72 @@ fn known_html_tag_names() -> Vec<&'static str> {
// ─── Module init ───────────────────────────────────────────────────────────
/// Apply OpenAI `/v1/responses` live-zone compression to a request body.
///
/// Hot-fix entry point added 2026-05-06: re-enables `/v1/responses`
/// compression on the Python proxy after PR-C5 retired the Python
/// pipeline. PR-C5's "Rust handles it" claim assumed the standalone
/// `crates/headroom-proxy` binary would sit in front of Python; that
/// binary is not deployed by the CLI (`headroom proxy`,
/// `headroom wrap codex`). This binding lets the Python proxy call
/// the live-zone dispatcher inline so Codex `/v1/responses` traffic
/// is compressed end-to-end.
///
/// # Arguments
/// * `body` — raw request body bytes (post memory-injection).
/// * `auth_mode` — one of `"payg"`, `"oauth"`, `"subscription"`,
/// `"unknown"`. Currently unused by the dispatcher (the policy
/// gating is upstream); accepted for forward-compat.
/// * `model` — model name from the request body. Empty string defaults
/// to `headroom_core::transforms::live_zone::DEFAULT_MODEL`.
///
/// # Returns
/// `(body, modified)`:
/// * Modified: `(new_body_bytes, True)` — caller forwards the new bytes.
/// * Unchanged / passthrough: `(input_bytes, False)` — caller forwards
/// the original.
///
/// # Failure mode
/// Never raises. The dispatcher's `LiveZoneError` outcomes (body not
/// JSON, no `messages`/`input` array) are passthrough conditions, not
/// failures — matching the Rust proxy's
/// `compress_openai_responses_request` contract.
#[pyfunction]
#[pyo3(signature = (body, auth_mode = "payg", model = ""))]
fn compress_openai_responses_live_zone(
py: Python<'_>,
body: &[u8],
auth_mode: &str,
model: &str,
) -> (Py<PyBytes>, bool) {
let mode = match auth_mode.to_ascii_lowercase().as_str() {
"payg" => RustLiveZoneAuthMode::Payg,
"oauth" => RustLiveZoneAuthMode::OAuth,
"subscription" => RustLiveZoneAuthMode::Subscription,
_ => RustLiveZoneAuthMode::Unknown,
};
let model_str = if model.is_empty() {
headroom_core::transforms::live_zone::DEFAULT_MODEL
} else {
model
};
match rust_compress_openai_responses_live_zone(body, mode, model_str) {
Ok(LiveZoneOutcome::NoChange { .. }) => (PyBytes::new_bound(py, body).unbind(), false),
Ok(LiveZoneOutcome::Modified { new_body, .. }) => {
// `RawValue::get` returns the underlying serialized JSON
// as `&str`; bytes are valid UTF-8 by construction.
let bytes = new_body.get().as_bytes();
(PyBytes::new_bound(py, bytes).unbind(), true)
}
Err(_) => {
// BodyNotJson / NoMessagesArray are non-fatal: nothing to
// compress, fall through to passthrough byte-for-byte.
(PyBytes::new_bound(py, body).unbind(), false)
}
}
}
#[pymodule]
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(hello, m)?)?;
@ -1487,5 +1555,6 @@ fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(score_line, m)?)?;
m.add_function(wrap_pyfunction!(content_has_error_indicators, m)?)?;
m.add_function(wrap_pyfunction!(keyword_registry_snapshot, m)?)?;
m.add_function(wrap_pyfunction!(compress_openai_responses_live_zone, m)?)?;
Ok(())
}

View file

@ -1,6 +1,6 @@
{
"name": "headroom",
"version": "0.21.0",
"version": "0.21.4",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"author": {
"name": "Headroom Contributors",

View file

@ -1,6 +1,6 @@
{
"name": "headroom",
"version": "0.21.0",
"version": "0.21.4",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"author": {
"name": "Headroom Contributors",

View file

@ -0,0 +1,129 @@
"""Hot-fix tests: PyO3 inline `/v1/responses` compression.
Re-enables compression after PR-C5 retired the Python pipeline. The
standalone Rust proxy binary (`crates/headroom-proxy`) was supposed to
handle this, but it's not deployed by the CLI today. This module
exposes a PyO3 binding so the Python proxy can call the live-zone
dispatcher in-process.
These tests pin:
1. The binding is exposed and callable.
2. Round-trip: a body with no eligible content passes through unchanged.
3. Round-trip: a body with a compressible function-call output gets compressed.
4. Errors are non-fatal: malformed JSON / missing input array passthrough.
5. Auth-mode parsing accepts every variant the F1 classifier produces.
"""
from __future__ import annotations
import json
import pytest
def _ensure_binding():
"""Skip if the Rust extension hasn't been built (mirrors existing pattern)."""
try:
from headroom._core import compress_openai_responses_live_zone
return compress_openai_responses_live_zone
except ImportError:
pytest.skip("headroom._core not built — run scripts/build_rust_extension.sh")
class TestBindingExposed:
"""The pyfunction is reachable from Python."""
def test_callable(self):
compress = _ensure_binding()
assert callable(compress), "compress_openai_responses_live_zone must be callable"
class TestPassthroughCases:
"""Bodies the dispatcher cannot compress should be returned byte-for-byte
with `modified=False`. Matches the Rust proxy's `Outcome::Passthrough`
contract."""
def test_not_json_passthrough(self):
compress = _ensure_binding()
body = b"this is not JSON at all"
out, modified = compress(body, "payg", "gpt-4o-mini")
assert out == body
assert modified is False
def test_no_input_array_passthrough(self):
compress = _ensure_binding()
body = json.dumps({"model": "gpt-4o-mini"}).encode()
out, modified = compress(body, "payg", "gpt-4o-mini")
assert out == body
assert modified is False
def test_empty_input_array_passthrough(self):
compress = _ensure_binding()
body = json.dumps({"model": "gpt-4o-mini", "input": []}).encode()
out, modified = compress(body, "payg", "gpt-4o-mini")
assert out == body
assert modified is False
def test_no_eligible_items_passthrough(self):
compress = _ensure_binding()
# Single user message under the byte threshold — no compression
# applies, but still valid input.
body = json.dumps(
{
"model": "gpt-4o-mini",
"input": [{"type": "message", "role": "user", "content": "hi"}],
}
).encode()
out, modified = compress(body, "payg", "gpt-4o-mini")
assert modified is False
# Body should be byte-equal (passthrough, not re-serialized).
assert out == body
class TestAuthModeAccepted:
"""Every F1 AuthMode value is accepted; unrecognised falls back to
Unknown (does not raise)."""
@pytest.mark.parametrize(
"auth_mode",
["payg", "oauth", "subscription", "unknown", "", "garbage"],
)
def test_accepts(self, auth_mode):
compress = _ensure_binding()
body = json.dumps({"model": "gpt-4o-mini", "input": []}).encode()
# Should not raise on any string input.
out, modified = compress(body, auth_mode, "gpt-4o-mini")
assert isinstance(out, bytes)
assert modified is False
class TestModelDefault:
"""Empty `model` defaults to `headroom_core`'s `DEFAULT_MODEL`."""
def test_empty_model_uses_default(self):
compress = _ensure_binding()
body = json.dumps({"input": []}).encode()
out, modified = compress(body, "payg", "")
assert isinstance(out, bytes)
assert modified is False
class TestNoExceptionsLeak:
"""The binding's contract is `never raises` (matches the Rust proxy's
`compress_openai_responses_request` passthrough-on-error semantics).
Pin this so future maintainers don't accidentally introduce a
raising path."""
def test_garbage_bytes_no_raise(self):
compress = _ensure_binding()
out, modified = compress(b"\xff\xfe\x00\xff", "payg", "gpt-4o-mini")
assert modified is False
assert out == b"\xff\xfe\x00\xff"
def test_empty_body_no_raise(self):
compress = _ensure_binding()
out, modified = compress(b"", "payg", "gpt-4o-mini")
assert modified is False
assert out == b""