087cc49a26
Two narrow repairs for #97948 symptom B (large-session rotation aborts with 'Compression lease lost before publication' / session_split_failed, then the next turn re-runs the identical doomed compression): 1. publish_compression_child gains require_lease_refresh: the lease is extended inside the same transaction as the expiry check (same conn, no TOCTOU), giving a worker whose refresher thread died from transient DB failures one final chance to keep its completed work. 2. A failed compression split now records a 60s failure cooldown, so the next turn cannot immediately re-trigger the same compression. Salvaged from #98137 (author: vsd2807). The timeout-reconciliation half of that PR is NOT carried: it has a blocking review (runtime sid vs persisted session_key, one-shot check cannot observe a 6-minute commit, no identity projection) and needs a redesign.
83 lines
3.1 KiB
Python
83 lines
3.1 KiB
Python
"""Regression tests: a failed compression split must arm the failure cooldown.
|
|
|
|
Extracted verbatim from PR #98137's test file (author: vsd2807); the sibling
|
|
timeout-reconciliation tests were not carried because that production path was
|
|
not salvaged (blocking review on #98137).
|
|
|
|
Issue #97948 symptom B: without the cooldown, the turn after a
|
|
session_split_failed abort immediately re-runs the identical doomed
|
|
compression.
|
|
"""
|
|
|
|
import time
|
|
from unittest.mock import MagicMock
|
|
|
|
def test_split_failure_records_cooldown():
|
|
"""After a session split failure, compression failure cooldown is recorded."""
|
|
from agent.context_compressor import ContextCompressor
|
|
|
|
compressor = ContextCompressor.__new__(ContextCompressor)
|
|
compressor._summary_failure_cooldown_until = 0.0
|
|
compressor._last_summary_error = None
|
|
compressor._cooldown_persist_failed = False
|
|
compressor._session_db = None
|
|
compressor._session_id = ""
|
|
|
|
compressor._record_compression_failure_cooldown(60, "session_split_failed: test")
|
|
|
|
assert compressor._summary_failure_cooldown_until > time.monotonic()
|
|
assert compressor._last_summary_error == "session_split_failed: test"
|
|
|
|
|
|
def test_cooldown_blocks_automatic_compression():
|
|
from agent.context_compressor import ContextCompressor
|
|
|
|
compressor = ContextCompressor.__new__(ContextCompressor)
|
|
compressor._summary_failure_cooldown_until = time.monotonic() + 60.0
|
|
compressor._last_summary_error = "session_split_failed"
|
|
compressor._cooldown_persist_failed = False
|
|
compressor.quiet_mode = True
|
|
|
|
assert compressor._automatic_compression_blocked_locally() is True
|
|
|
|
|
|
def test_manual_compress_bypasses_cooldown():
|
|
"""Manual /compress (force=True) bypasses cooldown — existing behavior preserved."""
|
|
from agent.conversation_compression import compress_context
|
|
|
|
compressor_mock = MagicMock()
|
|
compressor_mock._summary_failure_cooldown_until = time.monotonic() + 60.0
|
|
compressor_mock._last_summary_error = "session_split_failed"
|
|
compressor_mock._cooldown_persist_failed = False
|
|
compressor_mock._anti_thrash_recovery_deadline = 0.0
|
|
compressor_mock._consecutive_ineffective_compressions = 0
|
|
compressor_mock._summary_failure_streak = 0
|
|
compressor_mock._last_compaction_boundary_tokens = None
|
|
compressor_mock._verify_compaction_cleared_threshold = False
|
|
compressor_mock._proactive_prune_rearm_tokens = None
|
|
compressor_mock.compression_count = 0
|
|
compressor_mock.threshold_tokens = 10000
|
|
compressor_mock.context_length = 128000
|
|
compressor_mock.tail_token_budget = 25000
|
|
compressor_mock.summary_target_ratio = 0.1
|
|
compressor_mock._last_cooldown_refresh_was_authoritative = None
|
|
|
|
agent_mock = MagicMock()
|
|
agent_mock.context_compressor = compressor_mock
|
|
agent_mock.compression_enabled = True
|
|
agent_mock.session_id = "test-session"
|
|
agent_mock._session_db = MagicMock()
|
|
agent_mock._session_db.try_acquire_compression_lock.return_value = False
|
|
|
|
messages = [{"role": "user", "content": "hello"}]
|
|
|
|
result = compress_context(
|
|
agent_mock,
|
|
messages,
|
|
None,
|
|
force=True,
|
|
task_id="test",
|
|
)
|
|
|
|
assert result is not None
|