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>
This commit is contained in:
Claude Code
2025-10-14 08:44:16 +02:00
parent cfa7abd23d
commit 61a4050a8e
26 changed files with 3248 additions and 0 deletions

353
api/memory_service.py Normal file
View File

@@ -0,0 +1,353 @@
"""
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