Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions agent/context_compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2777,6 +2777,19 @@ def compress(self, messages: List[Dict[str, Any]], current_tokens: int = None, f
if summary_body and not self._previous_summary:
self._previous_summary = summary_body
turns_to_summarize = messages[max(compress_start, summary_idx + 1):compress_end]
if not turns_to_summarize:
self._ineffective_compression_count += 1
self._last_compression_savings_pct = 0.0
if not self.quiet_mode:
logger.warning(
"Compression skipped: latest context summary leaves no "
"new turns to summarize in window %d-%d. "
"ineffective_compression_count=%d",
compress_start,
compress_end,
self._ineffective_compression_count,
)
return messages
elif self._previous_summary:
# No handoff summary found in the current messages, but
# _previous_summary is non-empty — it was set by a different
Expand Down
31 changes: 31 additions & 0 deletions tests/agent/test_context_compressor_summary_continuity.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,34 @@ def fake_generate_summary(turns_to_summarize, focus_topic=None):
assert compressor._previous_summary == old_summary
assert seen_turns
assert all(old_summary not in str(msg.get("content", "")) for msg in seen_turns)


def test_empty_post_handoff_window_noops_without_summary_call():
"""A latest handoff that consumes the window must not trigger an empty summary."""
compressor = _compressor()
old_summary = "WINDOW-END-SUMMARY durable facts already captured"
messages = [
{"role": "system", "content": "system prompt"},
{"role": "user", "content": "protected first user turn"},
{"role": "assistant", "content": "old assistant turn already captured"},
{"role": "user", "content": f"{SUMMARY_PREFIX}\n{old_summary}"},
{"role": "assistant", "content": "recent tail response"},
{"role": "user", "content": "latest tail request"},
{"role": "assistant", "content": "latest tail answer"},
]

with (
patch.object(compressor, "_find_tail_cut_by_tokens", return_value=4),
patch.object(compressor, "_generate_summary") as mock_generate_summary,
):
result = compressor.compress(messages, current_tokens=90_000)

mock_generate_summary.assert_not_called()
assert result == messages
assert compressor._previous_summary == old_summary
assert compressor.compression_count == 0
assert compressor._ineffective_compression_count == 1
assert compressor._last_compression_savings_pct == 0.0
assert compressor._last_summary_dropped_count == 0
assert compressor._last_summary_fallback_used is False
assert compressor._last_compress_aborted is False