perf(agent): the token memo keeps no more idle entries than it always had

#147819 raised the limits to 65,536 entries / 64 MiB, so closed sessions could hold more than main
said 4,096 entries ever did. Both limits only evict entries idle for 60 s, so lowering them to 4,096 /
16 MiB costs a running conversation nothing.
This commit is contained in:
Teknium committed 2026-11-01 22:27:58 -08:00
1 parent 334fceea73
commit 4ea79443bc
2 files changed
+18 -6

No files matched your search

+7 -6
View File
@@ -3,10 +3,11 @@
Each entry pins every string its fingerprint holds (the pin keeps the ``id()`` key sound), so the memo is
bounded two ways, both least-recently-used first so a conversation that is still running never pays:
* a retained-bytes budget: pinned string bytes plus the fingerprint structure around them. A real-shaped
1M-token history retains about 9 MB, so the default keeps several maxed conversations hot at once while
the dead sessions of a long-lived process age out;
* an entry count, as a backstop for the bytes estimate.
* a retained-bytes budget: pinned string bytes plus the fingerprint structure around them;
* an entry count, the same 4,096 the memo always had.
Both bounds only govern IDLE entries (below), so they are set to hold no more than main's 4,096-entry memo
ever did: what a running conversation needs is protected by the active window, never by the budget.
Neither bound ever evicts an entry looked up in the last ``_ACTIVE_WINDOW_S`` seconds: a conversation larger
than the budget (image-heavy history) overshoots it while it runs instead of missing on every message, and the
@@ -28,8 +29,8 @@ from typing import Any
# fingerprint -> (pins, text tokens, image count). Order is recency: a hit moves its entry to the end, and an
# OrderedDict evicts from the front in O(1) where a plain dict rescans its deleted slots on every eviction.
_MSG_TOKENS_CACHE: OrderedDict[Any, tuple[list, int, int]] = OrderedDict()
_MSG_TOKENS_CACHE_MAX = 65536
_MSG_TOKENS_CACHE_MAX_BYTES = 64 * 1024 * 1024
_MSG_TOKENS_CACHE_MAX = 4096
_MSG_TOKENS_CACHE_MAX_BYTES = 16 * 1024 * 1024
# Measured on real histories: the fingerprint tuple, its id int and the pin slot cost ~120 B per string leaf.
_LEAF_OVERHEAD_BYTES = 120
# An entry looked up this recently is never evicted (see module docstring).
+11
View File
@@ -116,3 +116,14 @@ def test_a_hit_racing_eviction_never_makes_it_drop_a_different_active_entry(monk
memo.remember_message_tokens("new", (memo.MemoPins(["new text"]), 1, 0))
assert reader_done.wait(5)
assert "active" in cache and "new" in cache
def test_idle_entries_never_outnumber_the_memo_main_always_had(monkeypatch):
"""Closed sessions' entries (idle) stay within the original 4,096-entry footprint; only entries a running
conversation touched in the last minute may exceed it."""
mm._MSG_TOKENS_CACHE.clear()
now = _fake_clock(monkeypatch)
for i in range(6000):
now[0] += 61 # each message's session is idle before the next one arrives
mm.estimate_messages_tokens_rough([{"role": "tool", "content": f"closed-{i} " + "q" * 300}])
assert len(mm._MSG_TOKENS_CACHE) <= 4096