|
11 | 11 |
|
12 | 12 | from loguru import logger |
13 | 13 | from sqlalchemy import text |
| 14 | +from sqlalchemy.exc import OperationalError as SAOperationalError |
14 | 15 |
|
15 | 16 | from basic_memory.models import Project |
16 | 17 | from basic_memory.repository.project_repository import ProjectRepository |
@@ -1004,56 +1005,81 @@ async def get_embedding_status(self, project_id: int) -> EmbeddingStatus: |
1004 | 1005 | ) |
1005 | 1006 | total_indexed_entities = si_result.scalar() or 0 |
1006 | 1007 |
|
1007 | | - chunks_result = await self.repository.execute_query( |
1008 | | - text("SELECT COUNT(*) FROM search_vector_chunks WHERE project_id = :project_id"), |
1009 | | - {"project_id": project_id}, |
1010 | | - ) |
1011 | | - total_chunks = chunks_result.scalar() or 0 |
1012 | | - |
1013 | | - entities_with_chunks_result = await self.repository.execute_query( |
1014 | | - text( |
1015 | | - "SELECT COUNT(DISTINCT entity_id) FROM search_vector_chunks " |
1016 | | - "WHERE project_id = :project_id" |
1017 | | - ), |
1018 | | - {"project_id": project_id}, |
1019 | | - ) |
1020 | | - total_entities_with_chunks = entities_with_chunks_result.scalar() or 0 |
1021 | | - |
1022 | | - # Embeddings count — join pattern differs between SQLite and Postgres |
1023 | | - if is_postgres: |
1024 | | - embeddings_sql = text( |
1025 | | - "SELECT COUNT(*) FROM search_vector_chunks c " |
1026 | | - "JOIN search_vector_embeddings e ON e.chunk_id = c.id " |
1027 | | - "WHERE c.project_id = :project_id" |
1028 | | - ) |
1029 | | - else: |
1030 | | - embeddings_sql = text( |
1031 | | - "SELECT COUNT(*) FROM search_vector_chunks c " |
1032 | | - "JOIN search_vector_embeddings e ON e.rowid = c.id " |
1033 | | - "WHERE c.project_id = :project_id" |
| 1008 | + try: |
| 1009 | + chunks_result = await self.repository.execute_query( |
| 1010 | + text("SELECT COUNT(*) FROM search_vector_chunks WHERE project_id = :project_id"), |
| 1011 | + {"project_id": project_id}, |
1034 | 1012 | ) |
| 1013 | + total_chunks = chunks_result.scalar() or 0 |
1035 | 1014 |
|
1036 | | - embeddings_result = await self.repository.execute_query( |
1037 | | - embeddings_sql, {"project_id": project_id} |
1038 | | - ) |
1039 | | - total_embeddings = embeddings_result.scalar() or 0 |
| 1015 | + entities_with_chunks_result = await self.repository.execute_query( |
| 1016 | + text( |
| 1017 | + "SELECT COUNT(DISTINCT entity_id) FROM search_vector_chunks " |
| 1018 | + "WHERE project_id = :project_id" |
| 1019 | + ), |
| 1020 | + {"project_id": project_id}, |
| 1021 | + ) |
| 1022 | + total_entities_with_chunks = entities_with_chunks_result.scalar() or 0 |
| 1023 | + |
| 1024 | + # Embeddings count — join pattern differs between SQLite and Postgres |
| 1025 | + if is_postgres: |
| 1026 | + embeddings_sql = text( |
| 1027 | + "SELECT COUNT(*) FROM search_vector_chunks c " |
| 1028 | + "JOIN search_vector_embeddings e ON e.chunk_id = c.id " |
| 1029 | + "WHERE c.project_id = :project_id" |
| 1030 | + ) |
| 1031 | + else: |
| 1032 | + embeddings_sql = text( |
| 1033 | + "SELECT COUNT(*) FROM search_vector_chunks c " |
| 1034 | + "JOIN search_vector_embeddings e ON e.rowid = c.id " |
| 1035 | + "WHERE c.project_id = :project_id" |
| 1036 | + ) |
1040 | 1037 |
|
1041 | | - # Orphaned chunks (chunks without embeddings — indicates interrupted indexing) |
1042 | | - if is_postgres: |
1043 | | - orphan_sql = text( |
1044 | | - "SELECT COUNT(*) FROM search_vector_chunks c " |
1045 | | - "LEFT JOIN search_vector_embeddings e ON e.chunk_id = c.id " |
1046 | | - "WHERE c.project_id = :project_id AND e.chunk_id IS NULL" |
| 1038 | + embeddings_result = await self.repository.execute_query( |
| 1039 | + embeddings_sql, {"project_id": project_id} |
1047 | 1040 | ) |
1048 | | - else: |
1049 | | - orphan_sql = text( |
1050 | | - "SELECT COUNT(*) FROM search_vector_chunks c " |
1051 | | - "LEFT JOIN search_vector_embeddings e ON e.rowid = c.id " |
1052 | | - "WHERE c.project_id = :project_id AND e.rowid IS NULL" |
| 1041 | + total_embeddings = embeddings_result.scalar() or 0 |
| 1042 | + |
| 1043 | + # Orphaned chunks (chunks without embeddings — indicates interrupted indexing) |
| 1044 | + if is_postgres: |
| 1045 | + orphan_sql = text( |
| 1046 | + "SELECT COUNT(*) FROM search_vector_chunks c " |
| 1047 | + "LEFT JOIN search_vector_embeddings e ON e.chunk_id = c.id " |
| 1048 | + "WHERE c.project_id = :project_id AND e.chunk_id IS NULL" |
| 1049 | + ) |
| 1050 | + else: |
| 1051 | + orphan_sql = text( |
| 1052 | + "SELECT COUNT(*) FROM search_vector_chunks c " |
| 1053 | + "LEFT JOIN search_vector_embeddings e ON e.rowid = c.id " |
| 1054 | + "WHERE c.project_id = :project_id AND e.rowid IS NULL" |
| 1055 | + ) |
| 1056 | + |
| 1057 | + orphan_result = await self.repository.execute_query( |
| 1058 | + orphan_sql, {"project_id": project_id} |
1053 | 1059 | ) |
| 1060 | + orphaned_chunks = orphan_result.scalar() or 0 |
| 1061 | + except SAOperationalError as exc: |
| 1062 | + # Trigger: sqlite_master can list vec0 virtual tables even when sqlite-vec |
| 1063 | + # is not loaded in the current Python runtime. |
| 1064 | + # Why: project info should degrade gracefully instead of crashing on stats queries. |
| 1065 | + # Outcome: report vector tables as unavailable and point the user to install the |
| 1066 | + # missing dependency before rebuilding embeddings. |
| 1067 | + if is_postgres or "no such module: vec0" not in str(exc).lower(): |
| 1068 | + raise |
1054 | 1069 |
|
1055 | | - orphan_result = await self.repository.execute_query(orphan_sql, {"project_id": project_id}) |
1056 | | - orphaned_chunks = orphan_result.scalar() or 0 |
| 1070 | + return EmbeddingStatus( |
| 1071 | + semantic_search_enabled=True, |
| 1072 | + embedding_provider=provider, |
| 1073 | + embedding_model=model, |
| 1074 | + embedding_dimensions=dimensions, |
| 1075 | + total_indexed_entities=total_indexed_entities, |
| 1076 | + vector_tables_exist=False, |
| 1077 | + reindex_recommended=True, |
| 1078 | + reindex_reason=( |
| 1079 | + "SQLite vector tables exist but sqlite-vec is unavailable in this Python " |
| 1080 | + "environment — install/update basic-memory, then run: bm reindex --embeddings" |
| 1081 | + ), |
| 1082 | + ) |
1057 | 1083 |
|
1058 | 1084 | # --- Reindex recommendation logic (priority order) --- |
1059 | 1085 | reindex_recommended = False |
|
0 commit comments