fix(gateway): match _quick_key to _generate_session_key for WhatsApp DMs

This commit is contained in:
Farukest
2026-03-04 06:34:46 +03:00
parent 556a132f2d
commit e39de2e752
2 changed files with 94 additions and 7 deletions

View File

@@ -637,11 +637,12 @@ class GatewayRunner:
# PRIORITY: If an agent is already running for this session, interrupt it
# immediately. This is before command parsing to minimize latency -- the
# user's "stop" message reaches the agent as fast as possible.
_quick_key = (
f"agent:main:{source.platform.value}:{source.chat_type}:{source.chat_id}"
if source.chat_type != "dm"
else f"agent:main:{source.platform.value}:dm"
)
if source.chat_type != "dm":
_quick_key = f"agent:main:{source.platform.value}:{source.chat_type}:{source.chat_id}"
elif source.platform.value == "whatsapp" and source.chat_id:
_quick_key = f"agent:main:{source.platform.value}:dm:{source.chat_id}"
else:
_quick_key = f"agent:main:{source.platform.value}:dm"
if _quick_key in self._running_agents:
running_agent = self._running_agents[_quick_key]
logger.debug("PRIORITY interrupt for session %s", _quick_key[:20])
@@ -1361,8 +1362,12 @@ class GatewayRunner:
async def _handle_usage_command(self, event: MessageEvent) -> str:
"""Handle /usage command -- show token usage for the session's last agent run."""
source = event.source
session_key = f"agent:main:{source.platform.value}:" + \
(f"dm" if source.chat_type == "dm" else f"{source.chat_type}:{source.chat_id}")
if source.chat_type != "dm":
session_key = f"agent:main:{source.platform.value}:{source.chat_type}:{source.chat_id}"
elif source.platform.value == "whatsapp" and source.chat_id:
session_key = f"agent:main:{source.platform.value}:dm:{source.chat_id}"
else:
session_key = f"agent:main:{source.platform.value}:dm"
agent = self._running_agents.get(session_key)
if agent and hasattr(agent, "session_total_tokens") and agent.session_api_calls > 0:

View File

@@ -314,6 +314,88 @@ class TestSessionStoreRewriteTranscript:
assert reloaded == []
class TestWhatsAppDMSessionKeyConsistency:
"""Regression: inline session-key construction in handle_message must match
_generate_session_key for WhatsApp DMs, which include chat_id."""
@pytest.fixture()
def store(self, tmp_path):
config = GatewayConfig()
with patch("gateway.session.SessionStore._ensure_loaded"):
s = SessionStore(sessions_dir=tmp_path, config=config)
s._db = None
s._loaded = True
return s
def _build_quick_key(self, source: SessionSource) -> str:
"""Reproduce the _quick_key logic from gateway/run.py handle_message."""
if source.chat_type != "dm":
return f"agent:main:{source.platform.value}:{source.chat_type}:{source.chat_id}"
elif source.platform.value == "whatsapp" and source.chat_id:
return f"agent:main:{source.platform.value}:dm:{source.chat_id}"
else:
return f"agent:main:{source.platform.value}:dm"
def _build_usage_key(self, source: SessionSource) -> str:
"""Reproduce the session_key logic from _handle_usage_command."""
if source.chat_type != "dm":
return f"agent:main:{source.platform.value}:{source.chat_type}:{source.chat_id}"
elif source.platform.value == "whatsapp" and source.chat_id:
return f"agent:main:{source.platform.value}:dm:{source.chat_id}"
else:
return f"agent:main:{source.platform.value}:dm"
def test_whatsapp_dm_quick_key_includes_chat_id(self, store):
source = SessionSource(
platform=Platform.WHATSAPP,
chat_id="15551234567@s.whatsapp.net",
chat_type="dm",
user_name="Phone User",
)
real_key = store._generate_session_key(source)
quick_key = self._build_quick_key(source)
assert quick_key == real_key
assert "15551234567@s.whatsapp.net" in quick_key
def test_whatsapp_dm_usage_key_includes_chat_id(self, store):
source = SessionSource(
platform=Platform.WHATSAPP,
chat_id="15551234567@s.whatsapp.net",
chat_type="dm",
user_name="Phone User",
)
real_key = store._generate_session_key(source)
usage_key = self._build_usage_key(source)
assert usage_key == real_key
assert "15551234567@s.whatsapp.net" in usage_key
def test_telegram_dm_key_unchanged(self, store):
"""Non-WhatsApp DMs should still omit chat_id (single owner DM)."""
source = SessionSource(
platform=Platform.TELEGRAM,
chat_id="99",
chat_type="dm",
)
real_key = store._generate_session_key(source)
quick_key = self._build_quick_key(source)
usage_key = self._build_usage_key(source)
assert quick_key == real_key == "agent:main:telegram:dm"
assert usage_key == real_key
def test_discord_group_key_unchanged(self, store):
"""Group/channel keys should be unaffected by the fix."""
source = SessionSource(
platform=Platform.DISCORD,
chat_id="guild-123",
chat_type="group",
)
real_key = store._generate_session_key(source)
quick_key = self._build_quick_key(source)
usage_key = self._build_usage_key(source)
assert quick_key == real_key == "agent:main:discord:group:guild-123"
assert usage_key == real_key
class TestSessionStoreEntriesAttribute:
"""Regression: /reset must access _entries, not _sessions."""