htm 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +269 -79
- data/db/migrate/00003_create_file_sources.rb +5 -0
- data/db/migrate/00004_create_nodes.rb +17 -0
- data/db/migrate/00005_create_tags.rb +7 -0
- data/db/migrate/00006_create_node_tags.rb +2 -0
- data/db/migrate/00007_create_robot_nodes.rb +7 -0
- data/db/schema.sql +41 -29
- data/docs/api/yard/HTM/Configuration.md +54 -0
- data/docs/api/yard/HTM/Database.md +13 -10
- data/docs/api/yard/HTM/EmbeddingService.md +5 -1
- data/docs/api/yard/HTM/LongTermMemory.md +18 -277
- data/docs/api/yard/HTM/PropositionError.md +18 -0
- data/docs/api/yard/HTM/PropositionService.md +66 -0
- data/docs/api/yard/HTM/QueryCache.md +88 -0
- data/docs/api/yard/HTM/RobotGroup.md +481 -0
- data/docs/api/yard/HTM/SqlBuilder.md +108 -0
- data/docs/api/yard/HTM/TagService.md +4 -0
- data/docs/api/yard/HTM/Telemetry/NullInstrument.md +13 -0
- data/docs/api/yard/HTM/Telemetry/NullMeter.md +15 -0
- data/docs/api/yard/HTM/Telemetry.md +109 -0
- data/docs/api/yard/HTM/WorkingMemoryChannel.md +176 -0
- data/docs/api/yard/HTM.md +11 -23
- data/docs/api/yard/index.csv +102 -25
- data/docs/api/yard-reference.md +8 -0
- data/docs/assets/images/multi-provider-failover.svg +51 -0
- data/docs/assets/images/robot-group-architecture.svg +65 -0
- data/docs/database/README.md +3 -3
- data/docs/database/public.file_sources.svg +29 -21
- data/docs/database/public.node_tags.md +2 -0
- data/docs/database/public.node_tags.svg +53 -41
- data/docs/database/public.nodes.md +2 -0
- data/docs/database/public.nodes.svg +52 -40
- data/docs/database/public.robot_nodes.md +2 -0
- data/docs/database/public.robot_nodes.svg +30 -22
- data/docs/database/public.robots.svg +16 -12
- data/docs/database/public.tags.md +3 -0
- data/docs/database/public.tags.svg +41 -33
- data/docs/database/schema.json +66 -0
- data/docs/database/schema.svg +60 -48
- data/docs/development/index.md +13 -0
- data/docs/development/rake-tasks.md +1068 -0
- data/docs/getting-started/quick-start.md +144 -155
- data/docs/guides/adding-memories.md +2 -3
- data/docs/guides/context-assembly.md +185 -184
- data/docs/guides/getting-started.md +154 -148
- data/docs/guides/index.md +7 -0
- data/docs/guides/long-term-memory.md +60 -92
- data/docs/guides/mcp-server.md +617 -0
- data/docs/guides/multi-robot.md +249 -345
- data/docs/guides/recalling-memories.md +153 -163
- data/docs/guides/robot-groups.md +604 -0
- data/docs/guides/search-strategies.md +61 -58
- data/docs/guides/working-memory.md +103 -136
- data/docs/index.md +30 -26
- data/examples/robot_groups/robot_worker.rb +1 -2
- data/examples/robot_groups/same_process.rb +1 -4
- data/lib/htm/robot_group.rb +721 -0
- data/lib/htm/version.rb +1 -1
- data/lib/htm/working_memory_channel.rb +250 -0
- data/lib/htm.rb +2 -0
- data/mkdocs.yml +2 -0
- metadata +18 -9
- data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +0 -12
- data/db/migrate/00010_add_soft_delete_to_associations.rb +0 -29
- data/db/migrate/00011_add_performance_indexes.rb +0 -21
- data/db/migrate/00012_add_tags_trigram_index.rb +0 -18
- data/db/migrate/00013_enable_lz4_compression.rb +0 -43
- data/examples/robot_groups/lib/robot_group.rb +0 -419
- data/examples/robot_groups/lib/working_memory_channel.rb +0 -140
data/lib/htm/version.rb
CHANGED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# examples/robot_groups/lib/htm/working_memory_channel.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class HTM
|
|
5
|
+
# Provides real-time synchronization of working memory changes across multiple
|
|
6
|
+
# robots using PostgreSQL LISTEN/NOTIFY pub/sub mechanism.
|
|
7
|
+
#
|
|
8
|
+
# This class enables distributed robots to maintain synchronized working memory
|
|
9
|
+
# by broadcasting change notifications through PostgreSQL channels. When one robot
|
|
10
|
+
# adds, evicts, or clears working memory, all other robots in the group receive
|
|
11
|
+
# immediate notification.
|
|
12
|
+
#
|
|
13
|
+
# @example Basic usage
|
|
14
|
+
# channel = HTM::WorkingMemoryChannel.new('support-team', db_config)
|
|
15
|
+
#
|
|
16
|
+
# # Subscribe to changes
|
|
17
|
+
# channel.on_change do |event, node_id, robot_id|
|
|
18
|
+
# case event
|
|
19
|
+
# when :added then puts "Node #{node_id} added by robot #{robot_id}"
|
|
20
|
+
# when :evicted then puts "Node #{node_id} evicted by robot #{robot_id}"
|
|
21
|
+
# when :cleared then puts "Working memory cleared by robot #{robot_id}"
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# # Start listening in background thread
|
|
26
|
+
# channel.start_listening
|
|
27
|
+
#
|
|
28
|
+
# # Publish a change
|
|
29
|
+
# channel.notify(:added, node_id: 123, robot_id: 456)
|
|
30
|
+
#
|
|
31
|
+
# # Cleanup when done
|
|
32
|
+
# channel.stop_listening
|
|
33
|
+
#
|
|
34
|
+
# @see HTM::RobotGroup Higher-level coordination using this channel
|
|
35
|
+
#
|
|
36
|
+
class WorkingMemoryChannel
|
|
37
|
+
# Prefix used for all PostgreSQL channel names
|
|
38
|
+
# @return [String]
|
|
39
|
+
CHANNEL_PREFIX = 'htm_wm'
|
|
40
|
+
|
|
41
|
+
# Number of notifications received since channel was created
|
|
42
|
+
# @return [Integer]
|
|
43
|
+
attr_reader :notifications_received
|
|
44
|
+
|
|
45
|
+
# Creates a new working memory channel for a robot group.
|
|
46
|
+
#
|
|
47
|
+
# The channel name is derived from the group name with non-alphanumeric
|
|
48
|
+
# characters replaced by underscores to ensure PostgreSQL compatibility.
|
|
49
|
+
#
|
|
50
|
+
# @param group_name [String] Name of the robot group (used to create unique channel)
|
|
51
|
+
# @param db_config [Hash] PostgreSQL connection configuration hash
|
|
52
|
+
# @option db_config [String] :host Database host
|
|
53
|
+
# @option db_config [Integer] :port Database port
|
|
54
|
+
# @option db_config [String] :dbname Database name
|
|
55
|
+
# @option db_config [String] :user Database user
|
|
56
|
+
# @option db_config [String] :password Database password (optional)
|
|
57
|
+
#
|
|
58
|
+
# @example
|
|
59
|
+
# db_config = { host: 'localhost', port: 5432, dbname: 'htm_dev', user: 'postgres' }
|
|
60
|
+
# channel = HTM::WorkingMemoryChannel.new('customer-support', db_config)
|
|
61
|
+
#
|
|
62
|
+
def initialize(group_name, db_config)
|
|
63
|
+
@group_name = group_name
|
|
64
|
+
@channel = "#{CHANNEL_PREFIX}_#{group_name.gsub(/[^a-z0-9_]/i, '_')}"
|
|
65
|
+
@db_config = db_config
|
|
66
|
+
@listeners = []
|
|
67
|
+
@listen_thread = nil
|
|
68
|
+
@stop_requested = false
|
|
69
|
+
@notifications_received = 0
|
|
70
|
+
@mutex = Mutex.new
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @!group Publishing
|
|
74
|
+
|
|
75
|
+
# Broadcasts a working memory change notification to all listeners.
|
|
76
|
+
#
|
|
77
|
+
# Uses PostgreSQL's pg_notify function to send a JSON payload containing
|
|
78
|
+
# the event type, affected node ID, originating robot ID, and timestamp.
|
|
79
|
+
#
|
|
80
|
+
# @param event [Symbol] Type of change (:added, :evicted, or :cleared)
|
|
81
|
+
# @param node_id [Integer, nil] ID of the affected node (nil for :cleared events)
|
|
82
|
+
# @param robot_id [Integer] ID of the robot that triggered the change
|
|
83
|
+
# @return [void]
|
|
84
|
+
#
|
|
85
|
+
# @example Notify that a node was added
|
|
86
|
+
# channel.notify(:added, node_id: 123, robot_id: 1)
|
|
87
|
+
#
|
|
88
|
+
# @example Notify that working memory was cleared
|
|
89
|
+
# channel.notify(:cleared, node_id: nil, robot_id: 1)
|
|
90
|
+
#
|
|
91
|
+
def notify(event, node_id:, robot_id:)
|
|
92
|
+
payload = {
|
|
93
|
+
event: event,
|
|
94
|
+
node_id: node_id,
|
|
95
|
+
robot_id: robot_id,
|
|
96
|
+
timestamp: Time.now.iso8601
|
|
97
|
+
}.to_json
|
|
98
|
+
|
|
99
|
+
with_connection do |conn|
|
|
100
|
+
conn.exec_params('SELECT pg_notify($1, $2)', [@channel, payload])
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @!endgroup
|
|
105
|
+
|
|
106
|
+
# @!group Subscribing
|
|
107
|
+
|
|
108
|
+
# Registers a callback to be invoked when working memory changes occur.
|
|
109
|
+
#
|
|
110
|
+
# Multiple callbacks can be registered; all will be called for each event.
|
|
111
|
+
# Callbacks are invoked synchronously within the listener thread.
|
|
112
|
+
#
|
|
113
|
+
# @yield [event, node_id, robot_id] Block called for each notification
|
|
114
|
+
# @yieldparam event [Symbol] Type of change (:added, :evicted, or :cleared)
|
|
115
|
+
# @yieldparam node_id [Integer, nil] ID of the affected node
|
|
116
|
+
# @yieldparam robot_id [Integer] ID of the robot that triggered the change
|
|
117
|
+
# @return [void]
|
|
118
|
+
#
|
|
119
|
+
# @example Register a change handler
|
|
120
|
+
# channel.on_change do |event, node_id, robot_id|
|
|
121
|
+
# puts "Received #{event} event for node #{node_id}"
|
|
122
|
+
# end
|
|
123
|
+
#
|
|
124
|
+
def on_change(&callback)
|
|
125
|
+
@mutex.synchronize { @listeners << callback }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Starts listening for notifications in a background thread.
|
|
129
|
+
#
|
|
130
|
+
# Creates a dedicated PostgreSQL connection that uses LISTEN to receive
|
|
131
|
+
# notifications. The thread polls every 0.5 seconds, allowing for clean
|
|
132
|
+
# shutdown via {#stop_listening}.
|
|
133
|
+
#
|
|
134
|
+
# @return [Thread] The background listener thread
|
|
135
|
+
#
|
|
136
|
+
# @example Start and verify listening
|
|
137
|
+
# thread = channel.start_listening
|
|
138
|
+
# puts "Listening: #{channel.listening?}" # => true
|
|
139
|
+
#
|
|
140
|
+
def start_listening
|
|
141
|
+
@stop_requested = false
|
|
142
|
+
@listen_thread = Thread.new do
|
|
143
|
+
listen_loop
|
|
144
|
+
end
|
|
145
|
+
@listen_thread.abort_on_exception = true
|
|
146
|
+
@listen_thread
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Stops the background listener thread.
|
|
150
|
+
#
|
|
151
|
+
# Signals the listener to stop, waits up to 0.5 seconds for clean exit,
|
|
152
|
+
# then forcefully terminates if still running. The PostgreSQL connection
|
|
153
|
+
# is closed automatically.
|
|
154
|
+
#
|
|
155
|
+
# @return [void]
|
|
156
|
+
#
|
|
157
|
+
# @example Clean shutdown
|
|
158
|
+
# channel.stop_listening
|
|
159
|
+
# puts "Listening: #{channel.listening?}" # => false
|
|
160
|
+
#
|
|
161
|
+
def stop_listening
|
|
162
|
+
@stop_requested = true
|
|
163
|
+
# Give the thread a moment to exit cleanly
|
|
164
|
+
@listen_thread&.join(0.5)
|
|
165
|
+
@listen_thread&.kill if @listen_thread&.alive?
|
|
166
|
+
@listen_thread = nil
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @!endgroup
|
|
170
|
+
|
|
171
|
+
# @!group Status
|
|
172
|
+
|
|
173
|
+
# Checks if the listener thread is currently active.
|
|
174
|
+
#
|
|
175
|
+
# @return [Boolean] true if listening for notifications, false otherwise
|
|
176
|
+
#
|
|
177
|
+
# @example
|
|
178
|
+
# channel.start_listening
|
|
179
|
+
# channel.listening? # => true
|
|
180
|
+
# channel.stop_listening
|
|
181
|
+
# channel.listening? # => false
|
|
182
|
+
#
|
|
183
|
+
def listening?
|
|
184
|
+
@listen_thread&.alive? || false
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Returns the PostgreSQL channel name used for notifications.
|
|
188
|
+
#
|
|
189
|
+
# The channel name is derived from the group name with a prefix and
|
|
190
|
+
# sanitization of special characters.
|
|
191
|
+
#
|
|
192
|
+
# @return [String] The PostgreSQL LISTEN/NOTIFY channel name
|
|
193
|
+
#
|
|
194
|
+
# @example
|
|
195
|
+
# channel = HTM::WorkingMemoryChannel.new('my-group', db_config)
|
|
196
|
+
# channel.channel_name # => "htm_wm_my_group"
|
|
197
|
+
#
|
|
198
|
+
def channel_name
|
|
199
|
+
@channel
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# @!endgroup
|
|
203
|
+
|
|
204
|
+
private
|
|
205
|
+
|
|
206
|
+
def listen_loop
|
|
207
|
+
conn = PG.connect(@db_config)
|
|
208
|
+
conn.exec("LISTEN #{conn.escape_identifier(@channel)}")
|
|
209
|
+
|
|
210
|
+
until @stop_requested
|
|
211
|
+
# Wait for notification with timeout (allows checking @stop_requested)
|
|
212
|
+
conn.wait_for_notify(0.5) do |_channel, _pid, payload|
|
|
213
|
+
handle_notification(payload)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
rescue PG::Error => e
|
|
217
|
+
unless @stop_requested
|
|
218
|
+
HTM.logger.error "WorkingMemoryChannel error: #{e.message}"
|
|
219
|
+
sleep 1
|
|
220
|
+
retry
|
|
221
|
+
end
|
|
222
|
+
ensure
|
|
223
|
+
conn&.close
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def handle_notification(payload)
|
|
227
|
+
data = JSON.parse(payload, symbolize_names: true)
|
|
228
|
+
|
|
229
|
+
@mutex.synchronize do
|
|
230
|
+
@notifications_received += 1
|
|
231
|
+
@listeners.each do |callback|
|
|
232
|
+
callback.call(
|
|
233
|
+
data[:event].to_sym,
|
|
234
|
+
data[:node_id],
|
|
235
|
+
data[:robot_id]
|
|
236
|
+
)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
rescue JSON::ParserError => e
|
|
240
|
+
HTM.logger.error "Invalid notification payload: #{e.message}"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def with_connection
|
|
244
|
+
conn = PG.connect(@db_config)
|
|
245
|
+
yield conn
|
|
246
|
+
ensure
|
|
247
|
+
conn&.close
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
data/lib/htm.rb
CHANGED
|
@@ -21,6 +21,8 @@ require_relative "htm/loaders/markdown_chunker"
|
|
|
21
21
|
require_relative "htm/loaders/markdown_loader"
|
|
22
22
|
require_relative "htm/observability"
|
|
23
23
|
require_relative "htm/telemetry"
|
|
24
|
+
require_relative "htm/working_memory_channel"
|
|
25
|
+
require_relative "htm/robot_group"
|
|
24
26
|
|
|
25
27
|
require "pg"
|
|
26
28
|
require "securerandom"
|
data/mkdocs.yml
CHANGED
|
@@ -199,6 +199,7 @@ nav:
|
|
|
199
199
|
- Multi-Robot Usage: guides/multi-robot.md
|
|
200
200
|
- Search Strategies: guides/search-strategies.md
|
|
201
201
|
- Context Assembly: guides/context-assembly.md
|
|
202
|
+
- MCP Server: guides/mcp-server.md
|
|
202
203
|
- API Reference:
|
|
203
204
|
- api/index.md
|
|
204
205
|
- HTM Class: api/htm.md
|
|
@@ -245,6 +246,7 @@ nav:
|
|
|
245
246
|
- Setup: development/setup.md
|
|
246
247
|
- Testing: development/testing.md
|
|
247
248
|
- Contributing: development/contributing.md
|
|
249
|
+
- Rake Tasks Reference: development/rake-tasks.md
|
|
248
250
|
- Database Schema: development/schema.md
|
|
249
251
|
- Database Tables:
|
|
250
252
|
- database/README.md
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: htm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -299,11 +299,6 @@ files:
|
|
|
299
299
|
- db/migrate/00005_create_tags.rb
|
|
300
300
|
- db/migrate/00006_create_node_tags.rb
|
|
301
301
|
- db/migrate/00007_create_robot_nodes.rb
|
|
302
|
-
- db/migrate/00009_add_working_memory_to_robot_nodes.rb
|
|
303
|
-
- db/migrate/00010_add_soft_delete_to_associations.rb
|
|
304
|
-
- db/migrate/00011_add_performance_indexes.rb
|
|
305
|
-
- db/migrate/00012_add_tags_trigram_index.rb
|
|
306
|
-
- db/migrate/00013_enable_lz4_compression.rb
|
|
307
302
|
- db/schema.sql
|
|
308
303
|
- db/seed_data/README.md
|
|
309
304
|
- db/seed_data/presidents.md
|
|
@@ -331,17 +326,26 @@ files:
|
|
|
331
326
|
- docs/api/yard/HTM/LongTermMemory.md
|
|
332
327
|
- docs/api/yard/HTM/NotFoundError.md
|
|
333
328
|
- docs/api/yard/HTM/Observability.md
|
|
329
|
+
- docs/api/yard/HTM/PropositionError.md
|
|
330
|
+
- docs/api/yard/HTM/PropositionService.md
|
|
331
|
+
- docs/api/yard/HTM/QueryCache.md
|
|
334
332
|
- docs/api/yard/HTM/QueryTimeoutError.md
|
|
335
333
|
- docs/api/yard/HTM/Railtie.md
|
|
336
334
|
- docs/api/yard/HTM/ResourceExhaustedError.md
|
|
335
|
+
- docs/api/yard/HTM/RobotGroup.md
|
|
336
|
+
- docs/api/yard/HTM/SqlBuilder.md
|
|
337
337
|
- docs/api/yard/HTM/TagError.md
|
|
338
338
|
- docs/api/yard/HTM/TagService.md
|
|
339
|
+
- docs/api/yard/HTM/Telemetry.md
|
|
340
|
+
- docs/api/yard/HTM/Telemetry/NullInstrument.md
|
|
341
|
+
- docs/api/yard/HTM/Telemetry/NullMeter.md
|
|
339
342
|
- docs/api/yard/HTM/Timeframe.md
|
|
340
343
|
- docs/api/yard/HTM/Timeframe/Result.md
|
|
341
344
|
- docs/api/yard/HTM/TimeframeExtractor.md
|
|
342
345
|
- docs/api/yard/HTM/TimeframeExtractor/Result.md
|
|
343
346
|
- docs/api/yard/HTM/ValidationError.md
|
|
344
347
|
- docs/api/yard/HTM/WorkingMemory.md
|
|
348
|
+
- docs/api/yard/HTM/WorkingMemoryChannel.md
|
|
345
349
|
- docs/api/yard/index.csv
|
|
346
350
|
- docs/architecture/adrs/001-postgresql-timescaledb.md
|
|
347
351
|
- docs/architecture/adrs/002-two-tier-memory.md
|
|
@@ -381,7 +385,9 @@ files:
|
|
|
381
385
|
- docs/assets/images/htm-working-memory-architecture.svg
|
|
382
386
|
- docs/assets/images/htm.jpg
|
|
383
387
|
- docs/assets/images/htm_demo.gif
|
|
388
|
+
- docs/assets/images/multi-provider-failover.svg
|
|
384
389
|
- docs/assets/images/project-structure.svg
|
|
390
|
+
- docs/assets/images/robot-group-architecture.svg
|
|
385
391
|
- docs/assets/images/test-directory-structure.svg
|
|
386
392
|
- docs/assets/js/mathjax.js
|
|
387
393
|
- docs/assets/videos/htm_video.mp4
|
|
@@ -419,6 +425,7 @@ files:
|
|
|
419
425
|
- docs/database_rake_tasks.md
|
|
420
426
|
- docs/development/contributing.md
|
|
421
427
|
- docs/development/index.md
|
|
428
|
+
- docs/development/rake-tasks.md
|
|
422
429
|
- docs/development/schema.md
|
|
423
430
|
- docs/development/setup.md
|
|
424
431
|
- docs/development/testing.md
|
|
@@ -430,8 +437,10 @@ files:
|
|
|
430
437
|
- docs/guides/getting-started.md
|
|
431
438
|
- docs/guides/index.md
|
|
432
439
|
- docs/guides/long-term-memory.md
|
|
440
|
+
- docs/guides/mcp-server.md
|
|
433
441
|
- docs/guides/multi-robot.md
|
|
434
442
|
- docs/guides/recalling-memories.md
|
|
443
|
+
- docs/guides/robot-groups.md
|
|
435
444
|
- docs/guides/search-strategies.md
|
|
436
445
|
- docs/guides/working-memory.md
|
|
437
446
|
- docs/images/htm-er-diagram.svg
|
|
@@ -450,8 +459,6 @@ files:
|
|
|
450
459
|
- examples/example_app/app.rb
|
|
451
460
|
- examples/file_loader_usage.rb
|
|
452
461
|
- examples/mcp_client.rb
|
|
453
|
-
- examples/robot_groups/lib/robot_group.rb
|
|
454
|
-
- examples/robot_groups/lib/working_memory_channel.rb
|
|
455
462
|
- examples/robot_groups/multi_process.rb
|
|
456
463
|
- examples/robot_groups/robot_worker.rb
|
|
457
464
|
- examples/robot_groups/same_process.rb
|
|
@@ -495,6 +502,7 @@ files:
|
|
|
495
502
|
- lib/htm/proposition_service.rb
|
|
496
503
|
- lib/htm/query_cache.rb
|
|
497
504
|
- lib/htm/railtie.rb
|
|
505
|
+
- lib/htm/robot_group.rb
|
|
498
506
|
- lib/htm/sql_builder.rb
|
|
499
507
|
- lib/htm/tag_service.rb
|
|
500
508
|
- lib/htm/tasks.rb
|
|
@@ -503,6 +511,7 @@ files:
|
|
|
503
511
|
- lib/htm/timeframe_extractor.rb
|
|
504
512
|
- lib/htm/version.rb
|
|
505
513
|
- lib/htm/working_memory.rb
|
|
514
|
+
- lib/htm/working_memory_channel.rb
|
|
506
515
|
- lib/tasks/db.rake
|
|
507
516
|
- lib/tasks/doc.rake
|
|
508
517
|
- lib/tasks/files.rake
|
|
@@ -532,7 +541,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
532
541
|
- !ruby/object:Gem::Version
|
|
533
542
|
version: '0'
|
|
534
543
|
requirements: []
|
|
535
|
-
rubygems_version: 4.0.0
|
|
544
|
+
rubygems_version: 4.0.0
|
|
536
545
|
specification_version: 4
|
|
537
546
|
summary: Hierarchical Temporal Memory for LLM robots
|
|
538
547
|
test_files: []
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class AddWorkingMemoryToRobotNodes < ActiveRecord::Migration[7.1]
|
|
4
|
-
def change
|
|
5
|
-
add_column :robot_nodes, :working_memory, :boolean, default: false, null: false,
|
|
6
|
-
comment: 'True if this node is currently in the robot working memory'
|
|
7
|
-
|
|
8
|
-
add_index :robot_nodes, [:robot_id, :working_memory],
|
|
9
|
-
where: 'working_memory = true',
|
|
10
|
-
name: 'idx_robot_nodes_working_memory'
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class AddSoftDeleteToAssociations < ActiveRecord::Migration[7.0]
|
|
4
|
-
def change
|
|
5
|
-
# Add deleted_at to robot_nodes for soft delete support
|
|
6
|
-
unless column_exists?(:robot_nodes, :deleted_at)
|
|
7
|
-
add_column :robot_nodes, :deleted_at, :datetime, null: true
|
|
8
|
-
end
|
|
9
|
-
unless index_exists?(:robot_nodes, :deleted_at)
|
|
10
|
-
add_index :robot_nodes, :deleted_at
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Add deleted_at to node_tags for soft delete support
|
|
14
|
-
unless column_exists?(:node_tags, :deleted_at)
|
|
15
|
-
add_column :node_tags, :deleted_at, :datetime, null: true
|
|
16
|
-
end
|
|
17
|
-
unless index_exists?(:node_tags, :deleted_at)
|
|
18
|
-
add_index :node_tags, :deleted_at
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Add deleted_at to tags for soft delete support
|
|
22
|
-
unless column_exists?(:tags, :deleted_at)
|
|
23
|
-
add_column :tags, :deleted_at, :datetime, null: true
|
|
24
|
-
end
|
|
25
|
-
unless index_exists?(:tags, :deleted_at)
|
|
26
|
-
add_index :tags, :deleted_at
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class AddPerformanceIndexes < ActiveRecord::Migration[7.1]
|
|
4
|
-
def change
|
|
5
|
-
# Partial index for soft-delete filter (used in almost every query)
|
|
6
|
-
# This complements idx_nodes_not_deleted_created_at for queries that
|
|
7
|
-
# don't need created_at ordering but still filter by deleted_at IS NULL
|
|
8
|
-
add_index :nodes, :id,
|
|
9
|
-
name: 'idx_nodes_active',
|
|
10
|
-
where: 'deleted_at IS NULL',
|
|
11
|
-
comment: 'Partial index for active (non-deleted) node queries'
|
|
12
|
-
|
|
13
|
-
# Composite index for embedding-based searches on active nodes
|
|
14
|
-
# Helps vector_search and hybrid_search which filter by deleted_at IS NULL
|
|
15
|
-
# and embedding IS NOT NULL
|
|
16
|
-
execute <<-SQL
|
|
17
|
-
CREATE INDEX idx_nodes_active_with_embedding ON nodes (id)
|
|
18
|
-
WHERE deleted_at IS NULL AND embedding IS NOT NULL
|
|
19
|
-
SQL
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class AddTagsTrigramIndex < ActiveRecord::Migration[7.0]
|
|
4
|
-
def up
|
|
5
|
-
# Add GIN trigram index on tags.name for fuzzy search
|
|
6
|
-
# Enables queries like: WHERE name % 'postgrsql' (typo-tolerant)
|
|
7
|
-
# Also speeds up LIKE '%pattern%' queries
|
|
8
|
-
execute <<~SQL
|
|
9
|
-
CREATE INDEX idx_tags_name_trgm ON tags USING gin(name gin_trgm_ops);
|
|
10
|
-
SQL
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def down
|
|
14
|
-
execute <<~SQL
|
|
15
|
-
DROP INDEX IF EXISTS idx_tags_name_trgm;
|
|
16
|
-
SQL
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class EnableLz4Compression < ActiveRecord::Migration[7.0]
|
|
4
|
-
def up
|
|
5
|
-
# Switch TOAST compression from pglz to lz4 for better read performance
|
|
6
|
-
# LZ4 decompression is ~32% faster than pglz with only marginally lower compression ratio
|
|
7
|
-
# See: https://www.depesz.com/2025/11/29/using-json-json-vs-jsonb-pglz-vs-lz4-key-optimization-parsing-speed/
|
|
8
|
-
|
|
9
|
-
# nodes.metadata - JSONB column for flexible key-value storage
|
|
10
|
-
execute <<~SQL
|
|
11
|
-
ALTER TABLE nodes ALTER COLUMN metadata SET COMPRESSION lz4;
|
|
12
|
-
SQL
|
|
13
|
-
|
|
14
|
-
# nodes.content - TEXT column containing memory content
|
|
15
|
-
execute <<~SQL
|
|
16
|
-
ALTER TABLE nodes ALTER COLUMN content SET COMPRESSION lz4;
|
|
17
|
-
SQL
|
|
18
|
-
|
|
19
|
-
# file_sources.frontmatter - JSONB column for parsed YAML frontmatter
|
|
20
|
-
execute <<~SQL
|
|
21
|
-
ALTER TABLE file_sources ALTER COLUMN frontmatter SET COMPRESSION lz4;
|
|
22
|
-
SQL
|
|
23
|
-
|
|
24
|
-
# Note: Existing rows retain their original compression until rewritten.
|
|
25
|
-
# To recompress existing data, run: VACUUM FULL nodes; VACUUM FULL file_sources;
|
|
26
|
-
# This is optional and can be done during a maintenance window.
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def down
|
|
30
|
-
# Revert to default pglz compression
|
|
31
|
-
execute <<~SQL
|
|
32
|
-
ALTER TABLE nodes ALTER COLUMN metadata SET COMPRESSION pglz;
|
|
33
|
-
SQL
|
|
34
|
-
|
|
35
|
-
execute <<~SQL
|
|
36
|
-
ALTER TABLE nodes ALTER COLUMN content SET COMPRESSION pglz;
|
|
37
|
-
SQL
|
|
38
|
-
|
|
39
|
-
execute <<~SQL
|
|
40
|
-
ALTER TABLE file_sources ALTER COLUMN frontmatter SET COMPRESSION pglz;
|
|
41
|
-
SQL
|
|
42
|
-
end
|
|
43
|
-
end
|