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
|
@@ -51,15 +51,10 @@ htm = HTM.new(
|
|
|
51
51
|
|
|
52
52
|
### Adding Memories
|
|
53
53
|
|
|
54
|
-
When you
|
|
54
|
+
When you remember content, it goes to both working and long-term memory:
|
|
55
55
|
|
|
56
56
|
```ruby
|
|
57
|
-
htm.
|
|
58
|
-
"fact_001",
|
|
59
|
-
"User prefers Ruby for scripting",
|
|
60
|
-
type: :fact,
|
|
61
|
-
importance: 7.0
|
|
62
|
-
)
|
|
57
|
+
htm.remember("User prefers Ruby for scripting")
|
|
63
58
|
|
|
64
59
|
# Internally:
|
|
65
60
|
# 1. Calculate token count
|
|
@@ -74,8 +69,8 @@ When you recall, memories are added to working memory:
|
|
|
74
69
|
|
|
75
70
|
```ruby
|
|
76
71
|
memories = htm.recall(
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
"database design",
|
|
73
|
+
timeframe: "last week"
|
|
79
74
|
)
|
|
80
75
|
|
|
81
76
|
# Internally:
|
|
@@ -123,7 +118,6 @@ class MemoryMonitor
|
|
|
123
118
|
|
|
124
119
|
def report
|
|
125
120
|
wm = @htm.working_memory
|
|
126
|
-
stats = @htm.memory_stats
|
|
127
121
|
|
|
128
122
|
puts "=== Working Memory Report ==="
|
|
129
123
|
puts "Capacity: #{wm.max_tokens} tokens"
|
|
@@ -134,8 +128,7 @@ class MemoryMonitor
|
|
|
134
128
|
puts "Average tokens per node: #{wm.token_count / wm.node_count}" if wm.node_count > 0
|
|
135
129
|
puts
|
|
136
130
|
puts "=== Long-term Memory ==="
|
|
137
|
-
puts "Total nodes: #{
|
|
138
|
-
puts "Database size: #{(stats[:database_size] / 1024.0 / 1024.0).round(2)} MB"
|
|
131
|
+
puts "Total nodes: #{HTM::Models::Node.count}"
|
|
139
132
|
end
|
|
140
133
|
|
|
141
134
|
def health_check
|
|
@@ -189,29 +182,24 @@ htm = HTM.new(
|
|
|
189
182
|
working_memory_size: 10_000 # Small for demo
|
|
190
183
|
)
|
|
191
184
|
|
|
192
|
-
# Add important fact (
|
|
193
|
-
htm.
|
|
194
|
-
"critical",
|
|
185
|
+
# Add important fact (stays longer due to higher access frequency)
|
|
186
|
+
critical_id = htm.remember(
|
|
195
187
|
"Critical system password",
|
|
196
|
-
|
|
188
|
+
metadata: { priority: "critical" }
|
|
197
189
|
)
|
|
198
190
|
|
|
199
|
-
# Add many
|
|
191
|
+
# Add many items
|
|
200
192
|
100.times do |i|
|
|
201
|
-
htm.
|
|
202
|
-
"temp_#{i}",
|
|
203
|
-
"Temporary note #{i}",
|
|
204
|
-
importance: 1.0
|
|
205
|
-
)
|
|
193
|
+
htm.remember("Temporary note #{i}")
|
|
206
194
|
end
|
|
207
195
|
|
|
208
|
-
# Check what survived
|
|
196
|
+
# Check what survived in working memory
|
|
209
197
|
wm = htm.working_memory
|
|
210
198
|
puts "Surviving nodes: #{wm.node_count}"
|
|
211
199
|
|
|
212
|
-
# Critical fact
|
|
213
|
-
critical = htm.retrieve(
|
|
214
|
-
puts "Critical fact present: #{!critical.nil?}"
|
|
200
|
+
# Critical fact is still in long-term memory
|
|
201
|
+
critical = htm.long_term_memory.retrieve(critical_id)
|
|
202
|
+
puts "Critical fact present in LTM: #{!critical.nil?}"
|
|
215
203
|
```
|
|
216
204
|
|
|
217
205
|
### Manual Eviction
|
|
@@ -235,35 +223,31 @@ end
|
|
|
235
223
|
|
|
236
224
|
## Best Practices
|
|
237
225
|
|
|
238
|
-
### 1.
|
|
226
|
+
### 1. Use Metadata for Priority Tracking
|
|
239
227
|
|
|
240
228
|
```ruby
|
|
241
|
-
# Critical data:
|
|
242
|
-
htm.
|
|
243
|
-
"
|
|
244
|
-
"
|
|
245
|
-
importance: 10.0
|
|
229
|
+
# Critical data: Mark with priority metadata
|
|
230
|
+
htm.remember(
|
|
231
|
+
"Production API key: secret123",
|
|
232
|
+
metadata: { priority: "critical", category: "credentials" }
|
|
246
233
|
)
|
|
247
234
|
|
|
248
|
-
# Important context:
|
|
249
|
-
htm.
|
|
250
|
-
"user_goal",
|
|
235
|
+
# Important context: Mark appropriately
|
|
236
|
+
htm.remember(
|
|
251
237
|
"User wants to optimize database",
|
|
252
|
-
|
|
238
|
+
metadata: { priority: "high", category: "goal" }
|
|
253
239
|
)
|
|
254
240
|
|
|
255
|
-
# Temporary context
|
|
256
|
-
htm.
|
|
257
|
-
"current_topic",
|
|
241
|
+
# Temporary context
|
|
242
|
+
htm.remember(
|
|
258
243
|
"Discussing query optimization",
|
|
259
|
-
|
|
244
|
+
metadata: { priority: "medium", category: "context" }
|
|
260
245
|
)
|
|
261
246
|
|
|
262
|
-
# Disposable notes
|
|
263
|
-
htm.
|
|
264
|
-
"scratch",
|
|
247
|
+
# Disposable notes
|
|
248
|
+
htm.remember(
|
|
265
249
|
"Temporary calculation result",
|
|
266
|
-
|
|
250
|
+
metadata: { priority: "low", category: "scratch" }
|
|
267
251
|
)
|
|
268
252
|
```
|
|
269
253
|
|
|
@@ -305,16 +289,16 @@ Don't load unnecessary data into working memory:
|
|
|
305
289
|
```ruby
|
|
306
290
|
# Bad: Load everything
|
|
307
291
|
all_memories = htm.recall(
|
|
292
|
+
"anything",
|
|
308
293
|
timeframe: "all time",
|
|
309
|
-
topic: "anything",
|
|
310
294
|
limit: 1000
|
|
311
295
|
)
|
|
312
296
|
# This fills working memory with potentially irrelevant data
|
|
313
297
|
|
|
314
298
|
# Good: Load what you need
|
|
315
299
|
relevant = htm.recall(
|
|
300
|
+
"current project",
|
|
316
301
|
timeframe: "last week",
|
|
317
|
-
topic: "current project",
|
|
318
302
|
limit: 20
|
|
319
303
|
)
|
|
320
304
|
# This keeps working memory focused
|
|
@@ -322,25 +306,27 @@ relevant = htm.recall(
|
|
|
322
306
|
|
|
323
307
|
### 4. Clean Up When Done
|
|
324
308
|
|
|
325
|
-
Remove temporary memories:
|
|
309
|
+
Remove temporary memories when no longer needed:
|
|
326
310
|
|
|
327
311
|
```ruby
|
|
328
|
-
def with_temporary_context(htm,
|
|
312
|
+
def with_temporary_context(htm, value)
|
|
329
313
|
# Add temporary context
|
|
330
|
-
htm.
|
|
314
|
+
node_id = htm.remember(value, metadata: { temporary: true })
|
|
331
315
|
|
|
332
|
-
yield
|
|
316
|
+
result = yield
|
|
333
317
|
|
|
334
|
-
# Clean up
|
|
335
|
-
htm.forget(
|
|
318
|
+
# Clean up - soft delete by default (recoverable)
|
|
319
|
+
htm.forget(node_id)
|
|
320
|
+
|
|
321
|
+
result
|
|
336
322
|
end
|
|
337
323
|
|
|
338
|
-
with_temporary_context(htm, "
|
|
324
|
+
with_temporary_context(htm, "Temp calculation data") do
|
|
339
325
|
# Use the temporary context
|
|
340
|
-
context = htm.
|
|
341
|
-
# ... do work
|
|
326
|
+
context = htm.working_memory.assemble_context(strategy: :recent)
|
|
327
|
+
# ... do work with context
|
|
342
328
|
end
|
|
343
|
-
# Temp data is now
|
|
329
|
+
# Temp data is now soft-deleted
|
|
344
330
|
```
|
|
345
331
|
|
|
346
332
|
### 5. Batch Operations Carefully
|
|
@@ -348,23 +334,9 @@ end
|
|
|
348
334
|
Be mindful when adding many memories at once:
|
|
349
335
|
|
|
350
336
|
```ruby
|
|
351
|
-
#
|
|
352
|
-
1000.times do |i|
|
|
353
|
-
htm.add_node("item_#{i}", "Data #{i}", importance: 5.0)
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
# Better: Add with appropriate importance
|
|
357
|
-
1000.times do |i|
|
|
358
|
-
htm.add_node(
|
|
359
|
-
"item_#{i}",
|
|
360
|
-
"Data #{i}",
|
|
361
|
-
importance: 3.0 # Lower importance for bulk data
|
|
362
|
-
)
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
# Or: Monitor during batch operations
|
|
337
|
+
# Add batch data with monitoring
|
|
366
338
|
batch_data.each_with_index do |data, i|
|
|
367
|
-
htm.
|
|
339
|
+
htm.remember(data, metadata: { batch: "import_001", index: i })
|
|
368
340
|
|
|
369
341
|
# Check capacity every 100 items
|
|
370
342
|
if i % 100 == 0
|
|
@@ -378,58 +350,56 @@ end
|
|
|
378
350
|
|
|
379
351
|
### Strategy 1: Sliding Window
|
|
380
352
|
|
|
381
|
-
Keep only recent memories:
|
|
353
|
+
Keep only recent memories by tracking node IDs:
|
|
382
354
|
|
|
383
355
|
```ruby
|
|
384
356
|
class SlidingWindow
|
|
385
357
|
def initialize(htm, window_size: 50)
|
|
386
358
|
@htm = htm
|
|
387
359
|
@window_size = window_size
|
|
388
|
-
@
|
|
360
|
+
@node_ids = []
|
|
389
361
|
end
|
|
390
362
|
|
|
391
|
-
def add(
|
|
392
|
-
@htm.
|
|
393
|
-
@
|
|
363
|
+
def add(value, **opts)
|
|
364
|
+
node_id = @htm.remember(value, **opts)
|
|
365
|
+
@node_ids << node_id
|
|
394
366
|
|
|
395
|
-
#
|
|
396
|
-
if @
|
|
397
|
-
|
|
398
|
-
@htm.forget(
|
|
367
|
+
# Forget oldest if window exceeded
|
|
368
|
+
if @node_ids.length > @window_size
|
|
369
|
+
oldest_id = @node_ids.shift
|
|
370
|
+
@htm.forget(oldest_id) rescue nil
|
|
399
371
|
end
|
|
372
|
+
|
|
373
|
+
node_id
|
|
400
374
|
end
|
|
401
375
|
end
|
|
402
376
|
```
|
|
403
377
|
|
|
404
|
-
### Strategy 2:
|
|
378
|
+
### Strategy 2: Priority-Based Management
|
|
405
379
|
|
|
406
|
-
|
|
380
|
+
Use metadata to track priority:
|
|
407
381
|
|
|
408
382
|
```ruby
|
|
409
|
-
class
|
|
410
|
-
def initialize(htm
|
|
383
|
+
class PriorityManager
|
|
384
|
+
def initialize(htm)
|
|
411
385
|
@htm = htm
|
|
412
|
-
@min_importance = min_importance
|
|
413
386
|
end
|
|
414
387
|
|
|
415
|
-
def add(
|
|
416
|
-
|
|
388
|
+
def add(value, priority: "medium", **opts)
|
|
389
|
+
metadata = (opts[:metadata] || {}).merge(priority: priority)
|
|
390
|
+
node_id = @htm.remember(value, **opts.merge(metadata: metadata))
|
|
417
391
|
|
|
418
|
-
# If low
|
|
419
|
-
|
|
420
|
-
@htm.working_memory.utilization_percentage > 80
|
|
392
|
+
# If low priority and memory is tight, it will evict naturally
|
|
393
|
+
# HTM uses LFU + LRU eviction based on access patterns
|
|
421
394
|
|
|
422
|
-
|
|
423
|
-
# (Note: HTM doesn't expose direct working memory removal,
|
|
424
|
-
# so we rely on natural eviction)
|
|
425
|
-
end
|
|
395
|
+
node_id
|
|
426
396
|
end
|
|
427
397
|
end
|
|
428
398
|
```
|
|
429
399
|
|
|
430
400
|
### Strategy 3: Topic-Based Management
|
|
431
401
|
|
|
432
|
-
Group memories by topic
|
|
402
|
+
Group memories by topic using tags:
|
|
433
403
|
|
|
434
404
|
```ruby
|
|
435
405
|
class TopicManager
|
|
@@ -438,15 +408,17 @@ class TopicManager
|
|
|
438
408
|
@topics = Hash.new { |h, k| h[k] = [] }
|
|
439
409
|
end
|
|
440
410
|
|
|
441
|
-
def add(
|
|
442
|
-
|
|
443
|
-
@
|
|
411
|
+
def add(value, topic:, **opts)
|
|
412
|
+
tags = (opts[:tags] || []) + ["topic:#{topic}"]
|
|
413
|
+
node_id = @htm.remember(value, **opts.merge(tags: tags))
|
|
414
|
+
@topics[topic] << node_id
|
|
415
|
+
node_id
|
|
444
416
|
end
|
|
445
417
|
|
|
446
418
|
def clear_topic(topic)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
@htm.forget(
|
|
419
|
+
node_ids = @topics[topic] || []
|
|
420
|
+
node_ids.each do |node_id|
|
|
421
|
+
@htm.forget(node_id) rescue nil
|
|
450
422
|
end
|
|
451
423
|
@topics.delete(topic)
|
|
452
424
|
end
|
|
@@ -471,8 +443,7 @@ medium = "A" * 100 # ~25 tokens
|
|
|
471
443
|
long = "word " * 1000 # ~1000 tokens
|
|
472
444
|
|
|
473
445
|
# Check token count of a string
|
|
474
|
-
|
|
475
|
-
tokens = embedding_service.count_tokens(long)
|
|
446
|
+
tokens = HTM.configuration.count_tokens(long)
|
|
476
447
|
puts "Token count: #{tokens}"
|
|
477
448
|
```
|
|
478
449
|
|
|
@@ -512,18 +483,18 @@ htm = HTM.new(robot_name: "Perf Test")
|
|
|
512
483
|
|
|
513
484
|
# Add 1000 memories
|
|
514
485
|
1000.times do |i|
|
|
515
|
-
htm.
|
|
486
|
+
htm.remember("Value #{i}")
|
|
516
487
|
end
|
|
517
488
|
|
|
518
489
|
# Benchmark working memory access
|
|
519
490
|
Benchmark.bm do |x|
|
|
520
|
-
x.report("
|
|
521
|
-
1000.times { htm.
|
|
491
|
+
x.report("assemble_context:") do
|
|
492
|
+
1000.times { htm.working_memory.assemble_context(strategy: :balanced) }
|
|
522
493
|
end
|
|
523
494
|
end
|
|
524
495
|
|
|
525
496
|
# Typical results:
|
|
526
|
-
#
|
|
497
|
+
# assemble_context: ~1ms per call
|
|
527
498
|
```
|
|
528
499
|
|
|
529
500
|
### Optimization Tips
|
|
@@ -532,7 +503,7 @@ end
|
|
|
532
503
|
# 1. Avoid frequent context assembly
|
|
533
504
|
# Bad: Assemble context every message
|
|
534
505
|
def process_message(message)
|
|
535
|
-
context = htm.
|
|
506
|
+
context = htm.working_memory.assemble_context(strategy: :balanced) # Slow if called frequently
|
|
536
507
|
llm.chat(context + message)
|
|
537
508
|
end
|
|
538
509
|
|
|
@@ -542,7 +513,7 @@ end
|
|
|
542
513
|
|
|
543
514
|
def process_message(message)
|
|
544
515
|
if @context_cache.nil? || @context_age > 10
|
|
545
|
-
@context_cache = htm.
|
|
516
|
+
@context_cache = htm.working_memory.assemble_context(strategy: :balanced)
|
|
546
517
|
@context_age = 0
|
|
547
518
|
end
|
|
548
519
|
@context_age += 1
|
|
@@ -552,7 +523,7 @@ end
|
|
|
552
523
|
|
|
553
524
|
# 2. Use appropriate token limits
|
|
554
525
|
# Don't request more than your LLM can handle
|
|
555
|
-
context = htm.
|
|
526
|
+
context = htm.working_memory.assemble_context(
|
|
556
527
|
strategy: :balanced,
|
|
557
528
|
max_tokens: 100_000 # Match LLM's context window
|
|
558
529
|
)
|
|
@@ -605,29 +576,25 @@ end
|
|
|
605
576
|
**Issue: Working memory always full**
|
|
606
577
|
|
|
607
578
|
```ruby
|
|
608
|
-
# Check
|
|
609
|
-
|
|
610
|
-
wm_util = stats[:working_memory][:utilization]
|
|
579
|
+
# Check working memory utilization
|
|
580
|
+
wm_util = htm.working_memory.utilization_percentage
|
|
611
581
|
|
|
612
582
|
if wm_util > 95
|
|
613
583
|
puts "Working memory consistently full"
|
|
614
584
|
puts "Solutions:"
|
|
615
|
-
puts "1. Increase working_memory_size"
|
|
616
|
-
puts "2.
|
|
617
|
-
puts "3.
|
|
618
|
-
puts "4. Clean up temporary data more frequently"
|
|
585
|
+
puts "1. Increase working_memory_size when creating HTM"
|
|
586
|
+
puts "2. Reduce recall limit"
|
|
587
|
+
puts "3. Clean up temporary data more frequently with forget()"
|
|
619
588
|
end
|
|
620
589
|
```
|
|
621
590
|
|
|
622
591
|
**Issue: Important data getting evicted**
|
|
623
592
|
|
|
624
593
|
```ruby
|
|
625
|
-
#
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
importance: 9.5 # High enough to avoid eviction
|
|
630
|
-
)
|
|
594
|
+
# HTM evicts based on access frequency and recency
|
|
595
|
+
# Access important data more frequently to keep it in working memory
|
|
596
|
+
# Or query it from long-term memory when needed:
|
|
597
|
+
critical = htm.long_term_memory.retrieve(critical_node_id)
|
|
631
598
|
```
|
|
632
599
|
|
|
633
600
|
**Issue: Memory utilization too low**
|
|
@@ -640,8 +607,8 @@ if wm_util < 20
|
|
|
640
607
|
puts "Working memory underutilized"
|
|
641
608
|
puts "Consider:"
|
|
642
609
|
puts "1. Reducing working_memory_size to save RAM"
|
|
643
|
-
puts "2. Recalling more context"
|
|
644
|
-
puts "3. Using larger token limits in
|
|
610
|
+
puts "2. Recalling more context with larger limit"
|
|
611
|
+
puts "3. Using larger token limits in assemble_context"
|
|
645
612
|
end
|
|
646
613
|
```
|
|
647
614
|
|
|
@@ -678,37 +645,37 @@ end
|
|
|
678
645
|
|
|
679
646
|
monitor = Monitor.new(htm)
|
|
680
647
|
|
|
681
|
-
# Add memories with different
|
|
648
|
+
# Add memories with different priorities via metadata
|
|
682
649
|
puts "Adding critical data..."
|
|
683
|
-
htm.
|
|
650
|
+
critical_id = htm.remember("Critical system data", metadata: { priority: "critical" })
|
|
684
651
|
monitor.report
|
|
685
652
|
|
|
686
653
|
puts "\nAdding important data..."
|
|
687
654
|
10.times do |i|
|
|
688
|
-
htm.
|
|
655
|
+
htm.remember("Important item #{i}", metadata: { priority: "high" })
|
|
689
656
|
end
|
|
690
657
|
monitor.report
|
|
691
658
|
|
|
692
659
|
puts "\nAdding regular data..."
|
|
693
660
|
50.times do |i|
|
|
694
|
-
htm.
|
|
661
|
+
htm.remember("Regular item #{i}", metadata: { priority: "medium" })
|
|
695
662
|
end
|
|
696
663
|
monitor.report
|
|
697
664
|
|
|
698
665
|
puts "\nAdding temporary data..."
|
|
699
666
|
100.times do |i|
|
|
700
|
-
htm.
|
|
667
|
+
htm.remember("Temporary item #{i}", metadata: { priority: "low" })
|
|
701
668
|
end
|
|
702
669
|
monitor.report
|
|
703
670
|
|
|
704
|
-
# Check
|
|
671
|
+
# Check that critical data is still in long-term memory
|
|
705
672
|
puts "\n=== Survival Check ==="
|
|
706
|
-
critical = htm.retrieve(
|
|
707
|
-
puts "Critical
|
|
673
|
+
critical = htm.long_term_memory.retrieve(critical_id)
|
|
674
|
+
puts "Critical in LTM: #{!critical.nil?}"
|
|
708
675
|
|
|
709
|
-
# Create context
|
|
676
|
+
# Create context from working memory
|
|
710
677
|
puts "\nCreating context..."
|
|
711
|
-
context = htm.
|
|
678
|
+
context = htm.working_memory.assemble_context(strategy: :balanced, max_tokens: 50_000)
|
|
712
679
|
puts "Context length: #{context.length} characters"
|
|
713
680
|
|
|
714
681
|
# Final stats
|
data/docs/index.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
<div align="center">
|
|
3
|
-
<div style="background: linear-gradient(135deg, #
|
|
4
|
-
<p style="color: #
|
|
5
|
-
|
|
3
|
+
<div style="background: linear-gradient(135deg, #ffd93d 0%, #f5c800 100%); border: 4px solid #e6b800; border-radius: 12px; padding: 20px; margin: 20px auto; max-width: 800px; box-shadow: 0 8px 16px rgba(230, 184, 0, 0.3);">
|
|
4
|
+
<p style="color: #000000; font-size: 42px; font-weight: bold; margin: 0;">
|
|
5
|
+
💣 CAUTION 💣
|
|
6
6
|
</p>
|
|
7
|
-
<p style="color: #
|
|
8
|
-
This documentation
|
|
9
|
-
|
|
7
|
+
<p style="color: #000; font-size: 27px; font-weight: bold; margin: 10px 0 0 0; line-height: 1.6;">
|
|
8
|
+
This documentation may contain <strong>inaccuracies</strong>.<br/>
|
|
9
|
+
Verify critical details in the source code and example demos.
|
|
10
10
|
</p>
|
|
11
11
|
</div>
|
|
12
12
|
|
|
@@ -63,14 +63,14 @@ HTM enables multiple AI robots to share a collective memory:
|
|
|
63
63
|
- Cross-robot context awareness and conversation continuity
|
|
64
64
|
- Query conversation timelines across multiple robots
|
|
65
65
|
|
|
66
|
-
### Knowledge Graph
|
|
66
|
+
### Pseudo Knowledge Graph
|
|
67
67
|
|
|
68
|
-
Build rich relationship networks between memories:
|
|
68
|
+
Build rich relationship networks between memories using a hierarchical taxonomy:
|
|
69
69
|
|
|
70
70
|
- Link related memories together
|
|
71
|
-
- Tag-based categorization
|
|
71
|
+
- Tag-based categorization with up to four levels of abstraction
|
|
72
72
|
- Importance scoring for prioritization
|
|
73
|
-
- Navigate memory
|
|
73
|
+
- Navigate memory relationship abstractions
|
|
74
74
|
|
|
75
75
|
## Quick Example
|
|
76
76
|
|
|
@@ -79,33 +79,36 @@ Here's how simple it is to get started with HTM:
|
|
|
79
79
|
```ruby
|
|
80
80
|
require 'htm'
|
|
81
81
|
|
|
82
|
+
# Configure HTM globally (optional - uses Ollama by default)
|
|
83
|
+
HTM.configure do |config|
|
|
84
|
+
config.embedding_provider = :ollama
|
|
85
|
+
config.embedding_model = 'nomic-embed-text:latest'
|
|
86
|
+
config.tag_provider = :ollama
|
|
87
|
+
config.tag_model = 'gemma3:latest'
|
|
88
|
+
end
|
|
89
|
+
|
|
82
90
|
# Initialize HTM for your robot
|
|
83
91
|
htm = HTM.new(
|
|
84
|
-
robot_name: "
|
|
85
|
-
working_memory_size: 128_000
|
|
86
|
-
embedding_service: :ollama, # Use Ollama for embeddings
|
|
87
|
-
embedding_model: 'gpt-oss' # Default embedding model
|
|
92
|
+
robot_name: "Chat Assistant",
|
|
93
|
+
working_memory_size: 128_000 # 128k tokens
|
|
88
94
|
)
|
|
89
95
|
|
|
90
|
-
#
|
|
91
|
-
htm.
|
|
92
|
-
"decision_001",
|
|
96
|
+
# Remember information (embeddings generated automatically in background)
|
|
97
|
+
node_id = htm.remember(
|
|
93
98
|
"We decided to use PostgreSQL for HTM storage",
|
|
94
|
-
|
|
95
|
-
category: "architecture",
|
|
96
|
-
importance: 9.0,
|
|
97
|
-
tags: ["database", "architecture"]
|
|
99
|
+
tags: ["database:postgresql", "architecture"],
|
|
100
|
+
metadata: { category: "architecture", priority: "high" }
|
|
98
101
|
)
|
|
99
102
|
|
|
100
103
|
# Recall memories from the past
|
|
101
104
|
memories = htm.recall(
|
|
105
|
+
"database decisions", # Topic (first positional argument)
|
|
102
106
|
timeframe: "last week",
|
|
103
|
-
topic: "database decisions",
|
|
104
107
|
strategy: :hybrid
|
|
105
108
|
)
|
|
106
109
|
|
|
107
|
-
# Create context for your LLM
|
|
108
|
-
context = htm.
|
|
110
|
+
# Create context for your LLM from working memory
|
|
111
|
+
context = htm.working_memory.assemble_context(
|
|
109
112
|
strategy: :balanced,
|
|
110
113
|
max_tokens: 50_000
|
|
111
114
|
)
|
|
@@ -117,8 +120,9 @@ response = llm.chat(
|
|
|
117
120
|
user: "What database did we decide to use?"
|
|
118
121
|
)
|
|
119
122
|
|
|
120
|
-
# Explicit deletion only when needed
|
|
121
|
-
htm.forget(
|
|
123
|
+
# Explicit deletion only when needed (soft delete by default)
|
|
124
|
+
htm.forget(node_id) # Soft delete (recoverable)
|
|
125
|
+
htm.forget(node_id, soft: false, confirm: :confirmed) # Permanent delete
|
|
122
126
|
```
|
|
123
127
|
|
|
124
128
|
## Use Cases
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
require 'logger'
|
|
21
21
|
require 'json'
|
|
22
22
|
require_relative '../../lib/htm'
|
|
23
|
-
require_relative 'lib/working_memory_channel'
|
|
24
23
|
|
|
25
24
|
robot_name = ARGV[0]
|
|
26
25
|
group_name = ARGV[1]
|
|
@@ -53,7 +52,7 @@ htm = HTM.new(robot_name: robot_name, working_memory_size: 8000)
|
|
|
53
52
|
db_config = HTM::Database.default_config
|
|
54
53
|
|
|
55
54
|
# Setup channel for cross-process notifications
|
|
56
|
-
channel = WorkingMemoryChannel.new(group_name, db_config)
|
|
55
|
+
channel = HTM::WorkingMemoryChannel.new(group_name, db_config)
|
|
57
56
|
|
|
58
57
|
# Track notifications received
|
|
59
58
|
notifications_count = 0
|
|
@@ -26,9 +26,6 @@
|
|
|
26
26
|
require_relative '../../lib/htm'
|
|
27
27
|
require 'json'
|
|
28
28
|
|
|
29
|
-
require_relative 'lib/working_memory_channel'
|
|
30
|
-
require_relative 'lib/robot_group'
|
|
31
|
-
|
|
32
29
|
# =============================================================================
|
|
33
30
|
# Demo Script
|
|
34
31
|
# =============================================================================
|
|
@@ -59,7 +56,7 @@ begin
|
|
|
59
56
|
# ---------------------------------------------------------------------------
|
|
60
57
|
puts "\n2. Creating robot group with primary + standby..."
|
|
61
58
|
|
|
62
|
-
group = RobotGroup.new(
|
|
59
|
+
group = HTM::RobotGroup.new(
|
|
63
60
|
name: 'customer-support-ha',
|
|
64
61
|
active: ['support-primary'],
|
|
65
62
|
passive: ['support-standby'],
|