Class: HTM::WorkingMemory
- Inherits:
-
Object
- Object
- HTM::WorkingMemory
- Defined in:
- lib/htm/working_memory.rb
Overview
Working Memory - Token-limited active context for immediate LLM use
WorkingMemory manages the active conversation context within token limits. When full, it evicts less important or older nodes back to long-term storage.
Thread Safety: All public methods are protected by a mutex to ensure safe concurrent access from multiple threads.
Instance Attribute Summary collapse
-
#max_tokens ⇒ Object
readonly
Returns the value of attribute max_tokens.
Instance Method Summary collapse
-
#add(key, value, token_count:, access_count: 0, last_accessed: nil, from_recall: false) ⇒ void
Add a node to working memory.
-
#add_from_sync(id:, content:, token_count:, created_at:) ⇒ void
Add a node from sync notification (bypasses normal add flow).
-
#assemble_context(strategy:, max_tokens: nil) ⇒ String
Assemble context string for LLM.
-
#clear ⇒ void
Clear all nodes from working memory.
-
#clear_from_sync ⇒ void
Clear all nodes from sync notification.
-
#evict_to_make_space(needed_tokens) ⇒ Array<Hash>
Evict nodes to make space.
-
#has_space?(token_count) ⇒ Boolean
Check if there’s space for a node.
-
#initialize(max_tokens:) ⇒ WorkingMemory
constructor
Initialize working memory.
-
#node_count ⇒ Integer
Get node count.
-
#remove(key) ⇒ void
Remove a node from working memory.
-
#remove_from_sync(node_id) ⇒ void
Remove a node from sync notification.
-
#token_count ⇒ Integer
Get current token count.
-
#utilization_percentage ⇒ Float
Get utilization percentage.
Constructor Details
#initialize(max_tokens:) ⇒ WorkingMemory
Initialize working memory
19 20 21 22 23 24 |
# File 'lib/htm/working_memory.rb', line 19 def initialize(max_tokens:) @max_tokens = max_tokens @nodes = {} @access_order = [] @mutex = Mutex.new end |
Instance Attribute Details
#max_tokens ⇒ Object (readonly)
Returns the value of attribute max_tokens.
13 14 15 |
# File 'lib/htm/working_memory.rb', line 13 def max_tokens @max_tokens end |
Instance Method Details
#add(key, value, token_count:, access_count: 0, last_accessed: nil, from_recall: false) ⇒ void
This method returns an undefined value.
Add a node to working memory
36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/htm/working_memory.rb', line 36 def add(key, value, token_count:, access_count: 0, last_accessed: nil, from_recall: false) @mutex.synchronize do @nodes[key] = { value: value, token_count: token_count, access_count: access_count, last_accessed: last_accessed || Time.now, added_at: Time.now, from_recall: from_recall } update_access_unlocked(key) end end |
#add_from_sync(id:, content:, token_count:, created_at:) ⇒ void
This method returns an undefined value.
Add a node from sync notification (bypasses normal add flow)
Called by RobotGroup when another robot adds to working memory. Does not trigger notifications to avoid infinite loops.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/htm/working_memory.rb', line 195 def add_from_sync(id:, content:, token_count:, created_at:) @mutex.synchronize do key = id.to_s return if @nodes.key?(key) # Already have this node @nodes[key] = { value: content, token_count: token_count, access_count: 0, last_accessed: Time.now, added_at: created_at, from_recall: false, from_sync: true } update_access_unlocked(key) end end |
#assemble_context(strategy:, max_tokens: nil) ⇒ String
Assemble context string for LLM
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/htm/working_memory.rb', line 122 def assemble_context(strategy:, max_tokens: nil) @mutex.synchronize do max = max_tokens || @max_tokens nodes = sorted_nodes_by_strategy(strategy) context_parts = [] current_tokens = 0 nodes.each do |node| break if current_tokens + node[:token_count] > max context_parts << node[:value] current_tokens += node[:token_count] end context_parts.join("\n\n") end end |
#clear ⇒ void
This method returns an undefined value.
Clear all nodes from working memory
173 174 175 176 177 178 |
# File 'lib/htm/working_memory.rb', line 173 def clear @mutex.synchronize do @nodes.clear @access_order.clear end end |
#clear_from_sync ⇒ void
This method returns an undefined value.
Clear all nodes from sync notification
Called by RobotGroup when another robot clears working memory.
234 235 236 237 238 239 |
# File 'lib/htm/working_memory.rb', line 234 def clear_from_sync @mutex.synchronize do @nodes.clear @access_order.clear end end |
#evict_to_make_space(needed_tokens) ⇒ Array<Hash>
Evict nodes to make space
Uses LFU + LRU strategy: Least Frequently Used + Least Recently Used Nodes with low access count and old timestamps are evicted first
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/htm/working_memory.rb', line 81 def evict_to_make_space(needed_tokens) @mutex.synchronize do evicted = [] tokens_freed = 0 # Sort by access frequency + recency (lower score = more evictable) candidates = @nodes.sort_by do |_key, node| access_frequency = node[:access_count] || 0 time_since_accessed = Time.now - (node[:last_accessed] || node[:added_at]) # Combined score: lower is more evictable # Frequently accessed = higher score (keep) # Recently accessed = higher score (keep) access_score = Math.log(1 + access_frequency) recency_score = 1.0 / (1 + (time_since_accessed / 3600.0)) -(access_score + recency_score) # Negative for ascending sort end candidates.each do |key, node| break if tokens_freed >= needed_tokens evicted << { key: key, value: node[:value] } tokens_freed += node[:token_count] @nodes.delete(key) @access_order.delete(key) end evicted end end |
#has_space?(token_count) ⇒ Boolean
Check if there’s space for a node
67 68 69 70 71 |
# File 'lib/htm/working_memory.rb', line 67 def has_space?(token_count) @mutex.synchronize do current_tokens_unlocked + token_count <= @max_tokens end end |
#node_count ⇒ Integer
Get node count
163 164 165 166 167 |
# File 'lib/htm/working_memory.rb', line 163 def node_count @mutex.synchronize do @nodes.size end end |
#remove(key) ⇒ void
This method returns an undefined value.
Remove a node from working memory
55 56 57 58 59 60 |
# File 'lib/htm/working_memory.rb', line 55 def remove(key) @mutex.synchronize do @nodes.delete(key) @access_order.delete(key) end end |
#remove_from_sync(node_id) ⇒ void
This method returns an undefined value.
Remove a node from sync notification
Called by RobotGroup when another robot evicts from working memory.
220 221 222 223 224 225 226 |
# File 'lib/htm/working_memory.rb', line 220 def remove_from_sync(node_id) @mutex.synchronize do key = node_id.to_s @nodes.delete(key) @access_order.delete(key) end end |
#token_count ⇒ Integer
Get current token count
143 144 145 146 147 |
# File 'lib/htm/working_memory.rb', line 143 def token_count @mutex.synchronize do current_tokens_unlocked end end |
#utilization_percentage ⇒ Float
Get utilization percentage
153 154 155 156 157 |
# File 'lib/htm/working_memory.rb', line 153 def utilization_percentage @mutex.synchronize do (current_tokens_unlocked.to_f / @max_tokens * 100).round(2) end end |