entity_sync_service

This commit is contained in:
phernandez
2025-01-02 18:21:37 -06:00
parent a9a3d90558
commit 4d3f2dc03d
4 changed files with 331 additions and 3 deletions
-2
View File
@@ -2,7 +2,6 @@
from .document_service import DocumentService
from .sync.file_change_scanner import FileChangeScanner
from .sync.document_sync_service import DocumentSyncService
from .entity_service import EntityService
from .file_service import FileService
from .knowledge import KnowledgeService
@@ -13,7 +12,6 @@ from .service import BaseService
__all__ = [
"BaseService",
"DocumentService",
"DocumentSyncService",
"EntityService",
"FileService",
"ObservationService",
@@ -0,0 +1,118 @@
"""Service for managing entities in the database."""
from typing import Dict
from loguru import logger
from basic_memory.models import Entity as EntityModel, Observation, Relation
from basic_memory.repository.entity_repository import EntityRepository
from basic_memory.markdown.schemas import EntityMarkdown
from basic_memory.services import EntityService
from basic_memory.services.service import BaseService
def entity_model_from_markdown(
markdown: EntityMarkdown
) -> EntityModel:
"""Convert markdown entity to model.
Args:
markdown: Parsed markdown entity
include_relations: Whether to include relations. Set False for first sync pass.
"""
model = EntityModel(
name=markdown.content.title,
entity_type=markdown.frontmatter.type,
path_id=markdown.frontmatter.id,
file_path=markdown.frontmatter.id,
description=markdown.content.description,
observations=[Observation(content=obs.content) for obs in markdown.content.observations],
)
return model
class EntitySyncService(EntityService):
"""Service for managing entities in the database."""
def __init__(self, entity_repository: EntityRepository):
super().__init__(entity_repository)
async def create_entity_without_relations(self, markdown: EntityMarkdown) -> EntityModel:
"""First pass: Create entity and observations only.
Creates the entity with null checksum to indicate sync not complete.
Relations will be added in second pass.
"""
logger.debug(f"Creating entity without relations: {markdown.frontmatter.id}")
model = entity_model_from_markdown(markdown)
model.checksum = None # Mark as incomplete sync
return await self.repository.add(model)
async def update_entity_without_relations(
self, path_id: str, markdown: EntityMarkdown
) -> EntityModel:
"""First pass: Update entity fields and observations.
Updates everything except relations and sets null checksum
to indicate sync not complete.
"""
logger.debug(f"Updating entity without relations: {path_id}")
db_entity = await self.get_by_path_id(path_id)
# Update fields from markdown
db_entity.name = markdown.content.title
db_entity.entity_type = markdown.frontmatter.type
db_entity.description = markdown.content.description
# Update observations
db_entity.observations = [
Observation(content=obs.content) for obs in markdown.content.observations
]
# Mark as incomplete
db_entity.checksum = None
return await self.repository.update(
db_entity.id,
{
"name": db_entity.name,
"entity_type": db_entity.entity_type,
"description": db_entity.description,
"observations": db_entity.observations,
"checksum": None,
},
)
async def update_entity_relations(self, markdown: EntityMarkdown, checksum: str) -> EntityModel:
"""Second pass: Update relations and set checksum.
Args:
markdown: Parsed markdown entity with relations
checksum: Final checksum to set after relations are updated
"""
logger.debug(f"Updating relations for entity: {markdown.frontmatter.id}")
db_entity = await self.get_by_path_id(markdown.frontmatter.id)
# get all entities from relations
target_entity_path_ids = [rel.target for rel in markdown.content.relations]
target_entities = await self.repository.find_by_path_ids(target_entity_path_ids)
# zip dict by path
entity_by_path: Dict[str, EntityModel] = dict(zip(target_entity_path_ids, target_entities))
# Update relations from markdown
db_entity.to_relations = [
Relation(
from_id=db_entity.id,
to_id=entity_by_path[rel.target].id,
relation_type=rel.type,
context=rel.context,
)
for rel in markdown.content.relations
]
# Set final checksum to mark sync complete
db_entity.checksum = checksum
return await self.repository.update(
db_entity.id, {"relations": db_entity.relations, "checksum": checksum}
)
@@ -5,6 +5,7 @@ from loguru import logger
from basic_memory.services import FileChangeScanner, EntityService, DocumentService
from basic_memory.markdown import KnowledgeParser
from basic_memory.services.sync.entity_sync_service import EntitySyncService
class SyncService:
@@ -19,7 +20,7 @@ class SyncService:
self,
scanner: FileChangeScanner,
document_service: DocumentService,
entity_service: EntityService,
entity_service: EntitySyncService,
knowledge_parser: KnowledgeParser,
):
self.scanner = scanner