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
@@ -166,8 +166,8 @@ Similarity scores indicate how related the memory is to your query:
166
166
  ```ruby
167
167
  # Query about concepts, not specific keywords
168
168
  memories = htm.recall(
169
+ "ways to speed up slow applications",
169
170
  timeframe: "last year",
170
- topic: "ways to speed up slow applications",
171
171
  strategy: :vector
172
172
  )
173
173
 
@@ -183,8 +183,8 @@ memories = htm.recall(
183
183
  ```ruby
184
184
  # Find related concepts even without exact keywords
185
185
  memories = htm.recall(
186
+ "machine learning",
186
187
  timeframe: "all time",
187
- topic: "machine learning",
188
188
  strategy: :vector
189
189
  )
190
190
 
@@ -207,7 +207,7 @@ queries = [
207
207
  ]
208
208
 
209
209
  queries.each do |q|
210
- results = htm.recall(timeframe: "all time", topic: q, strategy: :vector)
210
+ results = htm.recall(q, timeframe: "all time", strategy: :vector)
211
211
  # All queries return similar results!
212
212
  end
213
213
  ```
@@ -217,8 +217,8 @@ end
217
217
  ```ruby
218
218
  # If embeddings support multiple languages
219
219
  memories = htm.recall(
220
+ "base de données", # French: database
220
221
  timeframe: "all time",
221
- topic: "base de données", # French: database
222
222
  strategy: :vector
223
223
  )
224
224
 
@@ -233,8 +233,8 @@ memories = htm.recall(
233
233
  ```ruby
234
234
  # Bad for exact technical terms
235
235
  memories = htm.recall(
236
+ "JWT", # Specific acronym
236
237
  timeframe: "all time",
237
- topic: "JWT", # Specific acronym
238
238
  strategy: :vector
239
239
  )
240
240
 
@@ -247,8 +247,8 @@ memories = htm.recall(
247
247
  ```ruby
248
248
  # Not ideal for names
249
249
  memories = htm.recall(
250
+ "Alice Thompson",
250
251
  timeframe: "all time",
251
- topic: "Alice Thompson",
252
252
  strategy: :vector
253
253
  )
254
254
 
@@ -263,10 +263,11 @@ memories = htm.recall(
263
263
  ```ruby
264
264
  def vector_search_with_threshold(topic, threshold: 0.7)
265
265
  results = htm.recall(
266
+ topic,
266
267
  timeframe: "all time",
267
- topic: topic,
268
268
  strategy: :vector,
269
- limit: 50
269
+ limit: 50,
270
+ raw: true # Get hash with similarity scores
270
271
  )
271
272
 
272
273
  # Filter by threshold
@@ -280,10 +281,10 @@ high_quality = vector_search_with_threshold("database", threshold: 0.8)
280
281
 
281
282
  ```ruby
282
283
  # Vague: Returns less relevant results
283
- htm.recall(topic: "API", strategy: :vector)
284
+ htm.recall("API", strategy: :vector)
284
285
 
285
286
  # Descriptive: Returns more relevant results
286
- htm.recall(topic: "RESTful API design patterns and best practices", strategy: :vector)
287
+ htm.recall("RESTful API design patterns and best practices", strategy: :vector)
287
288
  ```
288
289
 
289
290
  **3. Query Expansion**
@@ -294,8 +295,8 @@ def expanded_vector_search(base_query, related_terms)
294
295
  expanded = "#{base_query} #{related_terms.join(' ')}"
295
296
 
296
297
  htm.recall(
298
+ expanded,
297
299
  timeframe: "all time",
298
- topic: expanded,
299
300
  strategy: :vector,
300
301
  limit: 20
301
302
  )
@@ -331,14 +332,15 @@ User Query: "PostgreSQL indexing"
331
332
 
332
333
  ```ruby
333
334
  memories = htm.recall(
335
+ "PostgreSQL indexing",
334
336
  timeframe: "last month",
335
- topic: "PostgreSQL indexing",
336
337
  strategy: :fulltext,
337
- limit: 10
338
+ limit: 10,
339
+ raw: true # Get hash with rank scores
338
340
  )
339
341
 
340
342
  memories.each do |m|
341
- puts "#{m['value']}"
343
+ puts "#{m['content']}"
342
344
  puts "Rank: #{m['rank']}" # Higher = better match
343
345
  puts
344
346
  end
@@ -351,8 +353,8 @@ end
351
353
  ```ruby
352
354
  # Finding specific technical terms
353
355
  memories = htm.recall(
356
+ "JWT OAuth2 authentication",
354
357
  timeframe: "all time",
355
- topic: "JWT OAuth2 authentication",
356
358
  strategy: :fulltext
357
359
  )
358
360
 
@@ -364,8 +366,8 @@ memories = htm.recall(
364
366
  ```ruby
365
367
  # Finding people, places, products
366
368
  memories = htm.recall(
369
+ "Alice Thompson",
367
370
  timeframe: "all time",
368
- topic: "Alice Thompson",
369
371
  strategy: :fulltext
370
372
  )
371
373
 
@@ -377,8 +379,8 @@ memories = htm.recall(
377
379
  ```ruby
378
380
  # Technical acronyms
379
381
  memories = htm.recall(
382
+ "REST API CRUD SQL",
380
383
  timeframe: "all time",
381
- topic: "REST API CRUD SQL",
382
384
  strategy: :fulltext
383
385
  )
384
386
 
@@ -390,8 +392,8 @@ memories = htm.recall(
390
392
  ```ruby
391
393
  # Finding specific code or commands
392
394
  memories = htm.recall(
395
+ "pg_dump VACUUM",
393
396
  timeframe: "all time",
394
- topic: "pg_dump VACUUM",
395
397
  strategy: :fulltext
396
398
  )
397
399
 
@@ -405,8 +407,8 @@ memories = htm.recall(
405
407
  ```ruby
406
408
  # PostgreSQL supports AND, OR, NOT
407
409
  memories = htm.recall(
410
+ "PostgreSQL AND (indexing OR optimization)",
408
411
  timeframe: "all time",
409
- topic: "PostgreSQL AND (indexing OR optimization)",
410
412
  strategy: :fulltext
411
413
  )
412
414
  ```
@@ -416,8 +418,8 @@ memories = htm.recall(
416
418
  ```ruby
417
419
  # Find exact phrases
418
420
  memories = htm.recall(
421
+ '"database connection pool"', # Exact phrase
419
422
  timeframe: "all time",
420
- topic: '"database connection pool"', # Exact phrase
421
423
  strategy: :fulltext
422
424
  )
423
425
  ```
@@ -429,8 +431,8 @@ memories = htm.recall(
429
431
  # "running" matches "run", "runs", "runner"
430
432
 
431
433
  memories = htm.recall(
434
+ "optimize", # Matches "optimizing", "optimized", etc.
432
435
  timeframe: "all time",
433
- topic: "optimize", # Matches "optimizing", "optimized", etc.
434
436
  strategy: :fulltext
435
437
  )
436
438
  ```
@@ -442,8 +444,8 @@ memories = htm.recall(
442
444
  ```ruby
443
445
  # Doesn't understand meaning
444
446
  memories = htm.recall(
447
+ "database",
445
448
  timeframe: "all time",
446
- topic: "database",
447
449
  strategy: :fulltext
448
450
  )
449
451
 
@@ -456,8 +458,8 @@ memories = htm.recall(
456
458
  ```ruby
457
459
  # Must use exact keywords
458
460
  memories = htm.recall(
461
+ "speed up application",
459
462
  timeframe: "all time",
460
- topic: "speed up application",
461
463
  strategy: :fulltext
462
464
  )
463
465
 
@@ -472,8 +474,8 @@ memories = htm.recall(
472
474
  ```ruby
473
475
  # Include variations and synonyms
474
476
  memories = htm.recall(
477
+ "database PostgreSQL SQL relational",
475
478
  timeframe: "all time",
476
- topic: "database PostgreSQL SQL relational",
477
479
  strategy: :fulltext
478
480
  )
479
481
  ```
@@ -694,15 +696,15 @@ require 'benchmark'
694
696
 
695
697
  Benchmark.bm(15) do |x|
696
698
  x.report("Vector:") do
697
- htm.recall(timeframe: "last month", topic: "database", strategy: :vector)
699
+ htm.recall("database", timeframe: "last month", strategy: :vector)
698
700
  end
699
701
 
700
702
  x.report("Full-text:") do
701
- htm.recall(timeframe: "last month", topic: "database", strategy: :fulltext)
703
+ htm.recall("database", timeframe: "last month", strategy: :fulltext)
702
704
  end
703
705
 
704
706
  x.report("Hybrid:") do
705
- htm.recall(timeframe: "last month", topic: "database", strategy: :hybrid)
707
+ htm.recall("database", timeframe: "last month", strategy: :hybrid)
706
708
  end
707
709
  end
708
710
 
@@ -779,8 +781,8 @@ class SmartSearch
779
781
  strategy = detect_strategy(query)
780
782
 
781
783
  @htm.recall(
784
+ query,
782
785
  timeframe: timeframe,
783
- topic: query,
784
786
  strategy: strategy,
785
787
  limit: 20
786
788
  )
@@ -821,31 +823,34 @@ search.search("performance issues") # → Uses :vector
821
823
 
822
824
  ```ruby
823
825
  def comprehensive_search(query, timeframe: "last month")
824
- # Run all three strategies
826
+ # Run all three strategies with raw: true for hash access
825
827
  vector_results = htm.recall(
828
+ query,
826
829
  timeframe: timeframe,
827
- topic: query,
828
830
  strategy: :vector,
829
- limit: 10
831
+ limit: 10,
832
+ raw: true
830
833
  )
831
834
 
832
835
  fulltext_results = htm.recall(
836
+ query,
833
837
  timeframe: timeframe,
834
- topic: query,
835
838
  strategy: :fulltext,
836
- limit: 10
839
+ limit: 10,
840
+ raw: true
837
841
  )
838
842
 
839
843
  hybrid_results = htm.recall(
844
+ query,
840
845
  timeframe: timeframe,
841
- topic: query,
842
846
  strategy: :hybrid,
843
- limit: 10
847
+ limit: 10,
848
+ raw: true
844
849
  )
845
850
 
846
851
  # Combine and deduplicate
847
852
  all_results = (vector_results + fulltext_results + hybrid_results)
848
- .uniq { |m| m['key'] }
853
+ .uniq { |m| m['id'] }
849
854
 
850
855
  # Sort by best score
851
856
  all_results.sort_by do |m|
@@ -860,8 +865,8 @@ end
860
865
  def search_with_fallback(query, timeframe: "last month")
861
866
  # Try hybrid first
862
867
  results = htm.recall(
868
+ query,
863
869
  timeframe: timeframe,
864
- topic: query,
865
870
  strategy: :hybrid,
866
871
  limit: 10
867
872
  )
@@ -870,8 +875,8 @@ def search_with_fallback(query, timeframe: "last month")
870
875
  if results.empty?
871
876
  warn "No hybrid results, trying vector search..."
872
877
  results = htm.recall(
878
+ query,
873
879
  timeframe: timeframe,
874
- topic: query,
875
880
  strategy: :vector,
876
881
  limit: 10
877
882
  )
@@ -881,8 +886,8 @@ def search_with_fallback(query, timeframe: "last month")
881
886
  if results.empty?
882
887
  warn "No vector results, trying full-text search..."
883
888
  results = htm.recall(
889
+ query,
884
890
  timeframe: timeframe,
885
- topic: query,
886
891
  strategy: :fulltext,
887
892
  limit: 10
888
893
  )
@@ -897,22 +902,19 @@ end
897
902
  ```ruby
898
903
  def search_with_confidence(query)
899
904
  results = htm.recall(
905
+ query,
900
906
  timeframe: "all time",
901
- topic: query,
902
907
  strategy: :hybrid,
903
- limit: 20
908
+ limit: 20,
909
+ raw: true # Need hash access for scoring
904
910
  )
905
911
 
906
912
  # Add confidence scores
907
913
  results.map do |m|
908
914
  similarity = m['similarity'].to_f
909
- importance = m['importance'].to_f
910
915
 
911
916
  # Calculate confidence (0-100)
912
- confidence = (
913
- similarity * 60 + # 60% weight on similarity
914
- (importance / 10.0) * 40 # 40% weight on importance
915
- ).round(2)
917
+ confidence = (similarity * 100).round(2)
916
918
 
917
919
  m.merge('confidence' => confidence)
918
920
  end.sort_by { |m| -m['confidence'] }
@@ -932,7 +934,7 @@ end
932
934
 
933
935
  if vector_results.empty?
934
936
  # Try full-text as fallback
935
- htm.recall(topic: query, strategy: :fulltext)
937
+ htm.recall(query, strategy: :fulltext)
936
938
  end
937
939
  ```
938
940
 
@@ -942,10 +944,11 @@ end
942
944
  # Filter by quality threshold
943
945
  def quality_search(query, min_similarity: 0.7)
944
946
  results = htm.recall(
947
+ query,
945
948
  timeframe: "all time",
946
- topic: query,
947
949
  strategy: :hybrid,
948
- limit: 50
950
+ limit: 50,
951
+ raw: true
949
952
  )
950
953
 
951
954
  results.select { |m| m['similarity'].to_f >= min_similarity }
@@ -960,24 +963,24 @@ require 'htm'
960
963
  htm = HTM.new(robot_name: "Search Demo")
961
964
 
962
965
  # Add test data
963
- htm.add_node("pg_001", "PostgreSQL indexing tutorial", type: :code, importance: 7.0)
964
- htm.add_node("perf_001", "Performance optimization guide", type: :fact, importance: 8.0)
965
- htm.add_node("cache_001", "Caching strategies for speed", type: :decision, importance: 9.0)
966
+ htm.remember("PostgreSQL indexing tutorial", tags: ["code:sql"], metadata: { category: "code" })
967
+ htm.remember("Performance optimization guide", tags: ["performance"], metadata: { category: "fact" })
968
+ htm.remember("Caching strategies for speed", tags: ["caching"], metadata: { category: "decision" })
966
969
 
967
970
  # Compare strategies
968
971
  query = "how to make database faster"
969
972
 
970
973
  puts "=== Vector Search (Semantic) ==="
971
- vector = htm.recall(timeframe: "all time", topic: query, strategy: :vector)
972
- vector.each { |m| puts "- #{m['value']} (#{m['similarity']})" }
974
+ vector = htm.recall(query, timeframe: "all time", strategy: :vector, raw: true)
975
+ vector.each { |m| puts "- #{m['content']} (#{m['similarity']})" }
973
976
 
974
977
  puts "\n=== Full-text Search (Keywords) ==="
975
- fulltext = htm.recall(timeframe: "all time", topic: query, strategy: :fulltext)
976
- fulltext.each { |m| puts "- #{m['value']} (#{m['rank']})" }
978
+ fulltext = htm.recall(query, timeframe: "all time", strategy: :fulltext, raw: true)
979
+ fulltext.each { |m| puts "- #{m['content']} (#{m['rank']})" }
977
980
 
978
981
  puts "\n=== Hybrid Search (Combined) ==="
979
- hybrid = htm.recall(timeframe: "all time", topic: query, strategy: :hybrid)
980
- hybrid.each { |m| puts "- #{m['value']} (#{m['similarity']})" }
982
+ hybrid = htm.recall(query, timeframe: "all time", strategy: :hybrid, raw: true)
983
+ hybrid.each { |m| puts "- #{m['content']} (#{m['similarity']})" }
981
984
  ```
982
985
 
983
986
  ## Next Steps