diff --git a/src/basic_memory/api/app.py b/src/basic_memory/api/app.py index 0423bfe3..2a2abc09 100644 --- a/src/basic_memory/api/app.py +++ b/src/basic_memory/api/app.py @@ -6,7 +6,6 @@ from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException from fastapi.exception_handlers import http_exception_handler from loguru import logger -import logfire from basic_memory import __version__ as version from basic_memory import db diff --git a/src/basic_memory/repository/entity_repository.py b/src/basic_memory/repository/entity_repository.py index 42d574ef..d5d5af1b 100644 --- a/src/basic_memory/repository/entity_repository.py +++ b/src/basic_memory/repository/entity_repository.py @@ -33,7 +33,7 @@ class EntityRepository(Repository[Entity]): """ super().__init__(session_maker, Entity, project_id=project_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_id(self, entity_id: int) -> Optional[Entity]: """Get entity by numeric ID. @@ -46,7 +46,7 @@ class EntityRepository(Repository[Entity]): async with db.scoped_session(self.session_maker) as session: return await self.select_by_id(session, entity_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_permalink(self, permalink: str) -> Optional[Entity]: """Get entity by permalink. @@ -56,7 +56,7 @@ class EntityRepository(Repository[Entity]): query = self.select().where(Entity.permalink == permalink).options(*self.get_load_options()) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_title(self, title: str) -> Sequence[Entity]: """Get entity by title. @@ -67,7 +67,7 @@ class EntityRepository(Repository[Entity]): result = await self.execute_query(query) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_file_path(self, file_path: Union[Path, str]) -> Optional[Entity]: """Get entity by file_path. @@ -81,7 +81,7 @@ class EntityRepository(Repository[Entity]): ) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_file_paths( self, session: AsyncSession, file_paths: Sequence[Union[Path, str]] ) -> List[Row[Any]]: @@ -110,7 +110,7 @@ class EntityRepository(Repository[Entity]): result = await session.execute(query) return list(result.all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_checksum(self, checksum: str) -> Sequence[Entity]: """Find entities with the given checksum. @@ -128,7 +128,7 @@ class EntityRepository(Repository[Entity]): result = await self.execute_query(query, use_query_options=False) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_checksums(self, checksums: Sequence[str]) -> Sequence[Entity]: """Find entities with any of the given checksums (batch query for move detection). @@ -157,7 +157,7 @@ class EntityRepository(Repository[Entity]): result = await self.execute_query(query, use_query_options=False) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_file_path(self, file_path: Union[Path, str]) -> bool: """Delete entity with the provided file_path. @@ -178,7 +178,7 @@ class EntityRepository(Repository[Entity]): selectinload(Entity.incoming_relations).selectinload(Relation.to_entity), ] - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_permalinks(self, permalinks: List[str]) -> Sequence[Entity]: """Find multiple entities by their permalink. @@ -197,7 +197,7 @@ class EntityRepository(Repository[Entity]): result = await self.execute_query(query) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def upsert_entity(self, entity: Entity) -> Entity: """Insert or update entity using simple try/catch with database-level conflict resolution. @@ -299,7 +299,7 @@ class EntityRepository(Repository[Entity]): entity = await self._handle_permalink_conflict(entity, session) return entity - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_all_file_paths(self) -> List[str]: """Get all file paths for this project - optimized for deletion detection. @@ -315,7 +315,7 @@ class EntityRepository(Repository[Entity]): result = await self.execute_query(query, use_query_options=False) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_distinct_directories(self) -> List[str]: """Extract unique directory paths from file_path column. @@ -344,7 +344,7 @@ class EntityRepository(Repository[Entity]): return sorted(directories) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_directory_prefix(self, directory_prefix: str) -> Sequence[Entity]: """Find entities whose file_path starts with the given directory prefix. @@ -377,7 +377,7 @@ class EntityRepository(Repository[Entity]): result = await self.execute_query(query, use_query_options=False) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def _handle_permalink_conflict(self, entity: Entity, session: AsyncSession) -> Entity: """Handle permalink conflicts by generating a unique permalink.""" base_permalink = entity.permalink diff --git a/src/basic_memory/repository/observation_repository.py b/src/basic_memory/repository/observation_repository.py index 6cae530c..e5528e68 100644 --- a/src/basic_memory/repository/observation_repository.py +++ b/src/basic_memory/repository/observation_repository.py @@ -22,35 +22,35 @@ class ObservationRepository(Repository[Observation]): """ super().__init__(session_maker, Observation, project_id=project_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_entity(self, entity_id: int) -> Sequence[Observation]: """Find all observations for a specific entity.""" query = select(Observation).filter(Observation.entity_id == entity_id) result = await self.execute_query(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_context(self, context: str) -> Sequence[Observation]: """Find observations with a specific context.""" query = select(Observation).filter(Observation.context == context) result = await self.execute_query(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_category(self, category: str) -> Sequence[Observation]: """Find observations with a specific context.""" query = select(Observation).filter(Observation.category == category) result = await self.execute_query(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def observation_categories(self) -> Sequence[str]: """Return a list of all observation categories.""" query = select(Observation.category).distinct() result = await self.execute_query(query, use_query_options=False) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_entities(self, entity_ids: List[int]) -> Dict[int, List[Observation]]: """Find all observations for multiple entities in a single query. diff --git a/src/basic_memory/repository/postgres_search_repository.py b/src/basic_memory/repository/postgres_search_repository.py index b4992f28..ee5c8beb 100644 --- a/src/basic_memory/repository/postgres_search_repository.py +++ b/src/basic_memory/repository/postgres_search_repository.py @@ -26,7 +26,7 @@ class PostgresSearchRepository(SearchRepositoryBase): - JSONB containment operators for metadata search """ - @logfire.instrument(record_return=True) + @logfire.instrument() async def init_search_index(self): """Create Postgres table with tsvector column and GIN indexes. @@ -147,7 +147,7 @@ class PostgresSearchRepository(SearchRepositoryBase): else: return cleaned_term - @logfire.instrument(record_return=True) + @logfire.instrument() async def search( self, search_text: Optional[str] = None, @@ -315,7 +315,7 @@ class PostgresSearchRepository(SearchRepositoryBase): return results - @logfire.instrument(record_return=True) + @logfire.instrument() async def bulk_index_items(self, search_index_rows: List[SearchIndexRow]) -> None: """Index multiple items in a single batch operation using UPSERT. diff --git a/src/basic_memory/repository/project_repository.py b/src/basic_memory/repository/project_repository.py index afd3b9ea..32f97a13 100644 --- a/src/basic_memory/repository/project_repository.py +++ b/src/basic_memory/repository/project_repository.py @@ -23,7 +23,7 @@ class ProjectRepository(Repository[Project]): """Initialize with session maker.""" super().__init__(session_maker, Project) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_name(self, name: str) -> Optional[Project]: """Get project by name. @@ -33,7 +33,7 @@ class ProjectRepository(Repository[Project]): query = self.select().where(Project.name == name) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_permalink(self, permalink: str) -> Optional[Project]: """Get project by permalink. @@ -43,7 +43,7 @@ class ProjectRepository(Repository[Project]): query = self.select().where(Project.permalink == permalink) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_path(self, path: Union[Path, str]) -> Optional[Project]: """Get project by filesystem path. @@ -53,7 +53,7 @@ class ProjectRepository(Repository[Project]): query = self.select().where(Project.path == Path(path).as_posix()) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_id(self, project_id: int) -> Optional[Project]: """Get project by numeric ID. @@ -66,20 +66,20 @@ class ProjectRepository(Repository[Project]): async with db.scoped_session(self.session_maker) as session: return await self.select_by_id(session, project_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_default_project(self) -> Optional[Project]: """Get the default project (the one marked as is_default=True).""" query = self.select().where(Project.is_default.is_not(None)) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_active_projects(self) -> Sequence[Project]: """Get all active projects.""" query = self.select().where(Project.is_active == True) # noqa: E712 result = await self.execute_query(query) return list(result.scalars().all()) - @logfire.instrument(record_return=True) + @logfire.instrument() async def set_as_default(self, project_id: int) -> Optional[Project]: """Set a project as the default and unset previous default. @@ -104,7 +104,7 @@ class ProjectRepository(Repository[Project]): return target_project return None # pragma: no cover - @logfire.instrument(record_return=True) + @logfire.instrument() async def update_path(self, project_id: int, new_path: str) -> Optional[Project]: """Update project path. diff --git a/src/basic_memory/repository/relation_repository.py b/src/basic_memory/repository/relation_repository.py index b2990973..f8aeb9bf 100644 --- a/src/basic_memory/repository/relation_repository.py +++ b/src/basic_memory/repository/relation_repository.py @@ -25,7 +25,7 @@ class RelationRepository(Repository[Relation]): """ super().__init__(session_maker, Relation, project_id=project_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_relation( self, from_permalink: str, to_permalink: str, relation_type: str ) -> Optional[Relation]: @@ -47,21 +47,21 @@ class RelationRepository(Repository[Relation]): ) return await self.find_one(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_entities(self, from_id: int, to_id: int) -> Sequence[Relation]: """Find all relations between two entities.""" query = select(Relation).where((Relation.from_id == from_id) & (Relation.to_id == to_id)) result = await self.execute_query(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_type(self, relation_type: str) -> Sequence[Relation]: """Find all relations of a specific type.""" query = select(Relation).filter(Relation.relation_type == relation_type) result = await self.execute_query(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_outgoing_relations_from_entity(self, entity_id: int) -> None: """Delete outgoing relations for an entity. @@ -71,14 +71,14 @@ class RelationRepository(Repository[Relation]): async with db.scoped_session(self.session_maker) as session: await session.execute(delete(Relation).where(Relation.from_id == entity_id)) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_unresolved_relations(self) -> Sequence[Relation]: """Find all unresolved relations, where to_id is null.""" query = select(Relation).filter(Relation.to_id.is_(None)) result = await self.execute_query(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_unresolved_relations_for_entity(self, entity_id: int) -> Sequence[Relation]: """Find unresolved relations for a specific entity. diff --git a/src/basic_memory/repository/repository.py b/src/basic_memory/repository/repository.py index 0720a284..035b8dc2 100644 --- a/src/basic_memory/repository/repository.py +++ b/src/basic_memory/repository/repository.py @@ -85,7 +85,7 @@ class Repository[T: Base]: result = await session.execute(query) return result.scalars().one_or_none() - @logfire.instrument(record_return=True) + @logfire.instrument() async def select_by_ids(self, session: AsyncSession, ids: List[int]) -> Sequence[T]: """Select multiple entities by IDs using an existing session.""" query = ( @@ -97,7 +97,7 @@ class Repository[T: Base]: result = await session.execute(query) return result.scalars().all() - @logfire.instrument(record_return=True) + @logfire.instrument() async def add(self, model: T) -> T: """ Add a model to the repository. This will also add related objects @@ -124,7 +124,7 @@ class Repository[T: Base]: ) return found - @logfire.instrument(record_return=True) + @logfire.instrument() async def add_all(self, models: List[T]) -> Sequence[T]: """ Add a list of models to the repository. This will also add related objects @@ -156,7 +156,7 @@ class Repository[T: Base]: # Add project filter if applicable return self._add_project_filter(query) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_all( self, skip: int = 0, limit: Optional[int] = None, use_load_options: bool = True ) -> Sequence[T]: @@ -188,7 +188,7 @@ class Repository[T: Base]: logger.debug(f"Found {len(items)} {self.Model.__name__} records") return items - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_id(self, entity_id: int) -> Optional[T]: """Fetch an entity by its unique identifier.""" logger.debug(f"Finding {self.Model.__name__} by ID: {entity_id}") @@ -196,7 +196,7 @@ class Repository[T: Base]: async with db.scoped_session(self.session_maker) as session: return await self.select_by_id(session, entity_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_by_ids(self, ids: List[int]) -> Sequence[T]: """Fetch multiple entities by their identifiers in a single query.""" logger.debug(f"Finding {self.Model.__name__} by IDs: {ids}") @@ -204,7 +204,7 @@ class Repository[T: Base]: async with db.scoped_session(self.session_maker) as session: return await self.select_by_ids(session, ids) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_one(self, query: Select[tuple[T]]) -> Optional[T]: """Execute a query and retrieve a single record.""" # add in load options @@ -218,7 +218,7 @@ class Repository[T: Base]: logger.trace(f"No {self.Model.__name__} found") return entity - @logfire.instrument(record_return=True) + @logfire.instrument() async def create(self, data: dict) -> T: """Create a new record from a model instance.""" logger.debug(f"Creating {self.Model.__name__} from entity_data: {data}") @@ -250,7 +250,7 @@ class Repository[T: Base]: ) return return_instance - @logfire.instrument(record_return=True) + @logfire.instrument() async def create_all(self, data_list: List[dict]) -> Sequence[T]: """Create multiple records in a single transaction.""" logger.debug(f"Bulk creating {len(data_list)} {self.Model.__name__} instances") @@ -276,7 +276,7 @@ class Repository[T: Base]: return await self.select_by_ids(session, [model.id for model in model_list]) # pyright: ignore [reportAttributeAccessIssue] - @logfire.instrument(record_return=True) + @logfire.instrument() async def update(self, entity_id: int, entity_data: dict | T) -> Optional[T]: """Update an entity with the given data.""" logger.debug(f"Updating {self.Model.__name__} {entity_id} with data: {entity_data}") @@ -306,7 +306,7 @@ class Repository[T: Base]: logger.debug(f"No {self.Model.__name__} found to update: {entity_id}") return None - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete(self, entity_id: int) -> bool: """Delete an entity from the database.""" logger.debug(f"Deleting {self.Model.__name__}: {entity_id}") @@ -324,7 +324,7 @@ class Repository[T: Base]: logger.debug(f"No {self.Model.__name__} found to delete: {entity_id}") return False - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_ids(self, ids: List[int]) -> int: """Delete records matching given IDs.""" logger.debug(f"Deleting {self.Model.__name__} by ids: {ids}") @@ -340,7 +340,7 @@ class Repository[T: Base]: logger.debug(f"Deleted {result.rowcount} records") return result.rowcount - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_fields(self, **filters: Any) -> bool: """Delete records matching given field values.""" logger.debug(f"Deleting {self.Model.__name__} by fields: {filters}") @@ -357,7 +357,7 @@ class Repository[T: Base]: logger.debug(f"Deleted {result.rowcount} records") return deleted - @logfire.instrument(record_return=True) + @logfire.instrument() async def count(self, query: Executable | None = None) -> int: """Count entities in the database table.""" async with db.scoped_session(self.session_maker) as session: @@ -379,7 +379,7 @@ class Repository[T: Base]: logger.debug(f"Counted {count} {self.Model.__name__} records") return count - @logfire.instrument(record_return=True) + @logfire.instrument() async def execute_query( self, query: Executable, diff --git a/src/basic_memory/repository/search_repository_base.py b/src/basic_memory/repository/search_repository_base.py index 9f84d047..a5281e9b 100644 --- a/src/basic_memory/repository/search_repository_base.py +++ b/src/basic_memory/repository/search_repository_base.py @@ -103,7 +103,7 @@ class SearchRepositoryBase(ABC): """ pass - @logfire.instrument(record_return=True) + @logfire.instrument() async def index_item(self, search_index_row: SearchIndexRow) -> None: """Index or update a single item. @@ -147,7 +147,7 @@ class SearchRepositoryBase(ABC): logger.debug(f"indexed row {search_index_row}") await session.commit() - @logfire.instrument(record_return=True) + @logfire.instrument() async def bulk_index_items(self, search_index_rows: List[SearchIndexRow]) -> None: """Index multiple items in a single batch operation. @@ -195,7 +195,7 @@ class SearchRepositoryBase(ABC): logger.debug(f"Bulk indexed {len(search_index_rows)} rows") await session.commit() - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_entity_id(self, entity_id: int) -> None: """Delete all search index entries for an entity. @@ -210,7 +210,7 @@ class SearchRepositoryBase(ABC): ) await session.commit() - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_permalink(self, permalink: str) -> None: """Delete a search index entry by permalink. @@ -225,7 +225,7 @@ class SearchRepositoryBase(ABC): ) await session.commit() - @logfire.instrument(record_return=True) + @logfire.instrument() async def execute_query( self, query: Executable, diff --git a/src/basic_memory/repository/sqlite_search_repository.py b/src/basic_memory/repository/sqlite_search_repository.py index 3a00b92e..8665806f 100644 --- a/src/basic_memory/repository/sqlite_search_repository.py +++ b/src/basic_memory/repository/sqlite_search_repository.py @@ -26,7 +26,7 @@ class SQLiteSearchRepository(SearchRepositoryBase): - Prefix wildcard matching with * """ - @logfire.instrument(record_return=True) + @logfire.instrument() async def init_search_index(self): """Create FTS5 virtual table for search. @@ -281,7 +281,7 @@ class SQLiteSearchRepository(SearchRepositoryBase): # For non-Boolean queries, use the single term preparation logic return self._prepare_single_term(term, is_prefix) - @logfire.instrument(record_return=True) + @logfire.instrument() async def search( self, search_text: Optional[str] = None, diff --git a/src/basic_memory/services/context_service.py b/src/basic_memory/services/context_service.py index 26d4ae48..aa228858 100644 --- a/src/basic_memory/services/context_service.py +++ b/src/basic_memory/services/context_service.py @@ -86,7 +86,7 @@ class ContextService: self.entity_repository = entity_repository self.observation_repository = observation_repository - @logfire.instrument(record_return=True) + @logfire.instrument() async def build_context( self, memory_url: Optional[MemoryUrl] = None, @@ -217,7 +217,7 @@ class ContextService: # Return the structured ContextResult return ContextResult(results=context_results, metadata=metadata) - @logfire.instrument(record_return=True) + @logfire.instrument() async def find_related( self, type_id_pairs: List[Tuple[str, int]], diff --git a/src/basic_memory/services/directory_service.py b/src/basic_memory/services/directory_service.py index fef93f59..3e504c83 100644 --- a/src/basic_memory/services/directory_service.py +++ b/src/basic_memory/services/directory_service.py @@ -24,7 +24,7 @@ class DirectoryService: """ self.entity_repository = entity_repository - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_directory_tree(self) -> DirectoryNode: """Build a hierarchical directory tree from indexed files.""" @@ -92,7 +92,7 @@ class DirectoryService: # Return the root node with its children return root_node - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_directory_structure(self) -> DirectoryNode: """Build a hierarchical directory structure without file details. @@ -136,7 +136,7 @@ class DirectoryService: return root_node - @logfire.instrument(record_return=True) + @logfire.instrument() async def list_directory( self, dir_name: str = "/", diff --git a/src/basic_memory/services/entity_service.py b/src/basic_memory/services/entity_service.py index 1df5af76..a2d53a3f 100644 --- a/src/basic_memory/services/entity_service.py +++ b/src/basic_memory/services/entity_service.py @@ -53,7 +53,7 @@ class EntityService(BaseService[EntityModel]): self.link_resolver = link_resolver self.app_config = app_config - @logfire.instrument(record_return=True) + @logfire.instrument() async def detect_file_path_conflicts( self, file_path: str, skip_check: bool = False ) -> List[Entity]: @@ -93,7 +93,7 @@ class EntityService(BaseService[EntityModel]): return conflicts - @logfire.instrument(record_return=True) + @logfire.instrument() async def resolve_permalink( self, file_path: Permalink | Path, @@ -152,7 +152,7 @@ class EntityService(BaseService[EntityModel]): return permalink - @logfire.instrument(record_return=True) + @logfire.instrument() async def create_or_update_entity(self, schema: EntitySchema) -> Tuple[EntityModel, bool]: """Create new entity or update existing one. Returns: (entity, is_new) where is_new is True if a new entity was created @@ -174,7 +174,7 @@ class EntityService(BaseService[EntityModel]): # Create new entity return await self.create_entity(schema), True - @logfire.instrument(record_return=True) + @logfire.instrument() async def create_entity(self, schema: EntitySchema) -> EntityModel: """Create a new entity and write to filesystem.""" logger.debug(f"Creating entity: {schema.title}") @@ -241,7 +241,7 @@ class EntityService(BaseService[EntityModel]): # Set final checksum to mark complete return await self.repository.update(entity.id, {"checksum": checksum}) - @logfire.instrument(record_return=True) + @logfire.instrument() async def update_entity(self, entity: EntityModel, schema: EntitySchema) -> EntityModel: """Update an entity's content and metadata.""" logger.debug( @@ -322,7 +322,7 @@ class EntityService(BaseService[EntityModel]): return entity - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_entity(self, permalink_or_id: str | int) -> bool: """Delete entity and its file.""" logger.debug(f"Deleting entity: {permalink_or_id}") @@ -352,7 +352,7 @@ class EntityService(BaseService[EntityModel]): logger.info(f"Entity not found: {permalink_or_id}") return True # Already deleted - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_by_permalink(self, permalink: str) -> EntityModel: """Get entity by type and name combination.""" logger.debug(f"Getting entity by permalink: {permalink}") @@ -361,24 +361,24 @@ class EntityService(BaseService[EntityModel]): raise EntityNotFoundError(f"Entity not found: {permalink}") return db_entity - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_entities_by_id(self, ids: List[int]) -> Sequence[EntityModel]: """Get specific entities and their relationships.""" logger.debug(f"Getting entities: {ids}") return await self.repository.find_by_ids(ids) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_entities_by_permalinks(self, permalinks: List[str]) -> Sequence[EntityModel]: """Get specific nodes and their relationships.""" logger.debug(f"Getting entities permalinks: {permalinks}") return await self.repository.find_by_permalinks(permalinks) - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_entity_by_file_path(self, file_path: Union[str, Path]) -> None: """Delete entity by file path.""" await self.repository.delete_by_file_path(str(file_path)) - @logfire.instrument(record_return=True) + @logfire.instrument() async def create_entity_from_markdown( self, file_path: Path, markdown: EntityMarkdown ) -> EntityModel: @@ -402,7 +402,7 @@ class EntityService(BaseService[EntityModel]): logger.error(f"Failed to upsert entity for {file_path}: {e}") raise EntityCreationError(f"Failed to create entity: {str(e)}") from e - @logfire.instrument(record_return=True) + @logfire.instrument() async def update_entity_and_observations( self, file_path: Path, markdown: EntityMarkdown ) -> EntityModel: @@ -443,7 +443,7 @@ class EntityService(BaseService[EntityModel]): db_entity, ) - @logfire.instrument(record_return=True) + @logfire.instrument() async def update_entity_relations( self, path: str, @@ -515,7 +515,7 @@ class EntityService(BaseService[EntityModel]): return await self.repository.get_by_file_path(path) - @logfire.instrument(record_return=True) + @logfire.instrument() async def edit_entity( self, identifier: str, @@ -573,7 +573,7 @@ class EntityService(BaseService[EntityModel]): return entity - @logfire.instrument(record_return=True) + @logfire.instrument() def apply_edit_operation( self, current_content: str, @@ -626,7 +626,7 @@ class EntityService(BaseService[EntityModel]): else: raise ValueError(f"Unsupported operation: {operation}") - @logfire.instrument(record_return=True) + @logfire.instrument() def replace_section_content( self, current_content: str, section_header: str, new_content: str ) -> str: @@ -746,7 +746,7 @@ class EntityService(BaseService[EntityModel]): return content + "\n" + current_content # pragma: no cover return content + current_content # pragma: no cover - @logfire.instrument(record_return=True) + @logfire.instrument() async def move_entity( self, identifier: str, diff --git a/src/basic_memory/services/file_service.py b/src/basic_memory/services/file_service.py index c884f2c7..102664f7 100644 --- a/src/basic_memory/services/file_service.py +++ b/src/basic_memory/services/file_service.py @@ -60,7 +60,7 @@ class FileService: """ return self.base_path / entity.file_path - @logfire.instrument(record_return=True) + @logfire.instrument() async def read_entity_content(self, entity: EntityModel) -> str: """Get entity's content without frontmatter or structured sections. @@ -79,7 +79,7 @@ class FileService: markdown = await self.markdown_processor.read_file(file_path) return markdown.content or "" - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_entity_file(self, entity: EntityModel) -> None: """Delete entity file from filesystem. @@ -92,7 +92,7 @@ class FileService: path = self.get_entity_path(entity) await self.delete_file(path) - @logfire.instrument(record_return=True) + @logfire.instrument() async def exists(self, path: FilePath) -> bool: """Check if file exists at the provided path. @@ -119,7 +119,7 @@ class FileService: logger.error("Failed to check file existence", path=str(path), error=str(e)) raise FileOperationError(f"Failed to check file existence: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def ensure_directory(self, path: FilePath) -> None: """Ensure directory exists, creating if necessary. @@ -147,7 +147,7 @@ class FileService: logger.error("Failed to create directory", path=str(path), error=str(e)) raise FileOperationError(f"Failed to create directory {path}: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def write_file(self, path: FilePath, content: str) -> str: """Write content to file and return checksum. @@ -191,7 +191,7 @@ class FileService: logger.exception("File write error", path=str(full_path), error=str(e)) raise FileOperationError(f"Failed to write file: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def read_file_content(self, path: FilePath) -> str: """Read file content using true async I/O with aiofiles. @@ -227,7 +227,7 @@ class FileService: logger.exception("File read error", path=str(full_path), error=str(e)) raise FileOperationError(f"Failed to read file: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def read_file(self, path: FilePath) -> Tuple[str, str]: """Read file and compute checksum using true async I/O. @@ -270,7 +270,7 @@ class FileService: logger.exception("File read error", path=str(full_path), error=str(e)) raise FileOperationError(f"Failed to read file: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_file(self, path: FilePath) -> None: """Delete file if it exists. @@ -285,7 +285,7 @@ class FileService: full_path = path_obj if path_obj.is_absolute() else self.base_path / path_obj full_path.unlink(missing_ok=True) - @logfire.instrument(record_return=True) + @logfire.instrument() async def update_frontmatter(self, path: FilePath, updates: Dict[str, Any]) -> str: """Update frontmatter fields in a file while preserving all content. @@ -354,7 +354,7 @@ class FileService: ) raise FileOperationError(f"Failed to update frontmatter: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def compute_checksum(self, path: FilePath) -> str: """Compute checksum for a file using true async I/O. diff --git a/src/basic_memory/services/initialization.py b/src/basic_memory/services/initialization.py index 25f5c90b..9707a6c8 100644 --- a/src/basic_memory/services/initialization.py +++ b/src/basic_memory/services/initialization.py @@ -18,7 +18,7 @@ from basic_memory.repository import ( ) -@logfire.instrument(record_return=True) +@logfire.instrument() async def initialize_database(app_config: BasicMemoryConfig) -> None: """Initialize database with migrations handled automatically by get_or_create_db. @@ -40,7 +40,7 @@ async def initialize_database(app_config: BasicMemoryConfig) -> None: # more specific error if the database is actually unusable -@logfire.instrument(record_return=True) +@logfire.instrument() async def reconcile_projects_with_config(app_config: BasicMemoryConfig): """Ensure all projects in config.json exist in the projects table and vice versa. @@ -74,7 +74,7 @@ async def reconcile_projects_with_config(app_config: BasicMemoryConfig): logger.info("Continuing with initialization despite synchronization error") -@logfire.instrument(record_return=True) +@logfire.instrument() async def initialize_file_sync( app_config: BasicMemoryConfig, ): @@ -145,7 +145,7 @@ async def initialize_file_sync( return None -@logfire.instrument(record_return=True) +@logfire.instrument() async def initialize_app( app_config: BasicMemoryConfig, ): diff --git a/src/basic_memory/services/link_resolver.py b/src/basic_memory/services/link_resolver.py index 09856262..09d24b05 100644 --- a/src/basic_memory/services/link_resolver.py +++ b/src/basic_memory/services/link_resolver.py @@ -27,7 +27,7 @@ class LinkResolver: self.entity_repository = entity_repository self.search_service = search_service - @logfire.instrument(record_return=True) + @logfire.instrument() async def resolve_link( self, link_text: str, use_search: bool = True, strict: bool = False ) -> Optional[Entity]: diff --git a/src/basic_memory/services/project_service.py b/src/basic_memory/services/project_service.py index 7a35697c..dc0f605c 100644 --- a/src/basic_memory/services/project_service.py +++ b/src/basic_memory/services/project_service.py @@ -82,7 +82,7 @@ class ProjectService: """ return os.environ.get("BASIC_MEMORY_PROJECT", self.config_manager.default_project) - @logfire.instrument(record_return=True) + @logfire.instrument() async def list_projects(self) -> Sequence[Project]: """List all projects without loading entity relationships. @@ -92,7 +92,7 @@ class ProjectService: """ return await self.repository.find_all(use_load_options=False) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_project(self, name: str) -> Optional[Project]: """Get the file path for a project by name or permalink.""" return await self.repository.get_by_name(name) or await self.repository.get_by_permalink( @@ -133,7 +133,7 @@ class ProjectService: # Not nested in either direction return False - @logfire.instrument(record_return=True) + @logfire.instrument() async def add_project(self, name: str, path: str, set_default: bool = False) -> None: """Add a new project to the configuration and database. @@ -225,7 +225,7 @@ class ProjectService: logger.info(f"Project '{name}' added at {resolved_path}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def remove_project(self, name: str, delete_notes: bool = False) -> None: """Remove a project from configuration and database. @@ -276,7 +276,7 @@ class ProjectService: except Exception as e: logger.warning(f"Failed to delete project directory {project_path}: {e}") - @logfire.instrument(record_return=True) + @logfire.instrument() async def set_default_project(self, name: str) -> None: """Set the default project in configuration and database. @@ -301,7 +301,7 @@ class ProjectService: logger.info(f"Project '{name}' set as default in configuration and database") - @logfire.instrument(record_return=True) + @logfire.instrument() async def _ensure_single_default_project(self) -> None: """Ensure only one project has is_default=True. @@ -343,7 +343,7 @@ class ProjectService: f"Set '{config_default}' as default project (was missing)" ) # pragma: no cover - @logfire.instrument(record_return=True) + @logfire.instrument() async def synchronize_projects(self) -> None: # pragma: no cover """Synchronize projects between database and configuration. @@ -428,7 +428,7 @@ class ProjectService: logger.info("Project synchronization complete") - @logfire.instrument(record_return=True) + @logfire.instrument() async def move_project(self, name: str, new_path: str) -> None: """Move a project to a new location. @@ -470,7 +470,7 @@ class ProjectService: self.config_manager.save_config(config) raise ValueError(f"Project '{name}' not found in database") - @logfire.instrument(record_return=True) + @logfire.instrument() async def update_project( # pragma: no cover self, name: str, updated_path: Optional[str] = None, is_active: Optional[bool] = None ) -> None: @@ -530,7 +530,7 @@ class ProjectService: f"Changed default project to '{new_default.name}' as '{name}' was deactivated" ) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_project_info(self, project_name: Optional[str] = None) -> ProjectInfoResponse: """Get comprehensive information about the specified Basic Memory project. @@ -598,7 +598,7 @@ class ProjectService: system=system, ) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_statistics(self, project_id: int) -> ProjectStatistics: """Get statistics about the specified project. @@ -715,7 +715,7 @@ class ProjectService: isolated_entities=isolated_count, ) - @logfire.instrument(record_return=True) + @logfire.instrument() async def get_activity_metrics(self, project_id: int) -> ActivityMetrics: """Get activity metrics for the specified project. diff --git a/src/basic_memory/services/search_service.py b/src/basic_memory/services/search_service.py index 09e200ff..429cad50 100644 --- a/src/basic_memory/services/search_service.py +++ b/src/basic_memory/services/search_service.py @@ -36,12 +36,12 @@ class SearchService: self.entity_repository = entity_repository self.file_service = file_service - @logfire.instrument(record_return=True) + @logfire.instrument() async def init_search_index(self): """Create FTS5 virtual table if it doesn't exist.""" await self.repository.init_search_index() - @logfire.instrument(record_return=True) + @logfire.instrument() async def reindex_all(self, background_tasks: Optional[BackgroundTasks] = None) -> None: """Reindex all content from database.""" @@ -58,7 +58,7 @@ class SearchService: logger.info("Reindex complete") - @logfire.instrument(record_return=True) + @logfire.instrument() async def search(self, query: SearchQuery, limit=10, offset=0) -> List[SearchIndexRow]: """Search across all indexed content. @@ -156,7 +156,7 @@ class SearchService: return [] # pragma: no cover - @logfire.instrument(record_return=True) + @logfire.instrument() async def index_entity( self, entity: Entity, @@ -168,7 +168,7 @@ class SearchService: else: await self.index_entity_data(entity, content) - @logfire.instrument(record_return=True) + @logfire.instrument() async def index_entity_data( self, entity: Entity, @@ -182,7 +182,7 @@ class SearchService: entity, content ) if entity.is_markdown else await self.index_entity_file(entity) - @logfire.instrument(record_return=True) + @logfire.instrument() async def index_entity_file( self, entity: Entity, @@ -205,7 +205,7 @@ class SearchService: ) ) - @logfire.instrument(record_return=True) + @logfire.instrument() async def index_entity_markdown( self, entity: Entity, @@ -343,17 +343,17 @@ class SearchService: # Batch insert all rows at once await self.repository.bulk_index_items(rows_to_index) - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_permalink(self, permalink: str): """Delete an item from the search index.""" await self.repository.delete_by_permalink(permalink) - @logfire.instrument(record_return=True) + @logfire.instrument() async def delete_by_entity_id(self, entity_id: int): """Delete an item from the search index.""" await self.repository.delete_by_entity_id(entity_id) - @logfire.instrument(record_return=True) + @logfire.instrument() async def handle_delete(self, entity: Entity): """Handle complete entity deletion from search index including observations and relations.