htm 0.0.15 → 0.0.17
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/.envrc +1 -0
- data/CHANGELOG.md +67 -0
- data/README.md +97 -1592
- data/bin/htm_mcp +31 -0
- data/config/database.yml +7 -4
- data/docs/getting-started/installation.md +31 -11
- data/docs/guides/mcp-server.md +456 -21
- data/docs/multi_framework_support.md +2 -2
- data/examples/mcp_client.rb +2 -2
- data/examples/rails_app/.gitignore +2 -0
- data/examples/rails_app/Gemfile +22 -0
- data/examples/rails_app/Gemfile.lock +438 -0
- data/examples/rails_app/Procfile.dev +1 -0
- data/examples/rails_app/README.md +98 -0
- data/examples/rails_app/Rakefile +5 -0
- data/examples/rails_app/app/assets/stylesheets/application.css +83 -0
- data/examples/rails_app/app/assets/stylesheets/inter-font.css +6 -0
- data/examples/rails_app/app/controllers/application_controller.rb +19 -0
- data/examples/rails_app/app/controllers/dashboard_controller.rb +27 -0
- data/examples/rails_app/app/controllers/files_controller.rb +205 -0
- data/examples/rails_app/app/controllers/memories_controller.rb +102 -0
- data/examples/rails_app/app/controllers/robots_controller.rb +44 -0
- data/examples/rails_app/app/controllers/search_controller.rb +46 -0
- data/examples/rails_app/app/controllers/tags_controller.rb +30 -0
- data/examples/rails_app/app/javascript/application.js +4 -0
- data/examples/rails_app/app/javascript/controllers/application.js +9 -0
- data/examples/rails_app/app/javascript/controllers/index.js +6 -0
- data/examples/rails_app/app/views/dashboard/index.html.erb +123 -0
- data/examples/rails_app/app/views/files/index.html.erb +108 -0
- data/examples/rails_app/app/views/files/new.html.erb +321 -0
- data/examples/rails_app/app/views/files/show.html.erb +130 -0
- data/examples/rails_app/app/views/layouts/application.html.erb +124 -0
- data/examples/rails_app/app/views/memories/_memory_card.html.erb +51 -0
- data/examples/rails_app/app/views/memories/deleted.html.erb +62 -0
- data/examples/rails_app/app/views/memories/edit.html.erb +35 -0
- data/examples/rails_app/app/views/memories/index.html.erb +81 -0
- data/examples/rails_app/app/views/memories/new.html.erb +71 -0
- data/examples/rails_app/app/views/memories/show.html.erb +126 -0
- data/examples/rails_app/app/views/robots/index.html.erb +106 -0
- data/examples/rails_app/app/views/robots/new.html.erb +36 -0
- data/examples/rails_app/app/views/robots/show.html.erb +79 -0
- data/examples/rails_app/app/views/search/index.html.erb +184 -0
- data/examples/rails_app/app/views/shared/_navbar.html.erb +52 -0
- data/examples/rails_app/app/views/shared/_stat_card.html.erb +52 -0
- data/examples/rails_app/app/views/tags/index.html.erb +131 -0
- data/examples/rails_app/app/views/tags/show.html.erb +67 -0
- data/examples/rails_app/bin/dev +8 -0
- data/examples/rails_app/bin/rails +4 -0
- data/examples/rails_app/bin/rake +4 -0
- data/examples/rails_app/config/application.rb +33 -0
- data/examples/rails_app/config/boot.rb +5 -0
- data/examples/rails_app/config/database.yml +15 -0
- data/examples/rails_app/config/environment.rb +5 -0
- data/examples/rails_app/config/importmap.rb +7 -0
- data/examples/rails_app/config/routes.rb +38 -0
- data/examples/rails_app/config/tailwind.config.js +35 -0
- data/examples/rails_app/config.ru +5 -0
- data/examples/rails_app/log/.keep +0 -0
- data/examples/rails_app/tmp/local_secret.txt +1 -0
- data/lib/htm/active_record_config.rb +2 -5
- data/lib/htm/configuration.rb +35 -2
- data/lib/htm/database.rb +3 -6
- data/lib/htm/mcp/cli.rb +333 -0
- data/lib/htm/mcp/group_tools.rb +476 -0
- data/lib/htm/mcp/resources.rb +89 -0
- data/lib/htm/mcp/server.rb +98 -0
- data/lib/htm/mcp/tools.rb +488 -0
- data/lib/htm/models/file_source.rb +5 -3
- data/lib/htm/railtie.rb +0 -4
- data/lib/htm/tasks.rb +7 -4
- data/lib/htm/version.rb +1 -1
- data/lib/tasks/htm.rake +6 -9
- metadata +59 -4
- data/bin/htm_mcp.rb +0 -621
data/README.md
CHANGED
|
@@ -1,1657 +1,163 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>HTM</h1>
|
|
3
|
-
<!-- img src="docs/assets/images/htm.jpg" alt="HTM - Hierarchical Temporal Memory" width="600" -->
|
|
4
3
|
<img src="docs/assets/images/htm_demo.gif" alt="Tree of Knowledge is Growing" width="400">
|
|
5
4
|
|
|
6
|
-
<p>
|
|
7
|
-
</p>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<br/><br/>
|
|
11
|
-
|
|
12
|
-
> [!CAUTION]
|
|
13
|
-
> This library is under active development and experimentation. APIs and features may change without notice. Not recommended for production use yet... and the documentation may lie to you!
|
|
14
|
-
> <br /><br/>
|
|
15
|
-
> Apologies to Jeff Hawkins for using his term in such a macro-superficial way.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## What does it mean?
|
|
19
|
-
|
|
20
|
-
- **Hierarchical**: operates across multiple levels of abstraction, from simple concepts to detailed relationships
|
|
21
|
-
- **Temporal**: functions across time, from the present moment to historical data
|
|
22
|
-
- **Memory function**: encodes, stores, and retrieves information
|
|
23
|
-
|
|
24
|
-
**HTM**: a hierarchical and temporal memory system that organizes and recalls information at multiple levels of detail over extended timeframes.
|
|
25
|
-
|
|
26
|
-
## Features
|
|
27
|
-
|
|
28
|
-
- **Client-Side Embeddings**
|
|
29
|
-
- Automatic embedding generation before database insertion
|
|
30
|
-
- Uses the [ruby_llm](https://ruby_llm.com) gem for LLM access
|
|
31
|
-
- Configurable embedding providers and models
|
|
32
|
-
|
|
33
|
-
- **Two-Tier Memory Architecture**
|
|
34
|
-
- Working Memory: Token-limited active context for immediate LLM use
|
|
35
|
-
- Long-term Memory: Durable PostgreSQL storage
|
|
36
|
-
|
|
37
|
-
- **Never Forgets (Unless Told)**
|
|
38
|
-
- All memories persist in long-term storage
|
|
39
|
-
- Only explicit `forget()` commands delete data
|
|
40
|
-
- Working memory evicts to long-term, never deletes
|
|
41
|
-
|
|
42
|
-
- **RAG-Based Retrieval**
|
|
43
|
-
- Vector similarity search (pgvector)
|
|
44
|
-
- Full-text search (PostgreSQL)
|
|
45
|
-
- Hybrid search (combines both)
|
|
46
|
-
- Temporal filtering ("last week", date ranges)
|
|
47
|
-
- Variable embedding dimensions (384 to 3072)
|
|
48
|
-
|
|
49
|
-
- **Hive Mind**
|
|
50
|
-
- All robots share global memory
|
|
51
|
-
- Cross-robot context awareness
|
|
52
|
-
- Track which robot said what
|
|
53
|
-
|
|
54
|
-
- **Robot Groups**
|
|
55
|
-
- Coordinate multiple robots with shared working memory
|
|
56
|
-
- Real-time synchronization via PostgreSQL LISTEN/NOTIFY
|
|
57
|
-
- Active/passive roles for instant failover
|
|
58
|
-
- Dynamic scaling (add/remove robots at runtime)
|
|
59
|
-
|
|
60
|
-
- **LLM-Driven Tag Extraction**
|
|
61
|
-
- Automatic hierarchical tag extraction from content
|
|
62
|
-
- Tags in colon-delimited format (e.g., `database:postgresql:performance`)
|
|
63
|
-
- LLM-powered asynchronous processing
|
|
64
|
-
- Enables both structured navigation and semantic discovery
|
|
65
|
-
- Complements vector embeddings (symbolic + sub-symbolic retrieval)
|
|
66
|
-
|
|
67
|
-
- **Knowledge Graph**
|
|
68
|
-
- Tag-based categorization
|
|
69
|
-
- Hierarchical tag structures
|
|
70
|
-
|
|
71
|
-
- **File Loading**
|
|
72
|
-
- Load markdown files into long-term memory
|
|
73
|
-
- Automatic paragraph-based chunking
|
|
74
|
-
- Source file tracking with re-sync support
|
|
75
|
-
- YAML frontmatter extraction as metadata
|
|
76
|
-
|
|
77
|
-
- **Telemetry (OpenTelemetry)**
|
|
78
|
-
- Optional metrics collection via OpenTelemetry
|
|
79
|
-
- Zero overhead when disabled (null object pattern)
|
|
80
|
-
- Works with 50+ backends (Jaeger, Prometheus, Datadog, etc.)
|
|
81
|
-
- Tracks job latency, search performance, and cache effectiveness
|
|
82
|
-
|
|
83
|
-
## Installation
|
|
84
|
-
|
|
85
|
-
Add this line to your application's Gemfile:
|
|
86
|
-
|
|
87
|
-
```ruby
|
|
88
|
-
gem 'htm'
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
And then execute:
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
bundle install
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Or install it yourself as:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
gem install htm
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Setup
|
|
104
|
-
|
|
105
|
-
### 1. Database Configuration
|
|
106
|
-
|
|
107
|
-
HTM uses PostgreSQL for durable long-term memory storage. Set up your database connection via environment variables:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
# Preferred: Full connection URL
|
|
111
|
-
export HTM_DBURL="postgresql://user:password@host:port/dbname?sslmode=require"
|
|
112
|
-
|
|
113
|
-
# Alternative: Individual parameters
|
|
114
|
-
export HTM_DBNAME="htm_production"
|
|
115
|
-
export HTM_DBUSER="postgres"
|
|
116
|
-
export HTM_DBPASS="your_password"
|
|
117
|
-
export HTM_DBHOST="localhost"
|
|
118
|
-
export HTM_DBPORT="5432"
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
See the [Environment Variables](#environment-variables) section below for complete details.
|
|
122
|
-
|
|
123
|
-
### 2. Configure LLM Providers
|
|
124
|
-
|
|
125
|
-
HTM uses LLM providers for embedding generation and tag extraction. By default, it uses Ollama (local).
|
|
126
|
-
|
|
127
|
-
**Start Ollama (Default Configuration)**:
|
|
128
|
-
```bash
|
|
129
|
-
# Install Ollama
|
|
130
|
-
curl https://ollama.ai/install.sh | sh
|
|
131
|
-
|
|
132
|
-
# Start Ollama and pull models
|
|
133
|
-
ollama serve
|
|
134
|
-
ollama pull nomic-embed-text # For embeddings (768 dimensions)
|
|
135
|
-
ollama pull llama3 # For tag extraction
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
**Configure HTM** (optional - uses defaults if not configured):
|
|
139
|
-
```ruby
|
|
140
|
-
require 'htm'
|
|
141
|
-
|
|
142
|
-
# Use defaults (Ollama with nomic-embed-text and llama3)
|
|
143
|
-
HTM.configure
|
|
144
|
-
|
|
145
|
-
# Or customize providers
|
|
146
|
-
HTM.configure do |config|
|
|
147
|
-
# Embedding configuration
|
|
148
|
-
config.embedding_provider = :ollama # or :openai
|
|
149
|
-
config.embedding_model = 'nomic-embed-text'
|
|
150
|
-
config.embedding_dimensions = 768
|
|
151
|
-
config.ollama_url = 'http://localhost:11434'
|
|
152
|
-
|
|
153
|
-
# Tag extraction configuration
|
|
154
|
-
config.tag_provider = :ollama # or :openai
|
|
155
|
-
config.tag_model = 'llama3'
|
|
156
|
-
|
|
157
|
-
# Logger configuration (optional)
|
|
158
|
-
config.logger = Logger.new($stdout)
|
|
159
|
-
config.logger.level = Logger::INFO
|
|
160
|
-
|
|
161
|
-
# Custom embedding generator (advanced)
|
|
162
|
-
config.embedding_generator = ->(text) {
|
|
163
|
-
# Your custom implementation
|
|
164
|
-
# Must return Array<Float>
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
# Custom tag extractor (advanced)
|
|
168
|
-
config.tag_extractor = ->(text, ontology) {
|
|
169
|
-
# Your custom implementation
|
|
170
|
-
# Must return Array<String>
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
# Token counter (optional, defaults to Tiktoken)
|
|
174
|
-
config.token_counter = ->(text) {
|
|
175
|
-
# Your custom implementation
|
|
176
|
-
# Must return Integer
|
|
177
|
-
}
|
|
178
|
-
end
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
See the [Configuration](#configuration) section below for complete details.
|
|
182
|
-
|
|
183
|
-
### 3. Initialize Database Schema
|
|
184
|
-
|
|
185
|
-
**Using Rake Tasks (Recommended)**:
|
|
186
|
-
|
|
187
|
-
HTM provides comprehensive rake tasks for database management. Add this to your application's `Rakefile`:
|
|
188
|
-
|
|
189
|
-
```ruby
|
|
190
|
-
require 'htm/tasks'
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Then use the tasks:
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
# Set up database schema and run migrations
|
|
197
|
-
rake htm:db:setup
|
|
198
|
-
|
|
199
|
-
# Verify connection
|
|
200
|
-
rake htm:db:test
|
|
201
|
-
|
|
202
|
-
# Check database status
|
|
203
|
-
rake htm:db:info
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**Or programmatically**:
|
|
207
|
-
|
|
208
|
-
```ruby
|
|
209
|
-
require 'htm'
|
|
210
|
-
|
|
211
|
-
# Run once to set up database schema
|
|
212
|
-
HTM::Database.setup
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**Or from command line**:
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
ruby -r ./lib/htm -e "HTM::Database.setup"
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
See [Using HTM Rake Tasks in Your Application](docs/using_rake_tasks_in_your_app.md) for complete integration guide.
|
|
222
|
-
|
|
223
|
-
### 4. Verify Setup
|
|
224
|
-
|
|
225
|
-
```bash
|
|
226
|
-
rake htm:db:test # Using rake tasks
|
|
227
|
-
# or
|
|
228
|
-
ruby test_connection.rb
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
See [docs/setup_local_database.md](docs/setup_local_database.md) for detailed local database setup instructions.
|
|
232
|
-
|
|
233
|
-
## Usage
|
|
234
|
-
|
|
235
|
-
### Basic Example
|
|
236
|
-
|
|
237
|
-
```ruby
|
|
238
|
-
require 'htm'
|
|
239
|
-
require 'ruby_llm'
|
|
240
|
-
require 'logger'
|
|
241
|
-
|
|
242
|
-
# Configure HTM with RubyLLM for embeddings and tag extraction
|
|
243
|
-
HTM.configure do |config|
|
|
244
|
-
# Configure logger (optional - uses STDOUT at INFO level if not provided)
|
|
245
|
-
config.logger = Logger.new($stdout)
|
|
246
|
-
config.logger.level = Logger::INFO
|
|
247
|
-
|
|
248
|
-
# Configure embedding generation using RubyLLM with Ollama
|
|
249
|
-
config.embedding_provider = :ollama
|
|
250
|
-
config.embedding_model = 'nomic-embed-text'
|
|
251
|
-
config.embedding_dimensions = 768
|
|
252
|
-
config.ollama_url = ENV['OLLAMA_URL'] || 'http://localhost:11434'
|
|
253
|
-
|
|
254
|
-
# Configure tag extraction using RubyLLM with Ollama
|
|
255
|
-
config.tag_provider = :ollama
|
|
256
|
-
config.tag_model = 'llama3'
|
|
257
|
-
|
|
258
|
-
# Apply configuration (sets up default RubyLLM implementations)
|
|
259
|
-
config.reset_to_defaults
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
# Initialize HTM for your robot
|
|
263
|
-
htm = HTM.new(
|
|
264
|
-
robot_name: "Code Helper",
|
|
265
|
-
working_memory_size: 128_000 # tokens
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
# Remember information - embeddings and tags generated asynchronously
|
|
269
|
-
node_id = htm.remember(
|
|
270
|
-
"We decided to use PostgreSQL for HTM storage",
|
|
271
|
-
source: "architect"
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
# Recall from the past (uses semantic search with embeddings)
|
|
275
|
-
memories = htm.recall(
|
|
276
|
-
"database decisions",
|
|
277
|
-
timeframe: "last week",
|
|
278
|
-
strategy: :hybrid # Combines vector + full-text search
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
# Forget (explicit deletion only)
|
|
282
|
-
htm.forget(node_id, confirm: :confirmed)
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Loading Files
|
|
286
|
-
|
|
287
|
-
HTM can load text-based files (currently markdown) into long-term memory with automatic chunking and source tracking.
|
|
288
|
-
|
|
289
|
-
```ruby
|
|
290
|
-
htm = HTM.new(robot_name: "Document Loader")
|
|
291
|
-
|
|
292
|
-
# Load a single markdown file
|
|
293
|
-
result = htm.load_file("docs/guide.md")
|
|
294
|
-
# => { file_source_id: 1, chunks_created: 5, chunks_updated: 0, chunks_deleted: 0 }
|
|
295
|
-
|
|
296
|
-
# Load all markdown files in a directory
|
|
297
|
-
results = htm.load_directory("docs/", pattern: "**/*.md")
|
|
298
|
-
|
|
299
|
-
# Get nodes from a specific file
|
|
300
|
-
nodes = htm.nodes_from_file("docs/guide.md")
|
|
301
|
-
|
|
302
|
-
# Unload a file (soft deletes chunks)
|
|
303
|
-
htm.unload_file("docs/guide.md")
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
**Features:**
|
|
307
|
-
- **Paragraph chunking**: Text split by blank lines, code blocks preserved
|
|
308
|
-
- **Source tracking**: Files tracked with mtime for automatic re-sync
|
|
309
|
-
- **YAML frontmatter**: Extracted and stored as metadata
|
|
310
|
-
- **Duplicate detection**: Content hash prevents duplicate nodes
|
|
311
|
-
|
|
312
|
-
**Rake tasks:**
|
|
313
|
-
```bash
|
|
314
|
-
rake 'htm:files:load[docs/guide.md]' # Load a single file
|
|
315
|
-
rake 'htm:files:load_dir[docs/]' # Load all markdown files from directory
|
|
316
|
-
rake htm:files:list # List all loaded file sources
|
|
317
|
-
rake htm:files:sync # Sync all files (reload changed)
|
|
318
|
-
rake htm:files:stats # Show file loading statistics
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Robot Groups
|
|
322
|
-
|
|
323
|
-
Robot Groups enable coordination of multiple robots with shared working memory and real-time synchronization. This is useful for high-availability setups, load balancing, and collaborative AI agents.
|
|
324
|
-
|
|
325
|
-
**Key Classes:**
|
|
326
|
-
- `HTM::RobotGroup` - Coordinates multiple robots with active/passive roles and shared working memory
|
|
327
|
-
- `HTM::WorkingMemoryChannel` - PostgreSQL LISTEN/NOTIFY pub/sub for real-time cross-process synchronization
|
|
328
|
-
|
|
329
|
-
**Basic Usage:**
|
|
330
|
-
|
|
331
|
-
```ruby
|
|
332
|
-
require 'htm'
|
|
333
|
-
|
|
334
|
-
HTM.configure do |config|
|
|
335
|
-
config.embedding_provider = :ollama
|
|
336
|
-
config.embedding_model = 'nomic-embed-text'
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
# Create a robot group with primary + standby
|
|
340
|
-
group = HTM::RobotGroup.new(
|
|
341
|
-
name: 'customer-support-ha',
|
|
342
|
-
active: ['support-primary'],
|
|
343
|
-
passive: ['support-standby'],
|
|
344
|
-
max_tokens: 8000
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
# Add memories to the shared working memory
|
|
348
|
-
group.remember("Customer #123 prefers email communication", originator: 'support-primary')
|
|
349
|
-
|
|
350
|
-
# All robots in the group can recall shared memories
|
|
351
|
-
memories = group.recall('customer', limit: 5, strategy: :fulltext)
|
|
352
|
-
|
|
353
|
-
# Simulate failover when primary fails
|
|
354
|
-
group.failover! # Promotes standby to active
|
|
355
|
-
|
|
356
|
-
# Dynamic scaling - add more robots at runtime
|
|
357
|
-
group.add_active('support-secondary')
|
|
358
|
-
group.sync_robot('support-secondary') # Sync existing memories
|
|
359
|
-
|
|
360
|
-
# Check group status
|
|
361
|
-
status = group.status
|
|
362
|
-
# => { name: "customer-support-ha", active: [...], passive: [...], in_sync: true, ... }
|
|
363
|
-
|
|
364
|
-
# Cleanup
|
|
365
|
-
group.shutdown
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
**Cross-Process Synchronization:**
|
|
369
|
-
|
|
370
|
-
For multi-process deployments, use `HTM::WorkingMemoryChannel` directly:
|
|
371
|
-
|
|
372
|
-
```ruby
|
|
373
|
-
# In each worker process
|
|
374
|
-
channel = HTM::WorkingMemoryChannel.new('my-group', HTM::Database.default_config)
|
|
375
|
-
|
|
376
|
-
# Listen for memory changes from other processes
|
|
377
|
-
channel.on_change do |event, node_id, origin_robot_id|
|
|
378
|
-
case event
|
|
379
|
-
when :added # Another robot added a memory
|
|
380
|
-
when :evicted # Another robot evicted a memory
|
|
381
|
-
when :cleared # Another robot cleared working memory
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
channel.start_listening
|
|
386
|
-
|
|
387
|
-
# Notify other processes when you add a memory
|
|
388
|
-
channel.notify(:added, node_id: node.id, robot_id: my_robot_id)
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**Demo Applications:**
|
|
392
|
-
|
|
393
|
-
See `examples/robot_groups/` for complete examples:
|
|
394
|
-
- `same_process.rb` - Single-process demo with multiple robots
|
|
395
|
-
- `multi_process.rb` - Multi-process demo with real-time sync
|
|
396
|
-
|
|
397
|
-
### Automatic Tag Extraction
|
|
398
|
-
|
|
399
|
-
HTM automatically extracts hierarchical tags from content using LLM analysis. Tags are inferred from the content itself - you never specify them manually.
|
|
400
|
-
|
|
401
|
-
**Example:**
|
|
402
|
-
```ruby
|
|
403
|
-
htm.remember(
|
|
404
|
-
"User prefers dark mode for all interfaces",
|
|
405
|
-
source: "user"
|
|
406
|
-
)
|
|
407
|
-
# Tags like "preference", "ui", "dark-mode" may be auto-extracted by the LLM
|
|
408
|
-
# The specific tags depend on the LLM's analysis of the content
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### Async Job Processing
|
|
412
|
-
|
|
413
|
-
HTM automatically generates embeddings and extracts tags asynchronously in background jobs. This avoids blocking the main request path.
|
|
414
|
-
|
|
415
|
-
**Monitor job processing:**
|
|
416
|
-
```bash
|
|
417
|
-
# Show statistics for nodes and async processing
|
|
418
|
-
rake htm:jobs:stats
|
|
419
|
-
|
|
420
|
-
# Output:
|
|
421
|
-
# Total nodes: 150
|
|
422
|
-
# Nodes with embeddings: 145 (96.7%)
|
|
423
|
-
# Nodes without embeddings: 5 (3.3%)
|
|
424
|
-
# Nodes with tags: 140 (93.3%)
|
|
425
|
-
# Total tags in ontology: 42
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
**Process pending jobs manually:**
|
|
429
|
-
```bash
|
|
430
|
-
# Process all pending embedding jobs
|
|
431
|
-
rake htm:jobs:process_embeddings
|
|
432
|
-
|
|
433
|
-
# Process all pending tag extraction jobs
|
|
434
|
-
rake htm:jobs:process_tags
|
|
435
|
-
|
|
436
|
-
# Process both embeddings and tags
|
|
437
|
-
rake htm:jobs:process_all
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
**Reprocess all nodes (force regeneration):**
|
|
441
|
-
```bash
|
|
442
|
-
# Regenerate embeddings for ALL nodes
|
|
443
|
-
rake htm:jobs:reprocess_embeddings
|
|
444
|
-
|
|
445
|
-
# WARNING: Prompts for confirmation
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
**Show failed/stuck jobs:**
|
|
449
|
-
```bash
|
|
450
|
-
# Show nodes that failed async processing (>1 hour old without embeddings/tags)
|
|
451
|
-
rake htm:jobs:failed
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
**Clear all for testing:**
|
|
455
|
-
```bash
|
|
456
|
-
# Clear ALL embeddings and tags (development/testing only)
|
|
457
|
-
rake htm:jobs:clear_all
|
|
458
|
-
|
|
459
|
-
# WARNING: Prompts for confirmation
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
See `rake -T htm:jobs` for complete list of job management tasks.
|
|
463
|
-
|
|
464
|
-
## Telemetry (OpenTelemetry)
|
|
465
|
-
|
|
466
|
-
HTM includes optional OpenTelemetry-based metrics for production observability. Telemetry is **disabled by default** with zero overhead when off.
|
|
467
|
-
|
|
468
|
-
### Enabling Telemetry
|
|
469
|
-
|
|
470
|
-
```ruby
|
|
471
|
-
HTM.configure do |config|
|
|
472
|
-
config.telemetry_enabled = true
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
# Or via environment variable
|
|
476
|
-
# HTM_TELEMETRY_ENABLED=true
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
### Configuring the Destination
|
|
480
|
-
|
|
481
|
-
HTM emits metrics via standard OpenTelemetry protocols. Configure your destination using environment variables:
|
|
482
|
-
|
|
483
|
-
```bash
|
|
484
|
-
# Export to any OTLP-compatible backend
|
|
485
|
-
export OTEL_METRICS_EXPORTER="otlp"
|
|
486
|
-
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
### Available Metrics
|
|
490
|
-
|
|
491
|
-
| Metric | Type | Attributes | Description |
|
|
492
|
-
|--------|------|------------|-------------|
|
|
493
|
-
| `htm.jobs` | Counter | `job`, `status` | Job execution counts (embedding, tags) |
|
|
494
|
-
| `htm.embedding.latency` | Histogram | `provider`, `status` | Embedding generation time (ms) |
|
|
495
|
-
| `htm.tag.latency` | Histogram | `provider`, `status` | Tag extraction time (ms) |
|
|
496
|
-
| `htm.search.latency` | Histogram | `strategy` | Search operation time (ms) |
|
|
497
|
-
| `htm.cache.operations` | Counter | `operation` | Cache hits/misses |
|
|
498
|
-
|
|
499
|
-
### Compatible Backends
|
|
500
|
-
|
|
501
|
-
HTM works with any OTLP-compatible observability platform:
|
|
502
|
-
|
|
503
|
-
**Open Source:** Jaeger, Prometheus, Grafana Tempo/Mimir, SigNoz, Uptrace
|
|
504
|
-
|
|
505
|
-
**Commercial:** Datadog, New Relic, Honeycomb, Splunk, Dynatrace, AWS X-Ray, Google Cloud Trace, Azure Monitor
|
|
506
|
-
|
|
507
|
-
### Optional Dependencies
|
|
508
|
-
|
|
509
|
-
Users who want telemetry should add these gems:
|
|
510
|
-
|
|
511
|
-
```ruby
|
|
512
|
-
gem 'opentelemetry-sdk'
|
|
513
|
-
gem 'opentelemetry-metrics-sdk'
|
|
514
|
-
gem 'opentelemetry-exporter-otlp' # For OTLP export
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
### Design
|
|
518
|
-
|
|
519
|
-
HTM uses a **null object pattern** for telemetry. When disabled or when the SDK is not installed:
|
|
520
|
-
- All metric operations are no-ops
|
|
521
|
-
- Zero runtime overhead
|
|
522
|
-
- No errors or exceptions
|
|
523
|
-
|
|
524
|
-
See [docs/telemetry.md](docs/telemetry.md) for detailed configuration and usage examples.
|
|
525
|
-
|
|
526
|
-
## MCP Server (Model Context Protocol)
|
|
527
|
-
|
|
528
|
-
HTM includes an MCP server that exposes memory capabilities to AI assistants like Claude Desktop, Claude Code, and AIA. This enables any MCP-compatible client to store, recall, and manage memories through a standardized protocol.
|
|
529
|
-
|
|
530
|
-
### Available Tools
|
|
531
|
-
|
|
532
|
-
| Tool | Description |
|
|
533
|
-
|------|-------------|
|
|
534
|
-
| `SetRobotTool` | Set the robot identity for the session (call first) |
|
|
535
|
-
| `GetRobotTool` | Get current robot information |
|
|
536
|
-
| `GetWorkingMemoryTool` | Get working memory contents for session restore |
|
|
537
|
-
| `RememberTool` | Store information with optional tags and metadata |
|
|
538
|
-
| `RecallTool` | Search memories using vector, fulltext, or hybrid strategies |
|
|
539
|
-
| `ForgetTool` | Soft-delete a memory (recoverable) |
|
|
540
|
-
| `RestoreTool` | Restore a soft-deleted memory |
|
|
541
|
-
| `ListTagsTool` | List tags with optional prefix filtering |
|
|
542
|
-
| `SearchTagsTool` | Fuzzy tag search with typo tolerance |
|
|
543
|
-
| `FindByTopicTool` | Find nodes by topic with optional fuzzy matching |
|
|
544
|
-
| `StatsTool` | Get memory usage statistics |
|
|
545
|
-
|
|
546
|
-
### Available Resources
|
|
547
|
-
|
|
548
|
-
| URI | Description |
|
|
549
|
-
|-----|-------------|
|
|
550
|
-
| `htm://statistics` | Memory statistics as JSON |
|
|
551
|
-
| `htm://tags/hierarchy` | Tag hierarchy as text tree |
|
|
552
|
-
| `htm://memories/recent` | Last 20 memories |
|
|
553
|
-
|
|
554
|
-
### Client Configuration
|
|
555
|
-
|
|
556
|
-
**Claude Desktop** (`~/.config/claude/claude_desktop_config.json`):
|
|
557
|
-
```json
|
|
558
|
-
{
|
|
559
|
-
"mcpServers": {
|
|
560
|
-
"htm-memory": {
|
|
561
|
-
"command": "htm_mcp.rb",
|
|
562
|
-
"env": {
|
|
563
|
-
"HTM_DBURL": "postgresql://user@localhost:5432/htm_development"
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
**Claude Code** (`~/.claude/claude_code_config.json`):
|
|
571
|
-
```json
|
|
572
|
-
{
|
|
573
|
-
"mcpServers": {
|
|
574
|
-
"htm-memory": {
|
|
575
|
-
"command": "htm_mcp.rb",
|
|
576
|
-
"env": {
|
|
577
|
-
"HTM_DBURL": "postgresql://user@localhost:5432/htm_development"
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
**AIA** (`~/.config/aia/config.yml`):
|
|
585
|
-
```yaml
|
|
586
|
-
mcp_servers:
|
|
587
|
-
htm-memory:
|
|
588
|
-
command: htm_mcp.rb
|
|
589
|
-
env:
|
|
590
|
-
HTM_DBURL: postgresql://user@localhost:5432/htm_development
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
See [docs/guides/mcp-server.md](docs/guides/mcp-server.md) for detailed configuration, usage examples, and troubleshooting.
|
|
594
|
-
|
|
595
|
-
## Configuration
|
|
596
|
-
|
|
597
|
-
HTM uses dependency injection for LLM access, allowing you to configure embedding generation, tag extraction, logging, and token counting.
|
|
598
|
-
|
|
599
|
-
### Default Configuration
|
|
600
|
-
|
|
601
|
-
By default, HTM uses:
|
|
602
|
-
- **Embedding Provider**: Ollama (local) with `nomic-embed-text` model (768 dimensions)
|
|
603
|
-
- **Tag Provider**: Ollama (local) with `llama3` model
|
|
604
|
-
- **Logger**: Ruby's standard Logger to STDOUT at INFO level
|
|
605
|
-
- **Token Counter**: Tiktoken with GPT-3.5-turbo encoding
|
|
606
|
-
|
|
607
|
-
```ruby
|
|
608
|
-
require 'htm'
|
|
609
|
-
|
|
610
|
-
# Use defaults
|
|
611
|
-
HTM.configure # or omit this - configuration is lazy-loaded
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
### Custom Configuration
|
|
615
|
-
|
|
616
|
-
Configure HTM before creating instances:
|
|
617
|
-
|
|
618
|
-
#### Using Ollama (Default)
|
|
619
|
-
|
|
620
|
-
```ruby
|
|
621
|
-
HTM.configure do |config|
|
|
622
|
-
# Embedding configuration
|
|
623
|
-
config.embedding_provider = :ollama
|
|
624
|
-
config.embedding_model = 'nomic-embed-text' # 768 dimensions
|
|
625
|
-
config.embedding_dimensions = 768
|
|
626
|
-
config.ollama_url = 'http://localhost:11434'
|
|
627
|
-
|
|
628
|
-
# Tag extraction configuration
|
|
629
|
-
config.tag_provider = :ollama
|
|
630
|
-
config.tag_model = 'llama3'
|
|
631
|
-
|
|
632
|
-
# Logger configuration
|
|
633
|
-
config.logger = Logger.new($stdout)
|
|
634
|
-
config.logger.level = Logger::INFO
|
|
635
|
-
end
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
**Ollama Setup:**
|
|
639
|
-
```bash
|
|
640
|
-
# Install Ollama
|
|
641
|
-
curl https://ollama.ai/install.sh | sh
|
|
642
|
-
|
|
643
|
-
# Pull models
|
|
644
|
-
ollama pull nomic-embed-text # For embeddings
|
|
645
|
-
ollama pull llama3 # For tag extraction
|
|
646
|
-
|
|
647
|
-
# Verify Ollama is running
|
|
648
|
-
curl http://localhost:11434/api/version
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
#### Using OpenAI
|
|
652
|
-
|
|
653
|
-
```ruby
|
|
654
|
-
HTM.configure do |config|
|
|
655
|
-
# Embedding configuration (OpenAI)
|
|
656
|
-
config.embedding_provider = :openai
|
|
657
|
-
config.embedding_model = 'text-embedding-3-small' # 1536 dimensions
|
|
658
|
-
config.embedding_dimensions = 1536
|
|
659
|
-
|
|
660
|
-
# Tag extraction (can mix providers)
|
|
661
|
-
config.tag_provider = :openai
|
|
662
|
-
config.tag_model = 'gpt-4'
|
|
663
|
-
|
|
664
|
-
# Logger
|
|
665
|
-
config.logger = Rails.logger # Use Rails logger
|
|
666
|
-
end
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
**OpenAI Setup:**
|
|
670
|
-
```bash
|
|
671
|
-
# Set your OpenAI API key
|
|
672
|
-
export OPENAI_API_KEY='sk-your-api-key-here'
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
**Important:** The database uses pgvector with HNSW indexing, which has a maximum dimension limit of 2000. Embeddings exceeding this limit will be automatically truncated with a warning.
|
|
676
|
-
|
|
677
|
-
#### Custom Providers
|
|
678
|
-
|
|
679
|
-
Provide your own LLM implementations:
|
|
680
|
-
|
|
681
|
-
```ruby
|
|
682
|
-
HTM.configure do |config|
|
|
683
|
-
# Custom embedding generator
|
|
684
|
-
config.embedding_generator = ->(text) {
|
|
685
|
-
# Call your custom LLM service
|
|
686
|
-
response = MyEmbeddingService.generate(text)
|
|
687
|
-
|
|
688
|
-
# Must return Array<Float>
|
|
689
|
-
response[:embedding] # e.g., [0.123, -0.456, ...]
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
# Custom tag extractor
|
|
693
|
-
config.tag_extractor = ->(text, existing_ontology) {
|
|
694
|
-
# Call your custom LLM service for tag extraction
|
|
695
|
-
# existing_ontology is Array<String> of recent tags for context
|
|
696
|
-
response = MyTagService.extract(text, context: existing_ontology)
|
|
697
|
-
|
|
698
|
-
# Must return Array<String> in hierarchical format
|
|
699
|
-
# e.g., ["ai:llm:embeddings", "database:postgresql"]
|
|
700
|
-
response[:tags]
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
# Custom token counter (optional)
|
|
704
|
-
config.token_counter = ->(text) {
|
|
705
|
-
# Your token counting implementation
|
|
706
|
-
# Must return Integer
|
|
707
|
-
MyTokenizer.count(text)
|
|
708
|
-
}
|
|
709
|
-
end
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
#### Logger Configuration
|
|
713
|
-
|
|
714
|
-
Customize logging behavior:
|
|
715
|
-
|
|
716
|
-
```ruby
|
|
717
|
-
HTM.configure do |config|
|
|
718
|
-
# Use custom logger
|
|
719
|
-
config.logger = Logger.new('log/htm.log')
|
|
720
|
-
config.logger.level = Logger::DEBUG
|
|
721
|
-
|
|
722
|
-
# Or use Rails logger
|
|
723
|
-
config.logger = Rails.logger
|
|
724
|
-
|
|
725
|
-
# Or disable logging
|
|
726
|
-
config.logger = Logger.new(IO::NULL)
|
|
727
|
-
config.logger.level = Logger::FATAL
|
|
728
|
-
end
|
|
729
|
-
|
|
730
|
-
# Control log level via environment variable
|
|
731
|
-
ENV['HTM_LOG_LEVEL'] = 'DEBUG' # or INFO, WARN, ERROR
|
|
732
|
-
HTM.configure # Respects HTM_LOG_LEVEL
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
#### Service Layer Architecture
|
|
736
|
-
|
|
737
|
-
HTM uses a service layer to process LLM responses:
|
|
738
|
-
|
|
739
|
-
- **EmbeddingService**: Calls your configured `embedding_generator`, validates responses, handles padding/truncation, and formats for storage
|
|
740
|
-
- **TagService**: Calls your configured `tag_extractor`, parses responses (String or Array), validates format, and filters invalid tags
|
|
741
|
-
|
|
742
|
-
This separation allows you to provide raw LLM access while HTM handles response processing, validation, and storage formatting.
|
|
743
|
-
|
|
744
|
-
### Recall Strategies
|
|
745
|
-
|
|
746
|
-
HTM supports three retrieval strategies:
|
|
747
|
-
|
|
748
|
-
```ruby
|
|
749
|
-
# Vector similarity search (semantic) - uses configured embedding provider
|
|
750
|
-
memories = htm.recall(
|
|
751
|
-
"HTM architecture",
|
|
752
|
-
timeframe: "last week",
|
|
753
|
-
strategy: :vector # Semantic similarity using embeddings
|
|
754
|
-
)
|
|
755
|
-
|
|
756
|
-
# Full-text search (keyword matching) - PostgreSQL full-text search with trigrams
|
|
757
|
-
memories = htm.recall(
|
|
758
|
-
"database performance",
|
|
759
|
-
timeframe: "last month",
|
|
760
|
-
strategy: :fulltext # Keyword-based matching
|
|
761
|
-
)
|
|
762
|
-
|
|
763
|
-
# Hybrid (combines both) - best of both worlds
|
|
764
|
-
memories = htm.recall(
|
|
765
|
-
"testing strategies",
|
|
766
|
-
timeframe: "yesterday",
|
|
767
|
-
strategy: :hybrid # Weighted combination of vector + full-text
|
|
768
|
-
)
|
|
769
|
-
```
|
|
5
|
+
<p><strong>Give your AI tools a shared, persistent memory.</strong></p>
|
|
770
6
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
## API Reference
|
|
777
|
-
|
|
778
|
-
HTM provides a minimal, focused API with only 3 core instance methods for memory operations:
|
|
779
|
-
|
|
780
|
-
### Core Memory Operations
|
|
781
|
-
|
|
782
|
-
#### `remember(content, source: "", metadata: {})`
|
|
783
|
-
|
|
784
|
-
Store information in memory. Embeddings and tags are automatically generated asynchronously.
|
|
785
|
-
|
|
786
|
-
**Parameters:**
|
|
787
|
-
- `content` (String, required) - The information to remember. Converted to string if nil. Returns ID of last node if empty.
|
|
788
|
-
- `source` (String, optional) - Where the content came from (e.g., "user", "assistant", "system"). Defaults to empty string.
|
|
789
|
-
- `metadata` (Hash, optional) - Arbitrary key-value metadata stored as JSONB. Keys must be strings or symbols. Defaults to `{}`.
|
|
790
|
-
|
|
791
|
-
**Returns:** Integer - The node ID of the stored memory
|
|
792
|
-
|
|
793
|
-
**Example:**
|
|
794
|
-
```ruby
|
|
795
|
-
# Store with source
|
|
796
|
-
node_id = htm.remember("PostgreSQL is excellent for vector search with pgvector", source: "architect")
|
|
797
|
-
|
|
798
|
-
# Store without source (uses default empty string)
|
|
799
|
-
node_id = htm.remember("HTM uses two-tier memory architecture")
|
|
800
|
-
|
|
801
|
-
# Store with metadata
|
|
802
|
-
node_id = htm.remember(
|
|
803
|
-
"User prefers dark mode",
|
|
804
|
-
metadata: { category: "preference", priority: "high", version: 2 }
|
|
805
|
-
)
|
|
806
|
-
|
|
807
|
-
# Nil/empty handling
|
|
808
|
-
node_id = htm.remember(nil) # Returns ID of last node without creating duplicate
|
|
809
|
-
node_id = htm.remember("") # Returns ID of last node without creating duplicate
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
---
|
|
813
|
-
|
|
814
|
-
#### `recall(topic, timeframe: nil, limit: 20, strategy: :vector, with_relevance: false, query_tags: [], metadata: {})`
|
|
815
|
-
|
|
816
|
-
Retrieve memories using temporal filtering and semantic/keyword search.
|
|
817
|
-
|
|
818
|
-
**Parameters:**
|
|
819
|
-
- `topic` (String, required) - Query text for semantic/keyword matching (first positional argument)
|
|
820
|
-
- `timeframe` (Range, String, optional) - Time range to search within. Default: "last 7 days"
|
|
821
|
-
- Range: `(Time.now - 3600)..Time.now`
|
|
822
|
-
- String: `"last hour"`, `"last week"`, `"yesterday"`
|
|
823
|
-
- `limit` (Integer, optional) - Maximum number of results. Default: 20
|
|
824
|
-
- `strategy` (Symbol, optional) - Search strategy. Default: `:vector`
|
|
825
|
-
- `:vector` - Semantic search using embeddings (cosine similarity)
|
|
826
|
-
- `:fulltext` - Keyword search using PostgreSQL full-text + trigrams
|
|
827
|
-
- `:hybrid` - Weighted combination (70% vector, 30% full-text)
|
|
828
|
-
- `with_relevance` (Boolean, optional) - Include dynamic relevance scores. Default: false
|
|
829
|
-
- `query_tags` (Array<String>, optional) - Filter results by tags. Default: []
|
|
830
|
-
- `metadata` (Hash, optional) - Filter results by metadata using JSONB containment (`@>`). Default: `{}`
|
|
831
|
-
|
|
832
|
-
**Returns:** Array<Hash> - Matching memories with fields: `id`, `content`, `source`, `created_at`, `access_count`, `metadata`, (optionally `relevance`)
|
|
833
|
-
|
|
834
|
-
**Example:**
|
|
835
|
-
```ruby
|
|
836
|
-
# Basic recall with time range
|
|
837
|
-
memories = htm.recall(
|
|
838
|
-
"database architecture",
|
|
839
|
-
timeframe: (Time.now - 86400)..Time.now
|
|
840
|
-
)
|
|
841
|
-
|
|
842
|
-
# Using human-readable timeframe
|
|
843
|
-
memories = htm.recall(
|
|
844
|
-
"PostgreSQL performance",
|
|
845
|
-
timeframe: "last week",
|
|
846
|
-
strategy: :hybrid,
|
|
847
|
-
limit: 10
|
|
848
|
-
)
|
|
849
|
-
|
|
850
|
-
# With relevance scoring
|
|
851
|
-
memories = htm.recall(
|
|
852
|
-
"HTM design decisions",
|
|
853
|
-
timeframe: "last month",
|
|
854
|
-
with_relevance: true,
|
|
855
|
-
query_tags: ["architecture"]
|
|
856
|
-
)
|
|
857
|
-
# => [{ "id" => 123, "content" => "...", "relevance" => 0.92, ... }, ...]
|
|
858
|
-
|
|
859
|
-
# Filter by metadata
|
|
860
|
-
memories = htm.recall(
|
|
861
|
-
"user preferences",
|
|
862
|
-
metadata: { category: "preference" }
|
|
863
|
-
)
|
|
864
|
-
# => Returns only nodes with metadata containing { category: "preference" }
|
|
865
|
-
|
|
866
|
-
# Combine metadata with other filters
|
|
867
|
-
memories = htm.recall(
|
|
868
|
-
"settings",
|
|
869
|
-
timeframe: "last month",
|
|
870
|
-
strategy: :hybrid,
|
|
871
|
-
metadata: { priority: "high", version: 2 }
|
|
872
|
-
)
|
|
873
|
-
```
|
|
874
|
-
|
|
875
|
-
---
|
|
876
|
-
|
|
877
|
-
#### `forget(node_id, confirm: false)`
|
|
878
|
-
|
|
879
|
-
Permanently delete a memory from both working memory and long-term storage.
|
|
880
|
-
|
|
881
|
-
**Parameters:**
|
|
882
|
-
- `node_id` (Integer, required) - The ID of the node to delete
|
|
883
|
-
- `confirm` (Symbol, Boolean, required) - Must be `:confirmed` or `true` to proceed
|
|
884
|
-
|
|
885
|
-
**Returns:** Boolean - `true` if deleted, `false` if not found
|
|
886
|
-
|
|
887
|
-
**Safety:** This is the ONLY way to delete memories. Requires explicit confirmation to prevent accidental deletion.
|
|
888
|
-
|
|
889
|
-
**Example:**
|
|
890
|
-
```ruby
|
|
891
|
-
# Delete with symbol confirmation (recommended)
|
|
892
|
-
htm.forget(node_id, confirm: :confirmed)
|
|
893
|
-
|
|
894
|
-
# Delete with boolean confirmation
|
|
895
|
-
htm.forget(node_id, confirm: true)
|
|
896
|
-
|
|
897
|
-
# Will raise error - confirmation required
|
|
898
|
-
htm.forget(node_id) # ArgumentError: Must confirm deletion
|
|
899
|
-
htm.forget(node_id, confirm: false) # ArgumentError: Must confirm deletion
|
|
900
|
-
```
|
|
901
|
-
|
|
902
|
-
---
|
|
903
|
-
|
|
904
|
-
### Complete Usage Example
|
|
905
|
-
|
|
906
|
-
```ruby
|
|
907
|
-
require 'htm'
|
|
908
|
-
|
|
909
|
-
# Configure once globally (optional - uses defaults if not called)
|
|
910
|
-
HTM.configure do |config|
|
|
911
|
-
config.embedding_provider = :ollama
|
|
912
|
-
config.embedding_model = 'nomic-embed-text'
|
|
913
|
-
config.tag_provider = :ollama
|
|
914
|
-
config.tag_model = 'llama3'
|
|
915
|
-
end
|
|
916
|
-
|
|
917
|
-
# Initialize for your robot
|
|
918
|
-
htm = HTM.new(robot_name: "Assistant", working_memory_size: 128_000)
|
|
919
|
-
|
|
920
|
-
# Store information
|
|
921
|
-
htm.remember("PostgreSQL with pgvector for vector search", source: "architect")
|
|
922
|
-
htm.remember("User prefers dark mode", source: "user")
|
|
923
|
-
htm.remember("Use debug_me for debugging, not puts", source: "system")
|
|
924
|
-
|
|
925
|
-
# Retrieve by time + topic
|
|
926
|
-
recent = htm.recall(
|
|
927
|
-
"PostgreSQL",
|
|
928
|
-
timeframe: "last week",
|
|
929
|
-
strategy: :hybrid,
|
|
930
|
-
limit: 5
|
|
931
|
-
)
|
|
932
|
-
|
|
933
|
-
# Delete if needed (requires node ID from remember or recall)
|
|
934
|
-
htm.forget(node_id, confirm: :confirmed)
|
|
935
|
-
```
|
|
936
|
-
|
|
937
|
-
## Use with Rails
|
|
938
|
-
|
|
939
|
-
HTM is designed to integrate seamlessly with Ruby on Rails applications. It uses ActiveRecord models and follows Rails conventions, making it easy to add AI memory capabilities to existing Rails apps.
|
|
940
|
-
|
|
941
|
-
### Why HTM Works Well with Rails
|
|
942
|
-
|
|
943
|
-
**1. Uses ActiveRecord**
|
|
944
|
-
- HTM models are standard ActiveRecord classes with associations, validations, and scopes
|
|
945
|
-
- Follows Rails naming conventions and patterns
|
|
946
|
-
- Models are namespaced under `HTM::Models::` to avoid conflicts
|
|
947
|
-
|
|
948
|
-
**2. Standard Rails Configuration**
|
|
949
|
-
- Uses `config/database.yml` (Rails convention)
|
|
950
|
-
- Respects `RAILS_ENV` environment variable
|
|
951
|
-
- Supports ERB in configuration files
|
|
952
|
-
|
|
953
|
-
**3. Separate Database**
|
|
954
|
-
- HTM uses its own PostgreSQL database connection
|
|
955
|
-
- Won't interfere with your Rails app's database
|
|
956
|
-
- Configured with `prepared_statements: false` and `advisory_locks: false` for compatibility
|
|
957
|
-
- Thread-safe for Rails multi-threading
|
|
958
|
-
|
|
959
|
-
**4. Connection Management**
|
|
960
|
-
- Separate connection pool from your Rails app
|
|
961
|
-
- Configurable pool size and timeouts
|
|
962
|
-
- Proper cleanup and disconnection support
|
|
963
|
-
|
|
964
|
-
### Integration Steps
|
|
965
|
-
|
|
966
|
-
#### 1. Add to Gemfile
|
|
967
|
-
|
|
968
|
-
```ruby
|
|
969
|
-
gem 'htm'
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
Then run:
|
|
973
|
-
```bash
|
|
974
|
-
bundle install
|
|
975
|
-
```
|
|
976
|
-
|
|
977
|
-
#### 2. Configure Environment Variables
|
|
978
|
-
|
|
979
|
-
Add HTM database configuration to your Rails environment. You can use `.env` files (with `dotenv-rails`) or set them directly:
|
|
7
|
+
<p>
|
|
8
|
+
<a href="https://rubygems.org/gems/htm"><img src="https://img.shields.io/gem/v/htm.svg" alt="Gem Version"></a>
|
|
9
|
+
<a href="https://github.com/madbomber/htm/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
980
12
|
|
|
981
|
-
|
|
982
|
-
# .env or config/application.rb
|
|
983
|
-
HTM_DBURL=postgresql://user:pass@localhost:5432/htm_production
|
|
984
|
-
```
|
|
13
|
+
<br/>
|
|
985
14
|
|
|
986
|
-
|
|
987
|
-
```ruby
|
|
988
|
-
HTM_DBHOST=localhost
|
|
989
|
-
HTM_DBNAME=htm_production
|
|
990
|
-
HTM_DBUSER=postgres
|
|
991
|
-
HTM_DBPASS=password
|
|
992
|
-
HTM_DBPORT=5432
|
|
993
|
-
```
|
|
15
|
+
## The Problem
|
|
994
16
|
|
|
995
|
-
|
|
17
|
+
Your AI assistants are brilliant but forgetful. Claude Desktop doesn't remember what Claude Code learned. Your custom chatbot can't recall what happened yesterday. Every tool starts fresh, every session.
|
|
996
18
|
|
|
997
|
-
|
|
19
|
+
**HTM fixes this.**
|
|
998
20
|
|
|
999
|
-
|
|
1000
|
-
# config/initializers/htm.rb
|
|
21
|
+
Connect all your MCP-enabled tools to the same long-term memory. What one robot learns, all robots remember. Context persists across sessions, tools, and time.
|
|
1001
22
|
|
|
1002
|
-
|
|
1003
|
-
HTM.configure do |config|
|
|
1004
|
-
# Use Rails logger
|
|
1005
|
-
config.logger = Rails.logger
|
|
23
|
+
## What is HTM?
|
|
1006
24
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
25
|
+
- **Hierarchical**: Organizes information from simple concepts to detailed relationships
|
|
26
|
+
- **Temporal**: Retrieves memories across time—from moments ago to months past
|
|
27
|
+
- **Memory**: Encodes, stores, and recalls information with semantic understanding
|
|
1010
28
|
|
|
1011
|
-
|
|
1012
|
-
config.tag_provider = :ollama
|
|
1013
|
-
config.tag_model = 'llama3'
|
|
29
|
+
HTM is a Ruby gem that provides durable, shared memory for LLM-powered applications.
|
|
1014
30
|
|
|
1015
|
-
|
|
1016
|
-
# config.embedding_generator = ->(text) { YourEmbeddingService.call(text) }
|
|
1017
|
-
# config.tag_extractor = ->(text, ontology) { YourTagService.call(text, ontology) }
|
|
1018
|
-
end
|
|
31
|
+
## Key Capabilities
|
|
1019
32
|
|
|
1020
|
-
|
|
1021
|
-
HTM::ActiveRecordConfig.establish_connection!
|
|
33
|
+
### MCP Server — Connect Any AI Tool
|
|
1022
34
|
|
|
1023
|
-
|
|
1024
|
-
HTM::ActiveRecordConfig.verify_extensions!
|
|
35
|
+
HTM includes a Model Context Protocol server with 23 tools for memory management. Connect Claude Desktop, Claude Code, AIA, or any MCP-compatible client:
|
|
1025
36
|
|
|
1026
|
-
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"htm": {
|
|
41
|
+
"command": "htm_mcp",
|
|
42
|
+
"env": { "HTM_DBURL": "postgresql://localhost/htm" }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
1027
46
|
```
|
|
1028
47
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
Run the HTM database setup:
|
|
48
|
+
The `htm_mcp` executable includes CLI commands for database management:
|
|
1032
49
|
|
|
1033
50
|
```bash
|
|
1034
|
-
#
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
#
|
|
1038
|
-
|
|
1039
|
-
> HTM::Database.setup
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
#### 5. Use in Your Rails App
|
|
1043
|
-
|
|
1044
|
-
**In Controllers:**
|
|
1045
|
-
|
|
1046
|
-
```ruby
|
|
1047
|
-
class ChatsController < ApplicationController
|
|
1048
|
-
before_action :initialize_memory
|
|
1049
|
-
|
|
1050
|
-
def create
|
|
1051
|
-
# Add user message to memory (embeddings + tags auto-extracted asynchronously)
|
|
1052
|
-
@memory.remember(
|
|
1053
|
-
params[:message],
|
|
1054
|
-
source: "user-#{current_user.id}"
|
|
1055
|
-
)
|
|
1056
|
-
|
|
1057
|
-
# Recall relevant context for the conversation
|
|
1058
|
-
context = @memory.create_context(strategy: :balanced)
|
|
1059
|
-
|
|
1060
|
-
# Use context with your LLM to generate response
|
|
1061
|
-
response = generate_llm_response(context, params[:message])
|
|
1062
|
-
|
|
1063
|
-
# Store assistant's response
|
|
1064
|
-
@memory.remember(
|
|
1065
|
-
response,
|
|
1066
|
-
source: "assistant"
|
|
1067
|
-
)
|
|
1068
|
-
|
|
1069
|
-
render json: { response: response }
|
|
1070
|
-
end
|
|
1071
|
-
|
|
1072
|
-
private
|
|
1073
|
-
|
|
1074
|
-
def initialize_memory
|
|
1075
|
-
@memory = HTM.new(
|
|
1076
|
-
robot_name: "ChatBot-#{current_user.id}",
|
|
1077
|
-
working_memory_size: 128_000
|
|
1078
|
-
)
|
|
1079
|
-
end
|
|
1080
|
-
end
|
|
51
|
+
htm_mcp setup # Initialize database schema
|
|
52
|
+
htm_mcp verify # Check connection and migrations
|
|
53
|
+
htm_mcp stats # Show memory statistics
|
|
54
|
+
htm_mcp help # Full help with environment variables
|
|
55
|
+
htm_mcp # Start MCP server (default)
|
|
1081
56
|
```
|
|
1082
57
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
queue_as :default
|
|
1088
|
-
|
|
1089
|
-
def perform(document_id)
|
|
1090
|
-
document = Document.find(document_id)
|
|
1091
|
-
|
|
1092
|
-
# Initialize HTM for this job
|
|
1093
|
-
memory = HTM.new(robot_name: "DocumentProcessor")
|
|
1094
|
-
|
|
1095
|
-
# Store document content in memory (embeddings + tags auto-extracted asynchronously)
|
|
1096
|
-
memory.remember(
|
|
1097
|
-
document.content,
|
|
1098
|
-
source: "document-#{document.id}"
|
|
1099
|
-
)
|
|
1100
|
-
|
|
1101
|
-
# Recall related documents (uses vector similarity)
|
|
1102
|
-
related = memory.recall(
|
|
1103
|
-
document.category,
|
|
1104
|
-
timeframe: "last month",
|
|
1105
|
-
strategy: :vector
|
|
1106
|
-
)
|
|
58
|
+
Now your AI assistant can remember and recall across sessions:
|
|
59
|
+
- Store decisions, preferences, and context
|
|
60
|
+
- Search memories with natural language
|
|
61
|
+
- Build knowledge that compounds over time
|
|
1107
62
|
|
|
1108
|
-
|
|
1109
|
-
end
|
|
1110
|
-
end
|
|
1111
|
-
```
|
|
63
|
+
### Hive Mind — Shared Knowledge Across Robots
|
|
1112
64
|
|
|
1113
|
-
|
|
65
|
+
All robots share the same global memory. What one learns, all can access:
|
|
1114
66
|
|
|
1115
67
|
```ruby
|
|
1116
|
-
#
|
|
1117
|
-
|
|
1118
|
-
extend ActiveSupport::Concern
|
|
1119
|
-
|
|
1120
|
-
included do
|
|
1121
|
-
after_create :store_in_memory
|
|
1122
|
-
after_update :update_memory
|
|
1123
|
-
end
|
|
1124
|
-
|
|
1125
|
-
def store_in_memory
|
|
1126
|
-
memory = HTM.new(robot_name: "Rails-#{Rails.env}")
|
|
1127
|
-
|
|
1128
|
-
memory.remember(
|
|
1129
|
-
memory_content,
|
|
1130
|
-
source: "#{self.class.name}-#{id}"
|
|
1131
|
-
)
|
|
1132
|
-
end
|
|
1133
|
-
|
|
1134
|
-
def update_memory
|
|
1135
|
-
# Update existing memory node
|
|
1136
|
-
store_in_memory
|
|
1137
|
-
end
|
|
1138
|
-
|
|
1139
|
-
private
|
|
1140
|
-
|
|
1141
|
-
def memory_content
|
|
1142
|
-
# Override in model to customize what gets stored
|
|
1143
|
-
attributes.to_json
|
|
1144
|
-
end
|
|
1145
|
-
end
|
|
1146
|
-
|
|
1147
|
-
# Then in your model:
|
|
1148
|
-
class Article < ApplicationRecord
|
|
1149
|
-
include Memorable
|
|
68
|
+
# Claude Desktop remembers a user preference
|
|
69
|
+
claude_desktop.remember("User prefers dark mode and concise responses")
|
|
1150
70
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
"Article: #{title}\n\n#{body}\n\nCategory: #{category}"
|
|
1155
|
-
end
|
|
1156
|
-
end
|
|
71
|
+
# Later, Claude Code can recall it
|
|
72
|
+
claude_code.recall("user preferences")
|
|
73
|
+
# => "User prefers dark mode and concise responses"
|
|
1157
74
|
```
|
|
1158
75
|
|
|
1159
|
-
|
|
76
|
+
Every tool builds on what came before. No more repeating yourself.
|
|
1160
77
|
|
|
1161
|
-
|
|
78
|
+
### Long-Term Memory — Never Forget
|
|
1162
79
|
|
|
1163
|
-
|
|
1164
|
-
# config/database.yml
|
|
1165
|
-
production:
|
|
1166
|
-
primary:
|
|
1167
|
-
<<: *default
|
|
1168
|
-
database: myapp_production
|
|
80
|
+
Two-tier architecture ensures nothing is lost:
|
|
1169
81
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
encoding: unicode
|
|
1173
|
-
database: <%= ENV['HTM_DBNAME'] || 'htm_production' %>
|
|
1174
|
-
host: <%= ENV['HTM_DBHOST'] || 'localhost' %>
|
|
1175
|
-
port: <%= ENV['HTM_DBPORT'] || 5432 %>
|
|
1176
|
-
username: <%= ENV['HTM_DBUSER'] || 'postgres' %>
|
|
1177
|
-
password: <%= ENV['HTM_DBPASS'] %>
|
|
1178
|
-
pool: <%= ENV['HTM_DB_POOL_SIZE'] || 10 %>
|
|
1179
|
-
```
|
|
82
|
+
- **Working Memory**: Token-limited context for immediate use
|
|
83
|
+
- **Long-Term Memory**: Durable PostgreSQL storage with vector search
|
|
1180
84
|
|
|
1181
|
-
|
|
85
|
+
Memories persist forever unless explicitly deleted. Working memory evicts to long-term storage—it never deletes.
|
|
1182
86
|
|
|
1183
87
|
```ruby
|
|
1184
|
-
|
|
1185
|
-
HTM::ActiveRecordConfig.establish_connection!
|
|
88
|
+
htm.remember("PostgreSQL with pgvector handles our vector search")
|
|
1186
89
|
|
|
1187
|
-
#
|
|
1188
|
-
|
|
1189
|
-
#
|
|
1190
|
-
# end
|
|
90
|
+
# Months later...
|
|
91
|
+
htm.recall("database decisions", timeframe: "last year")
|
|
92
|
+
# => Still there
|
|
1191
93
|
```
|
|
1192
94
|
|
|
1193
|
-
###
|
|
1194
|
-
|
|
1195
|
-
**In RSpec:**
|
|
1196
|
-
|
|
1197
|
-
```ruby
|
|
1198
|
-
# spec/rails_helper.rb
|
|
1199
|
-
RSpec.configure do |config|
|
|
1200
|
-
config.before(:suite) do
|
|
1201
|
-
HTM::ActiveRecordConfig.establish_connection!
|
|
1202
|
-
end
|
|
1203
|
-
|
|
1204
|
-
config.after(:suite) do
|
|
1205
|
-
HTM::ActiveRecordConfig.disconnect!
|
|
1206
|
-
end
|
|
1207
|
-
end
|
|
1208
|
-
|
|
1209
|
-
# spec/features/chat_spec.rb
|
|
1210
|
-
RSpec.describe "Chat with memory", type: :feature do
|
|
1211
|
-
let(:memory) { HTM.new(robot_name: "TestBot") }
|
|
1212
|
-
|
|
1213
|
-
before do
|
|
1214
|
-
memory.remember("User prefers Ruby over Python", source: "user")
|
|
1215
|
-
end
|
|
95
|
+
### Robot Groups — High Availability
|
|
1216
96
|
|
|
1217
|
-
|
|
1218
|
-
context = memory.create_context(strategy: :balanced)
|
|
1219
|
-
expect(context).to include("Ruby")
|
|
1220
|
-
end
|
|
1221
|
-
end
|
|
1222
|
-
```
|
|
1223
|
-
|
|
1224
|
-
**In Minitest:**
|
|
97
|
+
Coordinate multiple robots with shared working memory and instant failover:
|
|
1225
98
|
|
|
1226
99
|
```ruby
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
end
|
|
1233
|
-
|
|
1234
|
-
# test/integration/chat_test.rb
|
|
1235
|
-
class ChatTest < ActionDispatch::IntegrationTest
|
|
1236
|
-
test "stores chat messages in memory" do
|
|
1237
|
-
memory = HTM.new(robot_name: "TestBot")
|
|
1238
|
-
|
|
1239
|
-
post chats_path, params: { message: "Hello!" }
|
|
1240
|
-
|
|
1241
|
-
assert_response :success
|
|
1242
|
-
|
|
1243
|
-
# Verify message was stored
|
|
1244
|
-
nodes = memory.recall("Hello", timeframe: "last hour")
|
|
1245
|
-
assert_not_empty nodes
|
|
1246
|
-
end
|
|
1247
|
-
end
|
|
1248
|
-
```
|
|
1249
|
-
|
|
1250
|
-
### Production Considerations
|
|
1251
|
-
|
|
1252
|
-
1. **Connection Pooling**: Set appropriate pool size based on your Rails app's concurrency:
|
|
1253
|
-
```ruby
|
|
1254
|
-
ENV['HTM_DB_POOL_SIZE'] = '20' # Match or exceed Rails pool
|
|
1255
|
-
```
|
|
1256
|
-
|
|
1257
|
-
2. **Separate Database Server**: For production, use a dedicated PostgreSQL instance for HTM
|
|
1258
|
-
|
|
1259
|
-
3. **Required Extensions**: Ensure `pgvector` and `pg_trgm` extensions are installed on your PostgreSQL server
|
|
1260
|
-
|
|
1261
|
-
4. **Memory Management**: HTM's working memory is per-instance. Consider using a singleton pattern or Rails cache for shared instances
|
|
1262
|
-
|
|
1263
|
-
5. **Background Processing**: Use Sidekiq or similar for embedding generation if processing large amounts of data
|
|
1264
|
-
|
|
1265
|
-
### Example Rails App Structure
|
|
1266
|
-
|
|
1267
|
-
```
|
|
1268
|
-
app/
|
|
1269
|
-
├── controllers/
|
|
1270
|
-
│ └── chats_controller.rb # Uses HTM for conversation memory
|
|
1271
|
-
├── jobs/
|
|
1272
|
-
│ └── process_document_job.rb # Background HTM processing
|
|
1273
|
-
├── models/
|
|
1274
|
-
│ ├── concerns/
|
|
1275
|
-
│ │ └── memorable.rb # HTM integration concern
|
|
1276
|
-
│ └── article.rb # Includes Memorable
|
|
1277
|
-
└── services/
|
|
1278
|
-
└── memory_service.rb # Centralized HTM access
|
|
1279
|
-
|
|
1280
|
-
config/
|
|
1281
|
-
├── initializers/
|
|
1282
|
-
│ └── htm.rb # HTM setup
|
|
1283
|
-
└── database.yml # Optional: multi-database config
|
|
1284
|
-
|
|
1285
|
-
.env
|
|
1286
|
-
HTM_DBURL=postgresql://... # HTM database connection
|
|
1287
|
-
OPENAI_API_KEY=sk-... # If using OpenAI embeddings
|
|
1288
|
-
```
|
|
1289
|
-
|
|
1290
|
-
## Environment Variables
|
|
1291
|
-
|
|
1292
|
-
HTM uses environment variables for database and service configuration. These can be set in your shell profile, a `.env` file, or exported directly.
|
|
1293
|
-
|
|
1294
|
-
### Database Configuration
|
|
1295
|
-
|
|
1296
|
-
#### HTM_DBURL (Recommended)
|
|
1297
|
-
|
|
1298
|
-
The preferred method for database configuration is a single connection URL:
|
|
1299
|
-
|
|
1300
|
-
```bash
|
|
1301
|
-
export HTM_DBURL="postgresql://username:password@host:port/dbname?sslmode=require"
|
|
1302
|
-
```
|
|
1303
|
-
|
|
1304
|
-
**Format:** `postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=MODE`
|
|
1305
|
-
|
|
1306
|
-
**Example for Local PostgreSQL:**
|
|
1307
|
-
```bash
|
|
1308
|
-
export HTM_DBURL="postgresql://postgres:postgres@localhost:5432/htm_dev?sslmode=disable"
|
|
1309
|
-
```
|
|
1310
|
-
|
|
1311
|
-
#### Individual Database Parameters (Alternative)
|
|
1312
|
-
|
|
1313
|
-
If `HTM_DBURL` is not set, HTM will use individual parameters:
|
|
1314
|
-
|
|
1315
|
-
| Variable | Description | Default | Required |
|
|
1316
|
-
|----------|-------------|---------|----------|
|
|
1317
|
-
| `HTM_DBNAME` | Database name | None | Yes |
|
|
1318
|
-
| `HTM_DBUSER` | Database username | None | Yes |
|
|
1319
|
-
| `HTM_DBPASS` | Database password | None | Yes |
|
|
1320
|
-
| `HTM_DBHOST` | Database hostname or IP | `localhost` | No |
|
|
1321
|
-
| `HTM_DBPORT` | Database port | `5432` | No |
|
|
1322
|
-
| `HTM_SERVICE_NAME` | Service identifier (informational only) | None | No |
|
|
100
|
+
group = HTM::RobotGroup.new(
|
|
101
|
+
name: 'support-ha',
|
|
102
|
+
active: ['primary'],
|
|
103
|
+
passive: ['standby']
|
|
104
|
+
)
|
|
1323
105
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
export HTM_DBNAME="htm_production"
|
|
1327
|
-
export HTM_DBUSER="postgres"
|
|
1328
|
-
export HTM_DBPASS="secure_password_here"
|
|
1329
|
-
export HTM_DBHOST="localhost"
|
|
1330
|
-
export HTM_DBPORT="5432"
|
|
1331
|
-
export HTM_SERVICE_NAME="production-db"
|
|
106
|
+
# Primary fails? Standby takes over instantly
|
|
107
|
+
group.failover!
|
|
1332
108
|
```
|
|
1333
109
|
|
|
1334
|
-
|
|
1335
|
-
1. `HTM_DBURL` (if set, this takes precedence)
|
|
1336
|
-
2. Individual parameters (`HTM_DBNAME`, `HTM_DBUSER`, etc.)
|
|
1337
|
-
3. Direct configuration passed to `HTM.new(db_config: {...})`
|
|
1338
|
-
|
|
1339
|
-
### LLM Provider Configuration
|
|
1340
|
-
|
|
1341
|
-
HTM configuration is done programmatically via `HTM.configure` (see [Configuration](#configuration) section). However, a few environment variables are still used:
|
|
1342
|
-
|
|
1343
|
-
#### OPENAI_API_KEY
|
|
110
|
+
Real-time synchronization via PostgreSQL LISTEN/NOTIFY. Add or remove robots dynamically.
|
|
1344
111
|
|
|
1345
|
-
|
|
112
|
+
## Quick Start
|
|
1346
113
|
|
|
1347
114
|
```bash
|
|
1348
|
-
|
|
115
|
+
gem install htm
|
|
1349
116
|
```
|
|
1350
117
|
|
|
1351
|
-
This is required when you configure:
|
|
1352
118
|
```ruby
|
|
1353
|
-
|
|
1354
|
-
config.embedding_provider = :openai
|
|
1355
|
-
# or
|
|
1356
|
-
config.tag_provider = :openai
|
|
1357
|
-
end
|
|
1358
|
-
```
|
|
1359
|
-
|
|
1360
|
-
#### OLLAMA_URL
|
|
1361
|
-
|
|
1362
|
-
Custom Ollama server URL (optional):
|
|
1363
|
-
|
|
1364
|
-
```bash
|
|
1365
|
-
export OLLAMA_URL="http://localhost:11434" # Default
|
|
1366
|
-
export OLLAMA_URL="http://ollama-server:11434" # Custom
|
|
1367
|
-
```
|
|
1368
|
-
|
|
1369
|
-
HTM defaults to `http://localhost:11434` if not set.
|
|
1370
|
-
|
|
1371
|
-
#### HTM_LOG_LEVEL
|
|
1372
|
-
|
|
1373
|
-
Control logging verbosity:
|
|
1374
|
-
|
|
1375
|
-
```bash
|
|
1376
|
-
export HTM_LOG_LEVEL="DEBUG" # DEBUG, INFO, WARN, ERROR, FATAL
|
|
1377
|
-
export HTM_LOG_LEVEL="INFO" # Default
|
|
1378
|
-
```
|
|
1379
|
-
|
|
1380
|
-
This is used by the default logger when `HTM.configure` is called without a custom logger.
|
|
1381
|
-
|
|
1382
|
-
#### HTM_TELEMETRY_ENABLED
|
|
1383
|
-
|
|
1384
|
-
Enable OpenTelemetry metrics collection:
|
|
1385
|
-
|
|
1386
|
-
```bash
|
|
1387
|
-
export HTM_TELEMETRY_ENABLED="true" # Enable telemetry
|
|
1388
|
-
export HTM_TELEMETRY_ENABLED="false" # Disable (default)
|
|
1389
|
-
```
|
|
1390
|
-
|
|
1391
|
-
When enabled, HTM emits metrics to configured OpenTelemetry collectors. See [Telemetry](#telemetry-opentelemetry) for details.
|
|
1392
|
-
|
|
1393
|
-
### Quick Setup Examples
|
|
1394
|
-
|
|
1395
|
-
#### Local Development (PostgreSQL)
|
|
1396
|
-
|
|
1397
|
-
```bash
|
|
1398
|
-
# Simple local setup
|
|
1399
|
-
export HTM_DBURL="postgresql://postgres:postgres@localhost:5432/htm_dev?sslmode=disable"
|
|
1400
|
-
|
|
1401
|
-
# With custom user
|
|
1402
|
-
export HTM_DBURL="postgresql://myuser:mypass@localhost:5432/htm_dev"
|
|
1403
|
-
```
|
|
1404
|
-
|
|
1405
|
-
#### With OpenAI Embeddings
|
|
1406
|
-
|
|
1407
|
-
```bash
|
|
1408
|
-
# Database + OpenAI
|
|
1409
|
-
export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"
|
|
1410
|
-
export OPENAI_API_KEY="sk-proj-your-api-key-here"
|
|
1411
|
-
```
|
|
1412
|
-
|
|
1413
|
-
### Persistent Configuration
|
|
1414
|
-
|
|
1415
|
-
To make environment variables permanent, add them to your shell profile:
|
|
1416
|
-
|
|
1417
|
-
**For Bash (~/.bashrc or ~/.bash_profile):**
|
|
1418
|
-
```bash
|
|
1419
|
-
echo 'export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"' >> ~/.bashrc
|
|
1420
|
-
source ~/.bashrc
|
|
1421
|
-
```
|
|
1422
|
-
|
|
1423
|
-
**For Zsh (~/.zshrc):**
|
|
1424
|
-
```bash
|
|
1425
|
-
echo 'export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"' >> ~/.zshrc
|
|
1426
|
-
source ~/.zshrc
|
|
1427
|
-
```
|
|
1428
|
-
|
|
1429
|
-
**Or create a dedicated file (~/.bashrc__htm):**
|
|
1430
|
-
```bash
|
|
1431
|
-
# ~/.bashrc__htm
|
|
1432
|
-
export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"
|
|
1433
|
-
export OPENAI_API_KEY="sk-proj-your-api-key"
|
|
1434
|
-
export HTM_SERVICE_NAME="my-service"
|
|
1435
|
-
|
|
1436
|
-
# Then source it from your main profile
|
|
1437
|
-
echo 'source ~/.bashrc__htm' >> ~/.bashrc
|
|
1438
|
-
```
|
|
1439
|
-
|
|
1440
|
-
### Verification
|
|
1441
|
-
|
|
1442
|
-
Verify your configuration:
|
|
1443
|
-
|
|
1444
|
-
```bash
|
|
1445
|
-
# Check database connection
|
|
1446
|
-
ruby test_connection.rb
|
|
1447
|
-
|
|
1448
|
-
# Or in Ruby
|
|
1449
|
-
irb -r ./lib/htm
|
|
1450
|
-
> HTM::Database.default_config
|
|
1451
|
-
> # Should return a hash with your connection parameters
|
|
1452
|
-
```
|
|
1453
|
-
|
|
1454
|
-
## Development
|
|
1455
|
-
|
|
1456
|
-
After checking out the repo, run:
|
|
1457
|
-
|
|
1458
|
-
```bash
|
|
1459
|
-
# Install dependencies
|
|
1460
|
-
bundle install
|
|
1461
|
-
|
|
1462
|
-
# Enable direnv (loads .envrc environment variables)
|
|
1463
|
-
direnv allow
|
|
1464
|
-
|
|
1465
|
-
# Run tests
|
|
1466
|
-
rake test
|
|
1467
|
-
|
|
1468
|
-
# Run example
|
|
1469
|
-
ruby examples/basic_usage.rb
|
|
1470
|
-
```
|
|
1471
|
-
|
|
1472
|
-
**Important**: Make sure `direnv allow` has been run once in the project directory to load database environment variables from `.envrc`. Alternatively, you can manually export the environment variables:
|
|
1473
|
-
|
|
1474
|
-
```bash
|
|
1475
|
-
export HTM_DBURL="postgresql://user:password@host:port/dbname?sslmode=require"
|
|
1476
|
-
```
|
|
1477
|
-
|
|
1478
|
-
### HTM Rake Tasks Reference
|
|
1479
|
-
|
|
1480
|
-
HTM provides comprehensive rake tasks for database management, documentation, file loading, job processing, and tag management. Use `rake -T htm` to see all available tasks.
|
|
1481
|
-
|
|
1482
|
-
#### Database Tasks (`htm:db:*`)
|
|
1483
|
-
|
|
1484
|
-
| Task | Description |
|
|
1485
|
-
|------|-------------|
|
|
1486
|
-
| `rake htm:db:setup` | Set up HTM database schema and run migrations (sets up from scratch) |
|
|
1487
|
-
| `rake htm:db:create` | Create database if it doesn't exist (respects RAILS_ENV) |
|
|
1488
|
-
| `rake htm:db:migrate` | Run pending database migrations |
|
|
1489
|
-
| `rake htm:db:status` | Show migration status |
|
|
1490
|
-
| `rake htm:db:verify` | Verify database connection (respects RAILS_ENV) |
|
|
1491
|
-
| `rake htm:db:info` | Show database info (size, tables, extensions) |
|
|
1492
|
-
| `rake htm:db:stats` | Show record counts for all HTM tables |
|
|
1493
|
-
| `rake htm:db:console` | Open PostgreSQL console (respects RAILS_ENV) |
|
|
1494
|
-
| `rake htm:db:seed` | Seed database with sample data |
|
|
1495
|
-
| `rake htm:db:schema:dump` | Dump current schema to db/schema.sql |
|
|
1496
|
-
| `rake htm:db:schema:load` | Load schema from db/schema.sql |
|
|
1497
|
-
| `rake htm:db:rebuild:embeddings` | Rebuild embeddings for all nodes |
|
|
1498
|
-
| `rake htm:db:rebuild:propositions` | Rebuild propositions for all non-proposition nodes |
|
|
1499
|
-
| `rake htm:db:tags:cleanup` | Soft delete orphaned tags and stale node_tags entries |
|
|
1500
|
-
| `rake htm:db:drop` | Drop all HTM tables (WARNING: destructive!) |
|
|
1501
|
-
| `rake htm:db:reset` | Drop and recreate database (WARNING: destructive!) |
|
|
1502
|
-
|
|
1503
|
-
#### Documentation Tasks (`htm:doc:*`)
|
|
1504
|
-
|
|
1505
|
-
| Task | Description |
|
|
1506
|
-
|------|-------------|
|
|
1507
|
-
| `rake htm:doc:all` | Generate DB docs, YARD API docs, build site, and serve |
|
|
1508
|
-
| `rake htm:doc:yard` | Build YARD API documentation (markdown format for MkDocs) |
|
|
1509
|
-
| `rake htm:doc:db` | Generate/update database documentation in docs/database/ |
|
|
1510
|
-
| `rake htm:doc:build` | Build documentation site with MkDocs |
|
|
1511
|
-
| `rake htm:doc:serve` | Serve documentation site locally with MkDocs |
|
|
1512
|
-
| `rake htm:doc:server[port]` | Start YARD documentation server (live reload) |
|
|
1513
|
-
| `rake htm:doc:fix_anchors` | Fix YARD anchor links for MkDocs compatibility |
|
|
1514
|
-
| `rake htm:doc:stats` | Show documentation coverage statistics |
|
|
1515
|
-
| `rake htm:doc:clean` | Clean generated documentation |
|
|
1516
|
-
|
|
1517
|
-
#### File Loading Tasks (`htm:files:*`)
|
|
1518
|
-
|
|
1519
|
-
| Task | Description |
|
|
1520
|
-
|------|-------------|
|
|
1521
|
-
| `rake htm:files:load[path]` | Load a markdown file into long-term memory |
|
|
1522
|
-
| `rake htm:files:load_dir[path,pattern]` | Load all markdown files from a directory |
|
|
1523
|
-
| `rake htm:files:list` | List all loaded file sources |
|
|
1524
|
-
| `rake htm:files:info[path]` | Show details for a loaded file |
|
|
1525
|
-
| `rake htm:files:sync` | Sync all loaded files (reload changed files) |
|
|
1526
|
-
| `rake htm:files:unload[path]` | Unload a file from memory |
|
|
1527
|
-
| `rake htm:files:stats` | Show file loading statistics |
|
|
1528
|
-
|
|
1529
|
-
#### Job Processing Tasks (`htm:jobs:*`)
|
|
1530
|
-
|
|
1531
|
-
| Task | Description |
|
|
1532
|
-
|------|-------------|
|
|
1533
|
-
| `rake htm:jobs:stats` | Show statistics for nodes and async job processing |
|
|
1534
|
-
| `rake htm:jobs:process_embeddings` | Process pending embedding jobs for nodes without embeddings |
|
|
1535
|
-
| `rake htm:jobs:process_tags` | Process pending tag extraction jobs for nodes without tags |
|
|
1536
|
-
| `rake htm:jobs:process_all` | Process all pending jobs (embeddings and tags) |
|
|
1537
|
-
| `rake htm:jobs:reprocess_embeddings` | Reprocess embeddings for all nodes (force regeneration) |
|
|
1538
|
-
| `rake htm:jobs:failed` | Show nodes that failed async processing |
|
|
1539
|
-
| `rake htm:jobs:clear_all` | Clear all embeddings and tags (for testing/development) |
|
|
1540
|
-
|
|
1541
|
-
#### Tag Management Tasks (`htm:tags:*`)
|
|
1542
|
-
|
|
1543
|
-
| Task | Description |
|
|
1544
|
-
|------|-------------|
|
|
1545
|
-
| `rake htm:tags:tree[prefix]` | Display tags as a hierarchical tree (text format) |
|
|
1546
|
-
| `rake htm:tags:mermaid[prefix]` | Export tags as Mermaid flowchart to tags.md |
|
|
1547
|
-
| `rake htm:tags:svg[prefix]` | Export tags as SVG visualization to tags.svg |
|
|
1548
|
-
| `rake htm:tags:export[prefix]` | Export tags in all formats (tags.txt, tags.md, tags.svg) |
|
|
1549
|
-
| `rake htm:tags:rebuild` | Rebuild all tags from node content |
|
|
1550
|
-
|
|
1551
|
-
### Common Workflows
|
|
1552
|
-
|
|
1553
|
-
```bash
|
|
1554
|
-
# Initial setup (first time)
|
|
1555
|
-
direnv allow
|
|
1556
|
-
rake htm:db:setup # Creates schema and runs migrations
|
|
1557
|
-
|
|
1558
|
-
# After pulling new migrations
|
|
1559
|
-
rake htm:db:migrate # Run pending migrations
|
|
1560
|
-
|
|
1561
|
-
# Check database state
|
|
1562
|
-
rake htm:db:status # See migration status
|
|
1563
|
-
rake htm:db:info # See database details
|
|
1564
|
-
rake htm:db:stats # See record counts
|
|
1565
|
-
|
|
1566
|
-
# Monitor async processing
|
|
1567
|
-
rake htm:jobs:stats
|
|
1568
|
-
rake htm:jobs:failed # Show stuck jobs
|
|
1569
|
-
|
|
1570
|
-
# Process pending jobs manually
|
|
1571
|
-
rake htm:jobs:process_all
|
|
1572
|
-
|
|
1573
|
-
# Generate documentation
|
|
1574
|
-
rake htm:doc:all # Full documentation build
|
|
1575
|
-
rake htm:doc:serve # Local preview
|
|
1576
|
-
|
|
1577
|
-
# Load files into memory
|
|
1578
|
-
rake 'htm:files:load[docs/guide.md]'
|
|
1579
|
-
rake 'htm:files:load_dir[docs/,**/*.md]'
|
|
1580
|
-
|
|
1581
|
-
# View tag hierarchy
|
|
1582
|
-
rake htm:tags:tree
|
|
1583
|
-
rake 'htm:tags:svg[database]' # Export filtered tags
|
|
1584
|
-
|
|
1585
|
-
# Reset database (development only!)
|
|
1586
|
-
rake htm:db:reset # Drops and recreates everything
|
|
1587
|
-
|
|
1588
|
-
# Open database console for debugging
|
|
1589
|
-
rake htm:db:console # Opens psql
|
|
1590
|
-
```
|
|
1591
|
-
|
|
1592
|
-
See the [Async Job Processing](#async-job-processing) section for more details on background jobs.
|
|
1593
|
-
|
|
1594
|
-
## Testing
|
|
119
|
+
require 'htm'
|
|
1595
120
|
|
|
1596
|
-
|
|
121
|
+
# Initialize
|
|
122
|
+
htm = HTM.new(robot_name: "MyAssistant")
|
|
1597
123
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
rake test
|
|
124
|
+
# Remember
|
|
125
|
+
htm.remember("User's project uses Rails 7 with PostgreSQL")
|
|
1601
126
|
|
|
1602
|
-
#
|
|
1603
|
-
|
|
127
|
+
# Recall
|
|
128
|
+
memories = htm.recall("tech stack", timeframe: "last month")
|
|
1604
129
|
```
|
|
1605
130
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
See [htm_teamwork.md](htm_teamwork.md) for detailed design documentation and planning notes.
|
|
1609
|
-
|
|
1610
|
-
### Key Components
|
|
1611
|
-
|
|
1612
|
-
- **HTM**: Main API class that coordinates all components and provides the primary interface
|
|
1613
|
-
- **Configuration**: Dependency injection for LLM providers (embedding_generator, tag_extractor, token_counter, logger)
|
|
1614
|
-
- **WorkingMemory**: In-memory, token-limited active context for immediate LLM use
|
|
1615
|
-
- **LongTermMemory**: PostgreSQL-backed permanent storage with connection pooling
|
|
1616
|
-
- **EmbeddingService**: Processing and validation layer for vector embeddings (calls configured `embedding_generator`)
|
|
1617
|
-
- **TagService**: Processing and validation layer for hierarchical tags (calls configured `tag_extractor`)
|
|
1618
|
-
- **Database**: Schema setup, migrations, and management
|
|
1619
|
-
- **Background Jobs**: Async processing for embedding generation and tag extraction
|
|
131
|
+
For MCP integration, database setup, and configuration options, see the [full documentation](https://madbomber.github.io/htm).
|
|
1620
132
|
|
|
1621
|
-
|
|
133
|
+
## Features at a Glance
|
|
1622
134
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
135
|
+
| Feature | Description |
|
|
136
|
+
|---------|-------------|
|
|
137
|
+
| **MCP Server** | 23 tools for AI assistant integration |
|
|
138
|
+
| **Hive Mind** | Shared memory across all robots |
|
|
139
|
+
| **Vector Search** | Semantic retrieval with pgvector |
|
|
140
|
+
| **Hybrid Search** | Combines vector + full-text matching |
|
|
141
|
+
| **Temporal Queries** | "last week", "yesterday", date ranges |
|
|
142
|
+
| **Auto-Tagging** | LLM extracts hierarchical tags automatically |
|
|
143
|
+
| **Robot Groups** | High-availability with failover |
|
|
144
|
+
| **Rails Integration** | Auto-configures via Railtie, uses ActiveJob |
|
|
145
|
+
| **Telemetry** | Optional OpenTelemetry metrics |
|
|
1627
146
|
|
|
1628
|
-
|
|
1629
|
-
- `content`: The memory content
|
|
1630
|
-
- `embedding`: Vector embedding for semantic search (up to 2000 dimensions)
|
|
1631
|
-
- `metadata`: JSONB column for arbitrary key-value data (filterable via `@>` containment operator)
|
|
1632
|
-
- `content_hash`: SHA-256 hash for deduplication
|
|
147
|
+
## Requirements
|
|
1633
148
|
|
|
1634
|
-
|
|
149
|
+
- Ruby 3.0+
|
|
150
|
+
- PostgreSQL with pgvector and pg_trgm extensions
|
|
151
|
+
- Ollama (default) or any local/private provider for embeddings and classifications
|
|
1635
152
|
|
|
1636
|
-
|
|
153
|
+
## Documentation
|
|
1637
154
|
|
|
1638
|
-
|
|
1639
|
-
2. **Service Layer** (`EmbeddingService`, `TagService`): Processes and validates LLM responses, handles padding/truncation, formats for storage
|
|
1640
|
-
3. **Consumer Layer** (`GenerateEmbeddingJob`, `GenerateTagsJob`, rake tasks): Uses services for async or synchronous processing
|
|
1641
|
-
|
|
1642
|
-
This separation allows you to provide any LLM implementation while HTM handles response processing, validation, and storage.
|
|
1643
|
-
|
|
1644
|
-
## Roadmap
|
|
1645
|
-
|
|
1646
|
-
- [x] Phase 1: Foundation (basic two-tier memory)
|
|
1647
|
-
- [x] Phase 2: RAG retrieval (semantic search)
|
|
1648
|
-
- [x] Phase 3: Relationships & tags
|
|
1649
|
-
- [x] Phase 4: Working memory management
|
|
1650
|
-
- [x] Phase 5: Hive mind features
|
|
1651
|
-
- [x] Phase 6: Operations & observability
|
|
1652
|
-
- [x] Phase 7: Advanced features
|
|
1653
|
-
- [x] Phase 8: Production-ready gem
|
|
155
|
+
**[https://madbomber.github.io/htm](https://madbomber.github.io/htm)**
|
|
1654
156
|
|
|
157
|
+
- [Installation & Setup](https://madbomber.github.io/htm/getting-started/)
|
|
158
|
+
- [MCP Server Guide](https://madbomber.github.io/htm/guides/mcp-server/)
|
|
159
|
+
- [Rails Integration](https://madbomber.github.io/htm/guides/rails/)
|
|
160
|
+
- [API Reference](https://madbomber.github.io/htm/api/)
|
|
1655
161
|
|
|
1656
162
|
## Why "Robots" Instead of "Agents"?
|
|
1657
163
|
|
|
@@ -1677,7 +183,6 @@ HTM uses "robots" rather than the fashionable "agents" for several reasons:
|
|
|
1677
183
|
|
|
1678
184
|
Honest terminology leads to clearer thinking. These are robots: tireless workers with perfect memory, executing instructions on our behalf. That's exactly what HTM helps them do better.
|
|
1679
185
|
|
|
1680
|
-
|
|
1681
186
|
## Contributing
|
|
1682
187
|
|
|
1683
188
|
Bug reports and pull requests are welcome on GitHub at https://github.com/madbomber/htm.
|