"""The chat turn loop: persona + recalled memory + recent context -> reply. Each turn assembles the persona system prompt, semantically-relevant memories recalled from across all past sessions, and the recent turns of the current session, then asks the model for a reply and persists both sides. """ from __future__ import annotations from lyra import llm, memory, persona from lyra.llm import Backend, Message RECALL_K = 5 RECENT_N = 10 def _memory_note(exchanges: list[memory.Exchange]) -> Message: """Format recalled memories as a system note Lyra can draw on.""" lines = [] for ex in exchanges: when = ex.created_at[:10] # YYYY-MM-DD lines.append(f"- ({when}, {ex.role}) {ex.content}") body = "Relevant things you remember from past conversations:\n" + "\n".join(lines) return {"role": "system", "content": body} def build_messages(session_id: str, user_msg: str) -> list[Message]: """Assemble the full message list for one turn.""" messages: list[Message] = [{"role": "system", "content": persona.system_prompt()}] recent = memory.recent(session_id, n=RECENT_N) recent_ids = {ex.id for ex in recent} # Cross-session recall, minus anything already shown in the recent window. recalled = [ ex for ex in memory.recall(user_msg, k=RECALL_K) if ex.id not in recent_ids ] if recalled: messages.append(_memory_note(recalled)) for ex in recent: messages.append({"role": ex.role, "content": ex.content}) messages.append({"role": "user", "content": user_msg}) return messages def respond(session_id: str, user_msg: str, backend: Backend = "cloud") -> str: """Produce Lyra's reply to a single user message and persist the exchange.""" messages = build_messages(session_id, user_msg) reply = llm.complete(messages, backend=backend) memory.remember(session_id, "user", user_msg) memory.remember(session_id, "assistant", reply) return reply