Files
hermes-agent/tests/agent/test_prompt_cache_ttl_auto.py
teknium1 23301d705c feat(caching): prompt_caching.cache_ttl: auto picks the 1h tier for human-paced sessions, 5m for machine-paced ones
The 1h Anthropic cache tier writes at 2x base (5m: 1.25x) and only pays off when
turns are more than five minutes apart. That is exactly the shape of an interactive
session a person parks and resumes, and exactly not the shape of a subagent, cron
run, one-shot or webhook that calls every few seconds and is gone. A single global
`cache_ttl` cannot be right for both, so operators leave it on 5m and pay a full
context re-write every time they come back to a CLI session after a coffee.

Measured on one install (2 days of per-call API logs, Claude via the Nous route):
63% of interactive cache-write tokens were cold re-writes after a 5-60 minute idle
gap; 1h would cut interactive write cost ~42% while costing ~49% more on subagents
and ~23% more on cron. `auto` resolves once per session from the session source
(`_session_source_for_agent`): 1h for cli/tui/desktop/messaging platforms, 5m for
subagent, cron, oneshot, webhook, kanban, api. Auxiliary/stub calls keep 5m; the
delegate_tool child clamp (#104168) still applies. Default stays "5m".
2026-09-21 15:07:39 -07:00

42 lines
2.2 KiB
Python

"""``prompt_caching.cache_ttl: auto`` picks the Anthropic cache tier by who paces the session.
The 1h tier writes at 2x base (5m: 1.25x) and only pays off across >5-minute pauses, i.e.
for a person; machine-paced sessions (subagent, cron, oneshot, webhook, ...) never collect the
retention. ``auto`` resolves once per session in ``_init_prompt_cache_config``.
"""
from agent.prompt_caching import MACHINE_PACED_SOURCES, auto_cache_ttl_for_source
def test_auto_tier_follows_session_pace():
assert auto_cache_ttl_for_source("cli") == "1h"
assert auto_cache_ttl_for_source("telegram") == "1h"
assert auto_cache_ttl_for_source(None) == "1h", "an unknown/blank source is a person until proven otherwise"
for source in MACHINE_PACED_SOURCES:
assert auto_cache_ttl_for_source(source) == "5m", source
def test_real_agent_resolves_auto_from_its_platform(tmp_path, monkeypatch):
"""A real AIAgent under ``cache_ttl: auto``: a CLI session lands on 1h, a cron session on 5m, and
caching itself stays enabled on both (auto is a tier choice, never a disable)."""
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
monkeypatch.delenv("HERMES_SESSION_SOURCE", raising=False)
(tmp_path / "config.yaml").write_text(
"prompt_caching:\n cache_ttl: auto\nmodel:\n default: anthropic/claude-sonnet-4.6\n", encoding="utf-8")
from run_agent import AIAgent
kw = dict(api_key="k", base_url="https://openrouter.ai/api/v1", provider="openrouter",
api_mode="chat_completions", model="anthropic/claude-sonnet-4.6", quiet_mode=True,
skip_context_files=True, skip_memory=True, save_trajectories=False, enabled_toolsets=["file"])
interactive = AIAgent(session_id="p-cli", platform="cli", **kw)
scheduled = AIAgent(session_id="p-cron", platform="cron", **kw)
try:
assert interactive._cache_ttl == "1h"
assert scheduled._cache_ttl == "5m"
assert interactive._use_prompt_caching and scheduled._use_prompt_caching
assert not interactive._cache_disabled and not scheduled._cache_disabled
finally:
for a in (interactive, scheduled):
close = getattr(a, "close", None)
if callable(close):
close()