htm 0.0.11 → 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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.dictate.toml +46 -0
  3. data/.envrc +2 -0
  4. data/CHANGELOG.md +85 -2
  5. data/README.md +348 -79
  6. data/Rakefile +14 -2
  7. data/bin/htm_mcp.rb +94 -0
  8. data/config/database.yml +20 -13
  9. data/db/migrate/00003_create_file_sources.rb +5 -0
  10. data/db/migrate/00004_create_nodes.rb +17 -0
  11. data/db/migrate/00005_create_tags.rb +7 -0
  12. data/db/migrate/00006_create_node_tags.rb +2 -0
  13. data/db/migrate/00007_create_robot_nodes.rb +7 -0
  14. data/db/schema.sql +69 -100
  15. data/docs/api/index.md +1 -1
  16. data/docs/api/yard/HTM/Configuration.md +54 -0
  17. data/docs/api/yard/HTM/Database.md +13 -10
  18. data/docs/api/yard/HTM/EmbeddingService.md +5 -1
  19. data/docs/api/yard/HTM/LongTermMemory.md +18 -277
  20. data/docs/api/yard/HTM/PropositionError.md +18 -0
  21. data/docs/api/yard/HTM/PropositionService.md +66 -0
  22. data/docs/api/yard/HTM/QueryCache.md +88 -0
  23. data/docs/api/yard/HTM/RobotGroup.md +481 -0
  24. data/docs/api/yard/HTM/SqlBuilder.md +108 -0
  25. data/docs/api/yard/HTM/TagService.md +4 -0
  26. data/docs/api/yard/HTM/Telemetry/NullInstrument.md +13 -0
  27. data/docs/api/yard/HTM/Telemetry/NullMeter.md +15 -0
  28. data/docs/api/yard/HTM/Telemetry.md +109 -0
  29. data/docs/api/yard/HTM/WorkingMemoryChannel.md +176 -0
  30. data/docs/api/yard/HTM.md +8 -22
  31. data/docs/api/yard/index.csv +102 -25
  32. data/docs/api/yard-reference.md +8 -0
  33. data/docs/architecture/index.md +1 -1
  34. data/docs/assets/images/multi-provider-failover.svg +51 -0
  35. data/docs/assets/images/robot-group-architecture.svg +65 -0
  36. data/docs/database/README.md +3 -3
  37. data/docs/database/public.file_sources.svg +29 -21
  38. data/docs/database/public.node_tags.md +2 -0
  39. data/docs/database/public.node_tags.svg +53 -41
  40. data/docs/database/public.nodes.md +2 -0
  41. data/docs/database/public.nodes.svg +52 -40
  42. data/docs/database/public.robot_nodes.md +2 -0
  43. data/docs/database/public.robot_nodes.svg +30 -22
  44. data/docs/database/public.robots.svg +16 -12
  45. data/docs/database/public.tags.md +3 -0
  46. data/docs/database/public.tags.svg +41 -33
  47. data/docs/database/schema.json +66 -0
  48. data/docs/database/schema.svg +60 -48
  49. data/docs/development/index.md +14 -1
  50. data/docs/development/rake-tasks.md +1068 -0
  51. data/docs/getting-started/index.md +1 -1
  52. data/docs/getting-started/quick-start.md +144 -155
  53. data/docs/guides/adding-memories.md +2 -3
  54. data/docs/guides/context-assembly.md +185 -184
  55. data/docs/guides/getting-started.md +154 -148
  56. data/docs/guides/index.md +8 -1
  57. data/docs/guides/long-term-memory.md +60 -92
  58. data/docs/guides/mcp-server.md +617 -0
  59. data/docs/guides/multi-robot.md +249 -345
  60. data/docs/guides/recalling-memories.md +153 -163
  61. data/docs/guides/robot-groups.md +604 -0
  62. data/docs/guides/search-strategies.md +61 -58
  63. data/docs/guides/working-memory.md +103 -136
  64. data/docs/images/telemetry-architecture.svg +153 -0
  65. data/docs/index.md +30 -26
  66. data/docs/telemetry.md +391 -0
  67. data/examples/README.md +46 -1
  68. data/examples/cli_app/README.md +1 -1
  69. data/examples/cli_app/htm_cli.rb +1 -1
  70. data/examples/robot_groups/robot_worker.rb +1 -2
  71. data/examples/robot_groups/same_process.rb +1 -4
  72. data/examples/sinatra_app/app.rb +1 -1
  73. data/examples/telemetry/README.md +147 -0
  74. data/examples/telemetry/SETUP_README.md +169 -0
  75. data/examples/telemetry/demo.rb +498 -0
  76. data/examples/telemetry/grafana/dashboards/htm-metrics.json +457 -0
  77. data/lib/htm/configuration.rb +261 -70
  78. data/lib/htm/database.rb +46 -22
  79. data/lib/htm/embedding_service.rb +24 -14
  80. data/lib/htm/errors.rb +15 -1
  81. data/lib/htm/jobs/generate_embedding_job.rb +19 -0
  82. data/lib/htm/jobs/generate_propositions_job.rb +103 -0
  83. data/lib/htm/jobs/generate_tags_job.rb +24 -0
  84. data/lib/htm/loaders/markdown_chunker.rb +79 -0
  85. data/lib/htm/loaders/markdown_loader.rb +41 -15
  86. data/lib/htm/long_term_memory/fulltext_search.rb +138 -0
  87. data/lib/htm/long_term_memory/hybrid_search.rb +324 -0
  88. data/lib/htm/long_term_memory/node_operations.rb +209 -0
  89. data/lib/htm/long_term_memory/relevance_scorer.rb +355 -0
  90. data/lib/htm/long_term_memory/robot_operations.rb +34 -0
  91. data/lib/htm/long_term_memory/tag_operations.rb +428 -0
  92. data/lib/htm/long_term_memory/vector_search.rb +109 -0
  93. data/lib/htm/long_term_memory.rb +51 -1153
  94. data/lib/htm/models/node.rb +35 -2
  95. data/lib/htm/models/node_tag.rb +31 -0
  96. data/lib/htm/models/robot_node.rb +31 -0
  97. data/lib/htm/models/tag.rb +44 -0
  98. data/lib/htm/proposition_service.rb +169 -0
  99. data/lib/htm/query_cache.rb +214 -0
  100. data/lib/htm/robot_group.rb +721 -0
  101. data/lib/htm/sql_builder.rb +178 -0
  102. data/lib/htm/tag_service.rb +16 -6
  103. data/lib/htm/tasks.rb +8 -2
  104. data/lib/htm/telemetry.rb +224 -0
  105. data/lib/htm/version.rb +1 -1
  106. data/lib/htm/working_memory_channel.rb +250 -0
  107. data/lib/htm.rb +66 -3
  108. data/lib/tasks/doc.rake +1 -1
  109. data/lib/tasks/htm.rake +259 -13
  110. data/mkdocs.yml +98 -96
  111. metadata +55 -20
  112. data/.aigcm_msg +0 -1
  113. data/.claude/settings.local.json +0 -95
  114. data/CLAUDE.md +0 -603
  115. data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +0 -12
  116. data/examples/cli_app/temp.log +0 -93
  117. data/examples/robot_groups/lib/robot_group.rb +0 -419
  118. data/examples/robot_groups/lib/working_memory_channel.rb +0 -140
  119. data/lib/htm/loaders/paragraph_chunker.rb +0 -112
  120. data/notes/ARCHITECTURE_REVIEW.md +0 -1167
  121. data/notes/IMPLEMENTATION_SUMMARY.md +0 -606
  122. data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +0 -451
  123. data/notes/next_steps.md +0 -100
  124. data/notes/plan.md +0 -627
  125. data/notes/tag_ontology_enhancement_ideas.md +0 -222
  126. data/notes/timescaledb_removal_summary.md +0 -200
@@ -51,15 +51,10 @@ htm = HTM.new(
51
51
 
52
52
  ### Adding Memories
53
53
 
54
- When you add a node, it goes to both working and long-term memory:
54
+ When you remember content, it goes to both working and long-term memory:
55
55
 
56
56
  ```ruby
57
- htm.add_node(
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
- timeframe: "last week",
78
- topic: "database design"
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: #{stats[: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 (will stay)
193
- htm.add_node(
194
- "critical",
185
+ # Add important fact (stays longer due to higher access frequency)
186
+ critical_id = htm.remember(
195
187
  "Critical system password",
196
- importance: 10.0
188
+ metadata: { priority: "critical" }
197
189
  )
198
190
 
199
- # Add many low-importance items
191
+ # Add many items
200
192
  100.times do |i|
201
- htm.add_node(
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 should still be there
213
- critical = htm.retrieve("critical")
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. Set Appropriate Importance
226
+ ### 1. Use Metadata for Priority Tracking
239
227
 
240
228
  ```ruby
241
- # Critical data: Never evict
242
- htm.add_node(
243
- "api_key",
244
- "Production API key",
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: Retain longer
249
- htm.add_node(
250
- "user_goal",
235
+ # Important context: Mark appropriately
236
+ htm.remember(
251
237
  "User wants to optimize database",
252
- importance: 8.0
238
+ metadata: { priority: "high", category: "goal" }
253
239
  )
254
240
 
255
- # Temporary context: Evict when needed
256
- htm.add_node(
257
- "current_topic",
241
+ # Temporary context
242
+ htm.remember(
258
243
  "Discussing query optimization",
259
- importance: 5.0
244
+ metadata: { priority: "medium", category: "context" }
260
245
  )
261
246
 
262
- # Disposable notes: Evict first
263
- htm.add_node(
264
- "scratch",
247
+ # Disposable notes
248
+ htm.remember(
265
249
  "Temporary calculation result",
266
- importance: 1.0
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, key, value)
312
+ def with_temporary_context(htm, value)
329
313
  # Add temporary context
330
- htm.add_node(key, value, type: :context, importance: 2.0)
314
+ node_id = htm.remember(value, metadata: { temporary: true })
331
315
 
332
- yield
316
+ result = yield
333
317
 
334
- # Clean up
335
- htm.forget(key, confirm: :confirmed)
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, "scratch_001", "Temp data") do
324
+ with_temporary_context(htm, "Temp calculation data") do
339
325
  # Use the temporary context
340
- context = htm.create_context(strategy: :recent)
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 removed
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
- # Risky: Might fill working memory quickly
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.add_node("item_#{i}", data, importance: 5.0)
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
- @keys = []
360
+ @node_ids = []
389
361
  end
390
362
 
391
- def add(key, value, **opts)
392
- @htm.add_node(key, value, **opts)
393
- @keys << key
363
+ def add(value, **opts)
364
+ node_id = @htm.remember(value, **opts)
365
+ @node_ids << node_id
394
366
 
395
- # Evict oldest if window exceeded
396
- if @keys.length > @window_size
397
- oldest = @keys.shift
398
- @htm.forget(oldest, confirm: :confirmed) rescue nil
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: Importance Thresholding
378
+ ### Strategy 2: Priority-Based Management
405
379
 
406
- Only keep high-importance memories:
380
+ Use metadata to track priority:
407
381
 
408
382
  ```ruby
409
- class ImportanceFilter
410
- def initialize(htm, min_importance: 7.0)
383
+ class PriorityManager
384
+ def initialize(htm)
411
385
  @htm = htm
412
- @min_importance = min_importance
413
386
  end
414
387
 
415
- def add(key, value, importance:, **opts)
416
- @htm.add_node(key, value, importance: importance, **opts)
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 importance and memory is tight, evict immediately
419
- if importance < @min_importance &&
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
- # Let it evict naturally or remove from working memory
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 and manage separately:
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(key, value, topic:, **opts)
442
- @htm.add_node(key, value, **opts)
443
- @topics[topic] << key
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
- keys = @topics[topic] || []
448
- keys.each do |key|
449
- @htm.forget(key, confirm: :confirmed) rescue nil
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
- embedding_service = HTM::EmbeddingService.new
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.add_node("key_#{i}", "Value #{i}", importance: 5.0)
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("create_context:") do
521
- 1000.times { htm.create_context(strategy: :balanced) }
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
- # create_context: ~1ms per call
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.create_context # Slow if called frequently
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.create_context
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.create_context(
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 if you're adding too much
609
- stats = htm.memory_stats
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. Lower importance of bulk data"
617
- puts "3. Reduce recall limit"
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
- # Increase importance of critical data
626
- htm.add_node(
627
- "critical_data",
628
- "Important information",
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 create_context"
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 importance
648
+ # Add memories with different priorities via metadata
682
649
  puts "Adding critical data..."
683
- htm.add_node("critical", "Critical system data", importance: 10.0)
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.add_node("important_#{i}", "Important item #{i}", importance: 8.0)
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.add_node("regular_#{i}", "Regular item #{i}", importance: 5.0)
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.add_node("temp_#{i}", "Temporary item #{i}", importance: 2.0)
667
+ htm.remember("Temporary item #{i}", metadata: { priority: "low" })
701
668
  end
702
669
  monitor.report
703
670
 
704
- # Check what survived
671
+ # Check that critical data is still in long-term memory
705
672
  puts "\n=== Survival Check ==="
706
- critical = htm.retrieve("critical")
707
- puts "Critical survived: #{!critical.nil?}"
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.create_context(strategy: :important, max_tokens: 50_000)
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
@@ -0,0 +1,153 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500" style="background: transparent;">
2
+ <defs>
3
+ <!-- Gradients for visual depth -->
4
+ <linearGradient id="appGradient" x1="0%" y1="0%" x2="0%" y2="100%">
5
+ <stop offset="0%" style="stop-color:#3b82f6;stop-opacity:0.9"/>
6
+ <stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:0.9"/>
7
+ </linearGradient>
8
+ <linearGradient id="telemetryGradient" x1="0%" y1="0%" x2="0%" y2="100%">
9
+ <stop offset="0%" style="stop-color:#8b5cf6;stop-opacity:0.9"/>
10
+ <stop offset="100%" style="stop-color:#6d28d9;stop-opacity:0.9"/>
11
+ </linearGradient>
12
+ <linearGradient id="nullGradient" x1="0%" y1="0%" x2="0%" y2="100%">
13
+ <stop offset="0%" style="stop-color:#6b7280;stop-opacity:0.8"/>
14
+ <stop offset="100%" style="stop-color:#4b5563;stop-opacity:0.8"/>
15
+ </linearGradient>
16
+ <linearGradient id="sdkGradient" x1="0%" y1="0%" x2="0%" y2="100%">
17
+ <stop offset="0%" style="stop-color:#10b981;stop-opacity:0.9"/>
18
+ <stop offset="100%" style="stop-color:#059669;stop-opacity:0.9"/>
19
+ </linearGradient>
20
+ <linearGradient id="backendGradient" x1="0%" y1="0%" x2="0%" y2="100%">
21
+ <stop offset="0%" style="stop-color:#f59e0b;stop-opacity:0.9"/>
22
+ <stop offset="100%" style="stop-color:#d97706;stop-opacity:0.9"/>
23
+ </linearGradient>
24
+
25
+ <!-- Arrow marker -->
26
+ <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
27
+ <polygon points="0 0, 10 3.5, 0 7" fill="#94a3b8"/>
28
+ </marker>
29
+ <marker id="arrowheadGreen" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
30
+ <polygon points="0 0, 10 3.5, 0 7" fill="#10b981"/>
31
+ </marker>
32
+ </defs>
33
+
34
+ <!-- HTM Application Container -->
35
+ <rect x="50" y="30" width="700" height="280" rx="12" ry="12"
36
+ fill="none" stroke="#3b82f6" stroke-width="2" stroke-dasharray="5,5"/>
37
+ <text x="400" y="55" text-anchor="middle" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="16" font-weight="600">
38
+ HTM Application
39
+ </text>
40
+
41
+ <!-- Job boxes at top -->
42
+ <rect x="80" y="75" width="180" height="50" rx="8" ry="8" fill="url(#appGradient)"/>
43
+ <text x="170" y="105" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="13" font-weight="500">
44
+ GenerateEmbeddingJob
45
+ </text>
46
+
47
+ <rect x="310" y="75" width="180" height="50" rx="8" ry="8" fill="url(#appGradient)"/>
48
+ <text x="400" y="105" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="13" font-weight="500">
49
+ GenerateTagsJob
50
+ </text>
51
+
52
+ <rect x="540" y="75" width="180" height="50" rx="8" ry="8" fill="url(#appGradient)"/>
53
+ <text x="630" y="105" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="13" font-weight="500">
54
+ Search Methods
55
+ </text>
56
+
57
+ <!-- Arrows from jobs to telemetry -->
58
+ <line x1="170" y1="125" x2="170" y2="155" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
59
+ <line x1="400" y1="125" x2="400" y2="155" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
60
+ <line x1="630" y1="125" x2="630" y2="155" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
61
+
62
+ <!-- Horizontal connector line -->
63
+ <line x1="170" y1="155" x2="630" y2="155" stroke="#94a3b8" stroke-width="2"/>
64
+ <line x1="400" y1="155" x2="400" y2="170" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
65
+
66
+ <!-- HTM::Telemetry box -->
67
+ <rect x="280" y="175" width="240" height="45" rx="8" ry="8" fill="url(#telemetryGradient)"/>
68
+ <text x="400" y="203" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="14" font-weight="600">
69
+ HTM::Telemetry
70
+ </text>
71
+
72
+ <!-- Three branches from telemetry -->
73
+ <line x1="400" y1="220" x2="400" y2="240" stroke="#94a3b8" stroke-width="2"/>
74
+
75
+ <!-- Branch lines -->
76
+ <line x1="150" y1="240" x2="650" y2="240" stroke="#94a3b8" stroke-width="2"/>
77
+ <line x1="150" y1="240" x2="150" y2="255" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
78
+ <line x1="400" y1="240" x2="400" y2="255" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
79
+ <line x1="650" y1="240" x2="650" y2="255" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrowhead)"/>
80
+
81
+ <!-- Three meter options -->
82
+ <rect x="70" y="260" width="160" height="55" rx="8" ry="8" fill="url(#nullGradient)"/>
83
+ <text x="150" y="283" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="12" font-weight="500">
84
+ NullMeter
85
+ </text>
86
+ <text x="150" y="300" text-anchor="middle" fill="#d1d5db" font-family="system-ui, sans-serif" font-size="10">
87
+ (when disabled)
88
+ </text>
89
+
90
+ <rect x="320" y="260" width="160" height="55" rx="8" ry="8" fill="url(#sdkGradient)"/>
91
+ <text x="400" y="283" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="12" font-weight="500">
92
+ OpenTelemetry
93
+ </text>
94
+ <text x="400" y="300" text-anchor="middle" fill="#d1fae5" font-family="system-ui, sans-serif" font-size="10">
95
+ SDK Meter
96
+ </text>
97
+
98
+ <rect x="570" y="260" width="160" height="55" rx="8" ry="8" fill="url(#nullGradient)"/>
99
+ <text x="650" y="283" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="12" font-weight="500">
100
+ NullMeter
101
+ </text>
102
+ <text x="650" y="300" text-anchor="middle" fill="#d1d5db" font-family="system-ui, sans-serif" font-size="10">
103
+ (SDK missing)
104
+ </text>
105
+
106
+ <!-- Arrows to outcomes -->
107
+ <line x1="150" y1="315" x2="150" y2="345" stroke="#6b7280" stroke-width="2" stroke-dasharray="4,4"/>
108
+ <line x1="400" y1="315" x2="400" y2="345" stroke="#10b981" stroke-width="2" marker-end="url(#arrowheadGreen)"/>
109
+ <line x1="650" y1="315" x2="650" y2="345" stroke="#6b7280" stroke-width="2" stroke-dasharray="4,4"/>
110
+
111
+ <!-- Outcome labels -->
112
+ <text x="150" y="365" text-anchor="middle" fill="#9ca3af" font-family="system-ui, sans-serif" font-size="12" font-style="italic">
113
+ No-op
114
+ </text>
115
+
116
+ <text x="400" y="365" text-anchor="middle" fill="#10b981" font-family="system-ui, sans-serif" font-size="12" font-weight="500">
117
+ OTLP Export
118
+ </text>
119
+
120
+ <text x="650" y="365" text-anchor="middle" fill="#9ca3af" font-family="system-ui, sans-serif" font-size="12" font-style="italic">
121
+ No-op
122
+ </text>
123
+
124
+ <!-- Arrow to backend -->
125
+ <line x1="400" y1="380" x2="400" y2="410" stroke="#10b981" stroke-width="2" marker-end="url(#arrowheadGreen)"/>
126
+
127
+ <!-- Backend box -->
128
+ <rect x="250" y="420" width="300" height="60" rx="10" ry="10" fill="url(#backendGradient)"/>
129
+ <text x="400" y="448" text-anchor="middle" fill="#ffffff" font-family="system-ui, sans-serif" font-size="14" font-weight="600">
130
+ Observability Backend
131
+ </text>
132
+ <text x="400" y="468" text-anchor="middle" fill="#fef3c7" font-family="system-ui, sans-serif" font-size="11">
133
+ (Jaeger, Datadog, Prometheus, etc.)
134
+ </text>
135
+
136
+ <!-- Legend -->
137
+ <rect x="600" y="380" width="140" height="80" rx="6" ry="6" fill="#1e293b" fill-opacity="0.7" stroke="#475569" stroke-width="1"/>
138
+ <text x="670" y="398" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10" font-weight="600">
139
+ LEGEND
140
+ </text>
141
+ <line x1="615" y1="410" x2="645" y2="410" stroke="#10b981" stroke-width="2"/>
142
+ <text x="720" y="414" text-anchor="end" fill="#d1d5db" font-family="system-ui, sans-serif" font-size="10">
143
+ Active flow
144
+ </text>
145
+ <line x1="615" y1="430" x2="645" y2="430" stroke="#6b7280" stroke-width="2" stroke-dasharray="4,4"/>
146
+ <text x="720" y="434" text-anchor="end" fill="#d1d5db" font-family="system-ui, sans-serif" font-size="10">
147
+ Disabled path
148
+ </text>
149
+ <circle cx="630" cy="450" r="6" fill="url(#telemetryGradient)"/>
150
+ <text x="720" y="454" text-anchor="end" fill="#d1d5db" font-family="system-ui, sans-serif" font-size="10">
151
+ HTM component
152
+ </text>
153
+ </svg>