Skip to content

Commit c36a54e

Browse files
committed
fix(sessions): clean up branch-only messages on delete
1 parent 4c3de2d commit c36a54e

2 files changed

Lines changed: 53 additions & 3 deletions

File tree

src/agents/extensions/memory/advanced_sqlite_session.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,14 +786,19 @@ def _delete_sync():
786786

787787
structure_deleted = cursor.rowcount
788788

789+
orphaned_messages_deleted = self._cleanup_orphaned_messages_sync(conn)
790+
789791
conn.commit()
790792

791-
return usage_deleted, structure_deleted
793+
return usage_deleted, structure_deleted, orphaned_messages_deleted
792794

793-
usage_deleted, structure_deleted = await asyncio.to_thread(_delete_sync)
795+
usage_deleted, structure_deleted, orphaned_messages_deleted = await asyncio.to_thread(
796+
_delete_sync
797+
)
794798

795799
self._logger.info(
796-
f"Deleted branch '{branch_id}': {structure_deleted} message entries, {usage_deleted} usage entries" # noqa: E501
800+
f"Deleted branch '{branch_id}': {structure_deleted} message entries, "
801+
f"{usage_deleted} usage entries, {orphaned_messages_deleted} orphaned messages"
797802
)
798803

799804
async def list_branches(self) -> list[dict[str, Any]]:

tests/extensions/memory/test_advanced_sqlite_session.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,51 @@ async def test_branching_functionality(agent: Agent):
442442
session.close()
443443

444444

445+
async def test_delete_branch_removes_branch_only_messages():
446+
"""Deleting a branch should not leave unreferenced branch-only messages behind."""
447+
session_id = "branch_delete_cleanup_test"
448+
session = AdvancedSQLiteSession(session_id=session_id, create_tables=True)
449+
450+
main_items: list[TResponseInputItem] = [
451+
{"role": "user", "content": "First question"},
452+
{"role": "assistant", "content": "First answer"},
453+
{"role": "user", "content": "Second question"},
454+
{"role": "assistant", "content": "Second answer"},
455+
]
456+
await session.add_items(main_items)
457+
458+
await session.create_branch_from_turn(2, "cleanup_branch")
459+
branch_items: list[TResponseInputItem] = [
460+
{"role": "user", "content": "Branch-only question"},
461+
{"role": "assistant", "content": "Branch-only answer"},
462+
]
463+
await session.add_items(branch_items)
464+
465+
await session.delete_branch("cleanup_branch", force=True)
466+
467+
with session._locked_connection() as conn:
468+
rows = conn.execute(
469+
f"""
470+
SELECT message_data
471+
FROM {session.messages_table}
472+
WHERE session_id = ?
473+
ORDER BY id
474+
""",
475+
(session.session_id,),
476+
).fetchall()
477+
478+
contents = [json.loads(message_data)["content"] for (message_data,) in rows]
479+
assert contents == [
480+
"First question",
481+
"First answer",
482+
"Second question",
483+
"Second answer",
484+
]
485+
assert await session.get_items(branch_id="main") == main_items
486+
487+
session.close()
488+
489+
445490
async def test_get_conversation_turns():
446491
"""Test get_conversation_turns functionality."""
447492
session_id = "conversation_turns_test"

0 commit comments

Comments
 (0)