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
|
@@ -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",
|
|
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(
|
|
284
|
+
htm.recall("API", strategy: :vector)
|
|
284
285
|
|
|
285
286
|
# Descriptive: Returns more relevant results
|
|
286
|
-
htm.recall(
|
|
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['
|
|
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",
|
|
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",
|
|
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",
|
|
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['
|
|
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(
|
|
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.
|
|
964
|
-
htm.
|
|
965
|
-
htm.
|
|
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",
|
|
972
|
-
vector.each { |m| puts "- #{m['
|
|
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",
|
|
976
|
-
fulltext.each { |m| puts "- #{m['
|
|
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",
|
|
980
|
-
hybrid.each { |m| puts "- #{m['
|
|
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
|