Change-detectors, tautologies, source-reading tests, redundant duplicates, mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane ledger (category + reason for every removal).
228 lines
8.7 KiB
Python
228 lines
8.7 KiB
Python
"""#84733: prompt-cache TTL/prefix propagation into MoA/aux paths + failover re-preflight.
|
|
|
|
The main loop threads ``agent._cache_ttl`` and the stable system prefix into
|
|
``build_prompt_cache_plan``, but the MoA/aux helper only accepted
|
|
``cache_disabled`` — so a configured ``1h`` regressed to the 5m default and
|
|
the destination system prompt was marked as one whole breakpoint. These
|
|
tests pin the threaded parameters (TTL + static prefix) on
|
|
``plan_cache_sections_for_destination`` and the MoA decoration helper, the
|
|
per-destination Qwen clamp (1h -> 5m), and the failover re-preflight
|
|
contract (every fallback activation must restart the outer iteration so the
|
|
pre-API preflight re-runs against the fallback's context window).
|
|
"""
|
|
|
|
|
|
|
|
def _collect_cache_controls(obj):
|
|
"""Return every ``cache_control`` marker dict reachable in ``obj``."""
|
|
markers = []
|
|
if isinstance(obj, dict):
|
|
if "cache_control" in obj:
|
|
markers.append(obj["cache_control"])
|
|
for value in obj.values():
|
|
markers.extend(_collect_cache_controls(value))
|
|
elif isinstance(obj, list):
|
|
for value in obj:
|
|
markers.extend(_collect_cache_controls(value))
|
|
return markers
|
|
|
|
|
|
class TestPlanCacheSectionsThreadsTtlAndPrefix:
|
|
def test_cache_ttl_1h_reaches_markers(self):
|
|
from agent.agent_runtime_helpers import plan_cache_sections_for_destination
|
|
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "hello"},
|
|
]
|
|
out_msgs, _ = plan_cache_sections_for_destination(
|
|
messages,
|
|
None,
|
|
provider="anthropic",
|
|
base_url="https://api.anthropic.com",
|
|
api_mode="anthropic_messages",
|
|
model="claude-opus-4.8",
|
|
cache_disabled=False,
|
|
cache_ttl="1h",
|
|
)
|
|
markers = _collect_cache_controls(out_msgs)
|
|
assert markers, "expected cache_control markers on a caching route"
|
|
assert all(m.get("ttl") == "1h" for m in markers), (
|
|
"the configured 1h tier must reach the destination plan markers"
|
|
)
|
|
|
|
def test_static_system_prefix_gets_early_breakpoint(self):
|
|
from agent.agent_runtime_helpers import plan_cache_sections_for_destination
|
|
|
|
messages = [
|
|
{"role": "system", "content": "stable prefix\nvolatile suffix"},
|
|
{"role": "user", "content": "hello"},
|
|
]
|
|
out_msgs, _ = plan_cache_sections_for_destination(
|
|
messages,
|
|
None,
|
|
provider="anthropic",
|
|
base_url="https://api.anthropic.com",
|
|
api_mode="anthropic_messages",
|
|
model="claude-opus-4.8",
|
|
cache_disabled=False,
|
|
cache_ttl="5m",
|
|
static_system_prefix="stable prefix",
|
|
)
|
|
system_content = out_msgs[0]["content"]
|
|
assert isinstance(system_content, list) and len(system_content) == 2, (
|
|
"the destination system prompt must split into [static, volatile] "
|
|
"parts instead of marking the whole prompt as one breakpoint"
|
|
)
|
|
assert system_content[0]["text"] == "stable prefix"
|
|
assert system_content[1]["text"] == "\nvolatile suffix"
|
|
|
|
def test_qwen_1h_clamped_to_5m(self):
|
|
from agent.agent_runtime_helpers import plan_cache_sections_for_destination
|
|
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "hello"},
|
|
]
|
|
out_msgs, _ = plan_cache_sections_for_destination(
|
|
messages,
|
|
None,
|
|
provider="opencode",
|
|
base_url="https://api.opencode.ai",
|
|
api_mode="chat_completions",
|
|
model="qwen3.6-plus",
|
|
cache_disabled=False,
|
|
cache_ttl="1h",
|
|
)
|
|
markers = _collect_cache_controls(out_msgs)
|
|
assert markers, "opencode+qwen is a cache-honoring route"
|
|
assert all("ttl" not in m for m in markers), (
|
|
"Qwen's 5-minute-only context cache must clamp a configured 1h"
|
|
)
|
|
|
|
|
|
class TestMoACacheControlThreadsTtl:
|
|
def test_moa_decoration_uses_threaded_1h(self):
|
|
from agent.moa_loop import _maybe_apply_moa_cache_control
|
|
|
|
messages = [
|
|
{"role": "system", "content": "sys"},
|
|
{"role": "user", "content": "q1"},
|
|
{"role": "assistant", "content": "a1"},
|
|
{"role": "user", "content": "q2"},
|
|
]
|
|
runtime = {
|
|
"provider": "anthropic",
|
|
"model": "claude-opus-4.8",
|
|
"base_url": "",
|
|
"api_mode": "anthropic_messages",
|
|
}
|
|
out = _maybe_apply_moa_cache_control(
|
|
messages, runtime, cache_disabled=False, cache_ttl="1h"
|
|
)
|
|
markers = _collect_cache_controls(out)
|
|
assert markers, "expected MoA decoration on a caching route"
|
|
assert all(m.get("ttl") == "1h" for m in markers), (
|
|
"the agent's 1h tier must stop regressing to 5m on MoA advisor calls"
|
|
)
|
|
# Caller messages must stay undecorated.
|
|
assert not _collect_cache_controls(messages)
|
|
|
|
def test_moa_qwen_1h_clamped_to_5m(self):
|
|
from agent.moa_loop import _maybe_apply_moa_cache_control
|
|
|
|
messages = [
|
|
{"role": "system", "content": "sys"},
|
|
{"role": "user", "content": "q1"},
|
|
]
|
|
runtime = {
|
|
"provider": "opencode",
|
|
"model": "qwen3.6-plus",
|
|
"base_url": "",
|
|
"api_mode": "chat_completions",
|
|
}
|
|
out = _maybe_apply_moa_cache_control(
|
|
messages, runtime, cache_disabled=False, cache_ttl="1h"
|
|
)
|
|
markers = _collect_cache_controls(out)
|
|
assert markers, "opencode+qwen is a cache-honoring MoA route"
|
|
assert all("ttl" not in m for m in markers), (
|
|
"MoA decoration must clamp 1h to 5m on Qwen destinations"
|
|
)
|
|
|
|
def test_moa_decoration_defaults_to_5m_without_ttl(self):
|
|
from agent.moa_loop import _maybe_apply_moa_cache_control
|
|
|
|
messages = [
|
|
{"role": "system", "content": "sys"},
|
|
{"role": "user", "content": "q1"},
|
|
]
|
|
runtime = {
|
|
"provider": "anthropic",
|
|
"model": "claude-opus-4.8",
|
|
"base_url": "",
|
|
"api_mode": "anthropic_messages",
|
|
}
|
|
out = _maybe_apply_moa_cache_control(
|
|
messages, runtime, cache_disabled=False
|
|
)
|
|
markers = _collect_cache_controls(out)
|
|
assert markers
|
|
assert all("ttl" not in m for m in markers)
|
|
|
|
|
|
class TestAuxFallbackReplanThreadsTtl:
|
|
"""#84733 follow-up: the auxiliary fallback replan path threads the
|
|
configured tier too — it has no live agent, so it reads the same
|
|
config key agent_init snapshots into ``agent._cache_ttl``."""
|
|
|
|
def test_configured_cache_ttl_reads_valid_tiers(self, monkeypatch):
|
|
import agent.agent_runtime_helpers as arh
|
|
|
|
monkeypatch.setattr(
|
|
"hermes_cli.config.load_config_readonly",
|
|
lambda: {"prompt_caching": {"cache_ttl": "1h"}},
|
|
)
|
|
assert arh.configured_cache_ttl() == "1h"
|
|
monkeypatch.setattr(
|
|
"hermes_cli.config.load_config_readonly",
|
|
lambda: {"prompt_caching": {"cache_ttl": "5m"}},
|
|
)
|
|
assert arh.configured_cache_ttl() == "5m"
|
|
|
|
def test_configured_cache_ttl_none_for_disabled_or_unknown(self, monkeypatch):
|
|
import agent.agent_runtime_helpers as arh
|
|
|
|
for value in ("off", False, None, "2h"):
|
|
monkeypatch.setattr(
|
|
"hermes_cli.config.load_config_readonly",
|
|
lambda value=value: {"prompt_caching": {"cache_ttl": value}},
|
|
)
|
|
assert arh.configured_cache_ttl() is None, value
|
|
|
|
def test_replan_threads_configured_ttl_to_markers(self, monkeypatch):
|
|
from agent import auxiliary_client
|
|
|
|
monkeypatch.setattr(
|
|
"hermes_cli.config.load_config_readonly",
|
|
lambda: {"prompt_caching": {"cache_ttl": "1h"}},
|
|
)
|
|
destination = auxiliary_client._FallbackDestination(
|
|
"anthropic",
|
|
"https://api.anthropic.com",
|
|
"anthropic_messages",
|
|
"claude-opus-4.8",
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "hello"},
|
|
]
|
|
out_msgs, _ = auxiliary_client._replan_synchronous_cache_sections(
|
|
messages, None, destination=destination
|
|
)
|
|
markers = _collect_cache_controls(out_msgs)
|
|
assert markers, "expected cache_control markers on a caching route"
|
|
assert all(m.get("ttl") == "1h" for m in markers), (
|
|
"the configured 1h tier must reach auxiliary fallback replans"
|
|
)
|