Class HTM::LongTermMemory ¶
| Inherits | Object |
| Includes | HTM::LongTermMemory::FulltextSearch, HTM::LongTermMemory::HybridSearch, HTM::LongTermMemory::NodeOperations, HTM::LongTermMemory::RelevanceScorer, HTM::LongTermMemory::RobotOperations, HTM::LongTermMemory::TagOperations, HTM::LongTermMemory::VectorSearch |
| Defined in | lib/htm/long_term_memory.rb, lib/htm/long_term_memory/hybrid_search.rb, lib/htm/long_term_memory/vector_search.rb, lib/htm/long_term_memory/tag_operations.rb, lib/htm/long_term_memory/fulltext_search.rb, lib/htm/long_term_memory/node_operations.rb, lib/htm/long_term_memory/relevance_scorer.rb, lib/htm/long_term_memory/robot_operations.rb |
Long-term Memory - PostgreSQL-backed permanent storage
LongTermMemory provides durable storage for all memory nodes with: * Vector similarity search (RAG) * Full-text search * Time-range queries * Relationship graphs * Tag system * Sequel ORM for data access * Query result caching for efficiency
This class uses standalone utility classes and modules:
Standalone classes (used via class methods or instances): * HTM::SqlBuilder: SQL condition building helpers (class methods) * HTM::QueryCache: Query result caching (instantiated as @cache)
Included modules: * RelevanceScorer: Dynamic relevance scoring * NodeOperations: Node CRUD operations * RobotOperations: Robot registration and activity * TagOperations: Tag management * VectorSearch: Vector similarity search * FulltextSearch: Full-text search * HybridSearch: Combined search strategies
Constants¶
DEFAULT_CACHE_SIZE ¶
Number of queries to cache
DEFAULT_CACHE_TTL ¶
Cache lifetime in seconds (5 minutes)
DEFAULT_QUERY_TIMEOUT ¶
milliseconds (30 seconds)
MAX_VECTOR_DIMENSION ¶
Maximum supported dimension with HNSW index (pgvector limitation)
Attributes¶
query_timeout [R] ¶
Returns the value of attribute query_timeout.
Public Instance Methods¶
add(content:, robot_id:, token_count: 0, embedding: nil, metadata: {}) ¶
Add a node to long-term memory (with deduplication)
If content already exists (by content_hash), links the robot to the existing
node and updates timestamps. Otherwise creates a new node.
- @param content [String] Conversation message/utterance
- @param token_count [Integer] Token count
- @param robot_id [Integer] Robot identifier
- @param embedding [Array@param metadata [Hash] Flexible metadata for the node (default: {})
- @raise [ArgumentError] If metadata is not a Hash
- @return [Hash] { node_id:, is_new:, robot_node: }
add_tag(node_id:, tag:) ¶
Add a tag to a node (creates tag and all parent tags)
When adding a hierarchical tag like "database:postgresql:extensions", this
also creates and associates the parent tags "database" and
"database:postgresql" with the node.
- @param node_id [Integer] Node database ID
- @param tag [String] Tag name
- @return [void]
@example
add_tag(node_id: 123, tag: "database:postgresql:extensions")
# Creates tags: "database", "database:postgresql", "database:postgresql:extensions"
# Associates all three with node 123
batch_load_node_tags(node_ids) ¶
Batch load tags for multiple nodes (avoids N+1 queries)
- @param node_ids [Array@return [Hash<Integer, Array
calculate_relevance(node:, query_tags: [], vector_similarity: nil, node_tags: nil) ¶
Calculate dynamic relevance score for a node given query context
- @param node [Hash] Node data with similarity, tags, created_at, access_count
- @param query_tags [Array@param vector_similarity [Float, nil] Pre-computed vector similarity (0-1)
- @param node_tags [Array@return [Float] Composite relevance score (RELEVANCE_MIN to RELEVANCE_MAX)
clear_cache!() ¶
Clear the query result cache
- @return [void]
delete(node_id) ¶
Delete a node
- @param node_id [Integer] Node database ID
- @return [void]
exists?(node_id) ¶
Check if a node exists
- @param node_id [Integer] Node database ID
- @return [Boolean] True if node exists
fetch_candidates_by_timeframe(timeframe:, metadata:, limit:) ¶
Fetch candidates by timeframe using raw SQL (avoids ORM overhead)
- @param timeframe [nil, Range, Array@param metadata [Hash] Filter by metadata fields
- @param limit [Integer] Maximum results
- @return [Array
find_query_matching_tags(query, include_extracted: false) ¶
Find tags that match terms in the query
Searches the tags table for tags where any hierarchy level matches query words. Uses semantic extraction via LLM to find relevant tags.
Performance: Uses a single UNION query instead of multiple sequential queries.
- @param query [String] Search query
- @param include_extracted [Boolean] If true, returns hash with :extracted and :matched keys
- @return [Array@return [Hash] If include_extracted: { extracted: [...], matched: [...] }
get_node_tags(node_id) ¶
Get tags for a specific node
- @param node_id [Integer] Node database ID
- @return [Array
initialize(config, pool_size: nil, query_timeout: DEFAULT_QUERY_TIMEOUT, cache_size: DEFAULT_CACHE_SIZE, cache_ttl: DEFAULT_CACHE_TTL) ¶
Initialize long-term memory storage
- @param config [Hash] Database configuration (host, port, dbname, user, password)
- @param pool_size [Integer, nil] Connection pool size (uses Sequel default if nil)
- @param query_timeout [Integer] Query timeout in milliseconds (default: 30000)
- @param cache_size [Integer] Number of query results to cache (default: 1000, use 0 to disable)
- @param cache_ttl [Integer] Cache time-to-live in seconds (default: 300)
- @return [LongTermMemory] a new instance of LongTermMemory
@example Initialize with defaults
@example Initialize with custom cache settings
@example Disable caching
link_robot_to_node(robot_id:, node:, working_memory: false) ¶
Link a robot to a node (create or update robot_node record)
- @param robot_id [Integer] Robot ID
- @param node [HTM::Models::Node] Node to link
- @param working_memory [Boolean] Whether node is in working memory (default: false)
- @return [HTM::Models::RobotNode] The robot_node link record
mark_evicted(robot_id:, node_ids:) ¶
Mark nodes as evicted from working memory
Sets working_memory = false on the robot_nodes join table for the specified
robot and node IDs.
- @param robot_id [Integer] Robot ID whose working memory is being evicted
- @param node_ids [Array@return [void]
node_topics(node_id) ¶
Get topics for a specific node
- @param node_id [Integer] Node database ID
- @return [Array
nodes_by_topic(topic_path, exact: false, fuzzy: false, min_similarity: DEFAULT_TAG_SIMILARITY_THRESHOLD, limit: 50) ¶
Retrieve nodes by ontological topic
Matching modes (in order of precedence):
* exact: true - Only exact tag name match
* fuzzy: true - Trigram similarity search (typo-tolerant)
* default - LIKE prefix match (e.g., "database" matches
"database:postgresql")
- @param topic_path [String] Topic hierarchy path
- @param exact [Boolean] Exact match only (highest priority)
- @param fuzzy [Boolean] Use trigram similarity for typo-tolerant search
- @param min_similarity [Float] Minimum similarity for fuzzy mode (0.0-1.0)
- @param limit [Integer] Maximum results (capped at MAX_TAG_QUERY_LIMIT)
- @return [Array
ontology_structure() ¶
Get ontology structure view
- @return [Array
pool_size() ¶
For backwards compatibility with tests/code that expect pool_size
popular_tags(limit: 20, timeframe: nil) ¶
Get most popular tags
- @param limit [Integer] Number of tags to return (capped at MAX_TAG_QUERY_LIMIT)
- @param timeframe [Range, nil] Optional time range filter
- @return [Array
recency_half_life_hours() ¶
Not documented.
register_robot(robot_name) ¶
Register a robot
- @param robot_name [String] Robot name
- @return [Integer] Robot ID
retrieve(node_id) ¶
Retrieve a node by ID
Automatically tracks access by incrementing access_count and updating
last_accessed. Uses a single UPDATE query instead of separate increment! and
touch calls.
- @param node_id [Integer] Node database ID
- @return [Hash, nil] Node data or nil
search(timeframe:, query:, limit:, embedding_service:, metadata: {}) ¶
Vector similarity search
- @param timeframe [nil, Range, Array@param query [String] Search query
- @param limit [Integer] Maximum results (capped at MAX_VECTOR_LIMIT)
- @param embedding_service [Object] Service to generate embeddings
- @param metadata [Hash] Filter by metadata fields (default: {})
- @return [Array
search_by_tags(tags:, match_all: false, timeframe: nil, limit: 20) ¶
Search nodes by tags
- @param tags [Array@param match_all [Boolean] If true, match ALL tags; if false, match ANY tag
- @param timeframe [Range, nil] Optional time range filter
- @param limit [Integer] Maximum results
- @return [Array
search_fulltext(timeframe:, query:, limit:, metadata: {}) ¶
Full-text search
- @param timeframe [Range] Time range to search
- @param query [String] Search query
- @param limit [Integer] Maximum results (capped at MAX_FULLTEXT_LIMIT)
- @param metadata [Hash] Filter by metadata fields (default: {})
- @return [Array
search_hybrid(timeframe:, query:, limit:, embedding_service:, prefilter_limit: 100, metadata: {}) ¶
Hybrid search using Reciprocal Rank Fusion
- @param timeframe [Range] Time range to search
- @param query [String] Search query
- @param limit [Integer] Maximum results (capped at MAX_HYBRID_LIMIT)
- @param embedding_service [Object] Service to generate embeddings
- @param prefilter_limit [Integer] Candidates per search (default: 100)
- @param metadata [Hash] Filter by metadata fields (default: {})
- @return [Array
search_tags(query, limit: 20, min_similarity: DEFAULT_TAG_SIMILARITY_THRESHOLD) ¶
Fuzzy search for tags using trigram similarity
Uses PostgreSQL pg_trgm extension to find tags that are similar to the query
string, tolerating typos and partial matches.
- @param query [String] Search query (tag name or partial)
- @param limit [Integer] Maximum results (capped at MAX_TAG_QUERY_LIMIT)
- @param min_similarity [Float] Minimum similarity threshold (0.0-1.0)
- @return [Array
search_with_relevance(timeframe:, query: nil, query_tags: [], limit: 20, embedding_service: nil, metadata: {}) ¶
Search with dynamic relevance scoring
Returns nodes with calculated relevance scores based on query context
- @param timeframe [nil, Range, Array@param query [String, nil] Search query
- @param query_tags [Array@param limit [Integer] Maximum results
- @param embedding_service [Object, nil] Service to generate embeddings
- @param metadata [Hash] Filter by metadata fields (default: {})
- @return [Array
shutdown() ¶
Shutdown - no-op with Sequel (connection pool managed by Sequel)
stats() ¶
Get memory statistics
- @return [Hash] Statistics
topic_relationships(min_shared_nodes: 2, limit: 50) ¶
Get topic relationships (co-occurrence)
- @param min_shared_nodes [Integer] Minimum shared nodes
- @param limit [Integer] Maximum relationships (capped at MAX_TAG_QUERY_LIMIT)
- @return [Array
track_access(node_ids) ¶
Track access for multiple nodes (bulk operation)
Updates access_count and last_accessed for all nodes in the array
- @param node_ids [Array@return [void]
update_last_accessed(node_id) ¶
Update last_accessed timestamp
- @param node_id [Integer] Node database ID
- @return [void]
update_robot_activity(robot_id) ¶
Update robot activity timestamp
- @param robot_id [Integer] Robot identifier
- @return [void]
weight_access() ¶
Not documented.
weight_recency() ¶
Not documented.
weight_semantic() ¶
Configurable scoring weights (via HTM.configuration)
weight_tag() ¶
Not documented.