htm 0.0.1

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 (155) hide show
  1. checksums.yaml +7 -0
  2. data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
  3. data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
  4. data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
  5. data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
  6. data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
  7. data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
  8. data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
  9. data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
  10. data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
  11. data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
  12. data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
  13. data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
  14. data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
  15. data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
  16. data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
  17. data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
  18. data/.architecture/members.yml +144 -0
  19. data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
  20. data/.architecture/reviews/initial-system-analysis.md +330 -0
  21. data/.envrc +32 -0
  22. data/.irbrc +145 -0
  23. data/CHANGELOG.md +150 -0
  24. data/COMMITS.md +196 -0
  25. data/LICENSE +21 -0
  26. data/README.md +1347 -0
  27. data/Rakefile +51 -0
  28. data/SETUP.md +268 -0
  29. data/config/database.yml +67 -0
  30. data/db/migrate/20250101000001_enable_extensions.rb +14 -0
  31. data/db/migrate/20250101000002_create_robots.rb +14 -0
  32. data/db/migrate/20250101000003_create_nodes.rb +42 -0
  33. data/db/migrate/20250101000005_create_tags.rb +38 -0
  34. data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
  35. data/db/schema.sql +473 -0
  36. data/db/seed_data/README.md +100 -0
  37. data/db/seed_data/presidents.md +136 -0
  38. data/db/seed_data/states.md +151 -0
  39. data/db/seeds.rb +208 -0
  40. data/dbdoc/README.md +173 -0
  41. data/dbdoc/public.node_stats.md +48 -0
  42. data/dbdoc/public.node_stats.svg +41 -0
  43. data/dbdoc/public.node_tags.md +40 -0
  44. data/dbdoc/public.node_tags.svg +112 -0
  45. data/dbdoc/public.nodes.md +54 -0
  46. data/dbdoc/public.nodes.svg +118 -0
  47. data/dbdoc/public.nodes_tags.md +39 -0
  48. data/dbdoc/public.nodes_tags.svg +112 -0
  49. data/dbdoc/public.ontology_structure.md +48 -0
  50. data/dbdoc/public.ontology_structure.svg +38 -0
  51. data/dbdoc/public.operations_log.md +42 -0
  52. data/dbdoc/public.operations_log.svg +130 -0
  53. data/dbdoc/public.relationships.md +39 -0
  54. data/dbdoc/public.relationships.svg +41 -0
  55. data/dbdoc/public.robot_activity.md +46 -0
  56. data/dbdoc/public.robot_activity.svg +35 -0
  57. data/dbdoc/public.robots.md +35 -0
  58. data/dbdoc/public.robots.svg +90 -0
  59. data/dbdoc/public.schema_migrations.md +29 -0
  60. data/dbdoc/public.schema_migrations.svg +26 -0
  61. data/dbdoc/public.tags.md +35 -0
  62. data/dbdoc/public.tags.svg +60 -0
  63. data/dbdoc/public.topic_relationships.md +45 -0
  64. data/dbdoc/public.topic_relationships.svg +32 -0
  65. data/dbdoc/schema.json +1437 -0
  66. data/dbdoc/schema.svg +154 -0
  67. data/docs/api/database.md +806 -0
  68. data/docs/api/embedding-service.md +532 -0
  69. data/docs/api/htm.md +797 -0
  70. data/docs/api/index.md +259 -0
  71. data/docs/api/long-term-memory.md +1096 -0
  72. data/docs/api/working-memory.md +665 -0
  73. data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
  74. data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
  75. data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
  76. data/docs/architecture/adrs/004-hive-mind.md +437 -0
  77. data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
  78. data/docs/architecture/adrs/006-context-assembly.md +496 -0
  79. data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
  80. data/docs/architecture/adrs/008-robot-identification.md +625 -0
  81. data/docs/architecture/adrs/009-never-forget.md +648 -0
  82. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
  83. data/docs/architecture/adrs/011-pgai-integration.md +494 -0
  84. data/docs/architecture/adrs/index.md +215 -0
  85. data/docs/architecture/hive-mind.md +736 -0
  86. data/docs/architecture/index.md +351 -0
  87. data/docs/architecture/overview.md +538 -0
  88. data/docs/architecture/two-tier-memory.md +873 -0
  89. data/docs/assets/css/custom.css +83 -0
  90. data/docs/assets/images/htm-core-components.svg +63 -0
  91. data/docs/assets/images/htm-database-schema.svg +93 -0
  92. data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
  93. data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
  94. data/docs/assets/images/htm-layered-architecture.svg +71 -0
  95. data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
  96. data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
  97. data/docs/assets/images/htm.jpg +0 -0
  98. data/docs/assets/images/htm_demo.gif +0 -0
  99. data/docs/assets/js/mathjax.js +18 -0
  100. data/docs/assets/videos/htm_video.mp4 +0 -0
  101. data/docs/database_rake_tasks.md +322 -0
  102. data/docs/development/contributing.md +787 -0
  103. data/docs/development/index.md +336 -0
  104. data/docs/development/schema.md +596 -0
  105. data/docs/development/setup.md +719 -0
  106. data/docs/development/testing.md +819 -0
  107. data/docs/guides/adding-memories.md +824 -0
  108. data/docs/guides/context-assembly.md +1009 -0
  109. data/docs/guides/getting-started.md +577 -0
  110. data/docs/guides/index.md +118 -0
  111. data/docs/guides/long-term-memory.md +941 -0
  112. data/docs/guides/multi-robot.md +866 -0
  113. data/docs/guides/recalling-memories.md +927 -0
  114. data/docs/guides/search-strategies.md +953 -0
  115. data/docs/guides/working-memory.md +717 -0
  116. data/docs/index.md +214 -0
  117. data/docs/installation.md +477 -0
  118. data/docs/multi_framework_support.md +519 -0
  119. data/docs/quick-start.md +655 -0
  120. data/docs/setup_local_database.md +302 -0
  121. data/docs/using_rake_tasks_in_your_app.md +383 -0
  122. data/examples/basic_usage.rb +93 -0
  123. data/examples/cli_app/README.md +317 -0
  124. data/examples/cli_app/htm_cli.rb +270 -0
  125. data/examples/custom_llm_configuration.rb +183 -0
  126. data/examples/example_app/Rakefile +71 -0
  127. data/examples/example_app/app.rb +206 -0
  128. data/examples/sinatra_app/Gemfile +21 -0
  129. data/examples/sinatra_app/app.rb +335 -0
  130. data/lib/htm/active_record_config.rb +113 -0
  131. data/lib/htm/configuration.rb +342 -0
  132. data/lib/htm/database.rb +594 -0
  133. data/lib/htm/embedding_service.rb +115 -0
  134. data/lib/htm/errors.rb +34 -0
  135. data/lib/htm/job_adapter.rb +154 -0
  136. data/lib/htm/jobs/generate_embedding_job.rb +65 -0
  137. data/lib/htm/jobs/generate_tags_job.rb +82 -0
  138. data/lib/htm/long_term_memory.rb +965 -0
  139. data/lib/htm/models/node.rb +109 -0
  140. data/lib/htm/models/node_tag.rb +33 -0
  141. data/lib/htm/models/robot.rb +52 -0
  142. data/lib/htm/models/tag.rb +76 -0
  143. data/lib/htm/railtie.rb +76 -0
  144. data/lib/htm/sinatra.rb +157 -0
  145. data/lib/htm/tag_service.rb +135 -0
  146. data/lib/htm/tasks.rb +38 -0
  147. data/lib/htm/version.rb +5 -0
  148. data/lib/htm/working_memory.rb +182 -0
  149. data/lib/htm.rb +400 -0
  150. data/lib/tasks/db.rake +19 -0
  151. data/lib/tasks/htm.rake +147 -0
  152. data/lib/tasks/jobs.rake +312 -0
  153. data/mkdocs.yml +190 -0
  154. data/scripts/install_local_database.sh +309 -0
  155. metadata +341 -0
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Basic usage example for HTM
5
+ #
6
+ # Prerequisites:
7
+ # 1. Source environment variables: source ~/.bashrc__tiger
8
+ # 2. Initialize database schema: ruby -r ./lib/htm -e "HTM::Database.setup"
9
+ # 3. Install dependencies: bundle install
10
+
11
+ require_relative '../lib/htm'
12
+
13
+ puts "HTM Basic Usage Example"
14
+ puts "=" * 60
15
+
16
+ # Check environment
17
+ unless ENV['HTM_DBURL']
18
+ puts "ERROR: HTM_DBURL not set. Please run: source ~/.bashrc__tiger"
19
+ exit 1
20
+ end
21
+
22
+ begin
23
+ # Configure HTM globally (uses RubyLLM with Ollama by default)
24
+ puts "\n1. Configuring HTM with Ollama provider..."
25
+ HTM.configure do |config|
26
+ config.embedding_provider = :ollama
27
+ config.embedding_model = 'nomic-embed-text'
28
+ config.embedding_dimensions = 768
29
+ config.tag_provider = :ollama
30
+ config.tag_model = 'llama3'
31
+ config.reset_to_defaults # Apply settings
32
+ end
33
+ puts "✓ HTM configured with Ollama provider"
34
+
35
+ # Initialize HTM for 'Code Helper' robot
36
+ puts "\n2. Initializing HTM for 'Code Helper' robot..."
37
+ htm = HTM.new(
38
+ robot_name: "Code Helper",
39
+ working_memory_size: 128_000
40
+ )
41
+ puts "✓ HTM initialized"
42
+ puts " Robot ID: #{htm.robot_id}"
43
+ puts " Robot Name: #{htm.robot_name}"
44
+ puts " Embedding Service: Ollama (#{HTM.configuration.embedding_model})"
45
+
46
+ # Remember some information
47
+ puts "\n3. Remembering information..."
48
+
49
+ 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
+ )
53
+ puts "✓ Remembered decision about database choice (node #{node_id_1})"
54
+
55
+ 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"
58
+ )
59
+ puts "✓ Remembered decision about RAG approach (node #{node_id_2})"
60
+
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"
64
+ )
65
+ puts "✓ Remembered fact about user preferences (node #{node_id_3})"
66
+
67
+ # Sleep briefly to allow async embedding/tag jobs to start
68
+ sleep 0.5
69
+
70
+ # Demonstrate recall
71
+ puts "\n4. Recalling memories about 'database'..."
72
+ memories = htm.recall(
73
+ "database",
74
+ timeframe: (Time.now - 3600)..Time.now, # Last hour
75
+ limit: 5
76
+ )
77
+ puts "✓ Found #{memories.length} memories"
78
+ memories.each do |memory|
79
+ puts " - Node #{memory['id']}: #{memory['content'][0..60]}..."
80
+ end
81
+
82
+ puts "\n" + "=" * 60
83
+ puts "✓ Example completed successfully!"
84
+ 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.forget(node_id, confirm:) - Delete a memory"
88
+
89
+ rescue => e
90
+ puts "\n✗ Error: #{e.message}"
91
+ puts e.backtrace.first(5).join("\n")
92
+ exit 1
93
+ end
@@ -0,0 +1,317 @@
1
+ # HTM CLI Application Example
2
+
3
+ This example demonstrates using HTM in a command-line application with synchronous job execution.
4
+
5
+ ## Features
6
+
7
+ - **Synchronous Execution**: Uses `:inline` job backend for immediate embedding/tag generation
8
+ - **Interactive Interface**: Command-line REPL for remembering and recalling information
9
+ - **Progress Feedback**: Real-time feedback on operations
10
+ - **Database Persistence**: All memories stored in PostgreSQL
11
+ - **Full HTM Features**: Vector search, tag extraction, hybrid search
12
+
13
+ ## Prerequisites
14
+
15
+ 1. **PostgreSQL Database**
16
+ ```bash
17
+ # Set database connection URL
18
+ export HTM_DBURL='postgresql://user:pass@host:port/dbname'
19
+
20
+ # Or use your existing config
21
+ source ~/.bashrc__tiger
22
+ ```
23
+
24
+ 2. **Ollama** (recommended but optional)
25
+ ```bash
26
+ # Install Ollama
27
+ curl https://ollama.ai/install.sh | sh
28
+
29
+ # Pull models
30
+ ollama pull nomic-embed-text # For embeddings
31
+ ollama pull llama3 # For tag extraction
32
+ ```
33
+
34
+ 3. **Ruby Dependencies**
35
+ ```bash
36
+ # From HTM root directory
37
+ bundle install
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### Run the CLI
43
+
44
+ ```bash
45
+ # Make executable
46
+ chmod +x htm_cli.rb
47
+
48
+ # Run
49
+ ./htm_cli.rb
50
+ # or
51
+ ruby htm_cli.rb
52
+ ```
53
+
54
+ ### Commands
55
+
56
+ **Store Information**
57
+ ```
58
+ htm> remember PostgreSQL is great for time-series data
59
+ ```
60
+
61
+ **Search Memories**
62
+ ```
63
+ htm> recall PostgreSQL
64
+ ```
65
+
66
+ **View Statistics**
67
+ ```
68
+ htm> stats
69
+ ```
70
+
71
+ **Show Help**
72
+ ```
73
+ htm> help
74
+ ```
75
+
76
+ **Exit**
77
+ ```
78
+ htm> exit
79
+ ```
80
+
81
+ ## How It Works
82
+
83
+ ### Synchronous Job Execution
84
+
85
+ Unlike web applications, CLI apps use `:inline` job backend:
86
+
87
+ ```ruby
88
+ HTM.configure do |config|
89
+ config.job_backend = :inline # Execute jobs synchronously
90
+ end
91
+ ```
92
+
93
+ This means:
94
+ - `remember()` waits for embeddings and tags to be generated
95
+ - Immediate feedback on completion
96
+ - Simpler debugging and testing
97
+ - No background job infrastructure needed
98
+
99
+ ### Job Backend Comparison
100
+
101
+ | Backend | CLI | Web Apps | When to Use |
102
+ |---------|-----|----------|-------------|
103
+ | `:inline` | ✅ | ❌ | CLI tools, tests |
104
+ | `:thread` | ⚠️ | ⚠️ | Simple standalone apps |
105
+ | `:sidekiq` | ❌ | ✅ | Sinatra, microservices |
106
+ | `:active_job` | ❌ | ✅ | Rails applications |
107
+
108
+ ## Example Session
109
+
110
+ ```
111
+ $ ruby htm_cli.rb
112
+
113
+ ============================================================
114
+ HTM CLI - Hierarchical Temporary Memory Assistant
115
+ ============================================================
116
+
117
+ Job Backend: inline (synchronous execution)
118
+ Robot: cli_assistant
119
+
120
+ Commands:
121
+ remember <text> - Store information
122
+ recall <topic> - Search memories
123
+ stats - Show memory statistics
124
+ help - Show this help
125
+ exit - Quit
126
+
127
+ ============================================================
128
+
129
+ htm> remember PostgreSQL with pgvector enables semantic search
130
+
131
+ Remembering: "PostgreSQL with pgvector enables semantic search"
132
+ Processing...
133
+ [✓] Stored as node 1 (1243.56ms)
134
+ Embedding: 768 dimensions
135
+ Tags: database:postgresql, search:semantic, ai:vector-search
136
+
137
+ htm> remember TimescaleDB is built on PostgreSQL for time-series
138
+
139
+ Remembering: "TimescaleDB is built on PostgreSQL for time-series"
140
+ Processing...
141
+ [✓] Stored as node 2 (1189.32ms)
142
+ Embedding: 768 dimensions
143
+ Tags: database:postgresql, database:timescaledb, time-series
144
+
145
+ htm> recall PostgreSQL
146
+
147
+ Searching for: "PostgreSQL"
148
+ Strategy: hybrid (vector + fulltext)
149
+ [✓] Found 2 memories (45.67ms)
150
+
151
+ 1. Node 1 (cli_user)
152
+ Created: 2025-11-09 14:23:15 UTC
153
+ Content: PostgreSQL with pgvector enables semantic search
154
+ Tags: database:postgresql, search:semantic, ai:vector-search
155
+
156
+ 2. Node 2 (cli_user)
157
+ Created: 2025-11-09 14:23:42 UTC
158
+ Content: TimescaleDB is built on PostgreSQL for time-series
159
+ Tags: database:postgresql, database:timescaledb, time-series
160
+
161
+ htm> stats
162
+
163
+ Memory Statistics:
164
+
165
+ Nodes:
166
+ Total: 2
167
+ With embeddings: 2 (100.0%)
168
+ With tags: 2 (100.0%)
169
+
170
+ Tags:
171
+ Total: 5
172
+ Average per node: 2.5
173
+
174
+ Robots:
175
+ Total: 1
176
+
177
+ Current Robot (cli_assistant):
178
+ Nodes: 2
179
+
180
+ htm> exit
181
+
182
+ Goodbye!
183
+ ```
184
+
185
+ ## Configuration Options
186
+
187
+ ### Custom Logger
188
+
189
+ ```ruby
190
+ HTM.configure do |config|
191
+ config.logger = Logger.new('htm_cli.log')
192
+ config.logger.level = Logger::DEBUG
193
+ end
194
+ ```
195
+
196
+ ### Custom Models
197
+
198
+ ```ruby
199
+ HTM.configure do |config|
200
+ config.embedding_model = 'custom-embedding-model'
201
+ config.tag_model = 'custom-tag-model'
202
+ end
203
+ ```
204
+
205
+ ### Timeouts
206
+
207
+ ```ruby
208
+ HTM.configure do |config|
209
+ config.embedding_timeout = 60 # 1 minute
210
+ config.tag_timeout = 120 # 2 minutes
211
+ end
212
+ ```
213
+
214
+ ## Performance Considerations
215
+
216
+ ### Synchronous Execution Trade-offs
217
+
218
+ **Pros:**
219
+ - Immediate feedback
220
+ - Simple error handling
221
+ - No background infrastructure needed
222
+ - Predictable timing
223
+
224
+ **Cons:**
225
+ - Slower user-facing operations (1-3 seconds per remember)
226
+ - Blocks on LLM API calls
227
+ - Not suitable for high-throughput scenarios
228
+
229
+ ### Optimization Tips
230
+
231
+ 1. **Batch Operations**: Group multiple `remember()` calls
232
+ 2. **Caching**: Use query cache for repeated searches
233
+ 3. **Faster Models**: Use smaller embedding models for CLI
234
+ 4. **Selective Features**: Disable tags if not needed
235
+
236
+ ```ruby
237
+ # Disable tag generation for faster CLI
238
+ HTM.configure do |config|
239
+ config.tag_extractor = ->(_text, _ontology) { [] }
240
+ end
241
+ ```
242
+
243
+ ## Troubleshooting
244
+
245
+ ### Database Connection Errors
246
+
247
+ ```bash
248
+ # Verify database URL
249
+ echo $HTM_DBURL
250
+
251
+ # Test connection
252
+ psql $HTM_DBURL -c "SELECT version();"
253
+
254
+ # Initialize schema if needed
255
+ cd ../.. && rake db_setup
256
+ ```
257
+
258
+ ### Ollama Connection Errors
259
+
260
+ ```bash
261
+ # Check if Ollama is running
262
+ curl http://localhost:11434/api/version
263
+
264
+ # Start Ollama
265
+ ollama serve
266
+
267
+ # Verify models are pulled
268
+ ollama list
269
+ ```
270
+
271
+ ### Slow Performance
272
+
273
+ ```bash
274
+ # Use faster models
275
+ export EMBEDDING_MODEL=all-minilm
276
+ export TAG_MODEL=gemma2:2b
277
+
278
+ # Or disable features
279
+ # (modify htm_cli.rb to skip tag generation)
280
+ ```
281
+
282
+ ## Extending the CLI
283
+
284
+ ### Add New Commands
285
+
286
+ ```ruby
287
+ # In htm_cli.rb, add to handle_command:
288
+ when 'export'
289
+ handle_export(args)
290
+
291
+ # Implement handler:
292
+ def handle_export(filename)
293
+ nodes = HTM::Models::Node.all
294
+ File.write(filename, nodes.to_json)
295
+ puts "[✓] Exported #{nodes.count} nodes to #{filename}"
296
+ end
297
+ ```
298
+
299
+ ### Custom Search Strategies
300
+
301
+ ```ruby
302
+ def handle_recall(topic)
303
+ # Use different strategies
304
+ vector_results = @htm.recall(topic, strategy: :vector)
305
+ fulltext_results = @htm.recall(topic, strategy: :fulltext)
306
+ hybrid_results = @htm.recall(topic, strategy: :hybrid)
307
+
308
+ # Compare and choose best
309
+ end
310
+ ```
311
+
312
+ ## See Also
313
+
314
+ - [HTM README](../../README.md) - Main documentation
315
+ - [Sinatra Example](../sinatra_app/README.md) - Web application example
316
+ - [Rails Example](../rails_app/README.md) - Rails integration
317
+ - [Job Backends Documentation](../../docs/job_backends.md) - Job backend guide
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # HTM CLI Application Example
5
+ #
6
+ # Demonstrates using HTM in a command-line application with:
7
+ # - Synchronous job execution (inline mode)
8
+ # - Interactive command interface
9
+ # - Progress feedback
10
+ # - Database persistence
11
+ #
12
+ # Usage:
13
+ # ruby htm_cli.rb
14
+ #
15
+ # Environment:
16
+ # HTM_DBURL - PostgreSQL connection URL (required)
17
+ # OLLAMA_URL - Ollama server URL (default: http://localhost:11434)
18
+ #
19
+
20
+ require_relative '../../lib/htm'
21
+ require 'io/console'
22
+
23
+ class HTMCli
24
+ def initialize
25
+ # Configure HTM for CLI usage
26
+ HTM.configure do |config|
27
+ # Use inline mode for synchronous execution
28
+ config.job_backend = :inline
29
+
30
+ # CLI-friendly logging
31
+ config.logger.level = Logger::INFO
32
+ config.logger.formatter = proc do |severity, datetime, progname, msg|
33
+ case severity
34
+ when 'INFO'
35
+ "[✓] #{msg}\n"
36
+ when 'WARN'
37
+ "[⚠] #{msg}\n"
38
+ when 'ERROR'
39
+ "[✗] #{msg}\n"
40
+ else
41
+ "[•] #{msg}\n"
42
+ end
43
+ end
44
+ end
45
+
46
+ # Initialize HTM instance
47
+ @htm = HTM.new(robot_name: "cli_assistant")
48
+ end
49
+
50
+ def run
51
+ puts
52
+ puts "=" * 60
53
+ puts "HTM CLI - Hierarchical Temporary Memory Assistant"
54
+ puts "=" * 60
55
+ puts
56
+ puts "Job Backend: #{HTM.configuration.job_backend} (synchronous execution)"
57
+ puts "Robot: #{@htm.robot_name}"
58
+ puts
59
+ puts "Commands:"
60
+ puts " remember <text> - Store information"
61
+ puts " recall <topic> - Search memories"
62
+ puts " stats - Show memory statistics"
63
+ puts " help - Show this help"
64
+ puts " exit - Quit"
65
+ puts
66
+ puts "=" * 60
67
+ puts
68
+
69
+ loop do
70
+ print "\nhtm> "
71
+ input = gets&.chomp
72
+ break if input.nil? || input == 'exit'
73
+
74
+ handle_command(input)
75
+ end
76
+
77
+ puts "\nGoodbye!"
78
+ end
79
+
80
+ private
81
+
82
+ def handle_command(input)
83
+ return if input.empty?
84
+
85
+ parts = input.split(' ', 2)
86
+ command = parts[0]
87
+ args = parts[1]
88
+
89
+ case command
90
+ when 'remember'
91
+ handle_remember(args)
92
+ when 'recall'
93
+ handle_recall(args)
94
+ when 'stats'
95
+ handle_stats
96
+ when 'help'
97
+ handle_help
98
+ else
99
+ puts "Unknown command: #{command}. Type 'help' for available commands."
100
+ end
101
+ rescue StandardError => e
102
+ puts "[✗] Error: #{e.message}"
103
+ puts " #{e.class}: #{e.backtrace.first}"
104
+ end
105
+
106
+ def handle_remember(text)
107
+ unless text && !text.empty?
108
+ puts "Usage: remember <text>"
109
+ return
110
+ end
111
+
112
+ puts "\nRemembering: \"#{text}\""
113
+ puts "Processing..."
114
+
115
+ start_time = Time.now
116
+ node_id = @htm.remember(text, source: 'cli_user')
117
+ duration = ((Time.now - start_time) * 1000).round(2)
118
+
119
+ puts "[✓] Stored as node #{node_id} (#{duration}ms)"
120
+
121
+ # Show what was generated (inline mode, so already complete)
122
+ node = HTM::Models::Node.includes(:tags).find(node_id)
123
+
124
+ if node.embedding
125
+ puts " Embedding: #{node.embedding_dimension} dimensions"
126
+ else
127
+ puts " Embedding: Not generated"
128
+ end
129
+
130
+ if node.tags.any?
131
+ puts " Tags: #{node.tags.map(&:name).join(', ')}"
132
+ else
133
+ puts " Tags: None"
134
+ end
135
+ end
136
+
137
+ def handle_recall(topic)
138
+ unless topic && !topic.empty?
139
+ puts "Usage: recall <topic>"
140
+ return
141
+ end
142
+
143
+ puts "\nSearching for: \"#{topic}\""
144
+ puts "Strategy: hybrid (vector + fulltext)"
145
+
146
+ start_time = Time.now
147
+ memories = @htm.recall(
148
+ topic,
149
+ limit: 10,
150
+ strategy: :hybrid,
151
+ raw: true
152
+ )
153
+ duration = ((Time.now - start_time) * 1000).round(2)
154
+
155
+ if memories.empty?
156
+ puts "[•] No memories found (#{duration}ms)"
157
+ return
158
+ end
159
+
160
+ puts "[✓] Found #{memories.length} memories (#{duration}ms)\n"
161
+
162
+ memories.each_with_index do |memory, index|
163
+ puts
164
+ puts "#{index + 1}. Node #{memory['id']} (#{memory['source']})"
165
+ puts " Created: #{memory['created_at']}"
166
+ puts " Content: #{memory['content']}"
167
+
168
+ # Show tags if any
169
+ node = HTM::Models::Node.includes(:tags).find(memory['id'])
170
+ if node.tags.any?
171
+ puts " Tags: #{node.tags.map(&:name).join(', ')}"
172
+ end
173
+ end
174
+ end
175
+
176
+ def handle_stats
177
+ puts "\nMemory Statistics:"
178
+ puts
179
+
180
+ total_nodes = HTM::Models::Node.count
181
+ nodes_with_embeddings = HTM::Models::Node.where.not(embedding: nil).count
182
+ nodes_with_tags = HTM::Models::Node.joins(:tags).distinct.count
183
+ total_tags = HTM::Models::Tag.count
184
+ total_robots = HTM::Models::Robot.count
185
+
186
+ puts "Nodes:"
187
+ puts " Total: #{total_nodes}"
188
+ puts " With embeddings: #{nodes_with_embeddings} (#{percentage(nodes_with_embeddings, total_nodes)})"
189
+ puts " With tags: #{nodes_with_tags} (#{percentage(nodes_with_tags, total_nodes)})"
190
+ puts
191
+
192
+ puts "Tags:"
193
+ puts " Total: #{total_tags}"
194
+ if total_tags > 0
195
+ puts " Average per node: #{(total_tags.to_f / total_nodes).round(2)}"
196
+ end
197
+ puts
198
+
199
+ puts "Robots:"
200
+ puts " Total: #{total_robots}"
201
+ puts
202
+
203
+ puts "Current Robot (#{@htm.robot_name}):"
204
+ robot_nodes = HTM::Models::Node.where(robot_id: @htm.robot_id).count
205
+ puts " Nodes: #{robot_nodes}"
206
+ end
207
+
208
+ def handle_help
209
+ puts
210
+ puts "HTM CLI Commands:"
211
+ puts
212
+ puts " remember <text>"
213
+ puts " Store information in long-term memory"
214
+ puts " Embeddings and tags are generated synchronously"
215
+ puts " Example: remember PostgreSQL is great for time-series data"
216
+ puts
217
+ puts " recall <topic>"
218
+ puts " Search for relevant memories using hybrid search"
219
+ puts " Combines vector similarity and full-text search"
220
+ puts " Example: recall PostgreSQL"
221
+ puts
222
+ puts " stats"
223
+ puts " Show memory statistics and current state"
224
+ puts
225
+ puts " help"
226
+ puts " Show this help message"
227
+ puts
228
+ puts " exit"
229
+ puts " Quit the CLI"
230
+ puts
231
+ end
232
+
233
+ def percentage(part, total)
234
+ return "0%" if total.zero?
235
+ "#{((part.to_f / total) * 100).round(1)}%"
236
+ end
237
+ end
238
+
239
+ # Check database configuration
240
+ unless ENV['HTM_DBURL']
241
+ puts
242
+ puts "[✗] Error: HTM_DBURL environment variable not set"
243
+ puts
244
+ puts "Please set your database connection URL:"
245
+ puts " export HTM_DBURL='postgresql://user:pass@host:port/dbname'"
246
+ puts
247
+ puts "Or source your environment configuration:"
248
+ puts " source ~/.bashrc__tiger"
249
+ puts
250
+ exit 1
251
+ end
252
+
253
+ # Check Ollama connection (optional but recommended)
254
+ begin
255
+ require 'net/http'
256
+ uri = URI(ENV['OLLAMA_URL'] || 'http://localhost:11434')
257
+ response = Net::HTTP.get_response(uri)
258
+ rescue StandardError => e
259
+ puts
260
+ puts "[⚠] Warning: Cannot connect to Ollama (#{e.message})"
261
+ puts " Embeddings and tags will not be generated"
262
+ puts " Install Ollama: https://ollama.ai"
263
+ puts
264
+ puts "Continue anyway? (y/n) "
265
+ answer = gets.chomp.downcase
266
+ exit unless answer == 'y' || answer == 'yes'
267
+ end
268
+
269
+ # Run CLI
270
+ HTMCli.new.run