fix(gateway): stop per-turn agent-cache eviction from model + message_id signature churn

Two independent bugs evicted the cached gateway AIAgent on every turn,
preventing the prompt cache from ever warming:

1. Model normalization mismatch: the post-run fallback-eviction check
   compared _agent.model (stripped in AIAgent.__init__) against the raw
   _resolve_gateway_model() config string. For vendor-prefixed config on
   native providers (e.g. 'deepseek/deepseek-v4-pro' vs 'deepseek-v4-pro')
   this was always unequal, so the agent was evicted after every
   successful run. Normalize _cfg_model the same way (skip aggregators).

2. Discord triggering message_id leaked into the cached system prompt via
   build_session_context_prompt()'s Discord IDs block. message_id changes
   every turn, so the agent-cache signature (computed from the ephemeral
   prompt) changed every Discord turn -> rebuild every message. The id is
   now injected per-turn into the user message (where per-turn content
   belongs and does not touch the cache signature); the cached IDs block
   carries a static pointer to it, preserving reply/react/pin via the
   discord tools.

Adapted from #28846. Bug #1 fix is the contributor's; bug #2 reworked to
be non-destructive (keeps the triggering-id capability instead of deleting
it). Redundant auto-reset eviction (already on main via #9893/#48031) and
the wrong-premise reset_context_note plumbing from the original PR were
dropped.

Co-authored-by: Hermes Agent <hermes@nousresearch.com>
This commit is contained in:
fayenix 2026-06-30 03:41:23 -07:00 committed by Teknium
parent e7ca53e6b8
commit d6c53dcdcb
4 changed files with 91 additions and 1 deletions

View file

@ -235,6 +235,48 @@ class TestBuildSessionContextPrompt:
assert "Discord" in prompt
assert "cannot search" in prompt.lower() or "do not have access" in prompt.lower()
def test_discord_prompt_stable_across_message_id(self):
"""The cached system prompt must NOT vary with the triggering message_id.
message_id changes every turn; baking it into the Discord IDs block
busts the gateway agent-cache signature and rebuilds the AIAgent on
every message (destroying prompt caching). The volatile id is injected
per-turn into the user message instead the cached block only carries
a static pointer.
"""
from unittest.mock import patch
import gateway.session as _gs
config = GatewayConfig(
platforms={
Platform.DISCORD: PlatformConfig(enabled=True, token="fake-d...oken"),
},
)
def _prompt_for(msg_id):
source = SessionSource(
platform=Platform.DISCORD,
chat_id="chan-1",
chat_name="Server",
chat_type="group",
user_name="alice",
guild_id="guild-123",
message_id=msg_id,
)
ctx = build_session_context(source, config)
return build_session_context_prompt(ctx)
# Force the Discord IDs block on (it only emits when discord tools load).
with patch.object(_gs, "_discord_tools_loaded", return_value=True):
p1 = _prompt_for("1001")
p2 = _prompt_for("2002")
p3 = _prompt_for("3003")
assert p1 == p2 == p3, "system prompt must be stable across message_id"
assert "1001" not in p1 and "2002" not in p2 and "3003" not in p3
# Static pointer tells the agent where the volatile id actually lives.
assert "provided per-turn in the incoming user message" in p1
def test_slack_prompt_includes_platform_notes(self):
config = GatewayConfig(
platforms={