htm 0.0.30 → 0.0.32

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/.irbrc +2 -3
  3. data/.rubocop.yml +184 -0
  4. data/CHANGELOG.md +46 -0
  5. data/README.md +2 -0
  6. data/Rakefile +93 -12
  7. data/db/migrate/00008_create_node_relationships.rb +54 -0
  8. data/db/migrate/00009_fix_node_relationships_column_types.rb +17 -0
  9. data/db/schema.sql +124 -1
  10. data/docs/api/database.md +35 -57
  11. data/docs/api/embedding-service.md +1 -1
  12. data/docs/api/index.md +26 -15
  13. data/docs/api/working-memory.md +8 -8
  14. data/docs/architecture/index.md +5 -7
  15. data/docs/architecture/overview.md +5 -8
  16. data/docs/assets/images/htm-architecture-overview.svg +1 -1
  17. data/docs/assets/images/htm-context-assembly-flow.svg +2 -2
  18. data/docs/assets/images/htm-layered-architecture.svg +3 -3
  19. data/docs/assets/images/two-tier-memory-architecture.svg +1 -1
  20. data/docs/database/README.md +1 -0
  21. data/docs/database_rake_tasks.md +20 -28
  22. data/docs/development/contributing.md +5 -5
  23. data/docs/development/index.md +4 -7
  24. data/docs/development/schema.md +71 -1
  25. data/docs/development/setup.md +40 -82
  26. data/docs/development/testing.md +1 -1
  27. data/docs/examples/file-loading.md +4 -4
  28. data/docs/examples/mcp-client.md +1 -1
  29. data/docs/getting-started/quick-start.md +4 -4
  30. data/docs/guides/adding-memories.md +14 -1
  31. data/docs/guides/configuration.md +5 -5
  32. data/docs/guides/context-assembly.md +4 -4
  33. data/docs/guides/file-loading.md +12 -12
  34. data/docs/guides/getting-started.md +2 -2
  35. data/docs/guides/long-term-memory.md +7 -27
  36. data/docs/guides/propositions.md +20 -19
  37. data/docs/guides/recalling-memories.md +5 -5
  38. data/docs/guides/tags.md +18 -13
  39. data/docs/multi_framework_support.md +1 -1
  40. data/docs/robots/hive-mind.md +1 -1
  41. data/docs/robots/multi-robot.md +2 -2
  42. data/docs/robots/robot-groups.md +1 -1
  43. data/docs/robots/two-tier-memory.md +72 -94
  44. data/docs/setup_local_database.md +8 -54
  45. data/docs/using_rake_tasks_in_your_app.md +6 -6
  46. data/examples/01_basic_usage.rb +1 -0
  47. data/examples/03_custom_llm_configuration.rb +1 -0
  48. data/examples/04_file_loader_usage.rb +1 -0
  49. data/examples/05_timeframe_demo.rb +1 -0
  50. data/examples/06_example_app/app.rb +1 -0
  51. data/examples/07_cli_app/htm_cli.rb +1 -0
  52. data/examples/09_mcp_client.rb +1 -0
  53. data/examples/10_telemetry/demo.rb +1 -0
  54. data/examples/11_robot_groups/multi_process.rb +1 -0
  55. data/examples/11_robot_groups/same_process.rb +1 -0
  56. data/examples/12_rails_app/.envrc +12 -0
  57. data/examples/12_rails_app/Gemfile +8 -3
  58. data/examples/12_rails_app/Gemfile.lock +94 -89
  59. data/examples/12_rails_app/README.md +70 -19
  60. data/examples/12_rails_app/app/controllers/application_controller.rb +6 -0
  61. data/examples/12_rails_app/app/controllers/chats_controller.rb +305 -0
  62. data/examples/12_rails_app/app/controllers/dashboard_controller.rb +3 -0
  63. data/examples/12_rails_app/app/controllers/files_controller.rb +17 -2
  64. data/examples/12_rails_app/app/controllers/home_controller.rb +8 -0
  65. data/examples/12_rails_app/app/controllers/memories_controller.rb +9 -4
  66. data/examples/12_rails_app/app/controllers/messages_controller.rb +214 -0
  67. data/examples/12_rails_app/app/controllers/robots_controller.rb +11 -1
  68. data/examples/12_rails_app/app/controllers/tags_controller.rb +14 -1
  69. data/examples/12_rails_app/app/javascript/application.js +1 -1
  70. data/examples/12_rails_app/app/models/application_record.rb +5 -0
  71. data/examples/12_rails_app/app/models/chat.rb +36 -0
  72. data/examples/12_rails_app/app/models/message.rb +5 -0
  73. data/examples/12_rails_app/app/models/model.rb +5 -0
  74. data/examples/12_rails_app/app/models/tool_call.rb +5 -0
  75. data/examples/12_rails_app/app/views/chats/index.html.erb +61 -0
  76. data/examples/12_rails_app/app/views/chats/show.html.erb +213 -0
  77. data/examples/12_rails_app/app/views/dashboard/index.html.erb +3 -0
  78. data/examples/12_rails_app/app/views/files/index.html.erb +10 -5
  79. data/examples/12_rails_app/app/views/files/new.html.erb +4 -2
  80. data/examples/12_rails_app/app/views/files/show.html.erb +19 -3
  81. data/examples/12_rails_app/app/views/home/index.html.erb +45 -0
  82. data/examples/12_rails_app/app/views/layouts/application.html.erb +20 -18
  83. data/examples/12_rails_app/app/views/memories/_memory_card.html.erb +1 -1
  84. data/examples/12_rails_app/app/views/memories/deleted.html.erb +3 -1
  85. data/examples/12_rails_app/app/views/memories/edit.html.erb +2 -0
  86. data/examples/12_rails_app/app/views/memories/index.html.erb +2 -0
  87. data/examples/12_rails_app/app/views/memories/new.html.erb +2 -0
  88. data/examples/12_rails_app/app/views/memories/show.html.erb +4 -2
  89. data/examples/12_rails_app/app/views/messages/_message.html.erb +20 -0
  90. data/examples/12_rails_app/app/views/robots/index.html.erb +2 -0
  91. data/examples/12_rails_app/app/views/robots/new.html.erb +2 -0
  92. data/examples/12_rails_app/app/views/robots/show.html.erb +2 -0
  93. data/examples/12_rails_app/app/views/search/index.html.erb +59 -8
  94. data/examples/12_rails_app/app/views/shared/_navbar.html.erb +75 -29
  95. data/examples/12_rails_app/app/views/tags/index.html.erb +2 -0
  96. data/examples/12_rails_app/app/views/tags/show.html.erb +3 -1
  97. data/examples/12_rails_app/config/application.rb +1 -1
  98. data/examples/12_rails_app/config/database.yml +9 -5
  99. data/examples/12_rails_app/config/importmap.rb +1 -1
  100. data/examples/12_rails_app/config/initializers/htm.rb +9 -2
  101. data/examples/12_rails_app/config/initializers/ruby_llm.rb +33 -0
  102. data/examples/12_rails_app/config/routes.rb +39 -23
  103. data/examples/12_rails_app/db/migrate/20250124000001_create_ruby_llm_tables.rb +34 -0
  104. data/examples/12_rails_app/db/migrate/20250124000002_create_models_table.rb +28 -0
  105. data/examples/12_rails_app/db/schema.rb +67 -0
  106. data/examples/examples_helper.rb +25 -0
  107. data/lib/htm/circuit_breaker.rb +5 -6
  108. data/lib/htm/config/builder.rb +12 -12
  109. data/lib/htm/config/database.rb +21 -27
  110. data/lib/htm/config/defaults.yml +25 -13
  111. data/lib/htm/config/validator.rb +12 -18
  112. data/lib/htm/config.rb +93 -173
  113. data/lib/htm/database.rb +193 -199
  114. data/lib/htm/embedding_service.rb +4 -9
  115. data/lib/htm/integrations/sinatra.rb +7 -7
  116. data/lib/htm/job_adapter.rb +14 -21
  117. data/lib/htm/jobs/generate_embedding_job.rb +28 -44
  118. data/lib/htm/jobs/generate_propositions_job.rb +29 -55
  119. data/lib/htm/jobs/generate_relationships_job.rb +137 -0
  120. data/lib/htm/jobs/generate_tags_job.rb +45 -67
  121. data/lib/htm/loaders/markdown_loader.rb +65 -112
  122. data/lib/htm/long_term_memory/fulltext_search.rb +1 -1
  123. data/lib/htm/long_term_memory/hybrid_search.rb +300 -128
  124. data/lib/htm/long_term_memory/node_operations.rb +2 -2
  125. data/lib/htm/long_term_memory/relevance_scorer.rb +100 -68
  126. data/lib/htm/long_term_memory/tag_operations.rb +87 -120
  127. data/lib/htm/long_term_memory/vector_search.rb +1 -1
  128. data/lib/htm/long_term_memory.rb +2 -1
  129. data/lib/htm/mcp/cli.rb +59 -58
  130. data/lib/htm/mcp/server.rb +5 -6
  131. data/lib/htm/mcp/tools.rb +30 -36
  132. data/lib/htm/migration.rb +10 -10
  133. data/lib/htm/models/node.rb +2 -3
  134. data/lib/htm/models/node_relationship.rb +72 -0
  135. data/lib/htm/models/node_tag.rb +2 -2
  136. data/lib/htm/models/robot_node.rb +2 -2
  137. data/lib/htm/models/tag.rb +41 -28
  138. data/lib/htm/observability.rb +45 -51
  139. data/lib/htm/proposition_service.rb +3 -7
  140. data/lib/htm/query_cache.rb +13 -15
  141. data/lib/htm/railtie.rb +1 -2
  142. data/lib/htm/robot_group.rb +9 -9
  143. data/lib/htm/sequel_config.rb +1 -0
  144. data/lib/htm/sql_builder.rb +1 -1
  145. data/lib/htm/tag_service.rb +2 -6
  146. data/lib/htm/timeframe.rb +4 -5
  147. data/lib/htm/timeframe_extractor.rb +42 -83
  148. data/lib/htm/version.rb +1 -1
  149. data/lib/htm/workflows/remember_workflow.rb +112 -115
  150. data/lib/htm/working_memory.rb +21 -26
  151. data/lib/htm.rb +103 -116
  152. data/lib/tasks/db.rake +0 -2
  153. data/lib/tasks/doc.rake +14 -13
  154. data/lib/tasks/files.rake +5 -12
  155. data/lib/tasks/htm.rake +70 -71
  156. data/lib/tasks/jobs.rake +41 -47
  157. data/lib/tasks/tags.rake +3 -8
  158. metadata +28 -106
  159. data/lib/htm/config/section.rb +0 -74
  160. data/lib/htm/loaders/defaults_loader.rb +0 -166
  161. data/lib/htm/loaders/xdg_config_loader.rb +0 -116
@@ -189,20 +189,13 @@ end
189
189
 
190
190
  ```ruby
191
191
  # User having back-and-forth coding conversation
192
- context = htm.create_context(strategy: :recent, max_tokens: 8000)
192
+ context = htm.working_memory.assemble_context(strategy: :recent, max_tokens: 8000)
193
193
  # Recent messages prioritized for coherent conversation flow
194
194
  ```
195
195
 
196
- #### 2. Important (`:important`)
196
+ #### 2. Frequent (`:frequent`)
197
197
 
198
- Sort by importance score, highest first.
199
-
200
- ```ruby
201
- def assemble_context(strategy: :important, max_tokens: nil)
202
- nodes = @nodes.sort_by { |k, v| -v[:importance] }.map(&:last)
203
- build_context(nodes, max_tokens || @max_tokens)
204
- end
205
- ```
198
+ Sort by access count, most accessed first.
206
199
 
207
200
  **Best For:**
208
201
 
@@ -215,8 +208,8 @@ end
215
208
 
216
209
  ```ruby
217
210
  # LLM helping with architectural review
218
- context = htm.create_context(strategy: :important)
219
- # Critical decisions and facts prioritized over recent chat
211
+ context = htm.working_memory.assemble_context(strategy: :frequent)
212
+ # Most frequently accessed decisions and facts prioritized
220
213
  ```
221
214
 
222
215
  #### 3. Balanced (`:balanced`) - **Recommended Default**
@@ -252,7 +245,7 @@ end
252
245
 
253
246
  ```ruby
254
247
  # Code helper assisting with debugging and design
255
- context = htm.create_context(strategy: :balanced)
248
+ context = htm.working_memory.assemble_context(strategy: :balanced)
256
249
  # Recent debugging context + important architectural decisions
257
250
  ```
258
251
 
@@ -287,7 +280,7 @@ Long-term memory provides unlimited, durable storage for all memories with advan
287
280
  |--------|---------|
288
281
  | **Purpose** | Permanent knowledge base |
289
282
  | **Capacity** | Effectively unlimited |
290
- | **Storage** | PostgreSQL 16+ with TimescaleDB |
283
+ | **Storage** | PostgreSQL 16+ with pgvector and pg_trgm |
291
284
  | **Access Pattern** | RAG-based retrieval (semantic + temporal) |
292
285
  | **Retention** | Permanent (explicit deletion only) |
293
286
  | **Lifetime** | Forever (survives process restarts) |
@@ -298,16 +291,16 @@ Long-term memory provides unlimited, durable storage for all memories with advan
298
291
  ```sql
299
292
  CREATE TABLE nodes (
300
293
  id BIGSERIAL PRIMARY KEY,
301
- key TEXT UNIQUE NOT NULL,
302
- value TEXT NOT NULL,
294
+ content TEXT NOT NULL,
295
+ content_hash TEXT UNIQUE,
303
296
  type TEXT,
304
- importance REAL DEFAULT 1.0,
305
297
  token_count INTEGER,
306
- in_working_memory BOOLEAN DEFAULT FALSE,
307
- robot_id TEXT NOT NULL REFERENCES robots(id),
308
- embedding vector(1536),
298
+ metadata JSONB DEFAULT '{}',
299
+ source_id BIGINT REFERENCES file_sources(id),
300
+ embedding vector(3072),
301
+ deleted_at TIMESTAMP,
309
302
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
310
- ...
303
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
311
304
  );
312
305
 
313
306
  -- HNSW index for vector similarity
@@ -316,58 +309,44 @@ CREATE INDEX idx_nodes_embedding ON nodes
316
309
  WITH (m = 16, ef_construction = 64);
317
310
 
318
311
  -- Full-text search
319
- CREATE INDEX idx_nodes_value_gin ON nodes
320
- USING gin(to_tsvector('english', value));
312
+ CREATE INDEX idx_nodes_content_gin ON nodes
313
+ USING gin(to_tsvector('english', content));
321
314
  ```
322
315
 
323
- ### Why PostgreSQL + TimescaleDB?
316
+ ### Why PostgreSQL?
324
317
 
325
318
  **PostgreSQL provides:**
326
319
 
327
320
  - ACID guarantees for data integrity
328
321
  - Rich ecosystem and tooling
329
322
  - pgvector for vector similarity search
330
- - Full-text search with GIN indexes
323
+ - Full-text search with GIN indexes and pg_trgm for fuzzy matching
324
+ - Soft delete support via `deleted_at` column
325
+ - JSONB metadata with GIN index for flexible filtering
331
326
  - Mature, production-proven
332
327
 
333
- **TimescaleDB adds:**
334
-
335
- - Hypertable partitioning by time
336
- - Automatic compression (70-90% reduction)
337
- - Time-range query optimization
338
- - Retention policies for data lifecycle
339
-
340
328
  !!! info "Related ADR"
341
- See [ADR-001: Use PostgreSQL with TimescaleDB for Storage](../architecture/adrs/001-postgresql-timescaledb.md) for complete rationale.
329
+ See [ADR-001: Use PostgreSQL for Storage](../architecture/adrs/001-postgresql-timescaledb.md) for complete rationale.
342
330
 
343
331
  ### Long-Term Memory Operations
344
332
 
345
333
  #### Add Node
346
334
 
347
335
  ```ruby
348
- def add(key:, value:, embedding:, importance:, token_count:, robot_id:, type: nil)
349
- result = @db.exec_params(<<~SQL, [key, value, type, importance, token_count, robot_id, embedding])
350
- INSERT INTO nodes (key, value, type, importance, token_count, robot_id, embedding, in_working_memory)
351
- VALUES ($1, $2, $3, $4, $5, $6, $7, TRUE)
352
- RETURNING id
353
- SQL
354
-
355
- result[0]['id'].to_i
356
- end
336
+ # High-level API (recommended)
337
+ node_id = htm.remember("PostgreSQL is great for vector search",
338
+ type: :fact,
339
+ tags: ["database:postgresql"],
340
+ metadata: { source: "docs" })
357
341
  ```
358
342
 
359
343
  **Time Complexity:** O(log n) with B-tree and HNSW indexes
360
344
 
361
- #### Retrieve by Key
345
+ #### Retrieve by ID
362
346
 
363
347
  ```ruby
364
- def retrieve(key)
365
- result = @db.exec_params(<<~SQL, [key])
366
- SELECT * FROM nodes WHERE key = $1
367
- SQL
368
-
369
- result.first
370
- end
348
+ # Direct model lookup
349
+ node = HTM::Models::Node.first(id: node_id)
371
350
  ```
372
351
 
373
352
  **Time Complexity:** O(1) with unique index on key
@@ -447,7 +426,7 @@ Pure semantic similarity using cosine distance between embeddings.
447
426
  ```ruby
448
427
  # Query: "database optimization"
449
428
  # Finds: "PostgreSQL index tuning", "query performance", "EXPLAIN ANALYZE"
450
- memories = htm.recall(timeframe: "last month", topic: "database optimization", strategy: :vector)
429
+ memories = htm.recall("database optimization", timeframe: "last month", strategy: :vector)
451
430
  ```
452
431
 
453
432
  ##### 2. Full-Text Search (`:fulltext`)
@@ -466,7 +445,7 @@ Keyword-based matching using PostgreSQL GIN indexes.
466
445
  ```ruby
467
446
  # Query: "TimescaleDB compression"
468
447
  # Finds exact matches for "TimescaleDB" and "compression"
469
- memories = htm.recall(timeframe: "last week", topic: "TimescaleDB compression", strategy: :fulltext)
448
+ memories = htm.recall("TimescaleDB compression", timeframe: "last week", strategy: :fulltext)
470
449
  ```
471
450
 
472
451
  ##### 3. Hybrid Search (`:hybrid`) - **Recommended**
@@ -483,7 +462,7 @@ Combines vector and full-text with RRF scoring.
483
462
 
484
463
  ```ruby
485
464
  # Combines semantic similarity AND keyword matching
486
- memories = htm.recall(timeframe: "last month", topic: "PostgreSQL performance", strategy: :hybrid)
465
+ memories = htm.recall("PostgreSQL performance", timeframe: "last month", strategy: :hybrid)
487
466
  ```
488
467
 
489
468
  ### Performance Characteristics
@@ -499,11 +478,11 @@ memories = htm.recall(timeframe: "last month", topic: "PostgreSQL performance",
499
478
 
500
479
  **Storage Efficiency:**
501
480
 
502
- With TimescaleDB compression (after 30 days):
481
+ With PostgreSQL TOAST compression:
503
482
 
504
- - Text node: ~1KB → ~200 bytes (80% reduction)
505
- - Node with embedding: ~7KB → ~1-2KB (70-85% reduction)
506
- - 100,000 nodes: ~700MB ~100-200MB
483
+ - Text node: ~1KB (minimal overhead)
484
+ - Node with embedding: ~7KB (1536-dim float32 vector)
485
+ - 100,000 nodes: ~700MB typical storage
507
486
 
508
487
  ## Memory Flow: Add → Evict → Recall
509
488
 
@@ -524,7 +503,7 @@ htm.memory_stats[:working_memory]
524
503
  # }
525
504
 
526
505
  # Add large memory
527
- htm.add_node("large_doc", large_documentation, importance: 7.0)
506
+ htm.remember(large_documentation)
528
507
 
529
508
  # HTM automatically:
530
509
  # 1. Generates embedding (Ollama: ~50ms)
@@ -545,8 +524,8 @@ htm.memory_stats[:working_memory]
545
524
  # }
546
525
 
547
526
  # Evicted nodes still in long-term memory!
548
- evicted_node = htm.retrieve("old_debug_log") # Still works
549
- # => { "key" => "old_debug_log", "in_working_memory" => false, ... }
527
+ # Retrieve via recall with the original content
528
+ evicted_results = htm.recall("old_debug_log", strategy: :fulltext) # Still works
550
529
  ```
551
530
 
552
531
  ## Context Assembly Strategies in Detail
@@ -556,8 +535,8 @@ evicted_node = htm.retrieve("old_debug_log") # Still works
556
535
  | Strategy | Sort Key | Best Use Case | Typical Output |
557
536
  |----------|----------|---------------|----------------|
558
537
  | **Recent** | Access order (newest first) | Conversations, debugging | Recent 5-10 messages |
559
- | **Important** | Importance score (highest first) | Planning, decisions | Top 10 most important facts |
560
- | **Balanced** | `importance / (1 + hours)` | General assistant | Mix of recent + important |
538
+ | **Frequent** | Access count (highest first) | Planning, decisions | Top 10 most accessed facts |
539
+ | **Balanced** | `importance / (1 + hours)` | General assistant | Mix of recent + frequent |
561
540
 
562
541
  ### Code Examples
563
542
 
@@ -565,7 +544,7 @@ evicted_node = htm.retrieve("old_debug_log") # Still works
565
544
 
566
545
  ```ruby
567
546
  # Assemble context from most recent memories
568
- context = htm.create_context(strategy: :recent, max_tokens: 8000)
547
+ context = htm.working_memory.assemble_context(strategy: :recent, max_tokens: 8000)
569
548
 
570
549
  # Typical output (recent conversation):
571
550
  # """
@@ -577,17 +556,17 @@ context = htm.create_context(strategy: :recent, max_tokens: 8000)
577
556
  # """
578
557
  ```
579
558
 
580
- #### Important Strategy
559
+ #### Frequent Strategy
581
560
 
582
561
  ```ruby
583
- # Assemble context from most important memories
584
- context = htm.create_context(strategy: :important, max_tokens: 4000)
562
+ # Assemble context from most frequently accessed memories
563
+ context = htm.working_memory.assemble_context(strategy: :frequent, max_tokens: 4000)
585
564
 
586
565
  # Typical output (critical facts):
587
566
  # """
588
- # Decision: Use PostgreSQL with TimescaleDB for storage (importance: 10.0)
589
- # User preference: Always use debug_me over puts (importance: 9.0)
590
- # Architecture: Two-tier memory system (importance: 9.0)
567
+ # Decision: Use PostgreSQL for storage (accessed 10 times)
568
+ # User preference: Always use debug_me over puts (accessed 8 times)
569
+ # Architecture: Two-tier memory system (accessed 7 times)
591
570
  # ...
592
571
  # """
593
572
  ```
@@ -596,13 +575,13 @@ context = htm.create_context(strategy: :important, max_tokens: 4000)
596
575
 
597
576
  ```ruby
598
577
  # Assemble context with time decay
599
- context = htm.create_context(strategy: :balanced)
578
+ context = htm.working_memory.assemble_context(strategy: :balanced)
600
579
 
601
580
  # Typical output (hybrid):
602
581
  # """
603
- # Recent debugging: ValueError in embedding service (importance: 7.0, 10 min ago) [score: 42.0]
604
- # Critical decision: PostgreSQL chosen (importance: 10.0, 3 days ago) [score: 0.14]
605
- # Current task: Implementing RAG search (importance: 6.0, 1 hour ago) [score: 3.0]
582
+ # Recent debugging: ValueError in embedding service (10 min ago)
583
+ # Current task: Implementing RAG search (1 hour ago)
584
+ # Critical decision: PostgreSQL chosen (accessed 10 times, 3 days ago)
606
585
  # ...
607
586
  # """
608
587
  ```
@@ -624,55 +603,54 @@ htm = HTM.new(working_memory_size: 128_000) # Default
624
603
  #### 2. Adjust Importance Scores
625
604
 
626
605
  ```ruby
627
- # High importance for critical information
628
- htm.add_node("user_preference", "User prefers Vim keybindings", importance: 9.0)
606
+ # Store critical information
607
+ htm.remember("User prefers Vim keybindings", metadata: { priority: "high" })
629
608
 
630
- # Low importance for transient information
631
- htm.add_node("debug_log", "Temporary debug output", importance: 1.0)
609
+ # Store transient information
610
+ htm.remember("Temporary debug output", metadata: { category: "debug" })
632
611
 
633
- # Medium importance for general context
634
- htm.add_node("discussion", "Discussed API design patterns", importance: 5.0)
612
+ # Store general context
613
+ htm.remember("Discussed API design patterns")
635
614
  ```
636
615
 
637
616
  #### 3. Use Appropriate Context Strategy
638
617
 
639
618
  ```ruby
640
619
  # For chat: recent strategy
641
- chat_context = htm.create_context(strategy: :recent, max_tokens: 8000)
620
+ chat_context = htm.working_memory.assemble_context(strategy: :recent, max_tokens: 8000)
642
621
 
643
- # For planning: important strategy
644
- planning_context = htm.create_context(strategy: :important, max_tokens: 4000)
622
+ # For planning: frequent strategy
623
+ planning_context = htm.working_memory.assemble_context(strategy: :frequent, max_tokens: 4000)
645
624
 
646
625
  # For general: balanced strategy (default)
647
- general_context = htm.create_context(strategy: :balanced)
626
+ general_context = htm.working_memory.assemble_context(strategy: :balanced)
648
627
  ```
649
628
 
650
629
  ### Long-Term Memory Optimization
651
630
 
652
- #### 1. Leverage TimescaleDB Compression
631
+ #### 1. Leverage PostgreSQL Partitioning
653
632
 
654
633
  ```sql
655
- -- Enable compression after 30 days
656
- SELECT add_compression_policy('nodes', INTERVAL '30 days');
634
+ -- Create a partial index for active (non-deleted) nodes
635
+ CREATE INDEX CONCURRENTLY idx_nodes_active
636
+ ON nodes (created_at DESC)
637
+ WHERE deleted_at IS NULL;
657
638
 
658
- -- Compress by robot_id and type for better ratio
659
- ALTER TABLE nodes SET (
660
- timescaledb.compress,
661
- timescaledb.compress_segmentby = 'robot_id,type'
662
- );
639
+ -- Periodically VACUUM to reclaim space from soft-deleted rows
640
+ VACUUM ANALYZE nodes;
663
641
  ```
664
642
 
665
643
  #### 2. Use Appropriate Search Strategy
666
644
 
667
645
  ```ruby
668
646
  # For exact matches: full-text
669
- exact_matches = htm.recall(timeframe: "last week", topic: "PostgreSQL", strategy: :fulltext)
647
+ exact_matches = htm.recall("PostgreSQL", timeframe: "last week", strategy: :fulltext)
670
648
 
671
649
  # For semantic similarity: vector
672
- similar_concepts = htm.recall(timeframe: "last month", topic: "database performance", strategy: :vector)
650
+ similar_concepts = htm.recall("database performance", timeframe: "last month", strategy: :vector)
673
651
 
674
652
  # For best results: hybrid (default)
675
- best_results = htm.recall(timeframe: "last month", topic: "PostgreSQL performance", strategy: :hybrid)
653
+ best_results = htm.recall("PostgreSQL performance", timeframe: "last month", strategy: :hybrid)
676
654
  ```
677
655
 
678
656
  #### 3. Index Tuning
@@ -36,24 +36,7 @@ psql --version
36
36
  brew install pgvector
37
37
  ```
38
38
 
39
- ### 2.2 Install TimescaleDB (Time-Series Database)
40
-
41
- ```bash
42
- # Add TimescaleDB tap
43
- brew tap timescale/tap
44
-
45
- # Install TimescaleDB
46
- brew install timescaledb
47
-
48
- # Configure PostgreSQL for TimescaleDB
49
- # This updates your postgresql.conf with TimescaleDB settings
50
- timescaledb-tune --quiet --yes
51
-
52
- # Restart PostgreSQL to load TimescaleDB
53
- brew services restart postgresql@17
54
- ```
55
-
56
- ### 2.3 pg_trgm (Trigram Matching)
39
+ ### 2.2 pg_trgm (Trigram Matching)
57
40
 
58
41
  This extension is included with PostgreSQL, no installation needed.
59
42
 
@@ -96,9 +79,6 @@ createdb htm_development
96
79
  # Enable pgvector
97
80
  psql -d htm_development -c "CREATE EXTENSION IF NOT EXISTS vector;"
98
81
 
99
- # Enable TimescaleDB
100
- psql -d htm_development -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
101
-
102
82
  # Enable pg_trgm (trigram matching)
103
83
  psql -d htm_development -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
104
84
  ```
@@ -112,20 +92,15 @@ be rake htm:db:setup
112
92
  This will:
113
93
  1. Verify extensions are available
114
94
  2. Create HTM schema (tables, indexes, triggers)
115
- 3. Set up TimescaleDB hypertables
116
- 4. Run any pending migrations
95
+ 3. Run any pending migrations
117
96
 
118
97
  Expected output:
119
98
 
120
99
  ```
121
- ✓ TimescaleDB version: X.X.X
122
100
  ✓ pgvector version: X.X.X
123
101
  ✓ pg_trgm version: X.X.X
124
102
  Creating HTM schema...
125
103
  ✓ Schema created
126
- ✓ Created hypertable for operations_log
127
- ✓ Created hypertable for nodes
128
- ✓ Enabled compression for nodes older than 30 days
129
104
  ✓ HTM database schema created successfully
130
105
  ```
131
106
 
@@ -176,17 +151,13 @@ brew install pgvector
176
151
  psql -d htm_development -c "CREATE EXTENSION IF NOT EXISTS vector;"
177
152
  ```
178
153
 
179
- ### Error: "TimescaleDB extension not found"
154
+ ### Error: "pg_trgm extension not found"
180
155
 
181
- **Problem:** TimescaleDB not installed or not enabled.
156
+ **Problem:** pg_trgm not enabled (it ships with PostgreSQL but needs to be enabled).
182
157
 
183
158
  **Solution:**
184
159
  ```bash
185
- brew tap timescale/tap
186
- brew install timescaledb
187
- timescaledb-tune --quiet --yes
188
- brew services restart postgresql@17
189
- psql -d htm_development -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
160
+ psql -d htm_development -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
190
161
  ```
191
162
 
192
163
  ### Error: "Connection refused" to Ollama
@@ -212,27 +183,12 @@ direnv allow
212
183
  echo $HTM_DATABASE__URL # Verify it's set
213
184
  ```
214
185
 
215
- ## Switching Back to TimescaleDB Cloud
186
+ ## Switching to a Remote Database
216
187
 
217
- To switch back to TimescaleDB Cloud (production), edit `.envrc`:
188
+ To switch to a remote PostgreSQL database (e.g. production), update your environment:
218
189
 
219
190
  ```bash
220
- # Comment out localhost config
221
- # export HTM_DATABASE__HOST=localhost
222
- # export HTM_DATABASE__PORT=5432
223
- # export HTM_DATABASE__NAME=htm_development
224
- # export HTM_DATABASE__USER=${USER}
225
- # export HTM_DATABASE__PASSWORD=
226
- # export HTM_DATABASE__URL="postgresql://${HTM_DATABASE__USER}@${HTM_DATABASE__HOST}:${HTM_DATABASE__PORT}/${HTM_DATABASE__NAME}?sslmode=prefer"
227
-
228
- # Uncomment TimescaleDB Cloud config
229
- export HTM_SERVICE_NAME=$TIGER_SERVICE_NAME
230
- export HTM_DATABASE__URL=$TIGER_DBURL
231
- export HTM_DATABASE__NAME=$TIGER_DBNAME
232
- export HTM_DATABASE__USER=$TIGER_DBUSER
233
- export HTM_DATABASE__PASSWORD=$TIGER_DBPASS
234
- export HTM_DATABASE__HOST=$TIGER_DBHOST
235
- export HTM_DATABASE__PORT=$TIGER_DBPORT
191
+ export HTM_DATABASE__URL="postgresql://user:password@remote-host:5432/htm_production"
236
192
  ```
237
193
 
238
194
  Then reload:
@@ -266,14 +222,12 @@ PostgreSQL Version:
266
222
  Extensions:
267
223
  pg_trgm (X.X.X)
268
224
  plpgsql (X.X.X)
269
- timescaledb (X.X.X)
270
225
  vector (X.X.X)
271
226
 
272
227
  HTM Tables:
273
228
  nodes: X rows
274
229
  tags: X rows
275
230
  robots: X rows
276
- operations_log: X rows
277
231
  schema_migrations: X rows
278
232
 
279
233
  Database Size: XX MB
@@ -66,10 +66,10 @@ Create a `.envrc` file in your application's root:
66
66
  export HTM_DATABASE__URL="postgresql://user:password@host:port/dbname?sslmode=require"
67
67
 
68
68
  # Or use individual parameters
69
- export HTM_DATABASE__HOST="your-host.tsdb.cloud.timescale.com"
70
- export HTM_DATABASE__PORT="37807"
71
- export HTM_DATABASE__NAME="tsdb"
72
- export HTM_DATABASE__USER="tsdbadmin"
69
+ export HTM_DATABASE__HOST="your-db-host.example.com"
70
+ export HTM_DATABASE__PORT="5432"
71
+ export HTM_DATABASE__NAME="htm_production"
72
+ export HTM_DATABASE__USER="postgres"
73
73
  export HTM_DATABASE__PASSWORD="your_password"
74
74
 
75
75
  # Embedding configuration
@@ -277,7 +277,7 @@ jobs:
277
277
 
278
278
  services:
279
279
  postgres:
280
- image: timescale/timescaledb-ha:pg17
280
+ image: pgvector/pgvector:pg17
281
281
  env:
282
282
  POSTGRES_PASSWORD: postgres
283
283
  options: >-
@@ -323,7 +323,7 @@ services:
323
323
  command: bash -c "rake htm:db:setup && rake app:start"
324
324
 
325
325
  db:
326
- image: timescale/timescaledb-ha:pg17
326
+ image: pgvector/pgvector:pg17
327
327
  environment:
328
328
  - POSTGRES_PASSWORD=postgres
329
329
  ```
@@ -18,6 +18,7 @@ ExamplesHelper.section "HTM Basic Usage Example"
18
18
  ExamplesHelper.print_environment
19
19
 
20
20
  # Verify database is available
21
+ ExamplesHelper.reset_if_requested!
21
22
  ExamplesHelper.require_database!
22
23
 
23
24
  begin
@@ -19,6 +19,7 @@ ExamplesHelper.section "HTM Custom LLM Configuration Example"
19
19
  ExamplesHelper.print_environment
20
20
 
21
21
  # Verify database is available
22
+ ExamplesHelper.reset_if_requested!
22
23
  ExamplesHelper.require_database!
23
24
 
24
25
  # Example 1: Use Default Configuration (RubyLLM with Ollama)
@@ -24,6 +24,7 @@ ExamplesHelper.section "HTM File Loader Example"
24
24
  ExamplesHelper.print_environment
25
25
 
26
26
  # Verify database is available
27
+ ExamplesHelper.reset_if_requested!
27
28
  ExamplesHelper.require_database!
28
29
 
29
30
  begin
@@ -13,6 +13,7 @@
13
13
 
14
14
  require_relative 'examples_helper'
15
15
 
16
+ ExamplesHelper.reset_if_requested!
16
17
  ExamplesHelper.require_database!
17
18
 
18
19
  puts <<~HEADER
@@ -20,6 +20,7 @@ class ExampleApp
20
20
  ExamplesHelper.print_environment
21
21
 
22
22
  # Verify database is available
23
+ ExamplesHelper.reset_if_requested!
23
24
  ExamplesHelper.require_database!
24
25
 
25
26
  puts "Database configured: #{HTM.config.actual_database_name}"
@@ -379,6 +379,7 @@ class HTMCli
379
379
  end
380
380
 
381
381
  # Check database configuration
382
+ ExamplesHelper.reset_if_requested!
382
383
  ExamplesHelper.require_database!
383
384
 
384
385
  # Check Ollama connection (optional but recommended)
@@ -58,6 +58,7 @@ class HTMMcpClient
58
58
  private
59
59
 
60
60
  def validate_environment
61
+ ExamplesHelper.reset_if_requested!
61
62
  ExamplesHelper.require_database!
62
63
 
63
64
  unless File.exist?(MCP_SERVER_PATH)
@@ -109,6 +109,7 @@ class TelemetryDemo
109
109
  puts " [OK] Environment: #{HTM.config.environment}"
110
110
  puts " [OK] Database: #{HTM.config.actual_database_name}"
111
111
 
112
+ ExamplesHelper.reset_if_requested!
112
113
  ExamplesHelper.require_database!
113
114
  puts
114
115
  end
@@ -123,6 +123,7 @@ def run_demo
123
123
  BANNER
124
124
 
125
125
  ExamplesHelper.print_environment
126
+ ExamplesHelper.reset_if_requested!
126
127
  ExamplesHelper.require_database!
127
128
 
128
129
  group_name = "demo-#{Time.now.to_i}"
@@ -34,6 +34,7 @@ require 'json'
34
34
 
35
35
  ExamplesHelper.section "HTM Robot Group Demo - Shared Working Memory & Failover"
36
36
  ExamplesHelper.print_environment
37
+ ExamplesHelper.reset_if_requested!
37
38
  ExamplesHelper.require_database!
38
39
 
39
40
  begin
@@ -0,0 +1,12 @@
1
+ # htm/examples/12_rails_app/.envrc
2
+
3
+ source_up
4
+
5
+ export RR=`pwd`
6
+
7
+ # ChatBot settings
8
+ export RAILS_ENV=development
9
+
10
+ # HTM Settings
11
+ unset HTM_DATABASE__URL
12
+ export HTM_DATABASE__NAME=htm_$HTM_ENV
@@ -2,18 +2,23 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- # Rails - full-stack setup
6
- gem 'rails', '~> 7.1'
5
+ # Rails 8.1 - full-stack setup
6
+ gem 'rails', '~> 8.1'
7
7
  gem 'puma', '~> 6.0'
8
8
 
9
9
  # HTM (use local development version)
10
10
  gem 'htm', path: '../..'
11
11
 
12
+ # RubyLLM for chat persistence
13
+ gem 'ruby_llm', '>= 1.0'
14
+
12
15
  # PostgreSQL (required for HTM)
13
16
  gem 'pg', '~> 1.5'
14
17
 
15
- # Ruby 4.0+ bundled gems (no longer in stdlib)
18
+ # Ruby 3.2+ bundled gems (no longer in stdlib)
16
19
  gem 'ostruct'
20
+ gem 'logger'
21
+ gem 'benchmark'
17
22
 
18
23
  # Frontend - using CDN for Tailwind and Hotwire (no build step)
19
24
  gem 'propshaft' # Asset pipeline for CSS/images