diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index 023fc8ae..e3154281 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -1488,13 +1488,12 @@ class AgentManager: self, agent_id: str, actor: PydanticUser, add_default_initial_messages: bool = False, needs_agent_state: bool = True ) -> Optional[PydanticAgentState]: """ - Removes all in-context messages for the specified agent except the original system message by: + Clears all in-context messages for the specified agent except the original system message by: 1) Preserving the first message ID (original system message). - 2) Deleting all other messages for the agent. - 3) Updating the agent's message_ids to only contain the system message. - 4) Optionally adding default initial messages after the system message. + 2) Updating the agent's message_ids to only contain the system message. + 3) Optionally adding default initial messages after the system message. - This action is destructive and cannot be undone once committed. + Note: This only clears messages from the agent's context, it does not delete them from the database. Args: add_default_initial_messages: If true, adds the default initial messages after resetting. @@ -1517,11 +1516,6 @@ class AgentManager: raise ValueError(f"Agent {agent_id} has no message_ids - cannot preserve system message") system_message_id = agent.message_ids[0] - - await self.message_manager.delete_all_messages_for_agent_async(agent_id=agent_id, actor=actor, exclude_ids=[system_message_id]) - - async with db_registry.async_session() as session: - agent = await AgentModel.read_async(db_session=session, identifier=agent_id, actor=actor) agent.message_ids = [system_message_id] await agent.update_async(db_session=session, actor=actor) diff --git a/tests/managers/test_message_manager.py b/tests/managers/test_message_manager.py index d4a7708d..06b4e967 100644 --- a/tests/managers/test_message_manager.py +++ b/tests/managers/test_message_manager.py @@ -109,8 +109,8 @@ from tests.utils import random_string @pytest.mark.asyncio async def test_reset_messages_no_messages(server: SyncServer, sarah_agent, default_user): """ - Test that resetting messages on an agent that has zero messages - does not fail and clears out message_ids if somehow it's non-empty. + Test that resetting messages on an agent clears message_ids to only system message, + but messages remain in the database. """ assert len(sarah_agent.message_ids) == 4 og_message_ids = sarah_agent.message_ids @@ -119,15 +119,15 @@ async def test_reset_messages_no_messages(server: SyncServer, sarah_agent, defau reset_agent = await server.agent_manager.reset_messages_async(agent_id=sarah_agent.id, actor=default_user) assert len(reset_agent.message_ids) == 1 assert og_message_ids[0] == reset_agent.message_ids[0] - # Double check that physically no messages exist - assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 1 + # Messages should still exist in the database (only cleared from context, not deleted) + assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 4 @pytest.mark.asyncio async def test_reset_messages_default_messages(server: SyncServer, sarah_agent, default_user): """ - Test that resetting messages on an agent that has zero messages - does not fail and clears out message_ids if somehow it's non-empty. + Test that resetting messages with add_default_initial_messages=True + clears context and adds new default messages, while old messages remain in database. """ assert len(sarah_agent.message_ids) == 4 og_message_ids = sarah_agent.message_ids @@ -141,15 +141,16 @@ async def test_reset_messages_default_messages(server: SyncServer, sarah_agent, assert og_message_ids[1] != reset_agent.message_ids[1] assert og_message_ids[2] != reset_agent.message_ids[2] assert og_message_ids[3] != reset_agent.message_ids[3] - # Double check that physically no messages exist - assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 4 + # Old messages (4) + new default messages (3) = 7 total in database + # (system message is preserved, so 4 old + 3 new non-system = 7) + assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 7 @pytest.mark.asyncio async def test_reset_messages_with_existing_messages(server: SyncServer, sarah_agent, default_user): """ Test that resetting messages on an agent with actual messages - deletes them from the database and clears message_ids. + clears message_ids but keeps messages in the database. """ # 1. Create multiple messages for the agent msg1 = await server.message_manager.create_many_messages_async( @@ -185,11 +186,11 @@ async def test_reset_messages_with_existing_messages(server: SyncServer, sarah_a # 2. Reset all messages reset_agent = await server.agent_manager.reset_messages_async(agent_id=sarah_agent.id, actor=default_user) - # 3. Verify the agent now has zero message_ids + # 3. Verify the agent now has only system message in context assert len(reset_agent.message_ids) == 1 - # 4. Verify the messages are physically removed - assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 1 + # 4. Verify the messages still exist in the database (only cleared from context) + assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 6 @pytest.mark.asyncio @@ -197,7 +198,7 @@ async def test_reset_messages_idempotency(server: SyncServer, sarah_agent, defau """ Test that calling reset_messages multiple times has no adverse effect. """ - # Clear messages first + # Clear messages first (actually delete from DB for this test setup) await server.message_manager.delete_messages_by_ids_async(message_ids=sarah_agent.message_ids[1:], actor=default_user) # Create a single message @@ -211,15 +212,16 @@ async def test_reset_messages_idempotency(server: SyncServer, sarah_agent, defau ], actor=default_user, ) - # First reset + # First reset - clears context but messages remain in DB reset_agent = await server.agent_manager.reset_messages_async(agent_id=sarah_agent.id, actor=default_user) assert len(reset_agent.message_ids) == 1 - assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 1 + # DB has system message + the user message we created = 2 + assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 2 # Second reset should do nothing new reset_agent_again = await server.agent_manager.reset_messages_async(agent_id=sarah_agent.id, actor=default_user) - assert len(reset_agent.message_ids) == 1 - assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 1 + assert len(reset_agent_again.message_ids) == 1 + assert await server.message_manager.size_async(agent_id=sarah_agent.id, actor=default_user) == 2 @pytest.mark.asyncio