122 lines
3.5 KiB
Python
122 lines
3.5 KiB
Python
"""Memory API routes"""
|
|
|
|
import asyncio
|
|
import logging
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/memory", tags=["memory"])
|
|
|
|
|
|
def _get_memory_retriever(request: Request):
|
|
retriever = getattr(request.app.state, "memory_retriever", None)
|
|
if retriever is None:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="Memory retriever is not configured",
|
|
)
|
|
return retriever
|
|
|
|
|
|
@router.get("/episodic")
|
|
async def search_episodic_memory(
|
|
query: str,
|
|
top_k: int = 5,
|
|
agent_name: str | None = None,
|
|
req: Request = None,
|
|
):
|
|
"""Search episodic memory."""
|
|
retriever = _get_memory_retriever(req)
|
|
|
|
if retriever._episodic is None:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="Episodic memory is not configured",
|
|
)
|
|
|
|
try:
|
|
filters = {}
|
|
if agent_name:
|
|
filters["agent_name"] = agent_name
|
|
items = await retriever._episodic.search(query, top_k=top_k, filters=filters or None)
|
|
except asyncio.CancelledError:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to search episodic memory: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to search episodic memory")
|
|
|
|
results = []
|
|
for item in items:
|
|
results.append({
|
|
"key": item.key,
|
|
"value": item.value,
|
|
"score": item.score,
|
|
"metadata": item.metadata,
|
|
})
|
|
return {"query": query, "results": results, "total": len(results)}
|
|
|
|
|
|
@router.get("/semantic/search")
|
|
async def search_semantic_memory(
|
|
query: str,
|
|
knowledge_base_ids: str | None = None,
|
|
top_k: int = 5,
|
|
req: Request = None,
|
|
):
|
|
"""Search semantic memory (knowledge bases)."""
|
|
retriever = _get_memory_retriever(req)
|
|
|
|
if retriever._semantic is None:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="Semantic memory is not configured",
|
|
)
|
|
|
|
try:
|
|
filters = {}
|
|
if knowledge_base_ids:
|
|
filters["knowledge_base_ids"] = [kid.strip() for kid in knowledge_base_ids.split(",")]
|
|
items = await retriever._semantic.search(query, top_k=top_k, filters=filters or None)
|
|
except asyncio.CancelledError:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to search semantic memory: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to search semantic memory")
|
|
|
|
results = []
|
|
for item in items:
|
|
results.append({
|
|
"key": item.key,
|
|
"value": item.value,
|
|
"score": item.score,
|
|
"metadata": item.metadata,
|
|
})
|
|
return {"query": query, "results": results, "total": len(results)}
|
|
|
|
|
|
@router.delete("/episodic/{key}")
|
|
async def delete_episodic_memory(key: str, req: Request = None):
|
|
"""Delete an episodic memory entry."""
|
|
retriever = _get_memory_retriever(req)
|
|
|
|
if retriever._episodic is None:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="Episodic memory is not configured",
|
|
)
|
|
|
|
try:
|
|
deleted = await retriever._episodic.delete(key)
|
|
except asyncio.CancelledError:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to delete episodic memory '{key}': {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to delete episodic memory")
|
|
|
|
if not deleted:
|
|
raise HTTPException(status_code=404, detail=f"Episodic memory '{key}' not found")
|
|
|
|
return {"key": key, "deleted": True}
|