"""FastAPI REST API for the memory manager.""" import os from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException, Query from fastapi.middleware.cors import CORSMiddleware from memory_manager import __version__ from memory_manager.api.schemas import ( CommitCreate, CommitResponse, DiffResponse, HealthResponse, MemoryEntryCreate, MemoryEntryResponse, MemoryEntryUpdate, StatsResponse, ) from memory_manager.core.services import MemoryManager from memory_manager.db.models import MemoryCategory from memory_manager.db.repository import MemoryRepository db_path = os.getenv("MEMORY_DB_PATH", ".memory/codebase_memory.db") repository = MemoryRepository(db_path) memory_manager = MemoryManager(repository) @asynccontextmanager async def lifespan(app: FastAPI): await memory_manager.initialize() yield await memory_manager.close() app = FastAPI( title="Agentic Codebase Memory Manager", description="A centralized memory store for AI coding agents", version=__version__, lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/health", response_model=HealthResponse) async def health(): return HealthResponse(status="ok", version=__version__) @app.get("/api/memory", response_model=list[MemoryEntryResponse]) async def list_memory( category: str | None = None, agent_id: str | None = None, project_path: str | None = None, limit: int = Query(default=100, ge=1, le=1000), offset: int = Query(default=0, ge=0), ): category_enum = None if category: try: category_enum = MemoryCategory(category) except ValueError: raise HTTPException(status_code=422, detail=f"Invalid category: {category}") entries = await memory_manager.memory_service.list_entries( category=category_enum, agent_id=agent_id, project_path=project_path, limit=limit, offset=offset, ) return entries @app.post("/api/memory", response_model=MemoryEntryResponse, status_code=201) async def create_memory(entry: MemoryEntryCreate): result = await memory_manager.memory_service.create_entry( title=entry.title, content=entry.content, category=entry.category, tags=entry.tags, agent_id=entry.agent_id, project_path=entry.project_path, ) return result @app.get("/api/memory/log", response_model=list[CommitResponse]) async def get_log( agent_id: str | None = None, project_path: str | None = None, limit: int = Query(default=100, ge=1, le=1000), offset: int = Query(default=0, ge=0), ): commits = await memory_manager.commit_service.list_commits( agent_id=agent_id, project_path=project_path, limit=limit, offset=offset, ) return commits @app.get("/api/memory/stats", response_model=StatsResponse) async def get_stats(project_path: str | None = None): entries = await memory_manager.memory_service.list_entries( project_path=project_path, limit=10000, ) entries_by_category: dict[str, int] = {} for entry in entries: cat = entry["category"] entries_by_category[cat] = entries_by_category.get(cat, 0) + 1 commits = await memory_manager.commit_service.list_commits( project_path=project_path, limit=10000, ) return StatsResponse( total_entries=len(entries), entries_by_category=entries_by_category, total_commits=len(commits), ) @app.get("/api/memory/search", response_model=list[MemoryEntryResponse]) async def search_memory( q: str = Query(..., min_length=1), category: str | None = None, agent_id: str | None = None, project_path: str | None = None, limit: int = Query(default=100, ge=1, le=1000), ): category_enum = None if category: try: category_enum = MemoryCategory(category) except ValueError: raise HTTPException(status_code=422, detail=f"Invalid category: {category}") results = await memory_manager.search_service.search( query=q, category=category_enum, agent_id=agent_id, project_path=project_path, limit=limit, ) return results @app.post("/api/memory/commit", response_model=CommitResponse, status_code=201) async def create_commit(commit: CommitCreate): result = await memory_manager.commit_service.create_commit( message=commit.message, agent_id=commit.agent_id, project_path=commit.project_path, ) return result @app.get("/api/memory/diff/{hash1}/{hash2}", response_model=DiffResponse) async def get_diff(hash1: str, hash2: str): diff = await memory_manager.commit_service.diff(hash1, hash2) if not diff: raise HTTPException( status_code=404, detail=f"Commit(s) not found: {hash1}, {hash2}. Check available commits with /api/memory/log" ) return diff @app.get("/api/memory/{entry_id}", response_model=MemoryEntryResponse) async def get_memory(entry_id: int): entry = await memory_manager.memory_service.get_entry(entry_id) if not entry: raise HTTPException(status_code=404, detail=f"Entry {entry_id} not found") return entry @app.put("/api/memory/{entry_id}", response_model=MemoryEntryResponse) async def update_memory(entry_id: int, entry: MemoryEntryUpdate): result = await memory_manager.memory_service.update_entry( entry_id=entry_id, title=entry.title, content=entry.content, category=entry.category, tags=entry.tags, ) if not result: raise HTTPException(status_code=404, detail=f"Entry {entry_id} not found") return result @app.delete("/api/memory/{entry_id}", status_code=204) async def delete_memory(entry_id: int): deleted = await memory_manager.memory_service.delete_entry(entry_id) if not deleted: raise HTTPException(status_code=404, detail=f"Entry {entry_id} not found")