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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +269 -79
  4. data/db/migrate/00003_create_file_sources.rb +5 -0
  5. data/db/migrate/00004_create_nodes.rb +17 -0
  6. data/db/migrate/00005_create_tags.rb +7 -0
  7. data/db/migrate/00006_create_node_tags.rb +2 -0
  8. data/db/migrate/00007_create_robot_nodes.rb +7 -0
  9. data/db/schema.sql +41 -29
  10. data/docs/api/yard/HTM/Configuration.md +54 -0
  11. data/docs/api/yard/HTM/Database.md +13 -10
  12. data/docs/api/yard/HTM/EmbeddingService.md +5 -1
  13. data/docs/api/yard/HTM/LongTermMemory.md +18 -277
  14. data/docs/api/yard/HTM/PropositionError.md +18 -0
  15. data/docs/api/yard/HTM/PropositionService.md +66 -0
  16. data/docs/api/yard/HTM/QueryCache.md +88 -0
  17. data/docs/api/yard/HTM/RobotGroup.md +481 -0
  18. data/docs/api/yard/HTM/SqlBuilder.md +108 -0
  19. data/docs/api/yard/HTM/TagService.md +4 -0
  20. data/docs/api/yard/HTM/Telemetry/NullInstrument.md +13 -0
  21. data/docs/api/yard/HTM/Telemetry/NullMeter.md +15 -0
  22. data/docs/api/yard/HTM/Telemetry.md +109 -0
  23. data/docs/api/yard/HTM/WorkingMemoryChannel.md +176 -0
  24. data/docs/api/yard/HTM.md +11 -23
  25. data/docs/api/yard/index.csv +102 -25
  26. data/docs/api/yard-reference.md +8 -0
  27. data/docs/assets/images/multi-provider-failover.svg +51 -0
  28. data/docs/assets/images/robot-group-architecture.svg +65 -0
  29. data/docs/database/README.md +3 -3
  30. data/docs/database/public.file_sources.svg +29 -21
  31. data/docs/database/public.node_tags.md +2 -0
  32. data/docs/database/public.node_tags.svg +53 -41
  33. data/docs/database/public.nodes.md +2 -0
  34. data/docs/database/public.nodes.svg +52 -40
  35. data/docs/database/public.robot_nodes.md +2 -0
  36. data/docs/database/public.robot_nodes.svg +30 -22
  37. data/docs/database/public.robots.svg +16 -12
  38. data/docs/database/public.tags.md +3 -0
  39. data/docs/database/public.tags.svg +41 -33
  40. data/docs/database/schema.json +66 -0
  41. data/docs/database/schema.svg +60 -48
  42. data/docs/development/index.md +13 -0
  43. data/docs/development/rake-tasks.md +1068 -0
  44. data/docs/getting-started/quick-start.md +144 -155
  45. data/docs/guides/adding-memories.md +2 -3
  46. data/docs/guides/context-assembly.md +185 -184
  47. data/docs/guides/getting-started.md +154 -148
  48. data/docs/guides/index.md +7 -0
  49. data/docs/guides/long-term-memory.md +60 -92
  50. data/docs/guides/mcp-server.md +617 -0
  51. data/docs/guides/multi-robot.md +249 -345
  52. data/docs/guides/recalling-memories.md +153 -163
  53. data/docs/guides/robot-groups.md +604 -0
  54. data/docs/guides/search-strategies.md +61 -58
  55. data/docs/guides/working-memory.md +103 -136
  56. data/docs/index.md +30 -26
  57. data/examples/robot_groups/robot_worker.rb +1 -2
  58. data/examples/robot_groups/same_process.rb +1 -4
  59. data/lib/htm/robot_group.rb +721 -0
  60. data/lib/htm/version.rb +1 -1
  61. data/lib/htm/working_memory_channel.rb +250 -0
  62. data/lib/htm.rb +2 -0
  63. data/mkdocs.yml +2 -0
  64. metadata +18 -9
  65. data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +0 -12
  66. data/db/migrate/00010_add_soft_delete_to_associations.rb +0 -29
  67. data/db/migrate/00011_add_performance_indexes.rb +0 -21
  68. data/db/migrate/00012_add_tags_trigram_index.rb +0 -18
  69. data/db/migrate/00013_enable_lz4_compression.rb +0 -43
  70. data/examples/robot_groups/lib/robot_group.rb +0 -419
  71. data/examples/robot_groups/lib/working_memory_channel.rb +0 -140
@@ -4,20 +4,20 @@ This guide covers HTM's powerful RAG-based retrieval system for finding relevant
4
4
 
5
5
  ## Basic Recall
6
6
 
7
- The `recall` method searches long-term memory using timeframe and topic:
7
+ The `recall` method searches long-term memory using topic and optional filters:
8
8
 
9
9
  ```ruby
10
10
  memories = htm.recall(
11
+ "database design", # Topic (first positional argument)
11
12
  timeframe: "last week", # Time range to search
12
- topic: "database design", # What to search for
13
13
  limit: 20, # Max results (default: 20)
14
- strategy: :vector # Search strategy (default: :vector)
14
+ strategy: :vector, # Search strategy (default: :fulltext)
15
+ raw: true # Return full node hashes
15
16
  )
16
17
 
17
18
  memories.each do |memory|
18
- puts memory['value']
19
+ puts memory['content']
19
20
  puts "Similarity: #{memory['similarity']}"
20
- puts "Importance: #{memory['importance']}"
21
21
  puts "Created: #{memory['created_at']}"
22
22
  puts
23
23
  end
@@ -148,23 +148,23 @@ HTM supports both natural language timeframes and explicit ranges.
148
148
 
149
149
  ```ruby
150
150
  # Last 24 hours (default if unparseable)
151
- htm.recall(timeframe: "today", topic: "...")
151
+ htm.recall("...", timeframe: "today")
152
152
 
153
153
  # Yesterday
154
- htm.recall(timeframe: "yesterday", topic: "...")
154
+ htm.recall("...", timeframe: "yesterday")
155
155
 
156
156
  # Last week
157
- htm.recall(timeframe: "last week", topic: "...")
157
+ htm.recall("...", timeframe: "last week")
158
158
 
159
159
  # Last N days
160
- htm.recall(timeframe: "last 7 days", topic: "...")
161
- htm.recall(timeframe: "last 30 days", topic: "...")
160
+ htm.recall("...", timeframe: "last 7 days")
161
+ htm.recall("...", timeframe: "last 30 days")
162
162
 
163
163
  # This month
164
- htm.recall(timeframe: "this month", topic: "...")
164
+ htm.recall("...", timeframe: "this month")
165
165
 
166
166
  # Last month
167
- htm.recall(timeframe: "last month", topic: "...")
167
+ htm.recall("...", timeframe: "last month")
168
168
  ```
169
169
 
170
170
  ### Explicit Time Ranges
@@ -176,27 +176,24 @@ For precise control, use Ruby time ranges:
176
176
  start_date = Time.new(2024, 1, 1)
177
177
  end_date = Time.new(2024, 12, 31)
178
178
  htm.recall(
179
- timeframe: start_date..end_date,
180
- topic: "annual report"
179
+ "annual report",
180
+ timeframe: start_date..end_date
181
181
  )
182
182
 
183
183
  # Last 24 hours precisely
184
184
  htm.recall(
185
- timeframe: (Time.now - 24*3600)..Time.now,
186
- topic: "errors"
185
+ "errors",
186
+ timeframe: (Time.now - 24*3600)..Time.now
187
187
  )
188
188
 
189
- # All time
190
- htm.recall(
191
- timeframe: Time.at(0)..Time.now,
192
- topic: "architecture decisions"
193
- )
189
+ # No time filter (all time)
190
+ htm.recall("architecture decisions")
194
191
 
195
192
  # Relative to current time
196
193
  three_days_ago = Time.now - (3 * 24 * 3600)
197
194
  htm.recall(
198
- timeframe: three_days_ago..Time.now,
199
- topic: "bug fixes"
195
+ "bug fixes",
196
+ timeframe: three_days_ago..Time.now
200
197
  )
201
198
  ```
202
199
 
@@ -215,8 +212,8 @@ Vector search uses embeddings to find semantically similar memories.
215
212
 
216
213
  ```ruby
217
214
  memories = htm.recall(
215
+ "improving application performance",
218
216
  timeframe: "last month",
219
- topic: "improving application performance",
220
217
  strategy: :vector,
221
218
  limit: 10
222
219
  )
@@ -240,8 +237,8 @@ memories = htm.recall(
240
237
  ```ruby
241
238
  # Will find memories about databases, even without the word "PostgreSQL"
242
239
  memories = htm.recall(
240
+ "data persistence strategies",
243
241
  timeframe: "last year",
244
- topic: "data persistence strategies",
245
242
  strategy: :vector
246
243
  )
247
244
 
@@ -257,8 +254,8 @@ Full-text search uses PostgreSQL's text search for exact keyword matching.
257
254
 
258
255
  ```ruby
259
256
  memories = htm.recall(
257
+ "PostgreSQL indexing",
260
258
  timeframe: "last week",
261
- topic: "PostgreSQL indexing",
262
259
  strategy: :fulltext,
263
260
  limit: 10
264
261
  )
@@ -282,8 +279,7 @@ memories = htm.recall(
282
279
  ```ruby
283
280
  # Will only find memories containing "JWT"
284
281
  memories = htm.recall(
285
- timeframe: "all time",
286
- topic: "JWT authentication",
282
+ "JWT authentication",
287
283
  strategy: :fulltext
288
284
  )
289
285
 
@@ -300,8 +296,8 @@ Hybrid search combines full-text and vector search for optimal results.
300
296
 
301
297
  ```ruby
302
298
  memories = htm.recall(
299
+ "database performance issues",
303
300
  timeframe: "last month",
304
- topic: "database performance issues",
305
301
  strategy: :hybrid,
306
302
  limit: 10
307
303
  )
@@ -325,8 +321,8 @@ memories = htm.recall(
325
321
  ```ruby
326
322
  # Combines keyword matching with semantic understanding
327
323
  memories = htm.recall(
324
+ "scaling our PostgreSQL database",
328
325
  timeframe: "last quarter",
329
- topic: "scaling our PostgreSQL database",
330
326
  strategy: :hybrid
331
327
  )
332
328
 
@@ -351,43 +347,43 @@ memories = htm.recall(
351
347
 
352
348
  ```ruby
353
349
  # Vague: Returns too many irrelevant results
354
- htm.recall(timeframe: "last year", topic: "data")
350
+ htm.recall("data", timeframe: "last year")
355
351
 
356
352
  # Specific: Returns targeted results
357
- htm.recall(timeframe: "last year", topic: "PostgreSQL query optimization")
353
+ htm.recall("PostgreSQL query optimization", timeframe: "last year")
358
354
  ```
359
355
 
360
356
  ### 2. Use Appropriate Timeframes
361
357
 
362
358
  ```ruby
363
359
  # Too wide: Includes outdated information
364
- htm.recall(timeframe: "last 5 years", topic: "current project status")
360
+ htm.recall("current project status", timeframe: "last 5 years")
365
361
 
366
362
  # Right size: Recent context
367
- htm.recall(timeframe: "last week", topic: "current project status")
363
+ htm.recall("current project status", timeframe: "last week")
368
364
  ```
369
365
 
370
366
  ### 3. Adjust Limit Based on Need
371
367
 
372
368
  ```ruby
373
369
  # Few results: Quick overview
374
- htm.recall(timeframe: "last month", topic: "errors", limit: 5)
370
+ htm.recall("errors", timeframe: "last month", limit: 5)
375
371
 
376
372
  # Many results: Comprehensive search
377
- htm.recall(timeframe: "last year", topic: "architecture decisions", limit: 50)
373
+ htm.recall("architecture decisions", timeframe: "last year", limit: 50)
378
374
  ```
379
375
 
380
376
  ### 4. Try Different Strategies
381
377
 
382
378
  ```ruby
383
379
  # Start with hybrid (best all-around)
384
- results = htm.recall(topic: "authentication", strategy: :hybrid)
380
+ results = htm.recall("authentication", strategy: :hybrid)
385
381
 
386
382
  # If too many results, try full-text (more precise)
387
- results = htm.recall(topic: "JWT authentication", strategy: :fulltext)
383
+ results = htm.recall("JWT authentication", strategy: :fulltext)
388
384
 
389
385
  # If no results, try vector (more flexible)
390
- results = htm.recall(topic: "user validation methods", strategy: :vector)
386
+ results = htm.recall("user validation methods", strategy: :vector)
391
387
  ```
392
388
 
393
389
  ## Filtering by Metadata
@@ -397,21 +393,21 @@ HTM supports metadata filtering directly in the `recall()` method. This is more
397
393
  ```ruby
398
394
  # Filter by single metadata field
399
395
  memories = htm.recall(
400
- topic: "user settings",
396
+ "user settings",
401
397
  metadata: { category: "preference" }
402
398
  )
403
399
  # => Returns only nodes with metadata containing { category: "preference" }
404
400
 
405
401
  # Filter by multiple metadata fields
406
402
  memories = htm.recall(
407
- topic: "API configuration",
403
+ "API configuration",
408
404
  metadata: { environment: "production", version: 2 }
409
405
  )
410
406
  # => Returns nodes with BOTH environment: "production" AND version: 2
411
407
 
412
408
  # Combine with other filters
413
409
  memories = htm.recall(
414
- topic: "database changes",
410
+ "database changes",
415
411
  timeframe: "last month",
416
412
  strategy: :hybrid,
417
413
  metadata: { breaking_change: true },
@@ -429,19 +425,17 @@ Metadata filtering uses PostgreSQL's JSONB containment operator (`@>`), which me
429
425
  While `recall` handles timeframes, topics, and metadata, you can filter results further:
430
426
 
431
427
  ```ruby
432
- # Recall memories
428
+ # Recall memories with full data
433
429
  memories = htm.recall(
430
+ "database",
434
431
  timeframe: "last month",
435
- topic: "database",
436
432
  strategy: :hybrid,
437
- limit: 50
433
+ limit: 50,
434
+ raw: true # Get full node hashes
438
435
  )
439
436
 
440
- # Filter by type
441
- decisions = memories.select { |m| m['type'] == 'decision' }
442
-
443
- # Filter by importance
444
- critical = memories.select { |m| m['importance'].to_f >= 8.0 }
437
+ # Filter by metadata
438
+ high_priority = memories.select { |m| m['metadata']&.dig('priority') == 'high' }
445
439
 
446
440
  # Filter by robot
447
441
  my_memories = memories.select { |m| m['robot_id'] == htm.robot_id }
@@ -459,26 +453,28 @@ end
459
453
  Search for multiple related topics:
460
454
 
461
455
  ```ruby
462
- def search_multiple_topics(timeframe, topics, strategy: :hybrid, limit: 10)
456
+ def search_multiple_topics(htm, timeframe, topics, strategy: :hybrid, limit: 10)
463
457
  results = []
464
458
 
465
459
  topics.each do |topic|
466
460
  results.concat(
467
461
  htm.recall(
462
+ topic,
468
463
  timeframe: timeframe,
469
- topic: topic,
470
464
  strategy: strategy,
471
- limit: limit
465
+ limit: limit,
466
+ raw: true
472
467
  )
473
468
  )
474
469
  end
475
470
 
476
- # Remove duplicates by key
477
- results.uniq { |m| m['key'] }
471
+ # Remove duplicates by id
472
+ results.uniq { |m| m['id'] }
478
473
  end
479
474
 
480
475
  # Usage
481
476
  memories = search_multiple_topics(
477
+ htm,
482
478
  "last month",
483
479
  ["database optimization", "query performance", "indexing strategies"]
484
480
  )
@@ -491,22 +487,23 @@ Start broad, then narrow:
491
487
  ```ruby
492
488
  # First pass: Broad search
493
489
  broad_results = htm.recall(
490
+ "architecture",
494
491
  timeframe: "last year",
495
- topic: "architecture",
496
492
  strategy: :vector,
497
- limit: 100
493
+ limit: 100,
494
+ raw: true
498
495
  )
499
496
 
500
497
  # Analyze results, refine query
501
498
  relevant_terms = broad_results
502
499
  .select { |m| m['similarity'].to_f > 0.7 }
503
- .flat_map { |m| m['tags'] }
500
+ .map { |m| m['content'].split.first(3).join(' ') }
504
501
  .uniq
505
502
 
506
503
  # Second pass: Refined search
507
504
  refined_results = htm.recall(
505
+ "architecture #{relevant_terms.first}",
508
506
  timeframe: "last year",
509
- topic: "architecture #{relevant_terms.join(' ')}",
510
507
  strategy: :hybrid,
511
508
  limit: 20
512
509
  )
@@ -517,12 +514,13 @@ refined_results = htm.recall(
517
514
  Only keep high-quality matches:
518
515
 
519
516
  ```ruby
520
- def recall_with_threshold(timeframe:, topic:, threshold: 0.7, strategy: :vector)
517
+ def recall_with_threshold(htm, topic, timeframe: nil, threshold: 0.7, strategy: :vector)
521
518
  results = htm.recall(
519
+ topic,
522
520
  timeframe: timeframe,
523
- topic: topic,
524
521
  strategy: strategy,
525
- limit: 50 # Get more candidates
522
+ limit: 50, # Get more candidates
523
+ raw: true
526
524
  )
527
525
 
528
526
  # Filter by similarity threshold
@@ -537,8 +535,9 @@ end
537
535
 
538
536
  # Usage
539
537
  high_quality = recall_with_threshold(
538
+ htm,
539
+ "performance optimization",
540
540
  timeframe: "last month",
541
- topic: "performance optimization",
542
541
  threshold: 0.8
543
542
  )
544
543
  ```
@@ -548,12 +547,13 @@ high_quality = recall_with_threshold(
548
547
  Weight results by recency:
549
548
 
550
549
  ```ruby
551
- def recall_time_weighted(timeframe:, topic:, recency_weight: 0.3)
550
+ def recall_time_weighted(htm, topic, timeframe: nil, recency_weight: 0.3)
552
551
  memories = htm.recall(
552
+ topic,
553
553
  timeframe: timeframe,
554
- topic: topic,
555
554
  strategy: :hybrid,
556
- limit: 50
555
+ limit: 50,
556
+ raw: true
557
557
  )
558
558
 
559
559
  # Calculate time-weighted score
@@ -593,14 +593,14 @@ class ContextualRecall
593
593
  @current_context << { key: key, value: value }
594
594
  end
595
595
 
596
- def recall(timeframe:, topic:, strategy: :hybrid)
596
+ def recall(topic, timeframe: nil, strategy: :hybrid)
597
597
  # Enhance topic with current context
598
598
  context_terms = @current_context.map { |c| c[:value] }.join(" ")
599
599
  enhanced_topic = "#{topic} #{context_terms}"
600
600
 
601
601
  @htm.recall(
602
+ enhanced_topic,
602
603
  timeframe: timeframe,
603
- topic: enhanced_topic,
604
604
  strategy: strategy,
605
605
  limit: 20
606
606
  )
@@ -614,71 +614,73 @@ recall.add_context("focus", "checkout flow")
614
614
 
615
615
  # Search includes context automatically
616
616
  results = recall.recall(
617
- timeframe: "last month",
618
- topic: "payment processing"
617
+ "payment processing",
618
+ timeframe: "last month"
619
619
  )
620
620
  ```
621
621
 
622
- ## Retrieving Specific Memories
622
+ ## Looking Up Specific Memories
623
623
 
624
- For known keys, use `retrieve` instead of `recall`:
624
+ For known node IDs, access the node directly via the model:
625
625
 
626
626
  ```ruby
627
- # Retrieve by exact key
628
- memory = htm.retrieve("decision_database")
627
+ # Look up by node ID
628
+ node = HTM::Models::Node.find_by(id: node_id)
629
629
 
630
- if memory
631
- puts memory['value']
632
- puts "Type: #{memory['type']}"
633
- puts "Created: #{memory['created_at']}"
630
+ if node
631
+ puts node.content
632
+ puts "Tags: #{node.tags.pluck(:name).join(', ')}"
633
+ puts "Created: #{node.created_at}"
634
634
  else
635
635
  puts "Memory not found"
636
636
  end
637
637
  ```
638
638
 
639
639
  !!! note
640
- `retrieve` is faster than `recall` because it doesn't require embedding generation or similarity calculation.
640
+ Direct model access is faster than `recall` because it doesn't require embedding generation or similarity calculation.
641
641
 
642
642
  ## Working with Search Results
643
643
 
644
644
  ### Result Structure
645
645
 
646
- Each memory returned by `recall` has these fields:
646
+ When using `raw: true`, each memory returned by `recall` has these fields:
647
647
 
648
648
  ```ruby
649
649
  memory = {
650
650
  'id' => 123, # Database ID
651
- 'key' => "decision_001", # Unique key
652
- 'value' => "Decision text...", # Content
653
- 'type' => "decision", # Memory type
654
- 'category' => "architecture", # Category (if set)
655
- 'importance' => 9.0, # Importance score
651
+ 'content' => "Decision text...", # The memory content
656
652
  'created_at' => "2024-01-15 10:30:00", # Timestamp
657
- 'robot_id' => "uuid...", # Which robot added it
658
653
  'token_count' => 150, # Token count
659
- 'metadata' => { 'priority' => 'high', 'version' => 2 }, # JSONB metadata
654
+ 'metadata' => { 'priority' => 'high' }, # JSONB metadata
660
655
  'similarity' => 0.85 # Similarity score (vector/hybrid)
661
656
  # or 'rank' for fulltext
662
657
  }
663
658
  ```
664
659
 
660
+ When using `raw: false` (default), `recall` returns just the content strings:
661
+
662
+ ```ruby
663
+ memories = htm.recall("database")
664
+ # => ["PostgreSQL is great...", "Use connection pooling...", ...]
665
+ ```
666
+
665
667
  ### Processing Results
666
668
 
667
669
  ```ruby
668
- memories = htm.recall(timeframe: "last month", topic: "errors")
670
+ memories = htm.recall("errors", timeframe: "last month", raw: true)
669
671
 
670
- # Sort by importance
671
- by_importance = memories.sort_by { |m| -m['importance'].to_f }
672
+ # Sort by similarity
673
+ by_similarity = memories.sort_by { |m| -m['similarity'].to_f }
672
674
 
673
- # Group by type
674
- by_type = memories.group_by { |m| m['type'] }
675
+ # Group by metadata category
676
+ by_category = memories.group_by { |m| m['metadata']&.dig('category') }
675
677
 
676
678
  # Extract just the content
677
- content = memories.map { |m| m['value'] }
679
+ content = memories.map { |m| m['content'] }
678
680
 
679
681
  # Create summary
680
682
  summary = memories.map do |m|
681
- "[#{m['type']}] #{m['value'][0..100]}... (#{m['importance']})"
683
+ "#{m['content'][0..100]}... (sim: #{m['similarity']})"
682
684
  end.join("\n\n")
683
685
  ```
684
686
 
@@ -691,15 +693,16 @@ Find recent errors and their solutions:
691
693
  ```ruby
692
694
  # Find recent errors
693
695
  errors = htm.recall(
696
+ "error exception failure",
694
697
  timeframe: "last 7 days",
695
- topic: "error exception failure",
696
698
  strategy: :fulltext,
697
- limit: 20
699
+ limit: 20,
700
+ raw: true
698
701
  )
699
702
 
700
- # Group by error type
703
+ # Group by error pattern
701
704
  error_types = errors
702
- .map { |e| e['value'][/Error: (.+?)\\n/, 1] }
705
+ .map { |e| e['content'][/Error: (.+?)$/, 1] }
703
706
  .compact
704
707
  .tally
705
708
 
@@ -714,20 +717,21 @@ end
714
717
  Track decision evolution:
715
718
 
716
719
  ```ruby
717
- # Get all decisions about a topic
720
+ # Get all decisions about a topic (filter by metadata)
718
721
  decisions = htm.recall(
719
- timeframe: Time.at(0)..Time.now, # All time
720
- topic: "authentication",
722
+ "authentication",
721
723
  strategy: :hybrid,
722
- limit: 50
723
- ).select { |m| m['type'] == 'decision' }
724
+ limit: 50,
725
+ metadata: { category: "decision" },
726
+ raw: true
727
+ )
724
728
 
725
729
  # Sort chronologically
726
730
  timeline = decisions.sort_by { |d| d['created_at'] }
727
731
 
728
732
  puts "Decision timeline:"
729
733
  timeline.each do |decision|
730
- puts "#{decision['created_at']}: #{decision['value'][0..100]}..."
734
+ puts "#{decision['created_at']}: #{decision['content'][0..100]}..."
731
735
  end
732
736
  ```
733
737
 
@@ -736,34 +740,24 @@ end
736
740
  Gather all knowledge about a topic:
737
741
 
738
742
  ```ruby
739
- def gather_knowledge(topic)
740
- # Gather different types of memories
741
- facts = htm.recall(
742
- timeframe: "all time",
743
- topic: topic,
744
- strategy: :hybrid
745
- ).select { |m| m['type'] == 'fact' }
746
-
747
- decisions = htm.recall(
748
- timeframe: "all time",
749
- topic: topic,
750
- strategy: :hybrid
751
- ).select { |m| m['type'] == 'decision' }
752
-
753
- code = htm.recall(
754
- timeframe: "all time",
755
- topic: topic,
756
- strategy: :hybrid
757
- ).select { |m| m['type'] == 'code' }
743
+ def gather_knowledge(htm, topic)
744
+ # Gather all memories about a topic
745
+ all_memories = htm.recall(
746
+ topic,
747
+ strategy: :hybrid,
748
+ limit: 100,
749
+ raw: true
750
+ )
758
751
 
752
+ # Group by metadata category
759
753
  {
760
- facts: facts,
761
- decisions: decisions,
762
- code_examples: code
754
+ facts: all_memories.select { |m| m['metadata']&.dig('category') == 'fact' },
755
+ decisions: all_memories.select { |m| m['metadata']&.dig('category') == 'decision' },
756
+ code_examples: all_memories.select { |m| m['metadata']&.dig('category') == 'code' }
763
757
  }
764
758
  end
765
759
 
766
- knowledge = gather_knowledge("PostgreSQL")
760
+ knowledge = gather_knowledge(htm, "PostgreSQL")
767
761
  ```
768
762
 
769
763
  ### Use Case 4: Conversation Context
@@ -771,16 +765,16 @@ knowledge = gather_knowledge("PostgreSQL")
771
765
  Recall recent conversation:
772
766
 
773
767
  ```ruby
774
- def get_conversation_context(session_id, turns: 5)
775
- # Get recent conversation turns
768
+ def get_conversation_context(htm, session_id, turns: 5)
769
+ # Get recent conversation turns by tag
776
770
  htm.recall(
771
+ "session:#{session_id}",
777
772
  timeframe: "last 24 hours",
778
- topic: "session_#{session_id}",
779
773
  strategy: :fulltext,
780
- limit: turns * 2 # user + assistant messages
781
- ).select { |m| m['type'] == 'context' }
782
- .sort_by { |m| m['created_at'] }
783
- .last(turns * 2)
774
+ limit: turns * 2, # user + assistant messages
775
+ raw: true
776
+ ).sort_by { |m| m['created_at'] }
777
+ .last(turns * 2)
784
778
  end
785
779
  ```
786
780
 
@@ -835,23 +829,23 @@ end
835
829
  ### No Results
836
830
 
837
831
  ```ruby
838
- results = htm.recall(timeframe: "last week", topic: "xyz")
832
+ results = htm.recall("xyz", timeframe: "last week")
839
833
 
840
834
  if results.empty?
841
835
  # Try wider timeframe
842
- results = htm.recall(timeframe: "last month", topic: "xyz")
836
+ results = htm.recall("xyz", timeframe: "last month")
843
837
 
844
838
  # Try different strategy
845
839
  results = htm.recall(
840
+ "xyz",
846
841
  timeframe: "last month",
847
- topic: "xyz",
848
842
  strategy: :vector # More flexible
849
843
  )
850
844
 
851
845
  # Try related terms
852
846
  results = htm.recall(
847
+ "xyz related similar",
853
848
  timeframe: "last month",
854
- topic: "xyz related similar",
855
849
  strategy: :vector
856
850
  )
857
851
  end
@@ -877,11 +871,11 @@ If vector search fails:
877
871
 
878
872
  ```ruby
879
873
  begin
880
- results = htm.recall(topic: "...", strategy: :vector)
874
+ results = htm.recall("...", strategy: :vector)
881
875
  rescue => e
882
876
  warn "Vector search failed: #{e.message}"
883
877
  warn "Falling back to full-text search"
884
- results = htm.recall(topic: "...", strategy: :fulltext)
878
+ results = htm.recall("...", strategy: :fulltext)
885
879
  end
886
880
  ```
887
881
 
@@ -899,33 +893,29 @@ require 'htm'
899
893
  htm = HTM.new(robot_name: "Search Demo")
900
894
 
901
895
  # Add test memories
902
- htm.add_node(
903
- "decision_db",
896
+ htm.remember(
904
897
  "Chose PostgreSQL for its reliability and ACID compliance",
905
- type: :decision,
906
- importance: 9.0,
907
- tags: ["database", "postgresql", "architecture"]
898
+ tags: ["database:postgresql", "architecture:decisions"],
899
+ metadata: { category: "decision" }
908
900
  )
909
901
 
910
- htm.add_node(
911
- "code_connection",
902
+ htm.remember(
912
903
  "conn = PG.connect(dbname: 'mydb')",
913
- type: :code,
914
- importance: 6.0,
915
- tags: ["postgresql", "ruby", "connection"]
904
+ tags: ["database:postgresql", "ruby:patterns"],
905
+ metadata: { category: "code" }
916
906
  )
917
907
 
918
908
  # Vector search: Semantic understanding
919
909
  puts "=== Vector Search ==="
920
910
  vector_results = htm.recall(
921
- timeframe: "all time",
922
- topic: "data persistence strategies",
911
+ "data persistence strategies",
923
912
  strategy: :vector,
924
- limit: 10
913
+ limit: 10,
914
+ raw: true
925
915
  )
926
916
 
927
917
  vector_results.each do |m|
928
- puts "#{m['value'][0..80]}..."
918
+ puts "#{m['content'][0..80]}..."
929
919
  puts " Similarity: #{m['similarity']}"
930
920
  puts
931
921
  end
@@ -933,14 +923,14 @@ end
933
923
  # Full-text search: Exact keywords
934
924
  puts "\n=== Full-text Search ==="
935
925
  fulltext_results = htm.recall(
936
- timeframe: "all time",
937
- topic: "PostgreSQL",
926
+ "PostgreSQL",
938
927
  strategy: :fulltext,
939
- limit: 10
928
+ limit: 10,
929
+ raw: true
940
930
  )
941
931
 
942
932
  fulltext_results.each do |m|
943
- puts "#{m['value'][0..80]}..."
933
+ puts "#{m['content'][0..80]}..."
944
934
  puts " Rank: #{m['rank']}"
945
935
  puts
946
936
  end
@@ -948,15 +938,15 @@ end
948
938
  # Hybrid search: Best of both
949
939
  puts "\n=== Hybrid Search ==="
950
940
  hybrid_results = htm.recall(
951
- timeframe: "all time",
952
- topic: "database connection setup",
941
+ "database connection setup",
953
942
  strategy: :hybrid,
954
- limit: 10
943
+ limit: 10,
944
+ raw: true
955
945
  )
956
946
 
957
947
  hybrid_results.each do |m|
958
- puts "[#{m['type']}] #{m['value'][0..80]}..."
959
- puts " Importance: #{m['importance']}, Similarity: #{m['similarity']}"
948
+ puts "#{m['content'][0..80]}..."
949
+ puts " Similarity: #{m['similarity']}"
960
950
  puts
961
951
  end
962
952
  ```