fix(compact_20260112): carry prior compaction summary into Phase C summary call

When a request already contains a compaction block, Phase A slices
`effective_messages` to the turns since that block. Previously Phase C
passed the original `system` to the summary model, so multi-round
compaction silently dropped accumulated history each time the polyfill
fired. Pass `augmented_system` (original system + prior summary
prefix) so the summary model can produce a comprehensive summary that
incorporates both the prior round's context and the current slice.
`summarized_system` for the downstream call stays built from the
original `system` + new `summary_text`.
This commit is contained in:
mateo-berri 2026-05-28 03:56:10 +00:00
parent 57a15fce2f
commit 9e381e1e4f
No known key found for this signature in database
2 changed files with 47 additions and 2 deletions

View file

@ -620,10 +620,12 @@ async def apply_compact_20260112(
applied_edits=[applied],
)
# Phase C: summarize.
# Phase C: summarize. ``augmented_system`` carries any prior compaction
# summary so multi-round compaction does not lose accumulated history —
# ``effective_messages`` only contains turns since the last compaction.
prompt = _build_summary_prompt(edit_spec, tools)
summary_messages = _build_summary_messages(
effective_messages, prompt, system=system
effective_messages, prompt, system=augmented_system
)
propagated_metadata = _propagate_metadata(metadata)

View file

@ -896,6 +896,49 @@ async def test_system_prompt_forwarded_to_summary_call_as_content_blocks():
assert "Initial task: review PR #123." in content
async def test_summary_call_carries_prior_compaction_summary_into_system():
"""Multi-round: when a prior compaction block is present, the summary
model receives the augmented system (with ``Previous conversation
summary: <prior>``) so it can produce a comprehensive summary that
incorporates both the prior round's context and the current slice.
Without this, multi-round compaction would silently drop accumulated
history each time the polyfill fires.
"""
messages = _messages_with_compaction(summary="ROUND_ONE_SUMMARY_TEXT")
mock_response = _make_mock_response("<summary>Round two</summary>")
captured_calls: list = []
async def _fake_call_summary_model(**kwargs):
captured_calls.append(kwargs)
return mock_response
with (
patch(
"litellm.llms.anthropic.experimental_pass_through.context_management.editors.compact._read_summary_model_setting",
return_value="claude-haiku-4-5",
),
patch("litellm.token_counter", return_value=200_000),
patch(
"litellm.llms.anthropic.experimental_pass_through.context_management.editors.compact._call_summary_model",
side_effect=_fake_call_summary_model,
),
):
await apply_compact_20260112(
model=MODEL,
messages=messages,
tools=None,
system="Original agent role.",
edit_spec=_EDIT_SPEC_DEFAULT,
)
summary_messages = captured_calls[0]["summary_messages"]
assert summary_messages[0]["role"] == "system"
system_content = summary_messages[0]["content"]
assert "ROUND_ONE_SUMMARY_TEXT" in system_content
assert "Original agent role." in system_content
async def test_summary_call_omits_system_message_when_system_is_none():
"""No system message is prepended when the caller did not provide one."""
messages = _simple_messages()