Files
Teknium 36708d1efa perf(memory): one lazy models.dev parse and bounded long-lived caches
Shed only entries no reader can use, or the least recently used past a count far
above any real working set; nothing a live session needs is ever dropped.

- models_catalog_static: _PROVIDER_MODELS fills its xAI rows from the
  process-wide models.dev copy on the first xAI lookup (or a whole-table walk),
  never at import. main parsed the catalog at import and threw it away; an idle
  gateway now never parses it, and a reader parses it exactly once.
- model_metadata: the per-credential /models memo sweeps expired entries on
  write (rotated keys left one catalog each); the Codex OAuth context memos keep
  the newest 128 token fingerprints, the bound codex_service_tiers already uses.
- models_pricing: _cache_catalog drops catalogs past their deadline, as the
  reader already does for the one key it reads.
- skill_commands: per-(platform, profile, project) command maps are one LRU of
  64 scopes across all three tables; an evicted scope rescans on next use.
- auxiliary_client: async semaphores are held per event-loop object and a closed
  loop's table is dropped on the next acquire (main keyed by id(loop) and kept
  every loop forever, and a weak key cannot work: a semaphore that made a caller
  wait binds its loop). Checked on stdlib asyncio and uvloop 0.23.
- agent_runtime_helpers: the sanitizer-heal log counters are an LRU of 1024
  sessions; the told-once set and pending notices are never evicted.

Helpers live in agent/cache_bounds.py and agent/model_metadata_memo_bounds.py;
model_metadata.py does not grow.
2026-11-01 21:53:36 -08:00

49 lines
2.2 KiB
Python

"""Bounds for long-lived in-process caches that would otherwise grow for the life of a gateway.
Each helper sheds only what no reader can use (expired entries) or what nobody has touched in the
longest time (least recently used, past a count far above any real working set). Callers hold
their own lock where the cache is shared across threads; snapshots are taken before iterating.
"""
from typing import Any, Callable
def drop_expired(values: dict, stamps: dict, ttl: float, now: float) -> None:
"""Pop every ``values`` key whose stamp (missing = 0, like the readers) is ``ttl`` old."""
for key in [k for k in list(values) if now - stamps.get(k, 0) >= ttl]:
values.pop(key, None)
stamps.pop(key, None)
def evict_oldest(cache: dict, key: Any, stamp_of: Callable[[Any], float], limit: int, *siblings: dict) -> None:
"""Make room for a NEW ``key``: past ``limit``, drop the oldest-stamped key from ``cache`` and every sibling."""
if key in cache or len(cache) < limit:
return
oldest = min(list(cache.items()), key=lambda item: stamp_of(item[1]))[0]
for table in (cache, *siblings):
table.pop(oldest, None)
def publish_lru(table: dict, key: Any, value: Any, limit: int, *siblings: dict) -> list:
"""Store ``key`` as most recently used; past ``limit``, drop the least recently used keys from
``table`` and every sibling. Returns the evicted keys (for companions that are not dicts)."""
table.pop(key, None)
table[key] = value
evicted = []
while len(table) > limit:
stale = next(iter(table))
evicted.append(stale)
for scoped in (table, *siblings):
scoped.pop(stale, None)
return evicted
def live_loop_table(tables: dict, loop: Any, lock: Any) -> dict:
"""``loop``'s table in ``tables`` (keyed by event loop object); tables of loops closed since the last
call are dropped, since a closed loop never runs again. Keys are strong on purpose: an asyncio
primitive that ever made a caller wait binds its loop, so a weak key held by its own value never dies."""
with lock:
for stale in [other for other in tables if other is not loop and other.is_closed()]:
del tables[stale]
return tables.setdefault(loop, {})