diff --git a/hindsight-api/hindsight_api/engine/memory_engine.py b/hindsight-api/hindsight_api/engine/memory_engine.py index 8ddb96ac..f6211416 100644 --- a/hindsight-api/hindsight_api/engine/memory_engine.py +++ b/hindsight-api/hindsight_api/engine/memory_engine.py @@ -473,29 +473,6 @@ class MemoryEngine(MemoryEngineInterface): _current_schema.set(tenant_context.schema_name) return tenant_context.schema_name - async def _handle_access_count_update(self, task_dict: dict[str, Any]): - """ - Handler for access count update tasks. - - Args: - task_dict: Dict with 'node_ids' key containing list of node IDs to update - - Raises: - Exception: Any exception from database operations (propagates to execute_task for retry) - """ - node_ids = task_dict.get("node_ids", []) - if not node_ids: - return - - pool = await self._get_pool() - # Convert string UUIDs to UUID type for faster matching - uuid_list = [uuid.UUID(nid) for nid in node_ids] - async with acquire_with_retry(pool) as conn: - await conn.execute( - f"UPDATE {fq_table('memory_units')} SET access_count = access_count + 1 WHERE id = ANY($1::uuid[])", - uuid_list, - ) - async def _handle_batch_retain(self, task_dict: dict[str, Any]): """ Handler for batch retain tasks. @@ -797,7 +774,7 @@ class MemoryEngine(MemoryEngineInterface): Args: task_dict: Task dictionary with 'type' key and other payload data - Example: {'type': 'access_count_update', 'node_ids': [...]} + Example: {'type': 'batch_retain', 'bank_id': '...', 'contents': [...]} """ task_type = task_dict.get("type") operation_id = task_dict.get("operation_id") @@ -822,9 +799,7 @@ class MemoryEngine(MemoryEngineInterface): # Continue with processing if we can't check status try: - if task_type == "access_count_update": - await self._handle_access_count_update(task_dict) - elif task_type == "batch_retain": + if task_type == "batch_retain": await self._handle_batch_retain(task_dict) elif task_type == "refresh_mental_models": await self._handle_refresh_mental_models(task_dict) @@ -2287,7 +2262,6 @@ class MemoryEngine(MemoryEngineInterface): text=sr.retrieval.text, context=sr.retrieval.context or "", event_date=sr.retrieval.occurred_start, - access_count=sr.retrieval.access_count, is_entry_point=(sr.id in [ep.node_id for ep in tracer.entry_points]), parent_node_id=None, # In parallel retrieval, there's no clear parent link_type=None, @@ -2299,18 +2273,6 @@ class MemoryEngine(MemoryEngineInterface): final_weight=sr.weight, ) - # Step 8: Queue access count updates for visited nodes - visited_ids = list(set([sr.id for sr in scored_results[:50]])) # Top 50 - if visited_ids: - await self._task_backend.submit_task( - { - "type": "access_count_update", - "bank_id": bank_id, - "node_ids": visited_ids, - } - ) - log_buffer.append(f" [7] Queued access count updates for {len(visited_ids)} nodes") - # Log fact_type distribution in results fact_type_counts = {} for sr in top_scored: diff --git a/hindsight-api/hindsight_api/engine/retain/fact_storage.py b/hindsight-api/hindsight_api/engine/retain/fact_storage.py index d1839e1a..43e4c2f0 100644 --- a/hindsight-api/hindsight_api/engine/retain/fact_storage.py +++ b/hindsight-api/hindsight_api/engine/retain/fact_storage.py @@ -41,7 +41,6 @@ async def insert_facts_batch( contexts = [] fact_types = [] confidence_scores = [] - access_counts = [] metadata_jsons = [] chunk_ids = [] document_ids = [] @@ -61,7 +60,6 @@ async def insert_facts_batch( fact_types.append(fact.fact_type) # confidence_score is only for opinion facts confidence_scores.append(1.0 if fact.fact_type == "opinion" else None) - access_counts.append(0) # Initial access count metadata_jsons.append(json.dumps(fact.metadata)) chunk_ids.append(fact.chunk_id) # Use per-fact document_id if available, otherwise fallback to batch-level document_id @@ -76,16 +74,16 @@ 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::float[], $11::int[], $12::jsonb[], $13::text[], $14::text[], $15::jsonb[] + $8::text[], $9::text[], $10::float[], $11::jsonb[], $12::text[], $13::text[], $14::jsonb[] ) AS t(text, embedding, event_date, occurred_start, occurred_end, mentioned_at, - context, fact_type, confidence_score, access_count, metadata, chunk_id, document_id, tags_json) + context, fact_type, confidence_score, metadata, chunk_id, document_id, tags_json) ) INSERT INTO {fq_table("memory_units")} (bank_id, text, embedding, event_date, occurred_start, occurred_end, mentioned_at, - context, fact_type, confidence_score, access_count, metadata, chunk_id, document_id, tags) + context, fact_type, confidence_score, metadata, chunk_id, document_id, tags) SELECT $1, text, embedding, event_date, occurred_start, occurred_end, mentioned_at, - context, fact_type, confidence_score, access_count, metadata, chunk_id, document_id, + context, fact_type, confidence_score, metadata, chunk_id, document_id, COALESCE( (SELECT array_agg(elem) FROM jsonb_array_elements_text(tags_json) AS elem), '{{}}'::varchar[] @@ -103,7 +101,6 @@ async def insert_facts_batch( contexts, fact_types, confidence_scores, - access_counts, metadata_jsons, chunk_ids, document_ids, diff --git a/hindsight-api/hindsight_api/engine/search/graph_retrieval.py b/hindsight-api/hindsight_api/engine/search/graph_retrieval.py index 60f3169a..8edea18e 100644 --- a/hindsight-api/hindsight_api/engine/search/graph_retrieval.py +++ b/hindsight-api/hindsight_api/engine/search/graph_retrieval.py @@ -162,7 +162,7 @@ class BFSGraphRetriever(GraphRetriever): entry_points = await conn.fetch( f""" SELECT id, text, context, event_date, occurred_start, occurred_end, - mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity FROM {fq_table("memory_units")} WHERE bank_id = $2 @@ -216,7 +216,7 @@ class BFSGraphRetriever(GraphRetriever): neighbors = await conn.fetch( f""" SELECT mu.id, mu.text, mu.context, mu.occurred_start, mu.occurred_end, - mu.mentioned_at, mu.access_count, mu.embedding, mu.fact_type, + mu.mentioned_at, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, mu.tags, ml.weight, ml.link_type, ml.from_unit_id FROM {fq_table("memory_links")} ml diff --git a/hindsight-api/hindsight_api/engine/search/link_expansion_retrieval.py b/hindsight-api/hindsight_api/engine/search/link_expansion_retrieval.py index 76d157b9..992b1541 100644 --- a/hindsight-api/hindsight_api/engine/search/link_expansion_retrieval.py +++ b/hindsight-api/hindsight_api/engine/search/link_expansion_retrieval.py @@ -45,7 +45,7 @@ async def _find_semantic_seeds( rows = await conn.fetch( f""" SELECT id, text, context, event_date, occurred_start, occurred_end, - mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity FROM {fq_table("memory_units")} WHERE bank_id = $2 @@ -168,7 +168,7 @@ class LinkExpansionRetriever(GraphRetriever): f""" SELECT mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, - mu.occurred_end, mu.mentioned_at, mu.access_count, mu.embedding, + mu.occurred_end, mu.mentioned_at, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, mu.tags, COUNT(*)::float AS score FROM {fq_table("unit_entities")} seed_ue @@ -193,7 +193,7 @@ class LinkExpansionRetriever(GraphRetriever): f""" SELECT DISTINCT ON (mu.id) mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, - mu.occurred_end, mu.mentioned_at, mu.access_count, mu.embedding, + mu.occurred_end, mu.mentioned_at, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, mu.tags, ml.weight + 1.0 AS score FROM {fq_table("memory_links")} ml diff --git a/hindsight-api/hindsight_api/engine/search/mpfp_retrieval.py b/hindsight-api/hindsight_api/engine/search/mpfp_retrieval.py index 269a0ac4..b186dcf2 100644 --- a/hindsight-api/hindsight_api/engine/search/mpfp_retrieval.py +++ b/hindsight-api/hindsight_api/engine/search/mpfp_retrieval.py @@ -449,7 +449,7 @@ async def fetch_memory_units_by_ids( rows = await conn.fetch( f""" SELECT id, text, context, event_date, occurred_start, occurred_end, - mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags + mentioned_at, embedding, fact_type, document_id, chunk_id, tags FROM {fq_table("memory_units")} WHERE id = ANY($1::uuid[]) AND fact_type = $2 diff --git a/hindsight-api/hindsight_api/engine/search/retrieval.py b/hindsight-api/hindsight_api/engine/search/retrieval.py index 4adc80cd..95f207a3 100644 --- a/hindsight-api/hindsight_api/engine/search/retrieval.py +++ b/hindsight-api/hindsight_api/engine/search/retrieval.py @@ -116,7 +116,7 @@ async def retrieve_semantic( results = await conn.fetch( f""" - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity FROM {fq_table("memory_units")} WHERE bank_id = $2 @@ -180,7 +180,7 @@ async def retrieve_bm25( results = await conn.fetch( f""" - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, ts_rank_cd(search_vector, to_tsquery('english', $1)) AS bm25_score FROM {fq_table("memory_units")} WHERE bank_id = $2 @@ -237,7 +237,7 @@ async def retrieve_semantic_bm25_combined( results = await conn.fetch( f""" WITH semantic_ranked AS ( - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity, NULL::float AS bm25_score, 'semantic' AS source, @@ -249,7 +249,7 @@ async def retrieve_semantic_bm25_combined( AND (1 - (embedding <=> $1::vector)) >= 0.3 {tags_clause} ) - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, similarity, bm25_score, source FROM semantic_ranked WHERE rn <= $4 @@ -281,7 +281,7 @@ async def retrieve_semantic_bm25_combined( results = await conn.fetch( f""" WITH semantic_ranked AS ( - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity, NULL::float AS bm25_score, 'semantic' AS source, @@ -294,7 +294,7 @@ async def retrieve_semantic_bm25_combined( {tags_clause} ), bm25_ranked AS ( - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, NULL::float AS similarity, ts_rank_cd(search_vector, to_tsquery('english', $5)) AS bm25_score, 'bm25' AS source, @@ -306,12 +306,12 @@ async def retrieve_semantic_bm25_combined( {tags_clause} ), semantic AS ( - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, similarity, bm25_score, source FROM semantic_ranked WHERE rn <= $4 ), bm25 AS ( - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, similarity, bm25_score, source FROM bm25_ranked WHERE rn <= $4 ) @@ -386,7 +386,7 @@ async def retrieve_temporal_combined( entry_points = await conn.fetch( f""" WITH ranked_entries AS ( - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity, ROW_NUMBER() OVER (PARTITION BY fact_type ORDER BY COALESCE(occurred_start, mentioned_at, occurred_end) DESC, embedding <=> $1::vector) AS rn FROM {fq_table("memory_units")} @@ -406,7 +406,7 @@ async def retrieve_temporal_combined( AND (1 - (embedding <=> $1::vector)) >= $6 {tags_clause} ) - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, similarity + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, similarity FROM ranked_entries WHERE rn <= 10 """, @@ -486,7 +486,7 @@ async def retrieve_temporal_combined( neighbors = await conn.fetch( f""" - SELECT mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, mu.occurred_end, mu.mentioned_at, mu.access_count, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, mu.tags, + SELECT mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, mu.occurred_end, mu.mentioned_at, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, mu.tags, ml.weight, ml.link_type, ml.from_unit_id, 1 - (mu.embedding <=> $1::vector) AS similarity FROM {fq_table("memory_links")} ml @@ -610,7 +610,7 @@ async def retrieve_temporal( entry_points = await conn.fetch( f""" - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, tags, + SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, embedding, fact_type, document_id, chunk_id, tags, 1 - (embedding <=> $1::vector) AS similarity FROM {fq_table("memory_units")} WHERE bank_id = $2 @@ -691,7 +691,7 @@ async def retrieve_temporal( # Batch fetch all neighbors for this batch of nodes neighbors = await conn.fetch( f""" - SELECT mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, mu.occurred_end, mu.mentioned_at, mu.access_count, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, + SELECT mu.id, mu.text, mu.context, mu.event_date, mu.occurred_start, mu.occurred_end, mu.mentioned_at, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, ml.weight, ml.link_type, ml.from_unit_id, 1 - (mu.embedding <=> $1::vector) AS similarity FROM {fq_table("memory_links")} ml @@ -1023,7 +1023,7 @@ async def _get_temporal_entry_points( rows = await conn.fetch( f""" SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, - access_count, embedding, fact_type, document_id, chunk_id, + embedding, fact_type, document_id, chunk_id, 1 - (embedding <=> $1::vector) AS similarity FROM {fq_table("memory_units")} WHERE bank_id = $2 diff --git a/hindsight-api/hindsight_api/engine/search/scoring.py b/hindsight-api/hindsight_api/engine/search/scoring.py index d0258175..a8c5f361 100644 --- a/hindsight-api/hindsight_api/engine/search/scoring.py +++ b/hindsight-api/hindsight_api/engine/search/scoring.py @@ -65,31 +65,6 @@ def calculate_recency_weight(days_since: float, half_life_days: float = 365.0) - return 1.0 / (1.0 + math.log1p(normalized_age)) -def calculate_frequency_weight(access_count: int, max_boost: float = 2.0) -> float: - """ - Calculate frequency weight based on access count. - - Frequently accessed memories are weighted higher. - Uses logarithmic scaling to avoid over-weighting. - - Args: - access_count: Number of times the memory was accessed - max_boost: Maximum multiplier for frequently accessed memories - - Returns: - Weight between 1.0 and max_boost - """ - import math - - if access_count <= 0: - return 1.0 - - # Logarithmic scaling: log(access_count + 1) / log(10) - # This gives: 0 accesses = 1.0, 9 accesses ~= 1.5, 99 accesses ~= 2.0 - normalized = math.log(access_count + 1) / math.log(10) - return 1.0 + min(normalized, max_boost - 1.0) - - def calculate_temporal_anchor(occurred_start: datetime, occurred_end: datetime) -> datetime: """ Calculate a single temporal anchor point from a temporal range. diff --git a/hindsight-api/hindsight_api/engine/search/trace.py b/hindsight-api/hindsight_api/engine/search/trace.py index e0ed874b..b61557bd 100644 --- a/hindsight-api/hindsight_api/engine/search/trace.py +++ b/hindsight-api/hindsight_api/engine/search/trace.py @@ -85,7 +85,6 @@ class NodeVisit(BaseModel): text: str = Field(description="Memory unit text content") context: str = Field(description="Memory unit context") event_date: datetime | None = Field(default=None, description="When the memory occurred") - access_count: int = Field(description="Number of times accessed before this search") # How this node was reached is_entry_point: bool = Field(description="Whether this is an entry point") diff --git a/hindsight-api/hindsight_api/engine/search/tracer.py b/hindsight-api/hindsight_api/engine/search/tracer.py index d5c5016e..c2d436b1 100644 --- a/hindsight-api/hindsight_api/engine/search/tracer.py +++ b/hindsight-api/hindsight_api/engine/search/tracer.py @@ -136,7 +136,6 @@ class SearchTracer: text: str, context: str, event_date: datetime | None, - access_count: int, is_entry_point: bool, parent_node_id: str | None, link_type: Literal["temporal", "semantic", "entity"] | None, @@ -155,7 +154,6 @@ class SearchTracer: text: Memory unit text context: Memory unit context event_date: When the memory occurred - access_count: Access count before this search is_entry_point: Whether this is an entry point parent_node_id: Node that led here (None for entry points) link_type: Type of link from parent @@ -194,7 +192,6 @@ class SearchTracer: text=text, context=context, event_date=event_date, - access_count=access_count, is_entry_point=is_entry_point, parent_node_id=parent_node_id, link_type=link_type, diff --git a/hindsight-api/hindsight_api/engine/search/types.py b/hindsight-api/hindsight_api/engine/search/types.py index b80bb2e3..7473fb66 100644 --- a/hindsight-api/hindsight_api/engine/search/types.py +++ b/hindsight-api/hindsight_api/engine/search/types.py @@ -46,7 +46,6 @@ class RetrievalResult: mentioned_at: datetime | None = None document_id: str | None = None chunk_id: str | None = None - access_count: int = 0 embedding: list[float] | None = None tags: list[str] | None = None # Visibility scope tags @@ -71,7 +70,6 @@ class RetrievalResult: mentioned_at=row.get("mentioned_at"), document_id=row.get("document_id"), chunk_id=row.get("chunk_id"), - access_count=row.get("access_count", 0), embedding=row.get("embedding"), tags=row.get("tags"), similarity=row.get("similarity"), @@ -156,7 +154,6 @@ class ScoredResult: "mentioned_at": self.retrieval.mentioned_at, "document_id": self.retrieval.document_id, "chunk_id": self.retrieval.chunk_id, - "access_count": self.retrieval.access_count, "embedding": self.retrieval.embedding, "tags": self.retrieval.tags, "semantic_similarity": self.retrieval.similarity, diff --git a/hindsight-api/hindsight_api/engine/utils.py b/hindsight-api/hindsight_api/engine/utils.py index 87f0824b..204622ee 100644 --- a/hindsight-api/hindsight_api/engine/utils.py +++ b/hindsight-api/hindsight_api/engine/utils.py @@ -124,31 +124,6 @@ def calculate_recency_weight(days_since: float, half_life_days: float = 365.0) - return 1.0 / (1.0 + math.log1p(normalized_age)) -def calculate_frequency_weight(access_count: int, max_boost: float = 2.0) -> float: - """ - Calculate frequency weight based on access count. - - Frequently accessed memories are weighted higher. - Uses logarithmic scaling to avoid over-weighting. - - Args: - access_count: Number of times the memory was accessed - max_boost: Maximum multiplier for frequently accessed memories - - Returns: - Weight between 1.0 and max_boost - """ - import math - - if access_count <= 0: - return 1.0 - - # Logarithmic scaling: log(access_count + 1) / log(10) - # This gives: 0 accesses = 1.0, 9 accesses ~= 1.5, 99 accesses ~= 2.0 - normalized = math.log(access_count + 1) / math.log(10) - return 1.0 + min(normalized, max_boost - 1.0) - - def calculate_temporal_anchor(occurred_start: datetime, occurred_end: datetime) -> datetime: """ Calculate a single temporal anchor point from a temporal range. diff --git a/hindsight-api/hindsight_api/models.py b/hindsight-api/hindsight_api/models.py index 15c890fe..b5ecf4ca 100644 --- a/hindsight-api/hindsight_api/models.py +++ b/hindsight-api/hindsight_api/models.py @@ -95,7 +95,6 @@ class MemoryUnit(Base): mentioned_at: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True)) # When fact was mentioned fact_type: Mapped[str] = mapped_column(Text, nullable=False, server_default="world") confidence_score: Mapped[float | None] = mapped_column(Float) - access_count: Mapped[int] = mapped_column(Integer, server_default="0") unit_metadata: Mapped[dict] = mapped_column( "metadata", JSONB, server_default=sql_text("'{}'::jsonb") ) # User-defined metadata (str->str) @@ -131,7 +130,6 @@ class MemoryUnit(Base): Index("idx_memory_units_document_id", "document_id"), Index("idx_memory_units_event_date", "event_date", postgresql_ops={"event_date": "DESC"}), Index("idx_memory_units_bank_date", "bank_id", "event_date", postgresql_ops={"event_date": "DESC"}), - Index("idx_memory_units_access_count", "access_count", postgresql_ops={"access_count": "DESC"}), Index("idx_memory_units_fact_type", "fact_type"), Index("idx_memory_units_bank_fact_type", "bank_id", "fact_type"), Index( diff --git a/hindsight-docs/docs/developer/api/operations.md b/hindsight-docs/docs/developer/api/operations.md index 343d84e0..48b90456 100644 --- a/hindsight-docs/docs/developer/api/operations.md +++ b/hindsight-docs/docs/developer/api/operations.md @@ -27,7 +27,6 @@ Support for external streaming platforms like Kafka for scale-out processing is | **batch_retain** | `retain_batch` with `async=True` | Processes large content batches in the background | | **form_opinion** | After each `reflect` call | Extracts and stores new opinions formed during reflection | | **reinforce_opinion** | After `retain` | Updates opinion confidence based on new supporting evidence | -| **access_count_update** | After `recall` | Tracks which memories are accessed for relevance scoring | | **regenerate_observations** | Bank profile update | Regenerates entity observations when disposition changes | ## Async Retain Example