Skip to content

Commit 8acf77c

Browse files
cursor[bot]cursoragentMervinPraison
authored
fix: DbSessionAdapter.clear_session must purge persisted messages (#1737)
clear_session only cleared the in-memory cache while BotSessionManager and managed agents rewrite history via clear_session + add_message. Stale DB rows were reloaded on the next on_agent_start, resurrecting trimmed or cleared chat history. - Call conversation.delete_messages when available - Reset session tracking and skip stale on_agent_start hydration - Add regression test for bot history trim roundtrip Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
1 parent 5ff79d3 commit 8acf77c

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

src/praisonai-agents/tests/managed/test_managed_persistence.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,50 @@ def test_metadata_roundtrip(self):
349349
assert meta["agent_id"] == "abc"
350350
assert meta["total_input_tokens"] == 42
351351

352+
def test_clear_session_purges_db_history(self):
353+
"""clear_session must not leave stale messages reloadable from the DB."""
354+
from praisonai.integrations.db_session_adapter import DbSessionAdapter
355+
from praisonaiagents.db.protocol import DbMessage
356+
357+
persisted: list = []
358+
359+
mock_db = MagicMock()
360+
361+
def _on_start(agent_name, session_id):
362+
return list(persisted)
363+
364+
def _on_user(sid, content, **kw):
365+
persisted.append(DbMessage(role="user", content=content))
366+
367+
def _on_assistant(sid, content, **kw):
368+
persisted.append(DbMessage(role="assistant", content=content))
369+
370+
mock_db.on_agent_start.side_effect = _on_start
371+
mock_db.on_user_message.side_effect = _on_user
372+
mock_db.on_agent_message.side_effect = _on_assistant
373+
374+
conv = MagicMock()
375+
conv.delete_messages.side_effect = lambda sid, ids: persisted.clear()
376+
mock_db.conversation = conv
377+
378+
adapter = DbSessionAdapter(mock_db)
379+
sid = "bot-trim-session"
380+
adapter.add_message(sid, "user", "old")
381+
adapter.add_message(sid, "assistant", "old-reply")
382+
assert len(persisted) == 2
383+
384+
adapter.clear_session(sid)
385+
conv.delete_messages.assert_called_once_with(sid, None)
386+
assert persisted == []
387+
assert adapter.get_chat_history(sid) == []
388+
389+
adapter.add_message(sid, "user", "new-only")
390+
assert len(adapter.get_chat_history(sid)) == 1
391+
assert len(persisted) == 1
392+
393+
adapter2 = DbSessionAdapter(mock_db)
394+
assert adapter2.get_chat_history(sid) == [{"role": "user", "content": "new-only"}]
395+
352396

353397
# ===========================================================================
354398
# 7. ManagedAgent factory wiring

src/praisonai/praisonai/integrations/db_session_adapter.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,43 @@ def __init__(self, db: Any):
3737
self._sessions: Set[str] = set()
3838
self._metadata: Dict[str, Dict[str, Any]] = {}
3939
self._history_cache: Dict[str, List[Dict[str, str]]] = {}
40+
self._cleared_sessions: Set[str] = set()
41+
42+
def _conversation_store(self) -> Any:
43+
"""Optional ConversationStore behind the DbAdapter (if present)."""
44+
conv = getattr(self._db, "conversation", None)
45+
if conv is not None:
46+
return conv
47+
orchestrator = getattr(self._db, "_orchestrator", None)
48+
return getattr(orchestrator, "conversation", None) if orchestrator else None
49+
50+
def _purge_persisted_messages(self, session_id: str) -> None:
51+
"""Remove persisted messages when the session is cleared or deleted."""
52+
conv = self._conversation_store()
53+
if conv is not None and hasattr(conv, "delete_messages"):
54+
try:
55+
conv.delete_messages(session_id, None)
56+
except Exception as e:
57+
logger.warning(
58+
"[db_session_adapter] delete_messages failed for %s: %s",
59+
session_id,
60+
e,
61+
)
4062

4163
def _ensure_session(self, session_id: str, agent_name: str = "ManagedAgent") -> None:
4264
"""Ensure session exists in the DB adapter."""
4365
if session_id not in self._sessions:
66+
skip_history = session_id in self._cleared_sessions
67+
if skip_history:
68+
self._cleared_sessions.discard(session_id)
4469
try:
4570
msgs = self._db.on_agent_start(agent_name, session_id)
46-
if msgs:
71+
if msgs and not skip_history:
4772
self._history_cache[session_id] = [
4873
{"role": m.role, "content": m.content} for m in msgs
4974
]
75+
elif skip_history:
76+
self._history_cache[session_id] = []
5077
self._sessions.add(session_id)
5178
except Exception as e:
5279
logger.warning("[db_session_adapter] on_agent_start failed: %s", e)
@@ -99,14 +126,24 @@ def get_chat_history(
99126

100127
def clear_session(self, session_id: str) -> bool:
101128
"""Clear all messages from a session."""
102-
self._history_cache.pop(session_id, None)
129+
if session_id in self._sessions:
130+
try:
131+
self._db.on_agent_end(session_id)
132+
except Exception as e:
133+
logger.warning("[db_session_adapter] on_agent_end failed: %s", e)
134+
self._purge_persisted_messages(session_id)
135+
self._history_cache[session_id] = []
136+
self._sessions.discard(session_id)
137+
self._cleared_sessions.add(session_id)
103138
return True
104139

105140
def delete_session(self, session_id: str) -> bool:
106141
"""Delete a session completely."""
142+
self._purge_persisted_messages(session_id)
107143
self._history_cache.pop(session_id, None)
108144
self._metadata.pop(session_id, None)
109145
self._sessions.discard(session_id)
146+
self._cleared_sessions.discard(session_id)
110147
try:
111148
self._db.on_agent_end(session_id)
112149
except Exception:

0 commit comments

Comments
 (0)