Module: HTM::LongTermMemory::NodeOperations
- Included in:
- HTM::LongTermMemory
- Defined in:
- lib/htm/long_term_memory/node_operations.rb
Overview
Node CRUD operations for LongTermMemory
Handles creating, reading, updating, and deleting memory nodes with:
-
Content deduplication via SHA-256 hash
-
Soft delete restoration on duplicate content
-
Robot-node linking with remember tracking
-
Bulk access tracking
Instance Method Summary collapse
-
#add(content:, robot_id:, token_count: 0, embedding: nil, metadata: {}) ⇒ Hash
Add a node to long-term memory (with deduplication).
-
#delete(node_id) ⇒ void
Delete a node.
-
#exists?(node_id) ⇒ Boolean
Check if a node exists.
-
#link_robot_to_node(robot_id:, node:, working_memory: false) ⇒ HTM::Models::RobotNode
Link a robot to a node (create or update robot_node record).
-
#mark_evicted(robot_id:, node_ids:) ⇒ void
Mark nodes as evicted from working memory.
-
#retrieve(node_id) ⇒ Hash?
Retrieve a node by ID.
-
#track_access(node_ids) ⇒ void
Track access for multiple nodes (bulk operation).
-
#update_last_accessed(node_id) ⇒ void
Update last_accessed timestamp.
Instance Method Details
#add(content:, robot_id:, token_count: 0, embedding: nil, metadata: {}) ⇒ Hash
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.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 27 def add(content:, robot_id:, token_count: 0, embedding: nil, metadata: {}) # Validate metadata parameter unless .is_a?(Hash) raise ArgumentError, "metadata must be a Hash, got #{.class}" end content_hash = HTM::Models::Node.generate_content_hash(content) # Wrap in transaction to ensure data consistency HTM.db.transaction do # Check for existing node with same content (including soft-deleted) # This avoids unique constraint violations on content_hash existing_node = HTM::Models::Node.with_deleted.first(content_hash: content_hash) # If found but soft-deleted, restore it if existing_node&.deleted? existing_node.restore! HTM.logger.info "Restored soft-deleted node #{existing_node.id} for content match" end if existing_node # Link robot to existing node (or update if already linked) robot_node = link_robot_to_node(robot_id: robot_id, node: existing_node) # Update the node's updated_at timestamp existing_node.update(updated_at: Time.now) { node_id: existing_node.id, is_new: false, robot_node: robot_node } else # Prepare embedding if provided = nil if # Use centralized padding and sanitization = HTM::SqlBuilder.() = HTM::SqlBuilder.() end # Create new node node = HTM::Models::Node.create( content: content, content_hash: content_hash, token_count: token_count, embedding: , metadata: ) # Link robot to new node robot_node = link_robot_to_node(robot_id: robot_id, node: node) # Selectively invalidate search-related cache entries only # (preserves unrelated cached data like tag queries) @cache&.invalidate_methods!(:search, :fulltext, :hybrid) { node_id: node.id, is_new: true, robot_node: robot_node } end end end |
#delete(node_id) ⇒ void
This method returns an undefined value.
Delete a node
158 159 160 161 162 163 164 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 158 def delete(node_id) node = HTM::Models::Node.first(id: node_id) node&.delete # Selectively invalidate search-related cache entries only @cache&.invalidate_methods!(:search, :fulltext, :hybrid) end |
#exists?(node_id) ⇒ Boolean
Check if a node exists
171 172 173 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 171 def exists?(node_id) HTM::Models::Node.where(id: node_id).any? end |
#link_robot_to_node(robot_id:, node:, working_memory: false) ⇒ HTM::Models::RobotNode
Link a robot to a node (create or update robot_node record)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 99 def link_robot_to_node(robot_id:, node:, working_memory: false) robot_node = HTM::Models::RobotNode.first(robot_id: robot_id, node_id: node.id) if robot_node # Existing link - record that robot remembered this again robot_node.record_remember! robot_node.update(working_memory: working_memory) if working_memory else # New link robot_node = HTM::Models::RobotNode.create( robot_id: robot_id, node_id: node.id, first_remembered_at: Time.now, last_remembered_at: Time.now, remember_count: 1, working_memory: working_memory ) end robot_node end |
#mark_evicted(robot_id:, node_ids:) ⇒ void
This method returns an undefined value.
Mark nodes as evicted from working memory
Sets working_memory = false on the robot_nodes join table for the specified robot and node IDs.
184 185 186 187 188 189 190 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 184 def mark_evicted(robot_id:, node_ids:) return if node_ids.empty? HTM::Models::RobotNode .where(robot_id: robot_id, node_id: node_ids) .update(working_memory: false) end |
#retrieve(node_id) ⇒ Hash?
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.
129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 129 def retrieve(node_id) node = HTM::Models::Node.first(id: node_id) return nil unless node # Track access in a single UPDATE query (instead of separate operations) node.this.update( access_count: Sequel[:access_count] + 1, last_accessed: Time.now ) # Reload to get updated values node.refresh.to_hash end |
#track_access(node_ids) ⇒ void
This method returns an undefined value.
Track access for multiple nodes (bulk operation)
Updates access_count and last_accessed for all nodes in the array
199 200 201 202 203 204 205 206 207 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 199 def track_access(node_ids) return if node_ids.empty? # Atomic batch update HTM::Models::Node.where(id: node_ids).update( access_count: Sequel[:access_count] + 1, last_accessed: Sequel.lit('NOW()') ) end |
#update_last_accessed(node_id) ⇒ void
This method returns an undefined value.
Update last_accessed timestamp
148 149 150 151 |
# File 'lib/htm/long_term_memory/node_operations.rb', line 148 def update_last_accessed(node_id) node = HTM::Models::Node.first(id: node_id) node&.update(last_accessed: Time.now) end |