Files
t6_mem0_v2/api/memory_service.py
Claude Code 61a4050a8e Complete implementation: REST API, MCP server, and documentation
Implementation Summary:
- REST API with FastAPI (complete CRUD operations)
- MCP Server with Python MCP SDK (7 tools)
- Supabase migrations (pgvector setup)
- Docker Compose orchestration
- Mintlify documentation site
- Environment configuration
- Shared config module

REST API Features:
- POST /v1/memories/ - Add memory
- GET /v1/memories/search - Semantic search
- GET /v1/memories/{id} - Get memory
- GET /v1/memories/user/{user_id} - User memories
- PATCH /v1/memories/{id} - Update memory
- DELETE /v1/memories/{id} - Delete memory
- GET /v1/health - Health check
- GET /v1/stats - Statistics
- Bearer token authentication
- OpenAPI documentation

MCP Server Tools:
- add_memory - Add from messages
- search_memories - Semantic search
- get_memory - Retrieve by ID
- get_all_memories - List all
- update_memory - Update content
- delete_memory - Delete by ID
- delete_all_memories - Bulk delete

Infrastructure:
- Neo4j 5.26 with APOC/GDS
- Supabase pgvector integration
- Docker network: localai
- Health checks and monitoring
- Structured logging

Documentation:
- Introduction page
- Quickstart guide
- Architecture deep dive
- Mintlify configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 08:44:16 +02:00

354 lines
9.0 KiB
Python

"""
Memory service wrapper for Mem0 core library
"""
import logging
from typing import List, Dict, Any, Optional
from mem0 import Memory
from config import mem0_config
logger = logging.getLogger(__name__)
class MemoryService:
"""Singleton service for memory operations using Mem0"""
_instance: Optional['MemoryService'] = None
_memory: Optional[Memory] = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
"""Initialize memory service with Mem0"""
if self._memory is None:
logger.info("Initializing Mem0 with configuration")
try:
self._memory = Memory.from_config(config_dict=mem0_config)
logger.info("Mem0 initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Mem0: {e}")
raise
@property
def memory(self) -> Memory:
"""Get Mem0 instance"""
if self._memory is None:
raise RuntimeError("MemoryService not initialized")
return self._memory
async def add_memory(
self,
messages: List[Dict[str, str]],
user_id: Optional[str] = None,
agent_id: Optional[str] = None,
run_id: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None
) -> List[Dict[str, Any]]:
"""
Add new memory from messages
Args:
messages: List of chat messages
user_id: User identifier
agent_id: Agent identifier
run_id: Run identifier
metadata: Additional metadata
Returns:
List of created memories
Raises:
Exception: If memory creation fails
"""
try:
logger.info(f"Adding memory for user_id={user_id}, agent_id={agent_id}")
result = self.memory.add(
messages=messages,
user_id=user_id,
agent_id=agent_id,
run_id=run_id,
metadata=metadata or {}
)
logger.info(f"Successfully added {len(result.get('results', []))} memories")
return result.get('results', [])
except Exception as e:
logger.error(f"Failed to add memory: {e}")
raise
async def search_memories(
self,
query: str,
user_id: Optional[str] = None,
agent_id: Optional[str] = None,
run_id: Optional[str] = None,
limit: int = 10
) -> List[Dict[str, Any]]:
"""
Search memories by query
Args:
query: Search query
user_id: User identifier filter
agent_id: Agent identifier filter
run_id: Run identifier filter
limit: Maximum results
Returns:
List of matching memories with scores
Raises:
Exception: If search fails
"""
try:
logger.info(f"Searching memories: query='{query}', user_id={user_id}, limit={limit}")
results = self.memory.search(
query=query,
user_id=user_id,
agent_id=agent_id,
run_id=run_id,
limit=limit
)
logger.info(f"Found {len(results)} matching memories")
return results
except Exception as e:
logger.error(f"Failed to search memories: {e}")
raise
async def get_memory(
self,
memory_id: str
) -> Optional[Dict[str, Any]]:
"""
Get specific memory by ID
Args:
memory_id: Memory identifier
Returns:
Memory data or None if not found
Raises:
Exception: If retrieval fails
"""
try:
logger.info(f"Getting memory: id={memory_id}")
result = self.memory.get(memory_id=memory_id)
if result:
logger.info(f"Retrieved memory: {memory_id}")
else:
logger.warning(f"Memory not found: {memory_id}")
return result
except Exception as e:
logger.error(f"Failed to get memory: {e}")
raise
async def get_all_memories(
self,
user_id: Optional[str] = None,
agent_id: Optional[str] = None,
run_id: Optional[str] = None
) -> List[Dict[str, Any]]:
"""
Get all memories for a user/agent/run
Args:
user_id: User identifier filter
agent_id: Agent identifier filter
run_id: Run identifier filter
Returns:
List of all matching memories
Raises:
Exception: If retrieval fails
"""
try:
logger.info(f"Getting all memories: user_id={user_id}, agent_id={agent_id}")
results = self.memory.get_all(
user_id=user_id,
agent_id=agent_id,
run_id=run_id
)
logger.info(f"Retrieved {len(results)} memories")
return results
except Exception as e:
logger.error(f"Failed to get all memories: {e}")
raise
async def update_memory(
self,
memory_id: str,
data: str
) -> Dict[str, Any]:
"""
Update existing memory
Args:
memory_id: Memory identifier
data: Updated memory text
Returns:
Updated memory data
Raises:
Exception: If update fails
"""
try:
logger.info(f"Updating memory: id={memory_id}")
result = self.memory.update(
memory_id=memory_id,
data=data
)
logger.info(f"Successfully updated memory: {memory_id}")
return result
except Exception as e:
logger.error(f"Failed to update memory: {e}")
raise
async def delete_memory(
self,
memory_id: str
) -> bool:
"""
Delete memory by ID
Args:
memory_id: Memory identifier
Returns:
True if deleted successfully
Raises:
Exception: If deletion fails
"""
try:
logger.info(f"Deleting memory: id={memory_id}")
self.memory.delete(memory_id=memory_id)
logger.info(f"Successfully deleted memory: {memory_id}")
return True
except Exception as e:
logger.error(f"Failed to delete memory: {e}")
raise
async def delete_all_memories(
self,
user_id: Optional[str] = None,
agent_id: Optional[str] = None,
run_id: Optional[str] = None
) -> bool:
"""
Delete all memories for a user/agent/run
Args:
user_id: User identifier filter
agent_id: Agent identifier filter
run_id: Run identifier filter
Returns:
True if deleted successfully
Raises:
Exception: If deletion fails
"""
try:
logger.info(f"Deleting all memories: user_id={user_id}, agent_id={agent_id}")
self.memory.delete_all(
user_id=user_id,
agent_id=agent_id,
run_id=run_id
)
logger.info("Successfully deleted all matching memories")
return True
except Exception as e:
logger.error(f"Failed to delete all memories: {e}")
raise
async def get_memory_history(
self,
memory_id: str
) -> List[Dict[str, Any]]:
"""
Get history of a memory
Args:
memory_id: Memory identifier
Returns:
List of memory history entries
Raises:
Exception: If retrieval fails
"""
try:
logger.info(f"Getting memory history: id={memory_id}")
result = self.memory.history(memory_id=memory_id)
logger.info(f"Retrieved history for memory: {memory_id}")
return result
except Exception as e:
logger.error(f"Failed to get memory history: {e}")
raise
async def health_check(self) -> Dict[str, str]:
"""
Check health of memory service
Returns:
Dict with component health status
"""
health = {}
try:
# Test Mem0 access
self.memory
health['mem0'] = 'healthy'
except Exception as e:
logger.error(f"Mem0 health check failed: {e}")
health['mem0'] = 'unhealthy'
return health
# Global service instance
_memory_service: Optional[MemoryService] = None
def get_memory_service() -> MemoryService:
"""
Get or create memory service singleton
Returns:
MemoryService instance
"""
global _memory_service
if _memory_service is None:
_memory_service = MemoryService()
return _memory_service