File size: 4,774 Bytes
a20767f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
MemoryAgent — Persistent long-term memory system (Phase 5)
SQLite-backed with semantic search simulation
"""
import json
import time
from typing import Dict, List, Optional
import structlog
from .base_agent import BaseAgent
from memory.db import save_memory, search_memory, get_history, get_project_memory

log = structlog.get_logger()


class MemoryAgent(BaseAgent):
    def __init__(self, ws_manager=None, ai_router=None):
        super().__init__("MemoryAgent", ws_manager, ai_router)

    async def run(self, task: str, context: Dict = {}, **kwargs) -> str:
        session_id = kwargs.get("session_id", "")
        task_id = kwargs.get("task_id", "")

        # Determine if retrieve or save
        task_lower = task.lower()
        if any(k in task_lower for k in ["remember", "save", "store", "record"]):
            content = context.get("content", task)
            await self.save(content, session_id=session_id, memory_type="user_directive")
            return f"✅ Saved to memory: {content[:100]}"
        else:
            results = await self.retrieve(task, session_id=session_id)
            if results:
                return "📚 **Memory Retrieved:**\n\n" + "\n".join(f"- {r['content'][:200]}" for r in results[:5])
            return "No relevant memories found."

    async def save(
        self,
        content: str,
        session_id: str = "",
        project_id: str = "",
        memory_type: str = "general",
        key: str = "",
        metadata: Dict = {},
    ):
        """Save content to persistent memory."""
        await save_memory(
            content=content,
            memory_type=memory_type,
            session_id=session_id,
            project_id=project_id,
            key=key,
            metadata=metadata,
        )

    async def retrieve(
        self,
        query: str,
        session_id: str = "",
        project_id: str = "",
        limit: int = 10,
    ) -> List[Dict]:
        """Retrieve relevant memories."""
        return await search_memory(query[:100], session_id=session_id, project_id=project_id, limit=limit)

    async def get_conversation_history(self, session_id: str, limit: int = 20) -> List[Dict]:
        """Get conversation history for a session."""
        return await get_history(session_id, limit=limit)

    async def save_interaction(
        self,
        user_message: str,
        assistant_response: str,
        session_id: str = "",
        intent: Dict = {},
    ):
        """Save a full interaction to memory."""
        await save_memory(
            content=user_message,
            memory_type="conversation",
            session_id=session_id,
            key="user_message",
            metadata={"intent": intent.get("intent", ""), "timestamp": time.time()},
        )
        await save_memory(
            content=assistant_response,
            memory_type="conversation",
            session_id=session_id,
            key="assistant_response",
            metadata={"agent": intent.get("primary_agent", "chat"), "timestamp": time.time()},
        )

    async def save_coding_style(self, style_notes: str, session_id: str = ""):
        """Remember user's coding preferences."""
        await save_memory(
            content=style_notes,
            memory_type="user_preference",
            session_id=session_id,
            key="coding_style",
            metadata={"category": "coding_style"},
        )

    async def save_project_context(self, project_id: str, context: Dict):
        """Save project-specific context."""
        await save_memory(
            content=json.dumps(context),
            memory_type="project_context",
            project_id=project_id,
            key="project_context",
            metadata={"timestamp": time.time()},
        )

    async def get_project_context(self, project_id: str) -> Optional[Dict]:
        """Get project context."""
        results = await get_project_memory(project_id, memory_type="project_context", limit=1)
        if results:
            try:
                return json.loads(results[0]["content"])
            except Exception:
                return {"raw": results[0]["content"]}
        return None

    async def build_context_for_agent(self, session_id: str, query: str) -> Dict:
        """Build rich context dict for agents from memory."""
        history = await self.get_conversation_history(session_id, limit=10)
        relevant = await self.retrieve(query, session_id=session_id, limit=5)

        return {
            "history": [{"role": "user" if i % 2 == 0 else "assistant", "content": h["content"]} for i, h in enumerate(reversed(history))],
            "relevant_memories": [r["content"][:200] for r in relevant],
            "session_id": session_id,
        }