diff --git a/hindsight-api-slim/hindsight_api/engine/memory_engine.py b/hindsight-api-slim/hindsight_api/engine/memory_engine.py index 313690df..797129aa 100644 --- a/hindsight-api-slim/hindsight_api/engine/memory_engine.py +++ b/hindsight-api-slim/hindsight_api/engine/memory_engine.py @@ -1734,11 +1734,13 @@ class MemoryEngine(MemoryEngineInterface): # Ensure embedding column dimension matches the model's dimension # This is done after migrations and after embeddings.initialize() + # Use migration URL (sync driver) when available, fallback to main URL + _sync_url = config.migration_database_url or self.db_url for tenant in tenants: schema = tenant.schema if schema: ensure_embedding_dimension( - self.db_url, + _sync_url, self.embeddings.dimension, schema=schema, vector_extension=config.vector_extension, @@ -1748,14 +1750,14 @@ class MemoryEngine(MemoryEngineInterface): for tenant in tenants: schema = tenant.schema if schema: - ensure_vector_extension(self.db_url, vector_extension=config.vector_extension, schema=schema) + ensure_vector_extension(_sync_url, vector_extension=config.vector_extension, schema=schema) # Ensure text search columns/indexes match the configured extension for tenant in tenants: schema = tenant.schema if schema: ensure_text_search_extension( - self.db_url, text_search_extension=config.text_search_extension, schema=schema + _sync_url, text_search_extension=config.text_search_extension, schema=schema ) logger.info(f"Connecting to PostgreSQL at {mask_network_location(self.db_url)}") diff --git a/hindsight-api-slim/hindsight_api/engine/retain/fact_storage.py b/hindsight-api-slim/hindsight_api/engine/retain/fact_storage.py index 85f80c07..d52cecc6 100644 --- a/hindsight-api-slim/hindsight_api/engine/retain/fact_storage.py +++ b/hindsight-api-slim/hindsight_api/engine/retain/fact_storage.py @@ -67,6 +67,9 @@ async def insert_facts_batch( tags_list = [] observation_scopes_list = [] text_signals_list = [] + rooms_list = [] + halls_list = [] + layers_list = [] for fact in facts: fact_texts.append(_sanitize_text(fact.fact_text)) @@ -120,7 +123,8 @@ async def insert_facts_batch( WITH input_data AS ( SELECT * FROM unnest( $2::text[], $3::vector[], $4::timestamptz[], $5::timestamptz[], $6::timestamptz[], $7::timestamptz[], - $8::text[], $9::text[], $10::jsonb[], $11::text[], $12::text[], $13::jsonb[], $14::jsonb[], $15::text[] + $8::text[], $9::text[], $10::jsonb[], $11::text[], $12::text[], $13::jsonb[], $14::jsonb[], $15::text[], + $16::text[], $17::text[], $18::text[] ) AS t(text, embedding, event_date, occurred_start, occurred_end, mentioned_at, context, fact_type, metadata, chunk_id, document_id, tags_json, observation_scopes_json, text_signals, room, hall, layer) @@ -155,7 +159,8 @@ async def insert_facts_batch( WITH input_data AS ( SELECT * FROM unnest( $2::text[], $3::vector[], $4::timestamptz[], $5::timestamptz[], $6::timestamptz[], $7::timestamptz[], - $8::text[], $9::text[], $10::jsonb[], $11::text[], $12::text[], $13::jsonb[], $14::jsonb[], $15::text[] + $8::text[], $9::text[], $10::jsonb[], $11::text[], $12::text[], $13::jsonb[], $14::jsonb[], $15::text[], + $16::text[], $17::text[], $18::text[] ) AS t(text, embedding, event_date, occurred_start, occurred_end, mentioned_at, context, fact_type, metadata, chunk_id, document_id, tags_json, observation_scopes_json, text_signals, room, hall, layer) @@ -197,6 +202,9 @@ async def insert_facts_batch( tags_list, observation_scopes_list, text_signals_list, + rooms_list, + halls_list, + layers_list, ) unit_ids = [str(row["id"]) for row in results] diff --git a/hindsight-api-slim/hindsight_api/engine/search/retrieval.py b/hindsight-api-slim/hindsight_api/engine/search/retrieval.py index 7983273d..4995d4b7 100644 --- a/hindsight-api-slim/hindsight_api/engine/search/retrieval.py +++ b/hindsight-api-slim/hindsight_api/engine/search/retrieval.py @@ -207,6 +207,9 @@ async def retrieve_semantic_bm25_combined( f" AND (1 - (embedding <=> $1::vector)) >= 0.3" f" {tags_clause}" f" {groups_clause}" + f" {room_clause}" + f" {hall_clause}" + f" {layer_clause}" f" ORDER BY embedding <=> $1::vector" f" LIMIT {hnsw_fetch})" ) @@ -247,6 +250,9 @@ async def retrieve_semantic_bm25_combined( f" {bm25_where_filter}" f" {tags_clause}" f" {groups_clause}" + f" {room_clause}" + f" {hall_clause}" + f" {layer_clause}" f" ORDER BY {bm25_order_by}" f" LIMIT $3)" ) @@ -260,6 +266,9 @@ async def retrieve_semantic_bm25_combined( if tags: params.append(tags) params.extend(groups_params) + params.extend(room_params) + params.extend(hall_params) + params.extend(layer_params) rows = await conn.fetch(query, *params) @@ -358,6 +367,9 @@ async def retrieve_temporal_combined( if tags: params.append(tags) params.extend(groups_params) + params.extend(room_params_list) + params.extend(hall_params_list) + params.extend(layer_params_list) # Two-phase entry point query: # Phase 1 (date_ranked): rank by date only — no embedding computation — for all units in @@ -389,6 +401,9 @@ async def retrieve_temporal_combined( ) {tags_clause} {groups_clause} + {room_clause} + {hall_clause} + {layer_clause} ), sim_ranked AS ( SELECT mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, mu.occurred_end, mu.mentioned_at, mu.fact_type, mu.proof_count, mu.document_id, mu.chunk_id, mu.tags, mu.metadata, mu.room, mu.hall, mu.layer, diff --git a/hindsight-api-slim/hindsight_api/models.py b/hindsight-api-slim/hindsight_api/models.py index d3a7c9a8..9e5fa501 100644 --- a/hindsight-api-slim/hindsight_api/models.py +++ b/hindsight-api-slim/hindsight_api/models.py @@ -296,6 +296,8 @@ class Bank(Base): updated_at: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), server_default=func.now()) __table_args__ = (Index("idx_banks_bank_id", "bank_id"),) + + class Tunnel(Base): """Cross-bank memory bridges — links between concepts in different banks (ADR-145 RCLL)."""