headroom/tests/conftest.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

274 lines
8.2 KiB
Python
Raw Normal View History

"""Shared pytest fixtures for Headroom tests."""
# CRITICAL: Must be set before ANY imports that could trigger sentence_transformers
# The Rust tokenizers use parallelism that deadlocks with pytest-asyncio
import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"
import json
import tempfile
from datetime import datetime
from pathlib import Path
from unittest.mock import Mock
import pytest
fix(ccr): skip Anthropic marker emission when tool injection is deferred (#1273) ## Description Anthropic request-side CCR can still compress a turn into retrieval markers after the frozen-prefix cache guard suppresses `headroom_retrieve` registration. That leaves the model with marker-only context it cannot redeem, so the proxy silently drops recoverable data on exactly the turns where cache preservation deferred tool injection. This change couples the Anthropic request-side CCR path to tool availability so a turn never emits retrieve-only markers without the retrieval tool, even when token mode or cache-mode prefix replay could otherwise reuse already-compressed marker text. Closes #1006 After a collaborator merged current `main` into this branch, CI also picked up unrelated offline-memory failures from the merged base. Those follow-up changes are test-only: they keep the offline Hugging Face cache lanes skipping cleanly instead of failing in memory tests that are outside the CCR runtime path. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Couple Anthropic request-side CCR compression to the same frozen-prefix guard that already defers `headroom_retrieve` registration. - Keep the existing cache-preservation behavior: frozen-prefix turns stop emitting CCR retrieval markers instead of forcing tool injection into the cached prefix. - Make the skip decision use the effective frozen prefix after token-mode reclamping, so turns that genuinely reclamp to zero still keep normal reversible CCR behavior. - Bypass cached marker reuse in both token mode and cache-mode prefix replay when tool injection is deferred. - Add focused regressions for the Anthropic request-path seams under this bug: - frozen-prefix turns do not emit marker-only payloads - unfrozen turns still keep normal reversible CCR behavior - token-mode reclamp back to zero still compresses normally - existing `headroom_retrieve` tools keep reversible CCR on frozen turns - cache-mode delta reuse and exact-prefix replay both forward original content when retrieval is unavailable - Add a `CHANGELOG.md` entry because the proxy's user-visible Anthropic CCR behavior changes. - Add a shared test skip helper for offline Hugging Face cache misses and apply it to the merged `main` memory tests that were failing only in the offline CI shards after the branch picked up current `main`. ## Testing - [x] Unit tests pass (`uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py`) - [x] Linting passes (`uv run ruff check . && uv run ruff format . --check`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py 14 passed, 1 warning in 34.19s uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q 4 passed, 5 skipped, 11 warnings in 7.65s uv run ruff check . All checks passed! uv run ruff format . --check 968 files already formatted ``` ## Real Behavior Proof - Environment: local FastAPI `TestClient` for the Anthropic request path, plus Windows Python 3.12 offline-memory repros with `TRANSFORMERS_OFFLINE=1` - Exact command / steps: Run the focused CCR regression command and the offline-memory repro subset below on the merged branch state. - `uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py` - `uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q` - Scenario coverage from the CCR pytest command: - frozen prefix with deferred tool injection and cached marker text available in token mode - unfrozen turn with normal CCR marker emission - token mode where the tracked frozen prefix reclamps back to zero - frozen prefix where the client already supplied `headroom_retrieve` - cache-mode append-only delta reuse with a previously forwarded compressed prefix - cache-mode exact-prefix replay where the previous forwarded prefix already contained a marker - Scenario coverage from the offline-memory repro command: - direct local embedder startup on CPU with no cached HF model - hierarchical memory embedder startup with offline model cache missing - bridge import through `LocalBackend` - `MemoryHandler` public init warmup path - `LocalBackend` save path under the offline lane - Observed result: The CCR regression keeps marker-free forwarding on frozen turns without tool availability, and the merged-`main` offline-memory lanes now skip cleanly instead of failing unrelated CI shards. - frozen-prefix Anthropic turns without tool availability forward the original long transcript across both token-mode and cache-mode reuse paths, while unfrozen turns, reclamped token-mode turns, and frozen turns that already advertise `headroom_retrieve` keep the reversible CCR marker path - the merged-`main` offline-memory regressions now skip cleanly when the Hugging Face cache is unavailable instead of failing unrelated CI shards - Not tested: live Zed session cache-hit behavior, provider latency under real Anthropic upstreams, and online Hugging Face download lanes ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes - Runtime scope is still intentionally narrow to Anthropic request-side CCR. OpenAI, Gemini, streaming, and response-side CCR behavior are unchanged. - The only non-CCR diff is the test-only offline-memory follow-up required after current `main` was merged into the branch. - The focused proxy pytest run still emits the Windows-local `StarletteDeprecationWarning` from `fastapi.testclient`'s `httpx` bridge. The offline-memory repro command also emits existing datetime deprecation warnings and a pytest teardown warning around skipped offline lanes; none of those warnings were introduced by the CCR runtime change.
2026-06-23 13:48:05 -04:00
from tests._skip_helpers import external_model_skip_reason
# =============================================================================
# Global test hooks
# =============================================================================
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
fix(ccr): skip Anthropic marker emission when tool injection is deferred (#1273) ## Description Anthropic request-side CCR can still compress a turn into retrieval markers after the frozen-prefix cache guard suppresses `headroom_retrieve` registration. That leaves the model with marker-only context it cannot redeem, so the proxy silently drops recoverable data on exactly the turns where cache preservation deferred tool injection. This change couples the Anthropic request-side CCR path to tool availability so a turn never emits retrieve-only markers without the retrieval tool, even when token mode or cache-mode prefix replay could otherwise reuse already-compressed marker text. Closes #1006 After a collaborator merged current `main` into this branch, CI also picked up unrelated offline-memory failures from the merged base. Those follow-up changes are test-only: they keep the offline Hugging Face cache lanes skipping cleanly instead of failing in memory tests that are outside the CCR runtime path. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Couple Anthropic request-side CCR compression to the same frozen-prefix guard that already defers `headroom_retrieve` registration. - Keep the existing cache-preservation behavior: frozen-prefix turns stop emitting CCR retrieval markers instead of forcing tool injection into the cached prefix. - Make the skip decision use the effective frozen prefix after token-mode reclamping, so turns that genuinely reclamp to zero still keep normal reversible CCR behavior. - Bypass cached marker reuse in both token mode and cache-mode prefix replay when tool injection is deferred. - Add focused regressions for the Anthropic request-path seams under this bug: - frozen-prefix turns do not emit marker-only payloads - unfrozen turns still keep normal reversible CCR behavior - token-mode reclamp back to zero still compresses normally - existing `headroom_retrieve` tools keep reversible CCR on frozen turns - cache-mode delta reuse and exact-prefix replay both forward original content when retrieval is unavailable - Add a `CHANGELOG.md` entry because the proxy's user-visible Anthropic CCR behavior changes. - Add a shared test skip helper for offline Hugging Face cache misses and apply it to the merged `main` memory tests that were failing only in the offline CI shards after the branch picked up current `main`. ## Testing - [x] Unit tests pass (`uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py`) - [x] Linting passes (`uv run ruff check . && uv run ruff format . --check`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py 14 passed, 1 warning in 34.19s uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q 4 passed, 5 skipped, 11 warnings in 7.65s uv run ruff check . All checks passed! uv run ruff format . --check 968 files already formatted ``` ## Real Behavior Proof - Environment: local FastAPI `TestClient` for the Anthropic request path, plus Windows Python 3.12 offline-memory repros with `TRANSFORMERS_OFFLINE=1` - Exact command / steps: Run the focused CCR regression command and the offline-memory repro subset below on the merged branch state. - `uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py` - `uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q` - Scenario coverage from the CCR pytest command: - frozen prefix with deferred tool injection and cached marker text available in token mode - unfrozen turn with normal CCR marker emission - token mode where the tracked frozen prefix reclamps back to zero - frozen prefix where the client already supplied `headroom_retrieve` - cache-mode append-only delta reuse with a previously forwarded compressed prefix - cache-mode exact-prefix replay where the previous forwarded prefix already contained a marker - Scenario coverage from the offline-memory repro command: - direct local embedder startup on CPU with no cached HF model - hierarchical memory embedder startup with offline model cache missing - bridge import through `LocalBackend` - `MemoryHandler` public init warmup path - `LocalBackend` save path under the offline lane - Observed result: The CCR regression keeps marker-free forwarding on frozen turns without tool availability, and the merged-`main` offline-memory lanes now skip cleanly instead of failing unrelated CI shards. - frozen-prefix Anthropic turns without tool availability forward the original long transcript across both token-mode and cache-mode reuse paths, while unfrozen turns, reclamped token-mode turns, and frozen turns that already advertise `headroom_retrieve` keep the reversible CCR marker path - the merged-`main` offline-memory regressions now skip cleanly when the Hugging Face cache is unavailable instead of failing unrelated CI shards - Not tested: live Zed session cache-hit behavior, provider latency under real Anthropic upstreams, and online Hugging Face download lanes ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes - Runtime scope is still intentionally narrow to Anthropic request-side CCR. OpenAI, Gemini, streaming, and response-side CCR behavior are unchanged. - The only non-CCR diff is the test-only offline-memory follow-up required after current `main` was merged into the branch. - The focused proxy pytest run still emits the Windows-local `StarletteDeprecationWarning` from `fastapi.testclient`'s `httpx` bridge. The offline-memory repro command also emits existing datetime deprecation warnings and a pytest teardown warning around skipped offline lanes; none of those warnings were introduced by the CCR runtime change.
2026-06-23 13:48:05 -04:00
"""Wrap test execution to skip transient or offline external model failures.
fix(ccr): skip Anthropic marker emission when tool injection is deferred (#1273) ## Description Anthropic request-side CCR can still compress a turn into retrieval markers after the frozen-prefix cache guard suppresses `headroom_retrieve` registration. That leaves the model with marker-only context it cannot redeem, so the proxy silently drops recoverable data on exactly the turns where cache preservation deferred tool injection. This change couples the Anthropic request-side CCR path to tool availability so a turn never emits retrieve-only markers without the retrieval tool, even when token mode or cache-mode prefix replay could otherwise reuse already-compressed marker text. Closes #1006 After a collaborator merged current `main` into this branch, CI also picked up unrelated offline-memory failures from the merged base. Those follow-up changes are test-only: they keep the offline Hugging Face cache lanes skipping cleanly instead of failing in memory tests that are outside the CCR runtime path. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Couple Anthropic request-side CCR compression to the same frozen-prefix guard that already defers `headroom_retrieve` registration. - Keep the existing cache-preservation behavior: frozen-prefix turns stop emitting CCR retrieval markers instead of forcing tool injection into the cached prefix. - Make the skip decision use the effective frozen prefix after token-mode reclamping, so turns that genuinely reclamp to zero still keep normal reversible CCR behavior. - Bypass cached marker reuse in both token mode and cache-mode prefix replay when tool injection is deferred. - Add focused regressions for the Anthropic request-path seams under this bug: - frozen-prefix turns do not emit marker-only payloads - unfrozen turns still keep normal reversible CCR behavior - token-mode reclamp back to zero still compresses normally - existing `headroom_retrieve` tools keep reversible CCR on frozen turns - cache-mode delta reuse and exact-prefix replay both forward original content when retrieval is unavailable - Add a `CHANGELOG.md` entry because the proxy's user-visible Anthropic CCR behavior changes. - Add a shared test skip helper for offline Hugging Face cache misses and apply it to the merged `main` memory tests that were failing only in the offline CI shards after the branch picked up current `main`. ## Testing - [x] Unit tests pass (`uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py`) - [x] Linting passes (`uv run ruff check . && uv run ruff format . --check`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py 14 passed, 1 warning in 34.19s uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q 4 passed, 5 skipped, 11 warnings in 7.65s uv run ruff check . All checks passed! uv run ruff format . --check 968 files already formatted ``` ## Real Behavior Proof - Environment: local FastAPI `TestClient` for the Anthropic request path, plus Windows Python 3.12 offline-memory repros with `TRANSFORMERS_OFFLINE=1` - Exact command / steps: Run the focused CCR regression command and the offline-memory repro subset below on the merged branch state. - `uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py` - `uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q` - Scenario coverage from the CCR pytest command: - frozen prefix with deferred tool injection and cached marker text available in token mode - unfrozen turn with normal CCR marker emission - token mode where the tracked frozen prefix reclamps back to zero - frozen prefix where the client already supplied `headroom_retrieve` - cache-mode append-only delta reuse with a previously forwarded compressed prefix - cache-mode exact-prefix replay where the previous forwarded prefix already contained a marker - Scenario coverage from the offline-memory repro command: - direct local embedder startup on CPU with no cached HF model - hierarchical memory embedder startup with offline model cache missing - bridge import through `LocalBackend` - `MemoryHandler` public init warmup path - `LocalBackend` save path under the offline lane - Observed result: The CCR regression keeps marker-free forwarding on frozen turns without tool availability, and the merged-`main` offline-memory lanes now skip cleanly instead of failing unrelated CI shards. - frozen-prefix Anthropic turns without tool availability forward the original long transcript across both token-mode and cache-mode reuse paths, while unfrozen turns, reclamped token-mode turns, and frozen turns that already advertise `headroom_retrieve` keep the reversible CCR marker path - the merged-`main` offline-memory regressions now skip cleanly when the Hugging Face cache is unavailable instead of failing unrelated CI shards - Not tested: live Zed session cache-hit behavior, provider latency under real Anthropic upstreams, and online Hugging Face download lanes ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes - Runtime scope is still intentionally narrow to Anthropic request-side CCR. OpenAI, Gemini, streaming, and response-side CCR behavior are unchanged. - The only non-CCR diff is the test-only offline-memory follow-up required after current `main` was merged into the branch. - The focused proxy pytest run still emits the Windows-local `StarletteDeprecationWarning` from `fastapi.testclient`'s `httpx` bridge. The offline-memory repro command also emits existing datetime deprecation warnings and a pytest teardown warning around skipped offline lanes; none of those warnings were introduced by the CCR runtime change.
2026-06-23 13:48:05 -04:00
This handles model-loading failures that occur when:
- HuggingFace Hub is slow during model downloads (sentence-transformers)
fix(ccr): skip Anthropic marker emission when tool injection is deferred (#1273) ## Description Anthropic request-side CCR can still compress a turn into retrieval markers after the frozen-prefix cache guard suppresses `headroom_retrieve` registration. That leaves the model with marker-only context it cannot redeem, so the proxy silently drops recoverable data on exactly the turns where cache preservation deferred tool injection. This change couples the Anthropic request-side CCR path to tool availability so a turn never emits retrieve-only markers without the retrieval tool, even when token mode or cache-mode prefix replay could otherwise reuse already-compressed marker text. Closes #1006 After a collaborator merged current `main` into this branch, CI also picked up unrelated offline-memory failures from the merged base. Those follow-up changes are test-only: they keep the offline Hugging Face cache lanes skipping cleanly instead of failing in memory tests that are outside the CCR runtime path. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Couple Anthropic request-side CCR compression to the same frozen-prefix guard that already defers `headroom_retrieve` registration. - Keep the existing cache-preservation behavior: frozen-prefix turns stop emitting CCR retrieval markers instead of forcing tool injection into the cached prefix. - Make the skip decision use the effective frozen prefix after token-mode reclamping, so turns that genuinely reclamp to zero still keep normal reversible CCR behavior. - Bypass cached marker reuse in both token mode and cache-mode prefix replay when tool injection is deferred. - Add focused regressions for the Anthropic request-path seams under this bug: - frozen-prefix turns do not emit marker-only payloads - unfrozen turns still keep normal reversible CCR behavior - token-mode reclamp back to zero still compresses normally - existing `headroom_retrieve` tools keep reversible CCR on frozen turns - cache-mode delta reuse and exact-prefix replay both forward original content when retrieval is unavailable - Add a `CHANGELOG.md` entry because the proxy's user-visible Anthropic CCR behavior changes. - Add a shared test skip helper for offline Hugging Face cache misses and apply it to the merged `main` memory tests that were failing only in the offline CI shards after the branch picked up current `main`. ## Testing - [x] Unit tests pass (`uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py`) - [x] Linting passes (`uv run ruff check . && uv run ruff format . --check`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py 14 passed, 1 warning in 34.19s uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q 4 passed, 5 skipped, 11 warnings in 7.65s uv run ruff check . All checks passed! uv run ruff format . --check 968 files already formatted ``` ## Real Behavior Proof - Environment: local FastAPI `TestClient` for the Anthropic request path, plus Windows Python 3.12 offline-memory repros with `TRANSFORMERS_OFFLINE=1` - Exact command / steps: Run the focused CCR regression command and the offline-memory repro subset below on the merged branch state. - `uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py` - `uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q` - Scenario coverage from the CCR pytest command: - frozen prefix with deferred tool injection and cached marker text available in token mode - unfrozen turn with normal CCR marker emission - token mode where the tracked frozen prefix reclamps back to zero - frozen prefix where the client already supplied `headroom_retrieve` - cache-mode append-only delta reuse with a previously forwarded compressed prefix - cache-mode exact-prefix replay where the previous forwarded prefix already contained a marker - Scenario coverage from the offline-memory repro command: - direct local embedder startup on CPU with no cached HF model - hierarchical memory embedder startup with offline model cache missing - bridge import through `LocalBackend` - `MemoryHandler` public init warmup path - `LocalBackend` save path under the offline lane - Observed result: The CCR regression keeps marker-free forwarding on frozen turns without tool availability, and the merged-`main` offline-memory lanes now skip cleanly instead of failing unrelated CI shards. - frozen-prefix Anthropic turns without tool availability forward the original long transcript across both token-mode and cache-mode reuse paths, while unfrozen turns, reclamped token-mode turns, and frozen turns that already advertise `headroom_retrieve` keep the reversible CCR marker path - the merged-`main` offline-memory regressions now skip cleanly when the Hugging Face cache is unavailable instead of failing unrelated CI shards - Not tested: live Zed session cache-hit behavior, provider latency under real Anthropic upstreams, and online Hugging Face download lanes ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes - Runtime scope is still intentionally narrow to Anthropic request-side CCR. OpenAI, Gemini, streaming, and response-side CCR behavior are unchanged. - The only non-CCR diff is the test-only offline-memory follow-up required after current `main` was merged into the branch. - The focused proxy pytest run still emits the Windows-local `StarletteDeprecationWarning` from `fastapi.testclient`'s `httpx` bridge. The offline-memory repro command also emits existing datetime deprecation warnings and a pytest teardown warning around skipped offline lanes; none of those warnings were introduced by the CCR runtime change.
2026-06-23 13:48:05 -04:00
- Required HuggingFace model files were not restored into the offline CI cache
- External embedding APIs timeout
- Network connectivity issues in CI
"""
outcome = yield
fix(ccr): skip Anthropic marker emission when tool injection is deferred (#1273) ## Description Anthropic request-side CCR can still compress a turn into retrieval markers after the frozen-prefix cache guard suppresses `headroom_retrieve` registration. That leaves the model with marker-only context it cannot redeem, so the proxy silently drops recoverable data on exactly the turns where cache preservation deferred tool injection. This change couples the Anthropic request-side CCR path to tool availability so a turn never emits retrieve-only markers without the retrieval tool, even when token mode or cache-mode prefix replay could otherwise reuse already-compressed marker text. Closes #1006 After a collaborator merged current `main` into this branch, CI also picked up unrelated offline-memory failures from the merged base. Those follow-up changes are test-only: they keep the offline Hugging Face cache lanes skipping cleanly instead of failing in memory tests that are outside the CCR runtime path. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Couple Anthropic request-side CCR compression to the same frozen-prefix guard that already defers `headroom_retrieve` registration. - Keep the existing cache-preservation behavior: frozen-prefix turns stop emitting CCR retrieval markers instead of forcing tool injection into the cached prefix. - Make the skip decision use the effective frozen prefix after token-mode reclamping, so turns that genuinely reclamp to zero still keep normal reversible CCR behavior. - Bypass cached marker reuse in both token mode and cache-mode prefix replay when tool injection is deferred. - Add focused regressions for the Anthropic request-path seams under this bug: - frozen-prefix turns do not emit marker-only payloads - unfrozen turns still keep normal reversible CCR behavior - token-mode reclamp back to zero still compresses normally - existing `headroom_retrieve` tools keep reversible CCR on frozen turns - cache-mode delta reuse and exact-prefix replay both forward original content when retrieval is unavailable - Add a `CHANGELOG.md` entry because the proxy's user-visible Anthropic CCR behavior changes. - Add a shared test skip helper for offline Hugging Face cache misses and apply it to the merged `main` memory tests that were failing only in the offline CI shards after the branch picked up current `main`. ## Testing - [x] Unit tests pass (`uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py`) - [x] Linting passes (`uv run ruff check . && uv run ruff format . --check`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py 14 passed, 1 warning in 34.19s uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q 4 passed, 5 skipped, 11 warnings in 7.65s uv run ruff check . All checks passed! uv run ruff format . --check 968 files already formatted ``` ## Real Behavior Proof - Environment: local FastAPI `TestClient` for the Anthropic request path, plus Windows Python 3.12 offline-memory repros with `TRANSFORMERS_OFFLINE=1` - Exact command / steps: Run the focused CCR regression command and the offline-memory repro subset below on the merged branch state. - `uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py` - `uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q` - Scenario coverage from the CCR pytest command: - frozen prefix with deferred tool injection and cached marker text available in token mode - unfrozen turn with normal CCR marker emission - token mode where the tracked frozen prefix reclamps back to zero - frozen prefix where the client already supplied `headroom_retrieve` - cache-mode append-only delta reuse with a previously forwarded compressed prefix - cache-mode exact-prefix replay where the previous forwarded prefix already contained a marker - Scenario coverage from the offline-memory repro command: - direct local embedder startup on CPU with no cached HF model - hierarchical memory embedder startup with offline model cache missing - bridge import through `LocalBackend` - `MemoryHandler` public init warmup path - `LocalBackend` save path under the offline lane - Observed result: The CCR regression keeps marker-free forwarding on frozen turns without tool availability, and the merged-`main` offline-memory lanes now skip cleanly instead of failing unrelated CI shards. - frozen-prefix Anthropic turns without tool availability forward the original long transcript across both token-mode and cache-mode reuse paths, while unfrozen turns, reclamped token-mode turns, and frozen turns that already advertise `headroom_retrieve` keep the reversible CCR marker path - the merged-`main` offline-memory regressions now skip cleanly when the Hugging Face cache is unavailable instead of failing unrelated CI shards - Not tested: live Zed session cache-hit behavior, provider latency under real Anthropic upstreams, and online Hugging Face download lanes ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes - Runtime scope is still intentionally narrow to Anthropic request-side CCR. OpenAI, Gemini, streaming, and response-side CCR behavior are unchanged. - The only non-CCR diff is the test-only offline-memory follow-up required after current `main` was merged into the branch. - The focused proxy pytest run still emits the Windows-local `StarletteDeprecationWarning` from `fastapi.testclient`'s `httpx` bridge. The offline-memory repro command also emits existing datetime deprecation warnings and a pytest teardown warning around skipped offline lanes; none of those warnings were introduced by the CCR runtime change.
2026-06-23 13:48:05 -04:00
if outcome.excinfo is not None:
exc_type, exc_value, exc_tb = outcome.excinfo
fix(ccr): skip Anthropic marker emission when tool injection is deferred (#1273) ## Description Anthropic request-side CCR can still compress a turn into retrieval markers after the frozen-prefix cache guard suppresses `headroom_retrieve` registration. That leaves the model with marker-only context it cannot redeem, so the proxy silently drops recoverable data on exactly the turns where cache preservation deferred tool injection. This change couples the Anthropic request-side CCR path to tool availability so a turn never emits retrieve-only markers without the retrieval tool, even when token mode or cache-mode prefix replay could otherwise reuse already-compressed marker text. Closes #1006 After a collaborator merged current `main` into this branch, CI also picked up unrelated offline-memory failures from the merged base. Those follow-up changes are test-only: they keep the offline Hugging Face cache lanes skipping cleanly instead of failing in memory tests that are outside the CCR runtime path. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Couple Anthropic request-side CCR compression to the same frozen-prefix guard that already defers `headroom_retrieve` registration. - Keep the existing cache-preservation behavior: frozen-prefix turns stop emitting CCR retrieval markers instead of forcing tool injection into the cached prefix. - Make the skip decision use the effective frozen prefix after token-mode reclamping, so turns that genuinely reclamp to zero still keep normal reversible CCR behavior. - Bypass cached marker reuse in both token mode and cache-mode prefix replay when tool injection is deferred. - Add focused regressions for the Anthropic request-path seams under this bug: - frozen-prefix turns do not emit marker-only payloads - unfrozen turns still keep normal reversible CCR behavior - token-mode reclamp back to zero still compresses normally - existing `headroom_retrieve` tools keep reversible CCR on frozen turns - cache-mode delta reuse and exact-prefix replay both forward original content when retrieval is unavailable - Add a `CHANGELOG.md` entry because the proxy's user-visible Anthropic CCR behavior changes. - Add a shared test skip helper for offline Hugging Face cache misses and apply it to the merged `main` memory tests that were failing only in the offline CI shards after the branch picked up current `main`. ## Testing - [x] Unit tests pass (`uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py`) - [x] Linting passes (`uv run ruff check . && uv run ruff format . --check`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py 14 passed, 1 warning in 34.19s uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q 4 passed, 5 skipped, 11 warnings in 7.65s uv run ruff check . All checks passed! uv run ruff format . --check 968 files already formatted ``` ## Real Behavior Proof - Environment: local FastAPI `TestClient` for the Anthropic request path, plus Windows Python 3.12 offline-memory repros with `TRANSFORMERS_OFFLINE=1` - Exact command / steps: Run the focused CCR regression command and the offline-memory repro subset below on the merged branch state. - `uv run pytest tests/test_proxy/test_anthropic_ccr_deferred_injection.py` - `uv run pytest tests/test_memory/test_skip_helpers.py tests/test_memory/test_embedder_mps_serialization.py::test_cpu_uses_shared_executor tests/test_memory/test_hierarchical.py::TestLocalEmbedder::test_embed_single tests/test_memory_bridge.py::TestMemoryBridgeImport::test_import_claude_code_memory tests/test_memory_handler_concurrent_init.py::test_real_localbackend_initializes_via_public_entrypoint tests/test_memory_system.py::TestLocalBackend::test_save_memory_basic -q` - Scenario coverage from the CCR pytest command: - frozen prefix with deferred tool injection and cached marker text available in token mode - unfrozen turn with normal CCR marker emission - token mode where the tracked frozen prefix reclamps back to zero - frozen prefix where the client already supplied `headroom_retrieve` - cache-mode append-only delta reuse with a previously forwarded compressed prefix - cache-mode exact-prefix replay where the previous forwarded prefix already contained a marker - Scenario coverage from the offline-memory repro command: - direct local embedder startup on CPU with no cached HF model - hierarchical memory embedder startup with offline model cache missing - bridge import through `LocalBackend` - `MemoryHandler` public init warmup path - `LocalBackend` save path under the offline lane - Observed result: The CCR regression keeps marker-free forwarding on frozen turns without tool availability, and the merged-`main` offline-memory lanes now skip cleanly instead of failing unrelated CI shards. - frozen-prefix Anthropic turns without tool availability forward the original long transcript across both token-mode and cache-mode reuse paths, while unfrozen turns, reclamped token-mode turns, and frozen turns that already advertise `headroom_retrieve` keep the reversible CCR marker path - the merged-`main` offline-memory regressions now skip cleanly when the Hugging Face cache is unavailable instead of failing unrelated CI shards - Not tested: live Zed session cache-hit behavior, provider latency under real Anthropic upstreams, and online Hugging Face download lanes ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes - Runtime scope is still intentionally narrow to Anthropic request-side CCR. OpenAI, Gemini, streaming, and response-side CCR behavior are unchanged. - The only non-CCR diff is the test-only offline-memory follow-up required after current `main` was merged into the branch. - The focused proxy pytest run still emits the Windows-local `StarletteDeprecationWarning` from `fastapi.testclient`'s `httpx` bridge. The offline-memory repro command also emits existing datetime deprecation warnings and a pytest teardown warning around skipped offline lanes; none of those warnings were introduced by the CCR runtime change.
2026-06-23 13:48:05 -04:00
reason = external_model_skip_reason(exc_value)
if reason is not None:
pytest.skip(reason)
fix: B1 — retire ICM, RollingWindow, scoring, relevance + dependents Phase B step 1 of the live-zone-only realignment. Removes ~10K LOC of "drop messages from history" machinery that became unreachable after PR-A1 made `/v1/messages` a passthrough on the proxy. Live-zone-only compression (PR-B2..B7) operates on content blocks within messages; message-list mutation no longer happens in the pipeline. Python deletes: - headroom/transforms/intelligent_context.py (1077 LOC) - headroom/transforms/rolling_window.py (395 LOC) - headroom/transforms/progressive_summarizer.py (508 LOC) - headroom/transforms/scoring.py (459 LOC) - headroom/transforms/tool_crusher.py (338 LOC) - 5 corresponding tests/test_transforms/* and tests/test_proxy_intelligent_context.py Rust deletes: - crates/headroom-core/src/context/* (manager, config, workspace, candidate, ccr_drop, strategy/, mod) + safety.rs replaced - crates/headroom-core/src/scoring/* (mod, score, scorer, traits, weights) - MessageScorerComparator from crates/headroom-parity (PR #338/#343 becomes deletable; sunk cost stays sunk) - 13 message_scorer fixtures + record_message_scorer.py Rust adds (move + rewrite): - crates/headroom-core/src/transforms/safety.rs — `tool_pair_indices` preserves the OpenAI/Anthropic tool_use ↔ tool_result pairing rule the live-zone dispatcher (PR-B2) needs. No IcmConfig dependency. Surface refactors: - HeadroomConfig: drop `tool_crusher`, `rolling_window`, `intelligent_context` fields; hoist `output_buffer_tokens` to top level (used by client.py). - ProxyConfig: drop `intelligent_context*` fields. - `headroom wrap` proxy server: retire IntelligentContextManager and RollingWindow imports + branch; pipeline is CacheAligner → ContentRouter (smart_routing) or CacheAligner → SmartCrusher (legacy). - CLI: drop `--no-intelligent-context`, `--no-intelligent-scoring`, `--no-compress-first` flags. - LangChain memory integration: rename `_apply_rolling_window` → `_apply_compression`, drop RollingWindowConfig dep. Threshold is now advisory — B6 will rework the contract. - TransformPipeline.create_pipeline now takes only cache_aligner_config. - headroom/__init__.py + headroom/transforms/__init__.py: strip exports of deleted symbols. Bug fixes uncovered by full pytest sweep: - providers/copilot/wrap.py: `environ or os.environ` collapsed empty-dict to falsy → callers passing `environ={}` accidentally pulled from os.environ. Use `environ if environ is not None else os.environ`. Test correctness fixes: - _DummyAnthropicHandler._retry_request gains **_kwargs to match the real handler signature post-A8. - test_ws_http_fallback extracts JSON from `content=` (post-A3 byte-faithful) rather than the obsolete `json=` kwarg. - test_ccr_response_handler_extra fixture joins SSE events with `\n\n` per spec (post-A8 byte-buffer parser requirement). - test_proxy_responses_phase_preservation: capture via direct handler attached to the named logger, so the assertion is order-independent (proxy `_setup_file_logging` flips `headroom.propagate=False` once any earlier test triggers it). - conftest.py autouse fixture resets `headroom.propagate=True` before each test as a defensive measure for the same pollution. - test_wrap_copilot_translated_backend_still_requires_byok: monkeypatch.delenv every provider key so the BYOK error actually fires. - test_native_installers: skip when system bash < 4.3 (macOS ships 3.2). - TestGeminiEmbedContent / TestGeminiBatchEmbedContents: pytest.mark.skip — proxy currently has no :embedContent route; feature gap, not regression. Acceptance: - cargo build --workspace + cargo clippy + cargo fmt --check: green. - cargo test --workspace --exclude headroom-py: 777 passed. - pytest: 4892 passed, 240 skipped, 0 failed. - git grep returns only intentional comments referencing the deletion. Per-PR-B1 plan: REALIGNMENT/04-phase-B-live-zone.md.
2026-05-02 12:23:17 -07:00
@pytest.fixture(autouse=True)
def _reset_headroom_logger_propagation():
"""Keep `headroom.*` log records flowing to pytest's caplog handler.
fix(tests): reset whole headroom logger subtree so caplog stays deterministic (#1117) ## Description Five `caplog`-based test assertions are order-dependent flakes: they pass in isolation but fail in full-suite runs. **Root cause** is a global logging-state leak. `benchmarks.claude_session_mode_benchmark._disable_headroom_benchmark_logging()` (exercised by `tests/test_claude_session_mode_benchmark.py`) sets `propagate = False` + `CRITICAL` on the `headroom`, `headroom.proxy`, `headroom.transforms` (and `headroom.cache`) loggers and never restores them. pytest's `caplog` attaches its handler to the **root** logger, so once any `headroom.*` child is left non-propagating, records from that subtree silently never reach `caplog` for **every test that runs afterwards** — which is exactly why these only fail in full-suite order. The repo already ships a `_reset_headroom_logger_propagation` autouse fixture for this hazard (its docstring documents the equivalent `_setup_file_logging` leak), but it only reset the **top** `headroom` logger, not children like `headroom.proxy`. A non-propagating child still blocks the record before it reaches root. This PR extends the existing fixture to reset the whole `headroom.*` subtree before each test. Scope is intentionally one file (`tests/conftest.py`) — test-harness only, no production change. > Design note: I extended the existing defensive fixture rather than restoring state inside the benchmark, because (a) the fixture already exists for exactly this and only needed completing, and (b) the same `propagate=False` hazard also originates from production `_setup_file_logging`, so a centralized per-test reset is the more durable fix. Happy to instead make the benchmark restore its own logging state if maintainers prefer fixing it at the source. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Extend the `_reset_headroom_logger_propagation` autouse fixture in `tests/conftest.py` to reset `propagate = True` for **every** existing `headroom.*` logger (previously only the top `headroom` logger), so `caplog` capture is deterministic regardless of test execution order. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) — N/A, change is under `tests/` - [ ] New tests added — N/A, this fixes existing tests; they are themselves the proof - [x] Manual testing performed ### Test Output ```text # Causal proof: run the polluter first, then the 5 victims, in one process. # BEFORE (fixture reset scoped to only "headroom"): $ pytest tests/test_claude_session_mode_benchmark.py \ tests/test_corrupt_golden_bytes_recovery.py \ tests/test_forwarded_headers.py \ 'tests/test_transforms/test_kompress_compressor.py::TestKompressBackendSelection::test_unrecognized_backend_warns_and_falls_back_to_auto' 5 failed, 54 passed FAILED tests/test_corrupt_golden_bytes_recovery.py::TestCorruptMemoryGoldenBytes::test_corrupt_bytes_logs_error FAILED tests/test_corrupt_golden_bytes_recovery.py::TestCorruptMemoryGoldenBytes::test_unicode_decode_error_handled FAILED tests/test_corrupt_golden_bytes_recovery.py::TestCorruptCcrGoldenBytes::test_corrupt_bytes_logs_error FAILED tests/test_forwarded_headers.py::test_non_allowlisted_peer_ignores_forwarded_and_logs FAILED tests/test_transforms/test_kompress_compressor.py::...test_unrecognized_backend_warns_and_falls_back_to_auto # AFTER (this PR — whole headroom.* subtree reset): $ pytest <same selection> 59 passed # Full suite (Rust core rebuilt locally): $ pytest 6251 passed, 496 skipped $ ruff check . All checks passed! $ ruff format --check tests/conftest.py 1 file already formatted ``` ## Real Behavior Proof - Environment: macOS, Python 3.13.3, branch off latest `main`, Rust `_core` rebuilt locally (`uv pip install -e .`). - Exact command / steps: ran the polluter (`test_claude_session_mode_benchmark`) together with the 5 victim tests in one process to reproduce the order-dependent failure, then toggled **only** the fixture change to confirm causality; then ran the full `pytest` suite and `ruff`. - Observed result: scoping the reset to only `"headroom"` → 5 failed / 54 passed; extending it to the `headroom.*` subtree → 59 passed. Full suite: 6251 passed, 496 skipped, 0 failed. Lint clean. - Not tested: behavior under CI's sharded `test (N)` jobs specifically — but the fix is order-independent (resets before *every* test), so sharding cannot reintroduce the leak. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation — N/A - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective — the 5 previously-flaky tests are the proof - [x] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md — N/A (test-harness only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
2026-06-27 01:05:26 +08:00
Two sources disable propagation on the headroom logger tree and never
restore it, which then makes later `caplog`-based assertions flaky in
full-suite runs (caplog attaches to root, so a `propagate=False` anywhere
on the chain silently drops the records):
- ``headroom.proxy.helpers._setup_file_logging`` sets
``getLogger("headroom").propagate = False`` on proxy startup.
- ``benchmarks.claude_session_mode_benchmark._disable_headroom_benchmark_logging``
(exercised by ``test_claude_session_mode_benchmark``) sets
``propagate = False`` + ``CRITICAL`` on ``headroom``, ``headroom.proxy``,
``headroom.transforms``, ``headroom.cache`` (and children).
Resetting only ``"headroom"`` is not enough a child like
``"headroom.proxy"`` left non-propagating blocks the record before it
reaches root. Reset the whole subtree before every test so capture is
deterministic regardless of run order.
fix: B1 — retire ICM, RollingWindow, scoring, relevance + dependents Phase B step 1 of the live-zone-only realignment. Removes ~10K LOC of "drop messages from history" machinery that became unreachable after PR-A1 made `/v1/messages` a passthrough on the proxy. Live-zone-only compression (PR-B2..B7) operates on content blocks within messages; message-list mutation no longer happens in the pipeline. Python deletes: - headroom/transforms/intelligent_context.py (1077 LOC) - headroom/transforms/rolling_window.py (395 LOC) - headroom/transforms/progressive_summarizer.py (508 LOC) - headroom/transforms/scoring.py (459 LOC) - headroom/transforms/tool_crusher.py (338 LOC) - 5 corresponding tests/test_transforms/* and tests/test_proxy_intelligent_context.py Rust deletes: - crates/headroom-core/src/context/* (manager, config, workspace, candidate, ccr_drop, strategy/, mod) + safety.rs replaced - crates/headroom-core/src/scoring/* (mod, score, scorer, traits, weights) - MessageScorerComparator from crates/headroom-parity (PR #338/#343 becomes deletable; sunk cost stays sunk) - 13 message_scorer fixtures + record_message_scorer.py Rust adds (move + rewrite): - crates/headroom-core/src/transforms/safety.rs — `tool_pair_indices` preserves the OpenAI/Anthropic tool_use ↔ tool_result pairing rule the live-zone dispatcher (PR-B2) needs. No IcmConfig dependency. Surface refactors: - HeadroomConfig: drop `tool_crusher`, `rolling_window`, `intelligent_context` fields; hoist `output_buffer_tokens` to top level (used by client.py). - ProxyConfig: drop `intelligent_context*` fields. - `headroom wrap` proxy server: retire IntelligentContextManager and RollingWindow imports + branch; pipeline is CacheAligner → ContentRouter (smart_routing) or CacheAligner → SmartCrusher (legacy). - CLI: drop `--no-intelligent-context`, `--no-intelligent-scoring`, `--no-compress-first` flags. - LangChain memory integration: rename `_apply_rolling_window` → `_apply_compression`, drop RollingWindowConfig dep. Threshold is now advisory — B6 will rework the contract. - TransformPipeline.create_pipeline now takes only cache_aligner_config. - headroom/__init__.py + headroom/transforms/__init__.py: strip exports of deleted symbols. Bug fixes uncovered by full pytest sweep: - providers/copilot/wrap.py: `environ or os.environ` collapsed empty-dict to falsy → callers passing `environ={}` accidentally pulled from os.environ. Use `environ if environ is not None else os.environ`. Test correctness fixes: - _DummyAnthropicHandler._retry_request gains **_kwargs to match the real handler signature post-A8. - test_ws_http_fallback extracts JSON from `content=` (post-A3 byte-faithful) rather than the obsolete `json=` kwarg. - test_ccr_response_handler_extra fixture joins SSE events with `\n\n` per spec (post-A8 byte-buffer parser requirement). - test_proxy_responses_phase_preservation: capture via direct handler attached to the named logger, so the assertion is order-independent (proxy `_setup_file_logging` flips `headroom.propagate=False` once any earlier test triggers it). - conftest.py autouse fixture resets `headroom.propagate=True` before each test as a defensive measure for the same pollution. - test_wrap_copilot_translated_backend_still_requires_byok: monkeypatch.delenv every provider key so the BYOK error actually fires. - test_native_installers: skip when system bash < 4.3 (macOS ships 3.2). - TestGeminiEmbedContent / TestGeminiBatchEmbedContents: pytest.mark.skip — proxy currently has no :embedContent route; feature gap, not regression. Acceptance: - cargo build --workspace + cargo clippy + cargo fmt --check: green. - cargo test --workspace --exclude headroom-py: 777 passed. - pytest: 4892 passed, 240 skipped, 0 failed. - git grep returns only intentional comments referencing the deletion. Per-PR-B1 plan: REALIGNMENT/04-phase-B-live-zone.md.
2026-05-02 12:23:17 -07:00
"""
import logging as _logging
fix(tests): reset whole headroom logger subtree so caplog stays deterministic (#1117) ## Description Five `caplog`-based test assertions are order-dependent flakes: they pass in isolation but fail in full-suite runs. **Root cause** is a global logging-state leak. `benchmarks.claude_session_mode_benchmark._disable_headroom_benchmark_logging()` (exercised by `tests/test_claude_session_mode_benchmark.py`) sets `propagate = False` + `CRITICAL` on the `headroom`, `headroom.proxy`, `headroom.transforms` (and `headroom.cache`) loggers and never restores them. pytest's `caplog` attaches its handler to the **root** logger, so once any `headroom.*` child is left non-propagating, records from that subtree silently never reach `caplog` for **every test that runs afterwards** — which is exactly why these only fail in full-suite order. The repo already ships a `_reset_headroom_logger_propagation` autouse fixture for this hazard (its docstring documents the equivalent `_setup_file_logging` leak), but it only reset the **top** `headroom` logger, not children like `headroom.proxy`. A non-propagating child still blocks the record before it reaches root. This PR extends the existing fixture to reset the whole `headroom.*` subtree before each test. Scope is intentionally one file (`tests/conftest.py`) — test-harness only, no production change. > Design note: I extended the existing defensive fixture rather than restoring state inside the benchmark, because (a) the fixture already exists for exactly this and only needed completing, and (b) the same `propagate=False` hazard also originates from production `_setup_file_logging`, so a centralized per-test reset is the more durable fix. Happy to instead make the benchmark restore its own logging state if maintainers prefer fixing it at the source. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Extend the `_reset_headroom_logger_propagation` autouse fixture in `tests/conftest.py` to reset `propagate = True` for **every** existing `headroom.*` logger (previously only the top `headroom` logger), so `caplog` capture is deterministic regardless of test execution order. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) — N/A, change is under `tests/` - [ ] New tests added — N/A, this fixes existing tests; they are themselves the proof - [x] Manual testing performed ### Test Output ```text # Causal proof: run the polluter first, then the 5 victims, in one process. # BEFORE (fixture reset scoped to only "headroom"): $ pytest tests/test_claude_session_mode_benchmark.py \ tests/test_corrupt_golden_bytes_recovery.py \ tests/test_forwarded_headers.py \ 'tests/test_transforms/test_kompress_compressor.py::TestKompressBackendSelection::test_unrecognized_backend_warns_and_falls_back_to_auto' 5 failed, 54 passed FAILED tests/test_corrupt_golden_bytes_recovery.py::TestCorruptMemoryGoldenBytes::test_corrupt_bytes_logs_error FAILED tests/test_corrupt_golden_bytes_recovery.py::TestCorruptMemoryGoldenBytes::test_unicode_decode_error_handled FAILED tests/test_corrupt_golden_bytes_recovery.py::TestCorruptCcrGoldenBytes::test_corrupt_bytes_logs_error FAILED tests/test_forwarded_headers.py::test_non_allowlisted_peer_ignores_forwarded_and_logs FAILED tests/test_transforms/test_kompress_compressor.py::...test_unrecognized_backend_warns_and_falls_back_to_auto # AFTER (this PR — whole headroom.* subtree reset): $ pytest <same selection> 59 passed # Full suite (Rust core rebuilt locally): $ pytest 6251 passed, 496 skipped $ ruff check . All checks passed! $ ruff format --check tests/conftest.py 1 file already formatted ``` ## Real Behavior Proof - Environment: macOS, Python 3.13.3, branch off latest `main`, Rust `_core` rebuilt locally (`uv pip install -e .`). - Exact command / steps: ran the polluter (`test_claude_session_mode_benchmark`) together with the 5 victim tests in one process to reproduce the order-dependent failure, then toggled **only** the fixture change to confirm causality; then ran the full `pytest` suite and `ruff`. - Observed result: scoping the reset to only `"headroom"` → 5 failed / 54 passed; extending it to the `headroom.*` subtree → 59 passed. Full suite: 6251 passed, 496 skipped, 0 failed. Lint clean. - Not tested: behavior under CI's sharded `test (N)` jobs specifically — but the fix is order-independent (resets before *every* test), so sharding cannot reintroduce the leak. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation — N/A - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective — the 5 previously-flaky tests are the proof - [x] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md — N/A (test-harness only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
2026-06-27 01:05:26 +08:00
for _name in ("headroom", *list(_logging.root.manager.loggerDict)):
if _name == "headroom" or _name.startswith("headroom."):
logger = _logging.getLogger(_name)
logger.disabled = False
logger.propagate = True
fix: B1 — retire ICM, RollingWindow, scoring, relevance + dependents Phase B step 1 of the live-zone-only realignment. Removes ~10K LOC of "drop messages from history" machinery that became unreachable after PR-A1 made `/v1/messages` a passthrough on the proxy. Live-zone-only compression (PR-B2..B7) operates on content blocks within messages; message-list mutation no longer happens in the pipeline. Python deletes: - headroom/transforms/intelligent_context.py (1077 LOC) - headroom/transforms/rolling_window.py (395 LOC) - headroom/transforms/progressive_summarizer.py (508 LOC) - headroom/transforms/scoring.py (459 LOC) - headroom/transforms/tool_crusher.py (338 LOC) - 5 corresponding tests/test_transforms/* and tests/test_proxy_intelligent_context.py Rust deletes: - crates/headroom-core/src/context/* (manager, config, workspace, candidate, ccr_drop, strategy/, mod) + safety.rs replaced - crates/headroom-core/src/scoring/* (mod, score, scorer, traits, weights) - MessageScorerComparator from crates/headroom-parity (PR #338/#343 becomes deletable; sunk cost stays sunk) - 13 message_scorer fixtures + record_message_scorer.py Rust adds (move + rewrite): - crates/headroom-core/src/transforms/safety.rs — `tool_pair_indices` preserves the OpenAI/Anthropic tool_use ↔ tool_result pairing rule the live-zone dispatcher (PR-B2) needs. No IcmConfig dependency. Surface refactors: - HeadroomConfig: drop `tool_crusher`, `rolling_window`, `intelligent_context` fields; hoist `output_buffer_tokens` to top level (used by client.py). - ProxyConfig: drop `intelligent_context*` fields. - `headroom wrap` proxy server: retire IntelligentContextManager and RollingWindow imports + branch; pipeline is CacheAligner → ContentRouter (smart_routing) or CacheAligner → SmartCrusher (legacy). - CLI: drop `--no-intelligent-context`, `--no-intelligent-scoring`, `--no-compress-first` flags. - LangChain memory integration: rename `_apply_rolling_window` → `_apply_compression`, drop RollingWindowConfig dep. Threshold is now advisory — B6 will rework the contract. - TransformPipeline.create_pipeline now takes only cache_aligner_config. - headroom/__init__.py + headroom/transforms/__init__.py: strip exports of deleted symbols. Bug fixes uncovered by full pytest sweep: - providers/copilot/wrap.py: `environ or os.environ` collapsed empty-dict to falsy → callers passing `environ={}` accidentally pulled from os.environ. Use `environ if environ is not None else os.environ`. Test correctness fixes: - _DummyAnthropicHandler._retry_request gains **_kwargs to match the real handler signature post-A8. - test_ws_http_fallback extracts JSON from `content=` (post-A3 byte-faithful) rather than the obsolete `json=` kwarg. - test_ccr_response_handler_extra fixture joins SSE events with `\n\n` per spec (post-A8 byte-buffer parser requirement). - test_proxy_responses_phase_preservation: capture via direct handler attached to the named logger, so the assertion is order-independent (proxy `_setup_file_logging` flips `headroom.propagate=False` once any earlier test triggers it). - conftest.py autouse fixture resets `headroom.propagate=True` before each test as a defensive measure for the same pollution. - test_wrap_copilot_translated_backend_still_requires_byok: monkeypatch.delenv every provider key so the BYOK error actually fires. - test_native_installers: skip when system bash < 4.3 (macOS ships 3.2). - TestGeminiEmbedContent / TestGeminiBatchEmbedContents: pytest.mark.skip — proxy currently has no :embedContent route; feature gap, not regression. Acceptance: - cargo build --workspace + cargo clippy + cargo fmt --check: green. - cargo test --workspace --exclude headroom-py: 777 passed. - pytest: 4892 passed, 240 skipped, 0 failed. - git grep returns only intentional comments referencing the deletion. Per-PR-B1 plan: REALIGNMENT/04-phase-B-live-zone.md.
2026-05-02 12:23:17 -07:00
yield
# =============================================================================
# Sample messages fixtures
# =============================================================================
# Sample messages fixtures
@pytest.fixture
def sample_messages():
"""Basic conversation messages."""
return [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing well, thank you!"},
]
@pytest.fixture
def sample_messages_with_tools():
"""Conversation with tool calls and responses."""
return [
{"role": "system", "content": "You are a helpful assistant with tools."},
{"role": "user", "content": "Search for user 12345"},
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {"name": "search_user", "arguments": '{"user_id": "12345"}'},
}
],
},
{
"role": "tool",
"tool_call_id": "call_123",
"content": '{"id": "12345", "name": "Alice", "email": "alice@example.com"}',
},
{"role": "assistant", "content": "I found user Alice with ID 12345."},
]
@pytest.fixture
def sample_tool_output_large():
"""Large tool output for compression testing (100 items)."""
return json.dumps(
[
{
"id": i,
"name": f"Item {i}",
"score": i * 0.1,
"status": "active" if i % 2 == 0 else "inactive",
}
for i in range(100)
]
)
@pytest.fixture
def sample_tool_output_with_errors():
"""Tool output containing error items."""
items = [{"id": i, "status": "success"} for i in range(20)]
items[5] = {"id": 5, "status": "error", "message": "Connection refused"}
items[15] = {"id": 15, "status": "failed", "exception": "TimeoutError"}
return json.dumps(items)
@pytest.fixture
def sample_system_prompt_with_date():
"""System prompt containing dynamic date."""
return "You are a helpful assistant. Current date: 2025-01-06. Help the user with their tasks."
@pytest.fixture
def sample_anthropic_messages():
"""Anthropic-style messages with content blocks."""
return [
{
"role": "user",
"content": [
{"type": "text", "text": "Analyze this image"},
{
"type": "image",
"source": {"type": "base64", "media_type": "image/png", "data": "..."},
},
],
}
]
# Mock client fixtures
@pytest.fixture
def mock_openai_response():
"""Mock OpenAI API response."""
mock = Mock()
mock.id = "chatcmpl-123"
mock.model = "gpt-4o"
mock.usage = Mock()
mock.usage.prompt_tokens = 100
mock.usage.completion_tokens = 50
mock.usage.total_tokens = 150
mock.choices = [Mock()]
mock.choices[0].message = Mock()
mock.choices[0].message.content = "This is a response."
mock.choices[0].message.role = "assistant"
mock.choices[0].finish_reason = "stop"
return mock
@pytest.fixture
def mock_openai_client(mock_openai_response):
"""Mock OpenAI client."""
client = Mock()
client.chat = Mock()
client.chat.completions = Mock()
client.chat.completions.create = Mock(return_value=mock_openai_response)
return client
# Storage fixtures
@pytest.fixture
def temp_sqlite_db():
"""Temporary SQLite database path."""
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
yield f.name
Path(f.name).unlink(missing_ok=True)
@pytest.fixture
def temp_jsonl_file():
"""Temporary JSONL file path."""
with tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False) as f:
yield f.name
Path(f.name).unlink(missing_ok=True)
# Provider fixtures
@pytest.fixture
def openai_provider():
"""OpenAI provider instance."""
from headroom.providers.openai import OpenAIProvider
return OpenAIProvider()
@pytest.fixture
def openai_tokenizer():
"""OpenAI token counter for gpt-4o."""
from headroom.providers.openai import OpenAITokenCounter
return OpenAITokenCounter("gpt-4o")
# Config fixtures
@pytest.fixture
def default_config():
"""Default HeadroomConfig."""
from headroom.config import HeadroomConfig
return HeadroomConfig()
@pytest.fixture
def smart_crusher_config():
"""SmartCrusher config for testing."""
from headroom.config import SmartCrusherConfig
return SmartCrusherConfig(
enabled=True,
min_items_to_analyze=3,
min_tokens_to_crush=0, # Always crush for tests
max_items_after_crush=10,
)
# Helper for creating RequestMetrics
@pytest.fixture
def sample_request_metrics():
"""Sample RequestMetrics for storage tests."""
from headroom.config import RequestMetrics
return RequestMetrics(
request_id="test-123",
timestamp=datetime(2025, 1, 6, 12, 0, 0),
model="gpt-4o",
stream=False,
mode="audit",
tokens_input_before=1000,
tokens_input_after=800,
tokens_output=200,
block_breakdown={"system": 100, "user": 200, "assistant": 500},
waste_signals={"json_bloat": 50},
stable_prefix_hash="abc123",
cache_alignment_score=85.0,
cached_tokens=100,
transforms_applied=["CacheAligner", "SmartCrusher"],
tool_units_dropped=1,
turns_dropped=0,
messages_hash="def456",
)