htm 0.0.20 → 0.0.30

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.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/Rakefile +104 -18
  4. data/db/migrate/00001_enable_extensions.rb +9 -5
  5. data/db/migrate/00002_create_robots.rb +18 -6
  6. data/db/migrate/00003_create_file_sources.rb +30 -17
  7. data/db/migrate/00004_create_nodes.rb +60 -48
  8. data/db/migrate/00005_create_tags.rb +24 -12
  9. data/db/migrate/00006_create_node_tags.rb +28 -13
  10. data/db/migrate/00007_create_robot_nodes.rb +40 -26
  11. data/db/schema.sql +17 -1
  12. data/db/seeds.rb +33 -33
  13. data/docs/database/naming-convention.md +244 -0
  14. data/docs/database_rake_tasks.md +31 -0
  15. data/docs/development/rake-tasks.md +80 -35
  16. data/docs/guides/mcp-server.md +70 -1
  17. data/examples/.envrc +6 -0
  18. data/examples/.gitignore +2 -0
  19. data/examples/00_create_examples_db.rb +94 -0
  20. data/examples/{basic_usage.rb → 01_basic_usage.rb} +12 -16
  21. data/examples/{custom_llm_configuration.rb → 03_custom_llm_configuration.rb} +13 -3
  22. data/examples/{file_loader_usage.rb → 04_file_loader_usage.rb} +11 -14
  23. data/examples/{timeframe_demo.rb → 05_timeframe_demo.rb} +10 -3
  24. data/examples/{example_app → 06_example_app}/app.rb +15 -15
  25. data/examples/{cli_app → 07_cli_app}/htm_cli.rb +15 -22
  26. data/examples/08_sinatra_app/Gemfile.lock +241 -0
  27. data/examples/{sinatra_app → 08_sinatra_app}/app.rb +19 -18
  28. data/examples/{mcp_client.rb → 09_mcp_client.rb} +5 -8
  29. data/examples/{telemetry → 10_telemetry}/SETUP_README.md +1 -1
  30. data/examples/{telemetry → 10_telemetry}/demo.rb +14 -10
  31. data/examples/11_robot_groups/README.md +335 -0
  32. data/examples/{robot_groups → 11_robot_groups/lib}/robot_worker.rb +17 -3
  33. data/examples/{robot_groups → 11_robot_groups}/multi_process.rb +9 -9
  34. data/examples/{robot_groups → 11_robot_groups}/same_process.rb +9 -12
  35. data/examples/{rails_app → 12_rails_app}/Gemfile +3 -0
  36. data/examples/{rails_app → 12_rails_app}/Gemfile.lock +87 -58
  37. data/examples/{rails_app → 12_rails_app}/app/controllers/dashboard_controller.rb +10 -6
  38. data/examples/{rails_app → 12_rails_app}/app/controllers/files_controller.rb +5 -5
  39. data/examples/{rails_app → 12_rails_app}/app/controllers/memories_controller.rb +11 -7
  40. data/examples/{rails_app → 12_rails_app}/app/controllers/robots_controller.rb +8 -8
  41. data/examples/12_rails_app/app/controllers/tags_controller.rb +36 -0
  42. data/examples/{rails_app → 12_rails_app}/app/views/dashboard/index.html.erb +2 -2
  43. data/examples/{rails_app → 12_rails_app}/app/views/files/new.html.erb +5 -2
  44. data/examples/{rails_app → 12_rails_app}/app/views/memories/_memory_card.html.erb +3 -3
  45. data/examples/{rails_app → 12_rails_app}/app/views/memories/deleted.html.erb +3 -3
  46. data/examples/{rails_app → 12_rails_app}/app/views/memories/edit.html.erb +3 -3
  47. data/examples/{rails_app → 12_rails_app}/app/views/memories/show.html.erb +4 -4
  48. data/examples/{rails_app → 12_rails_app}/app/views/robots/index.html.erb +2 -2
  49. data/examples/{rails_app → 12_rails_app}/app/views/robots/show.html.erb +4 -4
  50. data/examples/{rails_app → 12_rails_app}/app/views/search/index.html.erb +1 -1
  51. data/examples/{rails_app → 12_rails_app}/app/views/tags/index.html.erb +2 -2
  52. data/examples/{rails_app → 12_rails_app}/app/views/tags/show.html.erb +1 -1
  53. data/examples/12_rails_app/config/initializers/htm.rb +7 -0
  54. data/examples/12_rails_app/config/initializers/rack.rb +5 -0
  55. data/examples/README.md +230 -211
  56. data/examples/examples_helper.rb +138 -0
  57. data/lib/htm/config/builder.rb +167 -0
  58. data/lib/htm/config/database.rb +317 -0
  59. data/lib/htm/config/defaults.yml +37 -9
  60. data/lib/htm/config/section.rb +74 -0
  61. data/lib/htm/config/validator.rb +83 -0
  62. data/lib/htm/config.rb +64 -360
  63. data/lib/htm/database.rb +85 -127
  64. data/lib/htm/errors.rb +14 -0
  65. data/lib/htm/integrations/sinatra.rb +13 -44
  66. data/lib/htm/jobs/generate_embedding_job.rb +3 -4
  67. data/lib/htm/jobs/generate_propositions_job.rb +4 -5
  68. data/lib/htm/jobs/generate_tags_job.rb +16 -15
  69. data/lib/htm/loaders/defaults_loader.rb +23 -0
  70. data/lib/htm/loaders/markdown_loader.rb +17 -15
  71. data/lib/htm/loaders/xdg_config_loader.rb +9 -9
  72. data/lib/htm/long_term_memory/fulltext_search.rb +14 -14
  73. data/lib/htm/long_term_memory/hybrid_search.rb +396 -229
  74. data/lib/htm/long_term_memory/node_operations.rb +24 -23
  75. data/lib/htm/long_term_memory/relevance_scorer.rb +23 -20
  76. data/lib/htm/long_term_memory/robot_operations.rb +4 -4
  77. data/lib/htm/long_term_memory/tag_operations.rb +91 -77
  78. data/lib/htm/long_term_memory/vector_search.rb +4 -5
  79. data/lib/htm/long_term_memory.rb +13 -13
  80. data/lib/htm/mcp/cli.rb +115 -8
  81. data/lib/htm/mcp/resources.rb +4 -3
  82. data/lib/htm/mcp/server.rb +5 -4
  83. data/lib/htm/mcp/tools.rb +37 -28
  84. data/lib/htm/migration.rb +72 -0
  85. data/lib/htm/models/file_source.rb +52 -31
  86. data/lib/htm/models/node.rb +224 -108
  87. data/lib/htm/models/node_tag.rb +49 -28
  88. data/lib/htm/models/robot.rb +38 -27
  89. data/lib/htm/models/robot_node.rb +63 -35
  90. data/lib/htm/models/tag.rb +126 -123
  91. data/lib/htm/observability.rb +45 -41
  92. data/lib/htm/proposition_service.rb +76 -7
  93. data/lib/htm/railtie.rb +2 -2
  94. data/lib/htm/robot_group.rb +30 -18
  95. data/lib/htm/sequel_config.rb +215 -0
  96. data/lib/htm/sql_builder.rb +14 -16
  97. data/lib/htm/tag_service.rb +78 -0
  98. data/lib/htm/tasks.rb +3 -0
  99. data/lib/htm/version.rb +1 -1
  100. data/lib/htm/workflows/remember_workflow.rb +6 -5
  101. data/lib/htm.rb +26 -22
  102. data/lib/tasks/db.rake +0 -2
  103. data/lib/tasks/doc.rake +2 -2
  104. data/lib/tasks/files.rake +11 -18
  105. data/lib/tasks/htm.rake +190 -62
  106. data/lib/tasks/jobs.rake +179 -54
  107. data/lib/tasks/tags.rake +8 -13
  108. data/scripts/backfill_parent_tags.rb +376 -0
  109. data/scripts/normalize_plural_tags.rb +335 -0
  110. metadata +109 -80
  111. data/examples/rails_app/app/controllers/tags_controller.rb +0 -30
  112. data/examples/sinatra_app/Gemfile.lock +0 -166
  113. data/lib/htm/active_record_config.rb +0 -104
  114. /data/examples/{config_file_example → 02_config_file_example}/README.md +0 -0
  115. /data/examples/{config_file_example → 02_config_file_example}/config/htm.local.yml +0 -0
  116. /data/examples/{config_file_example → 02_config_file_example}/custom_config.yml +0 -0
  117. /data/examples/{config_file_example → 02_config_file_example}/show_config.rb +0 -0
  118. /data/examples/{example_app → 06_example_app}/Rakefile +0 -0
  119. /data/examples/{cli_app → 07_cli_app}/README.md +0 -0
  120. /data/examples/{sinatra_app → 08_sinatra_app}/Gemfile +0 -0
  121. /data/examples/{telemetry → 10_telemetry}/README.md +0 -0
  122. /data/examples/{telemetry → 10_telemetry}/grafana/dashboards/htm-metrics.json +0 -0
  123. /data/examples/{rails_app → 12_rails_app}/.gitignore +0 -0
  124. /data/examples/{rails_app → 12_rails_app}/Procfile.dev +0 -0
  125. /data/examples/{rails_app → 12_rails_app}/README.md +0 -0
  126. /data/examples/{rails_app → 12_rails_app}/Rakefile +0 -0
  127. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/application.css +0 -0
  128. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/inter-font.css +0 -0
  129. /data/examples/{rails_app → 12_rails_app}/app/controllers/application_controller.rb +0 -0
  130. /data/examples/{rails_app → 12_rails_app}/app/controllers/search_controller.rb +0 -0
  131. /data/examples/{rails_app → 12_rails_app}/app/javascript/application.js +0 -0
  132. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/application.js +0 -0
  133. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/index.js +0 -0
  134. /data/examples/{rails_app → 12_rails_app}/app/views/files/index.html.erb +0 -0
  135. /data/examples/{rails_app → 12_rails_app}/app/views/files/show.html.erb +0 -0
  136. /data/examples/{rails_app → 12_rails_app}/app/views/layouts/application.html.erb +0 -0
  137. /data/examples/{rails_app → 12_rails_app}/app/views/memories/index.html.erb +0 -0
  138. /data/examples/{rails_app → 12_rails_app}/app/views/memories/new.html.erb +0 -0
  139. /data/examples/{rails_app → 12_rails_app}/app/views/robots/new.html.erb +0 -0
  140. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_navbar.html.erb +0 -0
  141. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_stat_card.html.erb +0 -0
  142. /data/examples/{rails_app → 12_rails_app}/bin/dev +0 -0
  143. /data/examples/{rails_app → 12_rails_app}/bin/rails +0 -0
  144. /data/examples/{rails_app → 12_rails_app}/bin/rake +0 -0
  145. /data/examples/{rails_app → 12_rails_app}/config/application.rb +0 -0
  146. /data/examples/{rails_app → 12_rails_app}/config/boot.rb +0 -0
  147. /data/examples/{rails_app → 12_rails_app}/config/database.yml +0 -0
  148. /data/examples/{rails_app → 12_rails_app}/config/environment.rb +0 -0
  149. /data/examples/{rails_app → 12_rails_app}/config/importmap.rb +0 -0
  150. /data/examples/{rails_app → 12_rails_app}/config/routes.rb +0 -0
  151. /data/examples/{rails_app → 12_rails_app}/config/tailwind.config.js +0 -0
  152. /data/examples/{rails_app → 12_rails_app}/config.ru +0 -0
  153. /data/examples/{rails_app → 12_rails_app}/log/.keep +0 -0
  154. /data/examples/{rails_app → 12_rails_app}/tmp/local_secret.txt +0 -0
@@ -0,0 +1,335 @@
1
+ # Robot Groups - Multi-Robot Coordination with Shared Working Memory
2
+
3
+ This example demonstrates high-availability patterns for coordinating multiple HTM robots with shared working memory, automatic failover, and real-time synchronization.
4
+
5
+ There are two scenarios demonstrated. 1) multiple robots in their processes. These processes could be on the same computer are not. The only requirement is that all robot processes have access to the same HTM database.
6
+
7
+ The second scenario is all robots are running within the same process.
8
+
9
+ ## Overview
10
+
11
+ Robot Groups enable multiple AI agents (robots) to share context and collaborate on tasks. Key capabilities:
12
+
13
+ - **Shared Working Memory**: Multiple robots access the same context simultaneously
14
+ - **Active/Passive Roles**: Active robots handle requests; passive robots maintain warm standby
15
+ - **Instant Failover**: When an active robot fails, a passive robot takes over with full context
16
+ - **Real-time Sync**: PostgreSQL LISTEN/NOTIFY propagates changes across all robots instantly
17
+ - **Dynamic Scaling**: Add or remove robots without service interruption
18
+
19
+ ## Architecture
20
+
21
+ ```
22
+ ┌─────────────────────────────────────────────────────────────┐
23
+ │ Robot Group │
24
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
25
+ │ │ Robot A │ │ Robot B │ │ Robot C │ │
26
+ │ │ (active) │ │ (active) │ │ (passive) │ │
27
+ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
28
+ │ │ │ │ │
29
+ │ └────────────────┼────────────────┘ │
30
+ │ │ │
31
+ │ ┌───────────▼───────────┐ │
32
+ │ │ Shared Working Memory │ │
33
+ │ │ (PostgreSQL + NOTIFY)│ │
34
+ │ └───────────────────────┘ │
35
+ └─────────────────────────────────────────────────────────────┘
36
+ ```
37
+
38
+ ## Prerequisites
39
+
40
+ 1. **Database Setup**
41
+ ```bash
42
+ rake examples:setup
43
+ ```
44
+
45
+ 2. **Ollama Models** (for embeddings and tag extraction)
46
+ ```bash
47
+ ollama pull nomic-embed-text
48
+ ollama pull gemma3
49
+ ```
50
+
51
+ 3. **Ruby Dependencies**
52
+ ```bash
53
+ bundle install
54
+ ```
55
+
56
+ ## Available Scripts
57
+
58
+ ### same_process.rb
59
+
60
+ Demonstrates robot groups within a **single Ruby process**. Ideal for understanding the concepts without process management complexity.
61
+
62
+ ```bash
63
+ ruby examples/11_robot_groups/same_process.rb
64
+ ```
65
+
66
+ **Scenarios demonstrated:**
67
+
68
+ 1. **Create a Robot Group** - Initialize with primary (active) and standby (passive) robots
69
+ 2. **Add Shared Memories** - Primary robot stores information accessible to all
70
+ 3. **Verify Synchronization** - Confirm all robots see the same context
71
+ 4. **Simulate Failover** - Primary "fails", standby promotes to active
72
+ 5. **Verify Context Preservation** - Standby has full context after failover
73
+ 6. **Dynamic Scaling** - Add a second active robot on-the-fly
74
+ 7. **Collaborative Memory** - Multiple robots contribute to shared context
75
+ 8. **Real-time Sync** - PostgreSQL LISTEN/NOTIFY propagates changes instantly
76
+
77
+ **Example output:**
78
+ ```
79
+ 1. Configuring HTM...
80
+ ✓ HTM configured
81
+
82
+ 2. Creating robot group with primary + standby...
83
+ ✓ Group created: customer-support-ha
84
+ Active: support-primary
85
+ Passive: support-standby
86
+
87
+ 3. Adding memories to shared working memory...
88
+ ✓ Remembered customer preference
89
+ ✓ Remembered open ticket
90
+ ✓ Remembered customer status
91
+
92
+ 4. Verifying working memory synchronization...
93
+ Working memory nodes: 3
94
+ Token utilization: 12.5%
95
+ In sync: ✓ Yes
96
+
97
+ 5. Simulating failover scenario...
98
+ ⚠ Primary robot 'support-primary' has stopped responding!
99
+ Active robots now: support-standby
100
+ Passive robots now: (none)
101
+
102
+ 6. Verifying standby has full context after failover...
103
+ ✓ Standby recalled 3 memories about 'customer'
104
+ ```
105
+
106
+ ### multi_process.rb
107
+
108
+ Demonstrates robot groups across **separate Ruby processes**. This is closer to production deployments where each robot runs independently.
109
+
110
+ ```bash
111
+ ruby examples/11_robot_groups/multi_process.rb
112
+ ```
113
+
114
+ **Scenarios demonstrated:**
115
+
116
+ 1. **Start Robot Processes** - Spawn 3 independent worker processes
117
+ 2. **Cross-Process Memory Sharing** - One robot adds memories, others receive via NOTIFY
118
+ 3. **Collaborative Memory** - Multiple processes contribute to shared context
119
+ 4. **Simulated Failover** - Kill a process, verify others retain context
120
+ 5. **Dynamic Scaling** - Add a new robot process to the group
121
+
122
+ **Example output:**
123
+ ```
124
+ ╔══════════════════════════════════════════════════════════════╗
125
+ ║ HTM Multi-Process Robot Group Demo ║
126
+ ║ Real-time Sync via PostgreSQL LISTEN/NOTIFY ║
127
+ ╚══════════════════════════════════════════════════════════════╝
128
+
129
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
130
+ SCENARIO 1: Starting Robot Processes
131
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
132
+
133
+ ✓ robot-alpha (PID 12345)
134
+ ✓ robot-beta (PID 12346)
135
+ ✓ robot-gamma (PID 12347)
136
+
137
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
138
+ SCENARIO 4: Simulated Failover
139
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
140
+
141
+ Killing robot-alpha...
142
+ ⚠ robot-alpha terminated
143
+
144
+ Remaining robots retain context:
145
+ robot-beta: 3 nodes, recalls 2
146
+ robot-gamma: 3 nodes, recalls 2
147
+
148
+ ✓ Failover successful
149
+ ```
150
+
151
+ ### robot_worker.rb
152
+
153
+ A standalone worker process used by `multi_process.rb`. You typically don't run this directly.
154
+
155
+ **Communication protocol** (JSON over stdin/stdout):
156
+
157
+ | Command | Request | Response |
158
+ |---------|---------|----------|
159
+ | ping | `{"cmd": "ping"}` | `{"status": "ok", "message": "pong"}` |
160
+ | remember | `{"cmd": "remember", "content": "..."}` | `{"status": "ok", "node_id": 123}` |
161
+ | recall | `{"cmd": "recall", "query": "...", "limit": 5}` | `{"status": "ok", "count": 3}` |
162
+ | status | `{"cmd": "status"}` | `{"status": "ok", "working_memory_nodes": 5, ...}` |
163
+ | shutdown | `{"cmd": "shutdown"}` | `{"status": "ok", "message": "bye"}` |
164
+
165
+ ## Key Concepts
166
+
167
+ ### Active vs Passive Robots
168
+
169
+ | Role | Behavior |
170
+ |------|----------|
171
+ | **Active** | Handles requests, adds memories, participates in conversations |
172
+ | **Passive** | Mirrors active robot's context silently, ready for instant takeover |
173
+
174
+ ### Failover Process
175
+
176
+ 1. Active robot becomes unresponsive
177
+ 2. `group.failover!` promotes first passive robot to active
178
+ 3. Promoted robot has full context (warm standby)
179
+ 4. Service continues without context loss
180
+
181
+ ### Real-time Synchronization
182
+
183
+ PostgreSQL LISTEN/NOTIFY enables sub-second sync:
184
+
185
+ ```
186
+ Robot A adds memory → PostgreSQL NOTIFY → Robot B receives → Updates local cache
187
+ ```
188
+
189
+ Events propagated:
190
+ - `:added` - New node added to working memory
191
+ - `:evicted` - Node removed due to token limits
192
+ - `:cleared` - Working memory cleared entirely
193
+
194
+ ### Token Budget Management
195
+
196
+ Each group has a `max_tokens` limit. When exceeded:
197
+ 1. Oldest/least-accessed nodes are evicted
198
+ 2. Eviction notifications sent to all robots
199
+ 3. All robots stay within budget
200
+
201
+ ## Use Cases
202
+
203
+ ### Customer Support
204
+
205
+ Multiple support agents share customer context:
206
+ ```ruby
207
+ group = HTM::RobotGroup.new(
208
+ name: 'support-team',
209
+ active: ['agent-1', 'agent-2', 'agent-3'],
210
+ max_tokens: 16000
211
+ )
212
+
213
+ # Any agent can add context
214
+ group.remember('Customer prefers email', originator: 'agent-1')
215
+
216
+ # All agents see the context
217
+ group.recall('customer preferences') # Returns same results for all
218
+ ```
219
+
220
+ ### High Availability
221
+
222
+ Primary + standby for critical systems:
223
+ ```ruby
224
+ group = HTM::RobotGroup.new(
225
+ name: 'production-bot',
226
+ active: ['primary'],
227
+ passive: ['standby-1', 'standby-2'],
228
+ max_tokens: 32000
229
+ )
230
+
231
+ # If primary fails
232
+ group.failover! # standby-1 becomes active instantly
233
+ ```
234
+
235
+ ### Load Balancing
236
+
237
+ Multiple active robots share the load:
238
+ ```ruby
239
+ group = HTM::RobotGroup.new(
240
+ name: 'api-handlers',
241
+ active: ['handler-1', 'handler-2', 'handler-3', 'handler-4'],
242
+ max_tokens: 64000
243
+ )
244
+
245
+ # Any handler can process requests with shared context
246
+ ```
247
+
248
+ ## API Reference
249
+
250
+ ### HTM::RobotGroup
251
+
252
+ ```ruby
253
+ # Create a group
254
+ group = HTM::RobotGroup.new(
255
+ name: 'my-group',
256
+ active: ['robot-1'],
257
+ passive: ['robot-2'],
258
+ max_tokens: 8000
259
+ )
260
+
261
+ # Add memories
262
+ group.remember(content, originator: 'robot-1')
263
+
264
+ # Search memories
265
+ group.recall(query, limit: 10, strategy: :hybrid)
266
+
267
+ # Check status
268
+ group.status
269
+ # => { name:, active:, passive:, working_memory_nodes:, token_utilization:, in_sync: }
270
+
271
+ # Failover
272
+ group.failover!
273
+
274
+ # Add/remove robots
275
+ group.add_active('new-robot')
276
+ group.add_passive('standby-robot')
277
+
278
+ # Sync operations
279
+ group.sync_all
280
+ group.sync_robot('robot-name')
281
+
282
+ # Cleanup
283
+ group.clear_working_memory
284
+ group.shutdown
285
+ ```
286
+
287
+ ### HTM::WorkingMemoryChannel
288
+
289
+ ```ruby
290
+ # Create channel for cross-process sync
291
+ channel = HTM::WorkingMemoryChannel.new(group_name, db_config)
292
+
293
+ # Register callback for changes
294
+ channel.on_change do |event, node_id, origin_robot_id|
295
+ case event
296
+ when :added then # handle new node
297
+ when :evicted then # handle removal
298
+ when :cleared then # handle clear
299
+ end
300
+ end
301
+
302
+ # Start/stop listening
303
+ channel.start_listening
304
+ channel.stop_listening
305
+
306
+ # Send notifications
307
+ channel.notify(:added, node_id: 123, robot_id: 456)
308
+ ```
309
+
310
+ ## Troubleshooting
311
+
312
+ ### Robots not receiving notifications
313
+
314
+ 1. Verify PostgreSQL is running with LISTEN/NOTIFY support
315
+ 2. Check that all robots use the same `group_name`
316
+ 3. Ensure `channel.start_listening` was called
317
+
318
+ ### High memory usage
319
+
320
+ 1. Reduce `max_tokens` for the group
321
+ 2. Call `group.clear_working_memory` periodically
322
+ 3. Use more aggressive eviction strategies
323
+
324
+ ### Slow synchronization
325
+
326
+ 1. Check PostgreSQL connection latency
327
+ 2. Reduce notification frequency for bulk operations
328
+ 3. Consider batching memory additions
329
+
330
+ ## Environment Variables
331
+
332
+ | Variable | Description |
333
+ |----------|-------------|
334
+ | `HTM_DATABASE__URL` | PostgreSQL connection URL (required) |
335
+ | `HTM_ENV` | Environment name (set to `examples` by helper) |
@@ -16,16 +16,30 @@
16
16
  # { "cmd": "recall", "query": "...", "limit": 5 }
17
17
  # { "cmd": "status" }
18
18
  # { "cmd": "shutdown" }
19
+ #
20
+ # Note: This worker inherits HTM_ENV and HTM_DATABASE__URL from the parent
21
+ # process (multi_process.rb) which uses examples_helper.rb.
19
22
 
20
23
  require 'logger'
21
24
  require 'json'
22
- require_relative '../../lib/htm'
25
+ require_relative '../../examples_helper'
23
26
 
24
27
  robot_name = ARGV[0]
25
28
  group_name = ARGV[1]
26
29
 
27
30
  unless robot_name && group_name
28
- $stderr.puts "Usage: ruby robot_worker.rb <robot_name> <group_name>"
31
+ $stderr.puts <<~NOTICE
32
+
33
+ Usage: ruby robot_worker.rb <robot_name> <group_name>
34
+
35
+ This robot can be run as a standalone process or as part of a group.
36
+ It is not an actual demo program.
37
+ Its just a part of the two scenarios being demonstrated:
38
+
39
+ Scenario 1: Multiple Robots in Separate Processes (multi_process.rb)
40
+ Scenario 2: All Robots Running Within the Same Process (same_process.rb)
41
+
42
+ NOTICE
29
43
  exit 1
30
44
  end
31
45
 
@@ -64,7 +78,7 @@ channel.on_change do |event, node_id, origin_robot_id|
64
78
 
65
79
  case event
66
80
  when :added
67
- node = HTM::Models::Node.find_by(id: node_id)
81
+ node = HTM::Models::Node.first(id: node_id)
68
82
  if node
69
83
  htm.working_memory.add_from_sync(
70
84
  id: node.id,
@@ -13,10 +13,13 @@
13
13
  # 4. Dynamic scaling by spawning new processes
14
14
  #
15
15
  # Prerequisites:
16
- # 1. Set HTM_DATABASE__URL environment variable
17
- # 2. Initialize database schema: rake db_setup
16
+ # 1. Set up examples database: rake examples:setup
17
+ # 2. Install dependencies: bundle install
18
+ #
19
+ # Run via:
20
+ # ruby examples/robot_groups/multi_process.rb
18
21
 
19
- require_relative '../../lib/htm'
22
+ require_relative '../examples_helper'
20
23
  require 'json'
21
24
  require 'timeout'
22
25
  require 'open3'
@@ -26,7 +29,7 @@ require 'open3'
26
29
  # =============================================================================
27
30
 
28
31
  class RobotProcess
29
- WORKER_SCRIPT = File.expand_path('robot_worker.rb', __dir__)
32
+ WORKER_SCRIPT = File.expand_path('lib/robot_worker.rb', __dir__)
30
33
 
31
34
  attr_reader :name, :pid
32
35
 
@@ -119,11 +122,8 @@ def run_demo
119
122
 
120
123
  BANNER
121
124
 
122
- unless ENV['HTM_DATABASE__URL']
123
- puts 'ERROR: HTM_DATABASE__URL not set.'
124
- puts ' export HTM_DATABASE__URL="postgresql://user@localhost:5432/htm_development"'
125
- exit 1
126
- end
125
+ ExamplesHelper.print_environment
126
+ ExamplesHelper.require_database!
127
127
 
128
128
  group_name = "demo-#{Time.now.to_i}"
129
129
  robots = []
@@ -19,25 +19,22 @@
19
19
  # synchronization of in-memory working memory across robots.
20
20
  #
21
21
  # Prerequisites:
22
- # 1. Set HTM_DATABASE__URL environment variable
23
- # 2. Initialize database schema: rake db_setup
24
- # 3. Install dependencies: bundle install
22
+ # 1. Set up examples database: rake examples:setup
23
+ # 2. Install dependencies: bundle install
24
+ #
25
+ # Run via:
26
+ # ruby examples/robot_groups/same_process.rb
25
27
 
26
- require_relative '../../lib/htm'
28
+ require_relative '../examples_helper'
27
29
  require 'json'
28
30
 
29
31
  # =============================================================================
30
32
  # Demo Script
31
33
  # =============================================================================
32
34
 
33
- puts 'HTM Robot Group Demo - Shared Working Memory & Failover'
34
- puts '=' * 60
35
-
36
- unless ENV['HTM_DATABASE__URL']
37
- puts 'ERROR: HTM_DATABASE__URL not set. Please set it:'
38
- puts ' export HTM_DATABASE__URL="postgresql://postgres@localhost:5432/htm_development"'
39
- exit 1
40
- end
35
+ ExamplesHelper.section "HTM Robot Group Demo - Shared Working Memory & Failover"
36
+ ExamplesHelper.print_environment
37
+ ExamplesHelper.require_database!
41
38
 
42
39
  begin
43
40
  # Configure HTM
@@ -12,6 +12,9 @@ gem 'htm', path: '../..'
12
12
  # PostgreSQL (required for HTM)
13
13
  gem 'pg', '~> 1.5'
14
14
 
15
+ # Ruby 4.0+ bundled gems (no longer in stdlib)
16
+ gem 'ostruct'
17
+
15
18
  # Frontend - using CDN for Tailwind and Hotwire (no build step)
16
19
  gem 'propshaft' # Asset pipeline for CSS/images
17
20
  # Pagination uses simple offset/limit (no Kaminari needed)