fix(compression): keep default protect_first_n at 3 + align ABC

Follow-up on the salvaged feat commit:

- Keep the constructor / config / yaml-example default at 3 so existing
  gateway and CLI users see no behavioural change. PR #13754 (which this
  builds on) had lowered the default to 2 to chase pre-feature parity in
  the system-prompt-present case, at the cost of quietly halving the
  protected head for the gateway path (which strips the system prompt
  before calling compress()). With the new "system prompt is implicit"
  semantics, default 3 gives every caller a stable head shape.
- agent/context_engine.py: bring the ABC's protect_first_n docstring in
  line with the new semantics so plugin context engines interpret the
  config key the same way the built-in compressor does.
- tests: adjust the default-value test (3, not 2) and a stale comment;
  per-test protect_first_n=2/3/1 values added in PR #13754 stay as-is
  since those tests fix concrete head shapes.
This commit is contained in:
teknium1 2026-05-13 22:22:21 -07:00 committed by Teknium
parent dee71a31e5
commit 4ceab16893
6 changed files with 22 additions and 14 deletions

View file

@ -1299,14 +1299,16 @@ class TestSummaryTargetRatio:
c = ContextCompressor(model="test", quiet_mode=True)
assert c.protect_last_n == 20
def test_default_protect_first_n_is_2(self):
"""Default protect_first_n is 2 (system + 2 extra non-system messages =
3 protected messages total, preserving the pre-feature behaviour where
protect_first_n was hardcoded to protect 3 head messages total).
def test_default_protect_first_n_is_3(self):
"""Default protect_first_n is 3 (system + 3 extra non-system messages =
4 protected messages total when a system prompt is present). With the
new semantics, the constructor default is 3 the system prompt is
always implicitly protected ON TOP OF protect_first_n non-system
messages.
"""
with patch("agent.context_compressor.get_model_context_length", return_value=100_000):
c = ContextCompressor(model="test", quiet_mode=True)
assert c.protect_first_n == 2
assert c.protect_first_n == 3
def test_protect_first_n_override(self):
"""protect_first_n=0 should be honoured — for users who rely on rolling
@ -1342,8 +1344,8 @@ class TestSummaryTargetRatio:
assert result[0]["content"].startswith("System prompt")
# The first user/assistant exchange (msg 0, msg 1) should NOT be pinned
# as head verbatim — those would have been summarized or absorbed.
# Under default protect_first_n=2, result[1] and result[2] would be
# the literal "msg 0" / "msg 1"; with protect_first_n=0 they aren't.
# Under default protect_first_n=3, result[1..3] would be the literal
# "msg 0" / "msg 1" / "msg 2"; with protect_first_n=0 they aren't.
assert result[1].get("content") != "msg 0"
# Last 2 messages are tail-protected under protect_last_n=2
assert result[-1]["content"] == msgs[-1]["content"]