Compaction: the model's notes replace the middle of a long run (#158)
Before each driver call, over compact_at (0.6) of the prompt budget, the same model is asked for notes over the very request it was about to get, plus one message, so the server's cache covers all but the notes. The notes, with code's list of the calls they replace, become a compaction event that renders in place of its range, keeping word for word what started the work and the last few turns. Rare, large steps keep the prompt's start unchanged between them. A later compaction folds in the earlier; one from an earlier run's view is hidden. Failed notes are logged and fitting takes over. Fitting now applies compactions before it measures, records what it cuts (Trim) and logs each cut as context.trimmed. Wired into both agents' drivers and the benchmark; nisse-dev's helper loop is a follow-up. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
This commit is contained in:
1 parent
fa8d93cf41
commit
cb638fe687
20 files changed
+952
-44
No files matched your search
@@ -9,6 +9,7 @@ import pytest
|
||||
from nisse_core.log.canonical import (
|
||||
AssistantText,
|
||||
CanonicalEvent,
|
||||
Compaction,
|
||||
StoredEvent,
|
||||
ToolCall,
|
||||
ToolResult,
|
||||
@@ -189,6 +190,46 @@ def test_reported_usage_only_ever_raises_the_estimate() -> None:
|
||||
assert under_then_over._factors["m"] == 2.0 # never loosened
|
||||
|
||||
|
||||
def test_a_request_that_fits_is_sent_whole_and_nothing_is_logged() -> None:
|
||||
stream = io.StringIO()
|
||||
client = BudgetedClient(
|
||||
RecordingClient(), lambda m: (100_000, None), oplog=OpLog("t", stream)
|
||||
)
|
||||
events = transcript(3, 100)
|
||||
client.complete(events, None, "m")
|
||||
assert client.last_trim is None
|
||||
assert stream.getvalue() == ""
|
||||
|
||||
|
||||
def test_a_cut_is_recorded_and_logged() -> None:
|
||||
stream = io.StringIO()
|
||||
client = BudgetedClient(
|
||||
RecordingClient(),
|
||||
lambda m: (4000, 500),
|
||||
keep_recent_turns=2,
|
||||
oplog=OpLog("t", stream),
|
||||
)
|
||||
client.complete(transcript(20, 3000), None, "m", system="system")
|
||||
assert client.last_trim is not None and client.last_trim.step == 2
|
||||
assert '"event": "context.trimmed"' in stream.getvalue()
|
||||
|
||||
|
||||
def test_a_compaction_counts_for_what_it_replaced() -> None:
|
||||
events = transcript(20, 3000)
|
||||
last = events[-1].id
|
||||
compaction = StoredEvent(
|
||||
id=last + 1,
|
||||
ts="t",
|
||||
event=Compaction(summary="notes", model="m", replaces=[2, last - 4]),
|
||||
)
|
||||
inner = RecordingClient()
|
||||
client = BudgetedClient(inner, lambda m: (8000, 500), keep_recent_turns=2)
|
||||
client.complete([*events, compaction], None, "m")
|
||||
assert client.last_trim is None # fits once the summary stands in
|
||||
sent = inner.calls[0]["events"]
|
||||
assert isinstance(sent[1].event, Compaction)
|
||||
|
||||
|
||||
def test_last_usage_passes_through() -> None:
|
||||
client = BudgetedClient(RecordingClient(reported_input=7), lambda m: (100, None))
|
||||
assert client.last_usage["input"] == 7
|
||||
|
||||
Reference in new issue
Block a user