add search indexing to api routes

This commit is contained in:
phernandez
2025-01-04 20:54:52 -06:00
parent 5488ff49d1
commit dbe9cea261
9 changed files with 336 additions and 38 deletions
@@ -2,9 +2,9 @@
from typing import List
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter, HTTPException, BackgroundTasks, Depends
from basic_memory.deps import DocumentServiceDep
from basic_memory.deps import DocumentServiceDep, get_search_service
from basic_memory.schemas.request import DocumentRequest, DocumentPathId
from basic_memory.schemas.response import DocumentResponse, DocumentCreateResponse
from basic_memory.services.document_service import (
@@ -20,22 +20,23 @@ router = APIRouter(prefix="/documents", tags=["documents"])
@router.post("/create", response_model=DocumentCreateResponse, status_code=201)
async def create_document(
doc: DocumentRequest,
background_tasks: BackgroundTasks,
service: DocumentServiceDep,
search_service = Depends(get_search_service)
) -> DocumentCreateResponse:
"""Create a new document.
The document will be created with appropriate frontmatter including:
- Generated ID
- Creation timestamp
- Last modified timestamp
- Any provided doc_metadata
"""
"""Create a new document with search indexing."""
try:
document = await service.create_document(
path_id=doc.path_id,
content=doc.content,
metadata=doc.doc_metadata,
)
# Index the new document
await search_service.index_document(
document,
doc.content,
background_tasks=background_tasks
)
return DocumentCreateResponse.model_validate(document.__dict__)
except DocumentError as e:
raise HTTPException(status_code=400, detail=str(e))
@@ -71,9 +72,11 @@ async def get_document(
async def update_document(
path_id: DocumentPathId,
doc: DocumentRequest,
background_tasks: BackgroundTasks,
service: DocumentServiceDep,
search_service = Depends(get_search_service)
) -> DocumentResponse:
"""Update a document by ID."""
"""Update a document by ID with search indexing."""
# Verify FilePaths match
if doc.path_id != path_id:
raise HTTPException(
@@ -86,10 +89,16 @@ async def update_document(
content=doc.content,
metadata=doc.doc_metadata,
)
# Update search index
await search_service.index_document(
document,
doc.content,
background_tasks=background_tasks
)
doc_dict = document.__dict__ | {"content": doc.content}
return DocumentResponse.model_validate(doc_dict)
except DocumentNotFoundError:
raise HTTPException(status_code=404, detail=f"Document not found: {id}")
raise HTTPException(status_code=404, detail=f"Document not found: {path_id}")
except DocumentWriteError as e:
raise HTTPException(status_code=400, detail=str(e))
@@ -97,12 +106,17 @@ async def update_document(
@router.delete("/{path_id:path}", status_code=204)
async def delete_document(
path_id: DocumentPathId,
background_tasks: BackgroundTasks,
service: DocumentServiceDep,
search_service = Depends(get_search_service)
) -> None:
"""Delete a document by ID."""
"""Delete a document by ID and remove from search index."""
try:
# Delete from storage
await service.delete_document_by_path_id(path_id)
# Remove from search index (in background)
background_tasks.add_task(search_service.delete_by_path_id, path_id)
except DocumentNotFoundError:
raise HTTPException(status_code=404, detail=f"Document not found: {id}")
raise HTTPException(status_code=404, detail=f"Document not found: {path_id}")
except DocumentWriteError as e:
raise HTTPException(status_code=400, detail=str(e))
raise HTTPException(status_code=400, detail=str(e))
@@ -1,11 +1,12 @@
"""Router for knowledge graph operations."""
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter, HTTPException, BackgroundTasks, Depends
from loguru import logger
from basic_memory.deps import (
EntityServiceDep,
KnowledgeServiceDep,
get_search_service,
)
from basic_memory.schemas import (
CreateEntityRequest,
@@ -31,10 +32,18 @@ router = APIRouter(prefix="/knowledge", tags=["knowledge"])
@router.post("/entities", response_model=EntityListResponse)
async def create_entities(
data: CreateEntityRequest, knowledge_service: KnowledgeServiceDep
data: CreateEntityRequest,
background_tasks: BackgroundTasks,
knowledge_service: KnowledgeServiceDep,
search_service = Depends(get_search_service)
) -> EntityListResponse:
"""Create new entities in the knowledge graph."""
"""Create new entities in the knowledge graph and index them."""
entities = await knowledge_service.create_entities(data.entities)
# Index each entity
for entity in entities:
await search_service.index_entity(entity, background_tasks=background_tasks)
return EntityListResponse(
entities=[EntityResponse.model_validate(entity) for entity in entities]
)
@@ -42,10 +51,18 @@ async def create_entities(
@router.post("/relations", response_model=EntityListResponse)
async def create_relations(
data: CreateRelationsRequest, knowledge_service: KnowledgeServiceDep
data: CreateRelationsRequest,
background_tasks: BackgroundTasks,
knowledge_service: KnowledgeServiceDep,
search_service = Depends(get_search_service),
) -> EntityListResponse:
"""Create relations between entities."""
"""Create relations between entities and update search index."""
updated_entities = await knowledge_service.create_relations(data.relations)
# Reindex updated entities since relations have changed
for entity in updated_entities:
await search_service.index_entity(entity, background_tasks=background_tasks)
return EntityListResponse(
entities=[EntityResponse.model_validate(entity) for entity in updated_entities]
)
@@ -53,13 +70,20 @@ async def create_relations(
@router.post("/observations", response_model=EntityResponse)
async def add_observations(
data: AddObservationsRequest, knowledge_service: KnowledgeServiceDep
data: AddObservationsRequest,
background_tasks: BackgroundTasks,
knowledge_service: KnowledgeServiceDep,
search_service = Depends(get_search_service)
) -> EntityResponse:
"""Add observations to an entity."""
"""Add observations to an entity and update search index."""
logger.debug(f"Adding observations to entity: {data.path_id}")
updated_entity = await knowledge_service.add_observations(
data.path_id, data.observations, data.context
)
# Reindex the entity with new observations
await search_service.index_entity(updated_entity, background_tasks=background_tasks)
return EntityResponse.model_validate(updated_entity)
@@ -104,29 +128,52 @@ async def open_nodes(data: OpenNodesRequest, entity_service: EntityServiceDep) -
@router.post("/entities/delete", response_model=DeleteEntitiesResponse)
async def delete_entities(
data: DeleteEntitiesRequest, knowledge_service: KnowledgeServiceDep
data: DeleteEntitiesRequest,
background_tasks: BackgroundTasks,
knowledge_service: KnowledgeServiceDep,
search_service = Depends(get_search_service)
) -> DeleteEntitiesResponse:
"""Delete a specific entity by PathId."""
"""Delete entities and remove from search index."""
deleted = await knowledge_service.delete_entities(data.path_ids)
# Remove each deleted entity from search index
for path_id in data.path_ids:
background_tasks.add_task(search_service.delete_by_path_id, path_id)
return DeleteEntitiesResponse(deleted=deleted)
@router.post("/observations/delete", response_model=EntityResponse)
async def delete_observations(
data: DeleteObservationsRequest, knowledge_service: KnowledgeServiceDep
data: DeleteObservationsRequest,
background_tasks: BackgroundTasks,
knowledge_service: KnowledgeServiceDep,
search_service = Depends(get_search_service)
) -> EntityResponse:
"""Delete observations from an entity."""
"""Delete observations and update search index."""
path_id = data.path_id
updated_entity = await knowledge_service.delete_observations(path_id, data.observations)
# Reindex the entity since observations changed
await search_service.index_entity(updated_entity, background_tasks=background_tasks)
return EntityResponse.model_validate(updated_entity)
@router.post("/relations/delete", response_model=EntityListResponse)
async def delete_relations(
data: DeleteRelationsRequest, knowledge_service: KnowledgeServiceDep
data: DeleteRelationsRequest,
background_tasks: BackgroundTasks,
knowledge_service: KnowledgeServiceDep,
search_service = Depends(get_search_service)
) -> EntityListResponse:
"""Delete relations between entities."""
"""Delete relations and update search index."""
updated_entities = await knowledge_service.delete_relations(data.relations)
# Reindex entities since relations changed
for entity in updated_entities:
await search_service.index_entity(entity, background_tasks=background_tasks)
return EntityListResponse(
entities=[EntityResponse.model_validate(entity) for entity in updated_entities]
)
)