Skip to content

Commit f86e71c

Browse files
Workspace Userclaude
andcommitted
feat(config): auto_evict_tool_use_blocks — strip tool_use blocks after every turn
New Config field auto_evict_tool_use_blocks (default False). When True, the agent strips tool_use blocks from the just-persisted assistant row at the end of every turn, scoped to that row only (same pattern as auto_evict_after_write). Pinned rows skipped. ~870 bytes freed per block on average; safe because the model has already consumed the results by the time eviction fires. Enabled on all five persistent panel configs: substrate, theseus, majordomo, cognition-researcher, herald. 3 new tests; 373 passed, 5 skipped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f09d9b4 commit f86e71c

3 files changed

Lines changed: 56 additions & 0 deletions

File tree

src/mnemara/agent.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,25 @@ async def _stamping_on_tool_result(
345345
# Eviction failures must never crash a turn; audit-trail
346346
# eviction is opportunistic.
347347
log("auto_evict_pairs_error", error=str(exc))
348+
# Auto-evict tool_use blocks: if the toggle is on, strip all tool_use
349+
# blocks from the just-persisted assistant row after every turn.
350+
# ~870 bytes per block on average; safe since the model has already
351+
# consumed the results. Pinned rows skipped.
352+
if getattr(self.cfg, "auto_evict_tool_use_blocks", False):
353+
try:
354+
tu_result = self.store.evict_tool_use_blocks(
355+
ids=[assistant_row_id],
356+
skip_pinned=True,
357+
)
358+
if tu_result.get("blocks_evicted", 0) > 0:
359+
log(
360+
"auto_evict_tool_use",
361+
blocks=tu_result["blocks_evicted"],
362+
rows=tu_result["rows_modified"],
363+
bytes_freed=tu_result["bytes_freed"],
364+
)
365+
except Exception as exc:
366+
log("auto_evict_tool_use_error", error=str(exc))
348367
# Compress repeated Read results after every turn when the flag is on.
349368
# Outside the auto_evict_after_write guard so it fires on read-heavy
350369
# turns too, not just write turns. compress_repeated_reads is

src/mnemara/config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ class Config:
146146
# wherever the tool wrote; only the in-context audit body goes.
147147
# Off by default; opt-in per instance.
148148
auto_evict_after_write: bool = False
149+
# v0.3.2b — auto-evict tool_use blocks after every turn.
150+
# When True, after each turn the tool_use blocks from the just-persisted
151+
# assistant row are stripped entirely (audit-trail loss; see
152+
# evict_tool_use_blocks for the trade-off note). High byte savings
153+
# (~870 bytes per call on average) with zero effect on the turn that just
154+
# completed — the model has already processed the results. Pinned rows
155+
# are always skipped. Off by default; opt-in per instance.
156+
auto_evict_tool_use_blocks: bool = False
149157
# v0.3.3 — token-aware row-cap slack
150158
# When > 0, the cap-FIFO eviction loop allows n_turns to exceed
151159
# max_window_turns by up to this many rows, BUT only when current
@@ -323,6 +331,7 @@ def from_dict(cls, d: dict[str, Any]) -> "Config":
323331
rag_auto_index_memory=bool(d.get("rag_auto_index_memory", True)),
324332
rag_auto_index_wiki=bool(d.get("rag_auto_index_wiki", True)),
325333
auto_evict_after_write=bool(d.get("auto_evict_after_write", False)),
334+
auto_evict_tool_use_blocks=bool(d.get("auto_evict_tool_use_blocks", False)),
326335
row_cap_slack_when_token_headroom=int(
327336
d.get("row_cap_slack_when_token_headroom", 0)
328337
),

tests/test_smoke.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4671,6 +4671,34 @@ def test_config_auto_evict_after_write_missing_field_defaults_false():
46714671
assert cfg.auto_evict_after_write is False
46724672

46734673

4674+
def test_config_auto_evict_tool_use_blocks_default_false():
4675+
from mnemara.config import Config
4676+
4677+
cfg = Config()
4678+
assert cfg.auto_evict_tool_use_blocks is False
4679+
4680+
4681+
def test_config_auto_evict_tool_use_blocks_round_trips_through_dict():
4682+
from mnemara.config import Config
4683+
4684+
cfg = Config()
4685+
cfg.auto_evict_tool_use_blocks = True
4686+
d = cfg.to_dict()
4687+
assert d["auto_evict_tool_use_blocks"] is True
4688+
4689+
cfg2 = Config.from_dict(d)
4690+
assert cfg2.auto_evict_tool_use_blocks is True
4691+
4692+
4693+
def test_config_auto_evict_tool_use_blocks_missing_field_defaults_false():
4694+
"""Pre-existing config.json files without the field load as False."""
4695+
from mnemara.config import Config
4696+
4697+
minimal = {"role_doc_path": "", "model": "claude-opus-4-7"}
4698+
cfg = Config.from_dict(minimal)
4699+
assert cfg.auto_evict_tool_use_blocks is False
4700+
4701+
46744702
def test_config_default_includes_evict_write_pairs_policy():
46754703
"""Fresh instances get EvictWritePairs allowlisted by default."""
46764704
from mnemara.config import Config

0 commit comments

Comments
 (0)