import json import pytest from ollie_assistants.assistant.agent_loop import AgentLoop from ollie_assistants.assistant.tools import default_tool_registry from ollie_assistants.llm.types import ChatConfig, ChatMessage, ChatResult, Role from ollie_assistants.memory.store import InMemoryStore from ollie_assistants.memory.tools import MemoryTools from ollie_assistants.safety.guardrails import GuardrailService class CapturingProvider: provider_id = "test" def __init__(self) -> None: self.messages = [] self.calls = [] self.main_messages = [] async def chat(self, messages, config: ChatConfig) -> ChatResult: self.calls.append(list(messages)) self.messages = list(messages) system_text = messages[0].content if messages and messages[0].role == Role.SYSTEM else "" if "should_search" in system_text: return ChatResult( text=json.dumps( { "should_search": True, "query": "answer style preference", "reason": "user asks remembered preference", } ) ) if "should_write" in system_text: return ChatResult( text=json.dumps( { "should_write": True, "content": "I prefer concise answers", "kind": "preference", "reason": "stable preference", } ) ) self.main_messages = list(messages) return ChatResult(text="ok") @pytest.mark.asyncio async def test_agent_loop_executes_memory_tool_before_llm() -> None: guardrails = GuardrailService() provider = CapturingProvider() memory_tools = MemoryTools(InMemoryStore()) memory_tools.remember_user_fact("s1", "I prefer concise answers", "setup", "preference") loop = AgentLoop( provider, default_tool_registry(memory_tools), guardrails, ) safety = guardrails.check_input("what is my answer style preference?") result = await loop.run( run_id="run-1", session_id="s1", model="test", max_new_tokens=10, messages=[ChatMessage(Role.USER, "what is my answer style preference?")], user_message="what is my answer style preference?", safety_decision=safety, ) tool_names = [tool.name for tool in result.tool_results] assert "search_memory" in tool_names assert "remember_user_fact" in tool_names assert all(message.role != Role.TOOL for message in provider.main_messages) assert any("Relevant memory" in message.content for message in provider.main_messages) assert any(event.name == "memory_search_decision" for event in result.events) assert any(event.name == "memory_write_decision" for event in result.events)