htm 0.0.1 → 0.0.10

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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/.aigcm_msg +1 -0
  3. data/.architecture/reviews/comprehensive-codebase-review.md +577 -0
  4. data/.claude/settings.local.json +92 -0
  5. data/.envrc +1 -0
  6. data/.irbrc +283 -80
  7. data/.tbls.yml +31 -0
  8. data/CHANGELOG.md +314 -16
  9. data/CLAUDE.md +603 -0
  10. data/README.md +76 -5
  11. data/Rakefile +5 -0
  12. data/SETUP.md +132 -101
  13. data/db/migrate/{20250101000001_enable_extensions.rb → 00001_enable_extensions.rb} +0 -1
  14. data/db/migrate/00002_create_robots.rb +11 -0
  15. data/db/migrate/00003_create_file_sources.rb +20 -0
  16. data/db/migrate/00004_create_nodes.rb +65 -0
  17. data/db/migrate/00005_create_tags.rb +13 -0
  18. data/db/migrate/00006_create_node_tags.rb +18 -0
  19. data/db/migrate/00007_create_robot_nodes.rb +26 -0
  20. data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +12 -0
  21. data/db/schema.sql +390 -36
  22. data/docs/api/database.md +19 -232
  23. data/docs/api/embedding-service.md +1 -7
  24. data/docs/api/htm.md +305 -364
  25. data/docs/api/index.md +1 -7
  26. data/docs/api/long-term-memory.md +342 -590
  27. data/docs/api/yard/HTM/ActiveRecordConfig.md +23 -0
  28. data/docs/api/yard/HTM/AuthorizationError.md +11 -0
  29. data/docs/api/yard/HTM/CircuitBreaker.md +92 -0
  30. data/docs/api/yard/HTM/CircuitBreakerOpenError.md +34 -0
  31. data/docs/api/yard/HTM/Configuration.md +175 -0
  32. data/docs/api/yard/HTM/Database.md +99 -0
  33. data/docs/api/yard/HTM/DatabaseError.md +14 -0
  34. data/docs/api/yard/HTM/EmbeddingError.md +18 -0
  35. data/docs/api/yard/HTM/EmbeddingService.md +58 -0
  36. data/docs/api/yard/HTM/Error.md +11 -0
  37. data/docs/api/yard/HTM/JobAdapter.md +39 -0
  38. data/docs/api/yard/HTM/LongTermMemory.md +342 -0
  39. data/docs/api/yard/HTM/NotFoundError.md +17 -0
  40. data/docs/api/yard/HTM/Observability.md +107 -0
  41. data/docs/api/yard/HTM/QueryTimeoutError.md +19 -0
  42. data/docs/api/yard/HTM/Railtie.md +27 -0
  43. data/docs/api/yard/HTM/ResourceExhaustedError.md +13 -0
  44. data/docs/api/yard/HTM/TagError.md +18 -0
  45. data/docs/api/yard/HTM/TagService.md +67 -0
  46. data/docs/api/yard/HTM/Timeframe/Result.md +24 -0
  47. data/docs/api/yard/HTM/Timeframe.md +40 -0
  48. data/docs/api/yard/HTM/TimeframeExtractor/Result.md +24 -0
  49. data/docs/api/yard/HTM/TimeframeExtractor.md +45 -0
  50. data/docs/api/yard/HTM/ValidationError.md +20 -0
  51. data/docs/api/yard/HTM/WorkingMemory.md +131 -0
  52. data/docs/api/yard/HTM.md +80 -0
  53. data/docs/api/yard/index.csv +179 -0
  54. data/docs/api/yard-reference.md +51 -0
  55. data/docs/architecture/adrs/001-postgresql-timescaledb.md +1 -1
  56. data/docs/architecture/adrs/003-ollama-embeddings.md +1 -1
  57. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +2 -27
  58. data/docs/architecture/adrs/index.md +2 -13
  59. data/docs/architecture/hive-mind.md +165 -166
  60. data/docs/architecture/index.md +2 -2
  61. data/docs/architecture/overview.md +5 -171
  62. data/docs/architecture/two-tier-memory.md +1 -35
  63. data/docs/assets/images/adr-010-current-architecture.svg +37 -0
  64. data/docs/assets/images/adr-010-proposed-architecture.svg +48 -0
  65. data/docs/assets/images/adr-dependency-tree.svg +93 -0
  66. data/docs/assets/images/class-hierarchy.svg +55 -0
  67. data/docs/assets/images/exception-hierarchy.svg +45 -0
  68. data/docs/assets/images/htm-architecture-overview.svg +83 -0
  69. data/docs/assets/images/htm-complete-memory-flow.svg +160 -0
  70. data/docs/assets/images/htm-context-assembly-flow.svg +148 -0
  71. data/docs/assets/images/htm-eviction-process.svg +141 -0
  72. data/docs/assets/images/htm-memory-addition-flow.svg +138 -0
  73. data/docs/assets/images/htm-memory-recall-flow.svg +152 -0
  74. data/docs/assets/images/htm-node-states.svg +123 -0
  75. data/docs/assets/images/project-structure.svg +78 -0
  76. data/docs/assets/images/test-directory-structure.svg +38 -0
  77. data/{dbdoc → docs/database}/README.md +127 -125
  78. data/docs/database/public.file_sources.md +42 -0
  79. data/docs/database/public.file_sources.svg +211 -0
  80. data/{dbdoc → docs/database}/public.node_tags.md +7 -8
  81. data/docs/database/public.node_tags.svg +239 -0
  82. data/{dbdoc → docs/database}/public.nodes.md +22 -17
  83. data/docs/database/public.nodes.svg +271 -0
  84. data/docs/database/public.robot_nodes.md +46 -0
  85. data/docs/database/public.robot_nodes.svg +243 -0
  86. data/{dbdoc → docs/database}/public.robots.md +2 -3
  87. data/docs/database/public.robots.svg +161 -0
  88. data/docs/database/public.tags.svg +139 -0
  89. data/{dbdoc → docs/database}/schema.json +941 -630
  90. data/docs/database/schema.svg +282 -0
  91. data/docs/development/index.md +1 -29
  92. data/docs/development/schema.md +134 -309
  93. data/docs/development/testing.md +1 -9
  94. data/docs/getting-started/index.md +47 -0
  95. data/docs/{installation.md → getting-started/installation.md} +2 -2
  96. data/docs/{quick-start.md → getting-started/quick-start.md} +5 -5
  97. data/docs/guides/adding-memories.md +295 -643
  98. data/docs/guides/recalling-memories.md +36 -1
  99. data/docs/guides/search-strategies.md +85 -51
  100. data/docs/images/htm-er-diagram.svg +156 -0
  101. data/docs/index.md +16 -31
  102. data/docs/multi_framework_support.md +4 -4
  103. data/examples/README.md +280 -0
  104. data/examples/basic_usage.rb +18 -16
  105. data/examples/cli_app/htm_cli.rb +146 -8
  106. data/examples/cli_app/temp.log +93 -0
  107. data/examples/custom_llm_configuration.rb +1 -2
  108. data/examples/example_app/app.rb +11 -14
  109. data/examples/file_loader_usage.rb +177 -0
  110. data/examples/robot_groups/lib/robot_group.rb +419 -0
  111. data/examples/robot_groups/lib/working_memory_channel.rb +140 -0
  112. data/examples/robot_groups/multi_process.rb +286 -0
  113. data/examples/robot_groups/robot_worker.rb +136 -0
  114. data/examples/robot_groups/same_process.rb +229 -0
  115. data/examples/sinatra_app/Gemfile +1 -0
  116. data/examples/sinatra_app/Gemfile.lock +166 -0
  117. data/examples/sinatra_app/app.rb +219 -24
  118. data/examples/timeframe_demo.rb +276 -0
  119. data/lib/htm/active_record_config.rb +10 -3
  120. data/lib/htm/circuit_breaker.rb +202 -0
  121. data/lib/htm/configuration.rb +313 -80
  122. data/lib/htm/database.rb +67 -36
  123. data/lib/htm/embedding_service.rb +39 -2
  124. data/lib/htm/errors.rb +131 -11
  125. data/lib/htm/{sinatra.rb → integrations/sinatra.rb} +87 -12
  126. data/lib/htm/job_adapter.rb +10 -3
  127. data/lib/htm/jobs/generate_embedding_job.rb +5 -4
  128. data/lib/htm/jobs/generate_tags_job.rb +4 -0
  129. data/lib/htm/loaders/markdown_loader.rb +263 -0
  130. data/lib/htm/loaders/paragraph_chunker.rb +112 -0
  131. data/lib/htm/long_term_memory.rb +601 -321
  132. data/lib/htm/models/file_source.rb +99 -0
  133. data/lib/htm/models/node.rb +116 -12
  134. data/lib/htm/models/robot.rb +53 -4
  135. data/lib/htm/models/robot_node.rb +51 -0
  136. data/lib/htm/models/tag.rb +302 -0
  137. data/lib/htm/observability.rb +395 -0
  138. data/lib/htm/tag_service.rb +60 -3
  139. data/lib/htm/tasks.rb +29 -0
  140. data/lib/htm/timeframe.rb +194 -0
  141. data/lib/htm/timeframe_extractor.rb +307 -0
  142. data/lib/htm/version.rb +1 -1
  143. data/lib/htm/working_memory.rb +165 -70
  144. data/lib/htm.rb +352 -133
  145. data/lib/tasks/doc.rake +300 -0
  146. data/lib/tasks/files.rake +299 -0
  147. data/lib/tasks/htm.rake +188 -2
  148. data/lib/tasks/jobs.rake +10 -12
  149. data/lib/tasks/tags.rake +194 -0
  150. data/mkdocs.yml +91 -9
  151. data/notes/ARCHITECTURE_REVIEW.md +1167 -0
  152. data/notes/IMPLEMENTATION_SUMMARY.md +606 -0
  153. data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +451 -0
  154. data/notes/next_steps.md +100 -0
  155. data/notes/plan.md +627 -0
  156. data/notes/tag_ontology_enhancement_ideas.md +222 -0
  157. data/notes/timescaledb_removal_summary.md +200 -0
  158. metadata +177 -37
  159. data/db/migrate/20250101000002_create_robots.rb +0 -14
  160. data/db/migrate/20250101000003_create_nodes.rb +0 -42
  161. data/db/migrate/20250101000005_create_tags.rb +0 -38
  162. data/db/migrate/20250101000007_add_node_vector_indexes.rb +0 -30
  163. data/dbdoc/public.node_tags.svg +0 -112
  164. data/dbdoc/public.nodes.svg +0 -118
  165. data/dbdoc/public.robots.svg +0 -90
  166. data/dbdoc/public.tags.svg +0 -60
  167. data/dbdoc/schema.svg +0 -154
  168. data/{dbdoc → docs/database}/public.node_stats.md +0 -0
  169. data/{dbdoc → docs/database}/public.node_stats.svg +0 -0
  170. data/{dbdoc → docs/database}/public.nodes_tags.md +0 -0
  171. data/{dbdoc → docs/database}/public.nodes_tags.svg +0 -0
  172. data/{dbdoc → docs/database}/public.ontology_structure.md +0 -0
  173. data/{dbdoc → docs/database}/public.ontology_structure.svg +0 -0
  174. data/{dbdoc → docs/database}/public.operations_log.md +0 -0
  175. data/{dbdoc → docs/database}/public.operations_log.svg +0 -0
  176. data/{dbdoc → docs/database}/public.relationships.md +0 -0
  177. data/{dbdoc → docs/database}/public.relationships.svg +0 -0
  178. data/{dbdoc → docs/database}/public.robot_activity.md +0 -0
  179. data/{dbdoc → docs/database}/public.robot_activity.svg +0 -0
  180. data/{dbdoc → docs/database}/public.schema_migrations.md +0 -0
  181. data/{dbdoc → docs/database}/public.schema_migrations.svg +0 -0
  182. data/{dbdoc → docs/database}/public.tags.md +3 -3
  183. /data/{dbdoc → docs/database}/public.topic_relationships.md +0 -0
  184. /data/{dbdoc → docs/database}/public.topic_relationships.svg +0 -0
@@ -0,0 +1,280 @@
1
+ # HTM Examples
2
+
3
+ This directory contains example applications demonstrating various ways to use the HTM (Hierarchical Temporary Memory) gem.
4
+
5
+ ## Prerequisites
6
+
7
+ All examples require:
8
+
9
+ 1. **PostgreSQL Database** with pgvector extension:
10
+ ```bash
11
+ export HTM_DBURL="postgresql://user@localhost:5432/htm_development"
12
+ ```
13
+
14
+ 2. **Ollama** (recommended for local LLM):
15
+ ```bash
16
+ ollama pull nomic-embed-text # For embeddings
17
+ ollama pull gemma3 # For tag extraction
18
+ ```
19
+
20
+ 3. **Ruby Dependencies**:
21
+ ```bash
22
+ bundle install
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Standalone Scripts
28
+
29
+ ### basic_usage.rb
30
+
31
+ **Getting started with HTM fundamentals.**
32
+
33
+ Demonstrates the core API: configuring HTM, registering a robot, and using the three primary methods (`remember`, `recall`, `forget`).
34
+
35
+ ```bash
36
+ ruby examples/basic_usage.rb
37
+ ```
38
+
39
+ **Features:**
40
+ - HTM configuration with Ollama provider
41
+ - Robot initialization
42
+ - Storing memories with `remember()`
43
+ - Retrieving memories with `recall()` using timeframes
44
+ - Understanding async embedding/tag generation
45
+
46
+ ---
47
+
48
+ ### custom_llm_configuration.rb
49
+
50
+ **Flexible LLM integration patterns.**
51
+
52
+ Shows how to configure HTM with custom embedding and tag generation methods, supporting multiple LLM providers or custom infrastructure.
53
+
54
+ ```bash
55
+ ruby examples/custom_llm_configuration.rb
56
+ ```
57
+
58
+ **Features:**
59
+ - Default configuration (RubyLLM + Ollama)
60
+ - Custom lambdas for embedding generation
61
+ - Custom lambdas for tag extraction
62
+ - Service object integration pattern
63
+ - Mixed configuration (custom embedding + default tags)
64
+ - Provider settings (OpenAI, Anthropic, Gemini, etc.)
65
+
66
+ ---
67
+
68
+ ### file_loader_usage.rb
69
+
70
+ **Loading documents into long-term memory.**
71
+
72
+ Demonstrates loading markdown files with automatic paragraph chunking, YAML frontmatter extraction, and source tracking for re-sync.
73
+
74
+ ```bash
75
+ ruby examples/file_loader_usage.rb
76
+ ```
77
+
78
+ **Features:**
79
+ - Single file loading with `load_file()`
80
+ - Directory loading with glob patterns via `load_directory()`
81
+ - YAML frontmatter extraction (title, author, tags)
82
+ - Querying nodes from a specific file
83
+ - Re-sync behavior (skip unchanged files)
84
+ - Force reload option
85
+ - Unloading files with `unload_file()`
86
+
87
+ ---
88
+
89
+ ### timeframe_demo.rb
90
+
91
+ **Flexible time-based filtering for recall.**
92
+
93
+ Comprehensive demonstration of all timeframe options supported by `recall()`, including natural language parsing.
94
+
95
+ ```bash
96
+ ruby examples/timeframe_demo.rb
97
+ ```
98
+
99
+ **Features:**
100
+ - No filter (`nil`)
101
+ - Date/DateTime/Time objects (entire day)
102
+ - Range for precise time windows
103
+ - Natural language strings ("yesterday", "last week", "few days ago")
104
+ - Weekend expressions ("last weekend", "2 weekends ago")
105
+ - Automatic extraction (`:auto`) from query text
106
+ - Multiple time windows (array of ranges)
107
+
108
+ ---
109
+
110
+ ## Application Examples
111
+
112
+ ### example_app/
113
+
114
+ **Full-featured HTM demonstration with RubyLLM integration.**
115
+
116
+ A standalone application showing complete HTM workflow with database connection, Ollama integration, memory operations, and multiple search strategies.
117
+
118
+ ```bash
119
+ ruby examples/example_app/app.rb
120
+ ```
121
+
122
+ **Features:**
123
+ - Database connection verification
124
+ - RubyLLM configuration for embeddings and tags
125
+ - Async embedding/tag generation with wait
126
+ - Comparison of search strategies (:fulltext, :vector, :hybrid)
127
+ - Detailed output of generated tags and embeddings
128
+
129
+ ---
130
+
131
+ ### sinatra_app/
132
+
133
+ **Web application with Sidekiq background processing.**
134
+
135
+ A Sinatra-based web application demonstrating HTM in a multi-user web context with async job processing.
136
+
137
+ ```bash
138
+ cd examples/sinatra_app
139
+ bundle install
140
+ bundle exec ruby app.rb
141
+ ```
142
+
143
+ **Features:**
144
+ - Sidekiq integration for background jobs
145
+ - Session-based robot identification
146
+ - RESTful API endpoints:
147
+ - `POST /api/remember` - Store information
148
+ - `GET /api/recall` - Search memories with timeframe filtering
149
+ - `GET /api/stats` - Memory statistics
150
+ - `GET /api/tags` - Tag tree structure
151
+ - `GET /api/health` - Health check
152
+ - Interactive HTML UI with hybrid search scoring display
153
+ - Tag tree visualization
154
+
155
+ **Environment Variables:**
156
+ - `HTM_DBURL` - PostgreSQL connection (required)
157
+ - `REDIS_URL` - Redis for Sidekiq (default: redis://localhost:6379/0)
158
+ - `SESSION_SECRET` - Session encryption key
159
+
160
+ ---
161
+
162
+ ### cli_app/
163
+
164
+ **Interactive command-line application.**
165
+
166
+ A REPL-style CLI demonstrating synchronous job execution with the `:inline` backend, ideal for CLI tools and scripts.
167
+
168
+ ```bash
169
+ ruby examples/cli_app/htm_cli.rb
170
+ ```
171
+
172
+ **Commands:**
173
+ - `remember <text>` - Store information (waits for embedding/tags)
174
+ - `recall <topic>` - Hybrid search with LLM-powered response generation
175
+ - `tags [prefix]` - List all tags with linked node content
176
+ - `stats` - Memory statistics
177
+ - `help` - Show help
178
+ - `exit` - Quit
179
+
180
+ **Features:**
181
+ - Synchronous job execution (`:inline` backend)
182
+ - Real-time progress feedback
183
+ - Tag extraction visibility during search
184
+ - Hybrid search with scoring (similarity, tag_boost, combined)
185
+ - RubyLLM chat integration for context-aware responses
186
+ - Response storage in long-term memory
187
+
188
+ See [cli_app/README.md](cli_app/README.md) for detailed documentation.
189
+
190
+ ---
191
+
192
+ ### robot_groups/
193
+
194
+ **Multi-robot coordination with shared working memory.**
195
+
196
+ Demonstrates high-availability patterns with shared working memory, failover, and real-time synchronization via PostgreSQL LISTEN/NOTIFY.
197
+
198
+ #### same_process.rb
199
+
200
+ Single-process demonstration of robot groups:
201
+
202
+ ```bash
203
+ ruby examples/robot_groups/same_process.rb
204
+ ```
205
+
206
+ **Scenarios demonstrated:**
207
+ 1. Creating a group with primary + standby robots
208
+ 2. Adding shared memories
209
+ 3. Verifying synchronization
210
+ 4. Simulating failover (primary dies, standby takes over)
211
+ 5. Verifying standby has full context
212
+ 6. Dynamic scaling (adding new robots)
213
+ 7. Collaborative memory (multiple robots adding)
214
+ 8. Real-time sync via PostgreSQL LISTEN/NOTIFY
215
+
216
+ #### multi_process.rb
217
+
218
+ Cross-process demonstration with separate Ruby processes:
219
+
220
+ ```bash
221
+ ruby examples/robot_groups/multi_process.rb
222
+ ```
223
+
224
+ **Scenarios demonstrated:**
225
+ 1. Spawning robot worker processes
226
+ 2. Cross-process memory sharing
227
+ 3. Collaborative memory updates
228
+ 4. Failover when a process dies
229
+ 5. Dynamic scaling (adding new processes)
230
+
231
+ **Key concepts:**
232
+ - **Shared Working Memory**: Multiple robots share context via `robot_nodes` table
233
+ - **Active/Passive Roles**: Active robots participate; passive robots maintain warm standby
234
+ - **Failover**: Instant takeover with full context already loaded
235
+ - **Real-time Sync**: PostgreSQL LISTEN/NOTIFY for in-memory cache coordination
236
+
237
+ ---
238
+
239
+ ## Directory Structure
240
+
241
+ ```
242
+ examples/
243
+ ├── README.md # This file
244
+ ├── basic_usage.rb # Core API demonstration
245
+ ├── custom_llm_configuration.rb # LLM integration patterns
246
+ ├── file_loader_usage.rb # Document loading
247
+ ├── timeframe_demo.rb # Time-based filtering
248
+ ├── example_app/
249
+ │ ├── app.rb # Full-featured demo app
250
+ │ └── Rakefile
251
+ ├── sinatra_app/
252
+ │ ├── app.rb # Sinatra web application
253
+ │ ├── Gemfile
254
+ │ └── Gemfile.lock
255
+ ├── cli_app/
256
+ │ ├── htm_cli.rb # Interactive CLI
257
+ │ └── README.md # Detailed CLI documentation
258
+ └── robot_groups/
259
+ ├── same_process.rb # Single-process robot groups
260
+ ├── multi_process.rb # Multi-process coordination
261
+ ├── robot_worker.rb # Worker process for multi_process.rb
262
+ └── lib/
263
+ ├── robot_group.rb # RobotGroup coordination class
264
+ └── working_memory_channel.rb # PostgreSQL pub/sub
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Choosing the Right Example
270
+
271
+ | Use Case | Example |
272
+ |----------|---------|
273
+ | Learning HTM basics | `basic_usage.rb` |
274
+ | Custom LLM integration | `custom_llm_configuration.rb` |
275
+ | Loading documents/files | `file_loader_usage.rb` |
276
+ | Time-based queries | `timeframe_demo.rb` |
277
+ | Web application | `sinatra_app/` |
278
+ | CLI tool | `cli_app/` |
279
+ | Multi-robot coordination | `robot_groups/` |
280
+ | High availability | `robot_groups/` |
@@ -4,8 +4,8 @@
4
4
  # Basic usage example for HTM
5
5
  #
6
6
  # Prerequisites:
7
- # 1. Source environment variables: source ~/.bashrc__tiger
8
- # 2. Initialize database schema: ruby -r ./lib/htm -e "HTM::Database.setup"
7
+ # 1. Set HTM_DBURL environment variable (see SETUP.md)
8
+ # 2. Initialize database schema: rake db_setup
9
9
  # 3. Install dependencies: bundle install
10
10
 
11
11
  require_relative '../lib/htm'
@@ -15,19 +15,21 @@ puts "=" * 60
15
15
 
16
16
  # Check environment
17
17
  unless ENV['HTM_DBURL']
18
- puts "ERROR: HTM_DBURL not set. Please run: source ~/.bashrc__tiger"
18
+ puts "ERROR: HTM_DBURL not set. Please set it:"
19
+ puts " export HTM_DBURL=\"postgresql://postgres@localhost:5432/htm_development\""
20
+ puts "See SETUP.md for details."
19
21
  exit 1
20
22
  end
21
23
 
22
24
  begin
23
- # Configure HTM globally (uses RubyLLM with Ollama by default)
25
+ # Configure HTM globally (uses Ollama by default)
24
26
  puts "\n1. Configuring HTM with Ollama provider..."
25
27
  HTM.configure do |config|
26
28
  config.embedding_provider = :ollama
27
- config.embedding_model = 'nomic-embed-text'
29
+ config.embedding_model = 'nomic-embed-text:latest' # Ollama models need :tag suffix
28
30
  config.embedding_dimensions = 768
29
31
  config.tag_provider = :ollama
30
- config.tag_model = 'llama3'
32
+ config.tag_model = 'gemma3:latest' # Ollama models need :tag suffix
31
33
  config.reset_to_defaults # Apply settings
32
34
  end
33
35
  puts "✓ HTM configured with Ollama provider"
@@ -47,20 +49,17 @@ begin
47
49
  puts "\n3. Remembering information..."
48
50
 
49
51
  node_id_1 = htm.remember(
50
- "We decided to use PostgreSQL for HTM storage because it provides excellent time-series optimization and native vector search with pgvector.",
51
- source: "architect"
52
+ "We decided to use PostgreSQL for HTM storage because it provides excellent time-series optimization and native vector search with pgvector."
52
53
  )
53
54
  puts "✓ Remembered decision about database choice (node #{node_id_1})"
54
55
 
55
56
  node_id_2 = htm.remember(
56
- "We chose RAG (Retrieval-Augmented Generation) for memory recall, combining temporal filtering with semantic vector search.",
57
- source: "architect"
57
+ "We chose RAG (Retrieval-Augmented Generation) for memory recall, combining temporal filtering with semantic vector search."
58
58
  )
59
59
  puts "✓ Remembered decision about RAG approach (node #{node_id_2})"
60
60
 
61
61
  node_id_3 = htm.remember(
62
- "The user's name is Dewayne and they prefer using debug_me for debugging instead of puts.",
63
- source: "system"
62
+ "The user's name is Dewayne and they prefer using debug_me for debugging instead of puts."
64
63
  )
65
64
  puts "✓ Remembered fact about user preferences (node #{node_id_3})"
66
65
 
@@ -72,18 +71,21 @@ begin
72
71
  memories = htm.recall(
73
72
  "database",
74
73
  timeframe: (Time.now - 3600)..Time.now, # Last hour
75
- limit: 5
74
+ limit: 5,
75
+ raw: true # Return full node data (id, content, etc.)
76
76
  )
77
77
  puts "✓ Found #{memories.length} memories"
78
78
  memories.each do |memory|
79
- puts " - Node #{memory['id']}: #{memory['content'][0..60]}..."
79
+ content = memory['content'] || memory[:content]
80
+ node_id = memory['id'] || memory[:id]
81
+ puts " - Node #{node_id}: #{content[0..60]}..."
80
82
  end
81
83
 
82
84
  puts "\n" + "=" * 60
83
85
  puts "✓ Example completed successfully!"
84
86
  puts "\nThe HTM API provides 3 core methods:"
85
- puts " - htm.remember(content, source:) - Store information"
86
- puts " - htm.recall(timeframe:, topic:, ...) - Retrieve memories"
87
+ puts " - htm.remember(content, tags: []) - Store information"
88
+ puts " - htm.recall(topic, timeframe:, ...) - Retrieve memories"
87
89
  puts " - htm.forget(node_id, confirm:) - Delete a memory"
88
90
 
89
91
  rescue => e
@@ -19,6 +19,20 @@
19
19
 
20
20
  require_relative '../../lib/htm'
21
21
  require 'io/console'
22
+ require 'ruby_llm'
23
+
24
+ PROVIDER = :ollama
25
+ MODEL = 'gpt-oss:latest'
26
+
27
+ # Configure RubyLLM for Ollama provider (same pattern as HTM uses)
28
+ RubyLLM.configure do |config|
29
+ ollama_url = ENV.fetch('OLLAMA_URL', 'http://localhost:11434')
30
+ ollama_api_base = ollama_url.end_with?('/v1') ? ollama_url : "#{ollama_url}/v1"
31
+ config.ollama_api_base = ollama_api_base
32
+ end
33
+
34
+ # Create chat with Ollama model - use assume_model_exists to bypass registry check
35
+ # AI = RubyLLM.chat(model: MODEL, provider: PROVIDER, assume_model_exists: true)
22
36
 
23
37
  class HTMCli
24
38
  def initialize
@@ -43,6 +57,9 @@ class HTMCli
43
57
  end
44
58
  end
45
59
 
60
+ # Initialize RubyLLM chat for context-aware responses
61
+ @chat = RubyLLM.chat(model: MODEL, provider: PROVIDER)
62
+
46
63
  # Initialize HTM instance
47
64
  @htm = HTM.new(robot_name: "cli_assistant")
48
65
  end
@@ -59,6 +76,7 @@ class HTMCli
59
76
  puts "Commands:"
60
77
  puts " remember <text> - Store information"
61
78
  puts " recall <topic> - Search memories"
79
+ puts " tags - List all tags with linked nodes"
62
80
  puts " stats - Show memory statistics"
63
81
  puts " help - Show this help"
64
82
  puts " exit - Quit"
@@ -91,6 +109,8 @@ class HTMCli
91
109
  handle_remember(args)
92
110
  when 'recall'
93
111
  handle_recall(args)
112
+ when 'tags'
113
+ handle_tags(args)
94
114
  when 'stats'
95
115
  handle_stats
96
116
  when 'help'
@@ -113,7 +133,7 @@ class HTMCli
113
133
  puts "Processing..."
114
134
 
115
135
  start_time = Time.now
116
- node_id = @htm.remember(text, source: 'cli_user')
136
+ node_id = @htm.remember(text)
117
137
  duration = ((Time.now - start_time) * 1000).round(2)
118
138
 
119
139
  puts "[✓] Stored as node #{node_id} (#{duration}ms)"
@@ -141,7 +161,30 @@ class HTMCli
141
161
  end
142
162
 
143
163
  puts "\nSearching for: \"#{topic}\""
144
- puts "Strategy: hybrid (vector + fulltext)"
164
+ puts "Strategy: hybrid (vector + fulltext + tags)"
165
+
166
+ # Show tags extracted from query and which ones matched
167
+ tag_result = @htm.long_term_memory.find_query_matching_tags(topic, include_extracted: true)
168
+
169
+ if tag_result[:extracted].any?
170
+ puts "Extracted tags: #{tag_result[:extracted].join(', ')}"
171
+
172
+ # Show what was actually searched (exact + prefixes)
173
+ searched = tag_result[:extracted].dup
174
+ tag_result[:extracted].each do |tag|
175
+ levels = tag.split(':')
176
+ (1...levels.size).each { |i| searched << levels[0, i].join(':') }
177
+ end
178
+ puts "Searched for: #{searched.uniq.join(', ')}"
179
+
180
+ if tag_result[:matched].any?
181
+ puts "Matched in DB: #{tag_result[:matched].join(', ')}"
182
+ else
183
+ puts "Matched in DB: (none)"
184
+ end
185
+ else
186
+ puts "Extracted tags: (none)"
187
+ end
145
188
 
146
189
  start_time = Time.now
147
190
  memories = @htm.recall(
@@ -161,16 +204,103 @@ class HTMCli
161
204
 
162
205
  memories.each_with_index do |memory, index|
163
206
  puts
164
- puts "#{index + 1}. Node #{memory['id']} (#{memory['source']})"
207
+ puts "#{index + 1}. Node #{memory['id']}"
165
208
  puts " Created: #{memory['created_at']}"
166
209
  puts " Content: #{memory['content']}"
167
210
 
211
+ # Show scores from hybrid search
212
+ if memory['combined_score']
213
+ similarity = (memory['similarity'].to_f * 100).round(1)
214
+ tag_boost = (memory['tag_boost'].to_f * 100).round(1)
215
+ combined = (memory['combined_score'].to_f * 100).round(1)
216
+ puts " Scores: similarity=#{similarity}%, tag_boost=#{tag_boost}%, combined=#{combined}%"
217
+ end
218
+
168
219
  # Show tags if any
169
220
  node = HTM::Models::Node.includes(:tags).find(memory['id'])
170
221
  if node.tags.any?
171
222
  puts " Tags: #{node.tags.map(&:name).join(', ')}"
223
+ else
224
+ puts " Tags: (none)"
172
225
  end
173
226
  end
227
+
228
+ # Build LLM prompt with context from retrieved memories
229
+ context_content = memories.map { |m| m['content'] }.join("\n\n")
230
+
231
+ llm_prompt = <<~PROMPT
232
+ #{topic}
233
+ Your response should highlight information also found in the
234
+ following context:
235
+ <CONTEXT>
236
+ #{context_content}
237
+ </CONTEXT>
238
+ PROMPT
239
+
240
+ puts "\n" + "=" * 60
241
+ puts "Generating response for this prompt..."
242
+ puts llm_prompt
243
+ puts "=" * 60
244
+
245
+ begin
246
+ response = @chat.ask(llm_prompt)
247
+ puts "\n#{response.content}"
248
+
249
+ # Remember the LLM response in long-term memory
250
+ node_id = @htm.remember(response.content)
251
+ puts "\n[✓] Response stored as node #{node_id}"
252
+ rescue StandardError => e
253
+ puts "[✗] LLM Error: #{e.message}"
254
+ end
255
+ end
256
+
257
+ def handle_tags(filter = nil)
258
+ puts "\nTags Overview:"
259
+ puts
260
+
261
+ # Get all tags with their nodes, optionally filtered by prefix
262
+ tags_query = HTM::Models::Tag.includes(:nodes).order(:name)
263
+ tags_query = tags_query.where("name LIKE ?", "#{filter}%") if filter && !filter.empty?
264
+
265
+ tags = tags_query.to_a
266
+
267
+ if tags.empty?
268
+ if filter
269
+ puts "[•] No tags found matching '#{filter}'"
270
+ else
271
+ puts "[•] No tags in database"
272
+ end
273
+ return
274
+ end
275
+
276
+ puts "[✓] Found #{tags.length} tags#{filter ? " matching '#{filter}'" : ""}"
277
+ puts
278
+
279
+ tags.each do |tag|
280
+ nodes = tag.nodes.to_a
281
+ puts "━" * 60
282
+ puts "Tag: #{tag.name}"
283
+ puts " Nodes: #{nodes.length}"
284
+ puts
285
+
286
+ if nodes.any?
287
+ nodes.each do |node|
288
+ content_preview = node.content.to_s[0..70]
289
+ content_preview += "..." if node.content.to_s.length > 70
290
+ puts " [#{node.id}] #{content_preview}"
291
+ end
292
+ else
293
+ puts " (no nodes linked)"
294
+ end
295
+ puts
296
+ end
297
+
298
+ # Show tag hierarchy summary
299
+ root_tags = tags.map { |t| t.name.split(':').first }.uniq.sort
300
+ if root_tags.length > 1
301
+ puts "━" * 60
302
+ puts "Root categories: #{root_tags.join(', ')}"
303
+ end
174
304
  end
175
305
 
176
306
  def handle_stats
@@ -201,7 +331,7 @@ class HTMCli
201
331
  puts
202
332
 
203
333
  puts "Current Robot (#{@htm.robot_name}):"
204
- robot_nodes = HTM::Models::Node.where(robot_id: @htm.robot_id).count
334
+ robot_nodes = HTM::Models::RobotNode.where(robot_id: @htm.robot_id).count
205
335
  puts " Nodes: #{robot_nodes}"
206
336
  end
207
337
 
@@ -216,8 +346,17 @@ class HTMCli
216
346
  puts
217
347
  puts " recall <topic>"
218
348
  puts " Search for relevant memories using hybrid search"
219
- puts " Combines vector similarity and full-text search"
349
+ puts " Combines vector similarity, full-text search, and tag matching"
350
+ puts " Shows matching tags, scores (similarity, tag_boost, combined)"
220
351
  puts " Example: recall PostgreSQL"
352
+ puts " Example: recall database optimization"
353
+ puts
354
+ puts " tags [prefix]"
355
+ puts " List all tags with their linked node content"
356
+ puts " Optionally filter by tag prefix"
357
+ puts " Example: tags"
358
+ puts " Example: tags database"
359
+ puts " Example: tags ai:machine"
221
360
  puts
222
361
  puts " stats"
223
362
  puts " Show memory statistics and current state"
@@ -242,10 +381,9 @@ unless ENV['HTM_DBURL']
242
381
  puts "[✗] Error: HTM_DBURL environment variable not set"
243
382
  puts
244
383
  puts "Please set your database connection URL:"
245
- puts " export HTM_DBURL='postgresql://user:pass@host:port/dbname'"
384
+ puts " export HTM_DBURL='postgresql://postgres@localhost:5432/htm_development'"
246
385
  puts
247
- puts "Or source your environment configuration:"
248
- puts " source ~/.bashrc__tiger"
386
+ puts "See SETUP.md for database setup instructions."
249
387
  puts
250
388
  exit 1
251
389
  end