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.
- checksums.yaml +7 -0
- data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
- data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
- data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
- data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
- data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
- data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
- data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
- data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
- data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
- data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
- data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
- data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
- data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
- data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
- data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
- data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
- data/.architecture/members.yml +144 -0
- data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
- data/.architecture/reviews/initial-system-analysis.md +330 -0
- data/.envrc +32 -0
- data/.irbrc +145 -0
- data/CHANGELOG.md +150 -0
- data/COMMITS.md +196 -0
- data/LICENSE +21 -0
- data/README.md +1347 -0
- data/Rakefile +51 -0
- data/SETUP.md +268 -0
- data/config/database.yml +67 -0
- data/db/migrate/20250101000001_enable_extensions.rb +14 -0
- data/db/migrate/20250101000002_create_robots.rb +14 -0
- data/db/migrate/20250101000003_create_nodes.rb +42 -0
- data/db/migrate/20250101000005_create_tags.rb +38 -0
- data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
- data/db/schema.sql +473 -0
- data/db/seed_data/README.md +100 -0
- data/db/seed_data/presidents.md +136 -0
- data/db/seed_data/states.md +151 -0
- data/db/seeds.rb +208 -0
- data/dbdoc/README.md +173 -0
- data/dbdoc/public.node_stats.md +48 -0
- data/dbdoc/public.node_stats.svg +41 -0
- data/dbdoc/public.node_tags.md +40 -0
- data/dbdoc/public.node_tags.svg +112 -0
- data/dbdoc/public.nodes.md +54 -0
- data/dbdoc/public.nodes.svg +118 -0
- data/dbdoc/public.nodes_tags.md +39 -0
- data/dbdoc/public.nodes_tags.svg +112 -0
- data/dbdoc/public.ontology_structure.md +48 -0
- data/dbdoc/public.ontology_structure.svg +38 -0
- data/dbdoc/public.operations_log.md +42 -0
- data/dbdoc/public.operations_log.svg +130 -0
- data/dbdoc/public.relationships.md +39 -0
- data/dbdoc/public.relationships.svg +41 -0
- data/dbdoc/public.robot_activity.md +46 -0
- data/dbdoc/public.robot_activity.svg +35 -0
- data/dbdoc/public.robots.md +35 -0
- data/dbdoc/public.robots.svg +90 -0
- data/dbdoc/public.schema_migrations.md +29 -0
- data/dbdoc/public.schema_migrations.svg +26 -0
- data/dbdoc/public.tags.md +35 -0
- data/dbdoc/public.tags.svg +60 -0
- data/dbdoc/public.topic_relationships.md +45 -0
- data/dbdoc/public.topic_relationships.svg +32 -0
- data/dbdoc/schema.json +1437 -0
- data/dbdoc/schema.svg +154 -0
- data/docs/api/database.md +806 -0
- data/docs/api/embedding-service.md +532 -0
- data/docs/api/htm.md +797 -0
- data/docs/api/index.md +259 -0
- data/docs/api/long-term-memory.md +1096 -0
- data/docs/api/working-memory.md +665 -0
- data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
- data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
- data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
- data/docs/architecture/adrs/004-hive-mind.md +437 -0
- data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
- data/docs/architecture/adrs/006-context-assembly.md +496 -0
- data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
- data/docs/architecture/adrs/008-robot-identification.md +625 -0
- data/docs/architecture/adrs/009-never-forget.md +648 -0
- data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
- data/docs/architecture/adrs/011-pgai-integration.md +494 -0
- data/docs/architecture/adrs/index.md +215 -0
- data/docs/architecture/hive-mind.md +736 -0
- data/docs/architecture/index.md +351 -0
- data/docs/architecture/overview.md +538 -0
- data/docs/architecture/two-tier-memory.md +873 -0
- data/docs/assets/css/custom.css +83 -0
- data/docs/assets/images/htm-core-components.svg +63 -0
- data/docs/assets/images/htm-database-schema.svg +93 -0
- data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
- data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
- data/docs/assets/images/htm-layered-architecture.svg +71 -0
- data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
- data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
- data/docs/assets/images/htm.jpg +0 -0
- data/docs/assets/images/htm_demo.gif +0 -0
- data/docs/assets/js/mathjax.js +18 -0
- data/docs/assets/videos/htm_video.mp4 +0 -0
- data/docs/database_rake_tasks.md +322 -0
- data/docs/development/contributing.md +787 -0
- data/docs/development/index.md +336 -0
- data/docs/development/schema.md +596 -0
- data/docs/development/setup.md +719 -0
- data/docs/development/testing.md +819 -0
- data/docs/guides/adding-memories.md +824 -0
- data/docs/guides/context-assembly.md +1009 -0
- data/docs/guides/getting-started.md +577 -0
- data/docs/guides/index.md +118 -0
- data/docs/guides/long-term-memory.md +941 -0
- data/docs/guides/multi-robot.md +866 -0
- data/docs/guides/recalling-memories.md +927 -0
- data/docs/guides/search-strategies.md +953 -0
- data/docs/guides/working-memory.md +717 -0
- data/docs/index.md +214 -0
- data/docs/installation.md +477 -0
- data/docs/multi_framework_support.md +519 -0
- data/docs/quick-start.md +655 -0
- data/docs/setup_local_database.md +302 -0
- data/docs/using_rake_tasks_in_your_app.md +383 -0
- data/examples/basic_usage.rb +93 -0
- data/examples/cli_app/README.md +317 -0
- data/examples/cli_app/htm_cli.rb +270 -0
- data/examples/custom_llm_configuration.rb +183 -0
- data/examples/example_app/Rakefile +71 -0
- data/examples/example_app/app.rb +206 -0
- data/examples/sinatra_app/Gemfile +21 -0
- data/examples/sinatra_app/app.rb +335 -0
- data/lib/htm/active_record_config.rb +113 -0
- data/lib/htm/configuration.rb +342 -0
- data/lib/htm/database.rb +594 -0
- data/lib/htm/embedding_service.rb +115 -0
- data/lib/htm/errors.rb +34 -0
- data/lib/htm/job_adapter.rb +154 -0
- data/lib/htm/jobs/generate_embedding_job.rb +65 -0
- data/lib/htm/jobs/generate_tags_job.rb +82 -0
- data/lib/htm/long_term_memory.rb +965 -0
- data/lib/htm/models/node.rb +109 -0
- data/lib/htm/models/node_tag.rb +33 -0
- data/lib/htm/models/robot.rb +52 -0
- data/lib/htm/models/tag.rb +76 -0
- data/lib/htm/railtie.rb +76 -0
- data/lib/htm/sinatra.rb +157 -0
- data/lib/htm/tag_service.rb +135 -0
- data/lib/htm/tasks.rb +38 -0
- data/lib/htm/version.rb +5 -0
- data/lib/htm/working_memory.rb +182 -0
- data/lib/htm.rb +400 -0
- data/lib/tasks/db.rake +19 -0
- data/lib/tasks/htm.rake +147 -0
- data/lib/tasks/jobs.rake +312 -0
- data/mkdocs.yml +190 -0
- data/scripts/install_local_database.sh +309 -0
- metadata +341 -0
data/README.md
ADDED
|
@@ -0,0 +1,1347 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>HTM</h1>
|
|
3
|
+
<!-- img src="docs/assets/images/htm.jpg" alt="HTM - Hierarchical Temporal Memory" width="600" -->
|
|
4
|
+
<img src="docs/assets/images/htm_demo.gif" alt="Tree of Knowledge is Growing" width="400">
|
|
5
|
+
|
|
6
|
+
<p>A hierarchical and temporal system for encoding, storing, and retrieving information—operating across varying levels of abstraction (from simple to detailed concepts and their relationships) and across time (from the present to the past).<br/>
|
|
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
|
+
- Supports Ollama (local, default) and OpenAI
|
|
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
|
+
- **LLM-Driven Tag Extraction**
|
|
55
|
+
- Automatic hierarchical tag extraction from content
|
|
56
|
+
- Tags in colon-delimited format (e.g., `database:postgresql:performance`)
|
|
57
|
+
- LLM-powered asynchronous processing
|
|
58
|
+
- Enables both structured navigation and semantic discovery
|
|
59
|
+
- Complements vector embeddings (symbolic + sub-symbolic retrieval)
|
|
60
|
+
|
|
61
|
+
- **Knowledge Graph**
|
|
62
|
+
- Tag-based categorization
|
|
63
|
+
- Hierarchical tag structures
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
Add this line to your application's Gemfile:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
gem 'htm'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
And then execute:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
bundle install
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or install it yourself as:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
gem install htm
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Setup
|
|
86
|
+
|
|
87
|
+
### 1. Database Configuration
|
|
88
|
+
|
|
89
|
+
HTM uses PostgreSQL for durable long-term memory storage. Set up your database connection via environment variables:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Preferred: Full connection URL
|
|
93
|
+
export HTM_DBURL="postgresql://user:password@host:port/dbname?sslmode=require"
|
|
94
|
+
|
|
95
|
+
# Alternative: Individual parameters
|
|
96
|
+
export HTM_DBNAME="htm_production"
|
|
97
|
+
export HTM_DBUSER="postgres"
|
|
98
|
+
export HTM_DBPASS="your_password"
|
|
99
|
+
export HTM_DBHOST="localhost"
|
|
100
|
+
export HTM_DBPORT="5432"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See the [Environment Variables](#environment-variables) section below for complete details.
|
|
104
|
+
|
|
105
|
+
### 2. Configure LLM Providers
|
|
106
|
+
|
|
107
|
+
HTM uses LLM providers for embedding generation and tag extraction. By default, it uses Ollama (local).
|
|
108
|
+
|
|
109
|
+
**Start Ollama (Default Configuration)**:
|
|
110
|
+
```bash
|
|
111
|
+
# Install Ollama
|
|
112
|
+
curl https://ollama.ai/install.sh | sh
|
|
113
|
+
|
|
114
|
+
# Start Ollama and pull models
|
|
115
|
+
ollama serve
|
|
116
|
+
ollama pull nomic-embed-text # For embeddings (768 dimensions)
|
|
117
|
+
ollama pull llama3 # For tag extraction
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Configure HTM** (optional - uses defaults if not configured):
|
|
121
|
+
```ruby
|
|
122
|
+
require 'htm'
|
|
123
|
+
|
|
124
|
+
# Use defaults (Ollama with nomic-embed-text and llama3)
|
|
125
|
+
HTM.configure
|
|
126
|
+
|
|
127
|
+
# Or customize providers
|
|
128
|
+
HTM.configure do |config|
|
|
129
|
+
# Embedding configuration
|
|
130
|
+
config.embedding_provider = :ollama # or :openai
|
|
131
|
+
config.embedding_model = 'nomic-embed-text'
|
|
132
|
+
config.embedding_dimensions = 768
|
|
133
|
+
config.ollama_url = 'http://localhost:11434'
|
|
134
|
+
|
|
135
|
+
# Tag extraction configuration
|
|
136
|
+
config.tag_provider = :ollama # or :openai
|
|
137
|
+
config.tag_model = 'llama3'
|
|
138
|
+
|
|
139
|
+
# Logger configuration (optional)
|
|
140
|
+
config.logger = Logger.new($stdout)
|
|
141
|
+
config.logger.level = Logger::INFO
|
|
142
|
+
|
|
143
|
+
# Custom embedding generator (advanced)
|
|
144
|
+
config.embedding_generator = ->(text) {
|
|
145
|
+
# Your custom implementation
|
|
146
|
+
# Must return Array<Float>
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Custom tag extractor (advanced)
|
|
150
|
+
config.tag_extractor = ->(text, ontology) {
|
|
151
|
+
# Your custom implementation
|
|
152
|
+
# Must return Array<String>
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Token counter (optional, defaults to Tiktoken)
|
|
156
|
+
config.token_counter = ->(text) {
|
|
157
|
+
# Your custom implementation
|
|
158
|
+
# Must return Integer
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See the [Configuration](#configuration) section below for complete details.
|
|
164
|
+
|
|
165
|
+
### 3. Initialize Database Schema
|
|
166
|
+
|
|
167
|
+
**Using Rake Tasks (Recommended)**:
|
|
168
|
+
|
|
169
|
+
HTM provides comprehensive rake tasks for database management. Add this to your application's `Rakefile`:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
require 'htm/tasks'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Then use the tasks:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Set up database schema and run migrations
|
|
179
|
+
rake htm:db:setup
|
|
180
|
+
|
|
181
|
+
# Verify connection
|
|
182
|
+
rake htm:db:test
|
|
183
|
+
|
|
184
|
+
# Check database status
|
|
185
|
+
rake htm:db:info
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Or programmatically**:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
require 'htm'
|
|
192
|
+
|
|
193
|
+
# Run once to set up database schema
|
|
194
|
+
HTM::Database.setup
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Or from command line**:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
ruby -r ./lib/htm -e "HTM::Database.setup"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
See [Using HTM Rake Tasks in Your Application](docs/using_rake_tasks_in_your_app.md) for complete integration guide.
|
|
204
|
+
|
|
205
|
+
### 4. Verify Setup
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
rake htm:db:test # Using rake tasks
|
|
209
|
+
# or
|
|
210
|
+
ruby test_connection.rb
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
See [docs/setup_local_database.md](docs/setup_local_database.md) for detailed local database setup instructions.
|
|
214
|
+
|
|
215
|
+
## Usage
|
|
216
|
+
|
|
217
|
+
### Basic Example
|
|
218
|
+
|
|
219
|
+
```ruby
|
|
220
|
+
require 'htm'
|
|
221
|
+
require 'ruby_llm'
|
|
222
|
+
require 'logger'
|
|
223
|
+
|
|
224
|
+
# Configure HTM with RubyLLM for embeddings and tag extraction
|
|
225
|
+
HTM.configure do |config|
|
|
226
|
+
# Configure logger (optional - uses STDOUT at INFO level if not provided)
|
|
227
|
+
config.logger = Logger.new($stdout)
|
|
228
|
+
config.logger.level = Logger::INFO
|
|
229
|
+
|
|
230
|
+
# Configure embedding generation using RubyLLM with Ollama
|
|
231
|
+
config.embedding_provider = :ollama
|
|
232
|
+
config.embedding_model = 'nomic-embed-text'
|
|
233
|
+
config.embedding_dimensions = 768
|
|
234
|
+
config.ollama_url = ENV['OLLAMA_URL'] || 'http://localhost:11434'
|
|
235
|
+
|
|
236
|
+
# Configure tag extraction using RubyLLM with Ollama
|
|
237
|
+
config.tag_provider = :ollama
|
|
238
|
+
config.tag_model = 'llama3'
|
|
239
|
+
|
|
240
|
+
# Apply configuration (sets up default RubyLLM implementations)
|
|
241
|
+
config.reset_to_defaults
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Initialize HTM for your robot
|
|
245
|
+
htm = HTM.new(
|
|
246
|
+
robot_name: "Code Helper",
|
|
247
|
+
working_memory_size: 128_000 # tokens
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Remember information - embeddings and tags generated asynchronously
|
|
251
|
+
node_id = htm.remember(
|
|
252
|
+
"We decided to use PostgreSQL for HTM storage",
|
|
253
|
+
source: "architect"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Recall from the past (uses semantic search with embeddings)
|
|
257
|
+
memories = htm.recall(
|
|
258
|
+
"database decisions",
|
|
259
|
+
timeframe: "last week",
|
|
260
|
+
strategy: :hybrid # Combines vector + full-text search
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Forget (explicit deletion only)
|
|
264
|
+
htm.forget(node_id, confirm: :confirmed)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Automatic Tag Extraction
|
|
268
|
+
|
|
269
|
+
HTM automatically extracts hierarchical tags from content using LLM analysis. Tags are inferred from the content itself - you never specify them manually.
|
|
270
|
+
|
|
271
|
+
**Example:**
|
|
272
|
+
```ruby
|
|
273
|
+
htm.remember(
|
|
274
|
+
"User prefers dark mode for all interfaces",
|
|
275
|
+
source: "user"
|
|
276
|
+
)
|
|
277
|
+
# Tags like "preference", "ui", "dark-mode" may be auto-extracted by the LLM
|
|
278
|
+
# The specific tags depend on the LLM's analysis of the content
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Async Job Processing
|
|
282
|
+
|
|
283
|
+
HTM automatically generates embeddings and extracts tags asynchronously in background jobs. This avoids blocking the main request path.
|
|
284
|
+
|
|
285
|
+
**Monitor job processing:**
|
|
286
|
+
```bash
|
|
287
|
+
# Show statistics for nodes and async processing
|
|
288
|
+
rake htm:jobs:stats
|
|
289
|
+
|
|
290
|
+
# Output:
|
|
291
|
+
# Total nodes: 150
|
|
292
|
+
# Nodes with embeddings: 145 (96.7%)
|
|
293
|
+
# Nodes without embeddings: 5 (3.3%)
|
|
294
|
+
# Nodes with tags: 140 (93.3%)
|
|
295
|
+
# Total tags in ontology: 42
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Process pending jobs manually:**
|
|
299
|
+
```bash
|
|
300
|
+
# Process all pending embedding jobs
|
|
301
|
+
rake htm:jobs:process_embeddings
|
|
302
|
+
|
|
303
|
+
# Process all pending tag extraction jobs
|
|
304
|
+
rake htm:jobs:process_tags
|
|
305
|
+
|
|
306
|
+
# Process both embeddings and tags
|
|
307
|
+
rake htm:jobs:process_all
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Reprocess all nodes (force regeneration):**
|
|
311
|
+
```bash
|
|
312
|
+
# Regenerate embeddings for ALL nodes
|
|
313
|
+
rake htm:jobs:reprocess_embeddings
|
|
314
|
+
|
|
315
|
+
# WARNING: Prompts for confirmation
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Show failed/stuck jobs:**
|
|
319
|
+
```bash
|
|
320
|
+
# Show nodes that failed async processing (>1 hour old without embeddings/tags)
|
|
321
|
+
rake htm:jobs:failed
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Clear all for testing:**
|
|
325
|
+
```bash
|
|
326
|
+
# Clear ALL embeddings and tags (development/testing only)
|
|
327
|
+
rake htm:jobs:clear_all
|
|
328
|
+
|
|
329
|
+
# WARNING: Prompts for confirmation
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
See `rake -T htm:jobs` for complete list of job management tasks.
|
|
333
|
+
|
|
334
|
+
## Configuration
|
|
335
|
+
|
|
336
|
+
HTM uses dependency injection for LLM access, allowing you to configure embedding generation, tag extraction, logging, and token counting.
|
|
337
|
+
|
|
338
|
+
### Default Configuration
|
|
339
|
+
|
|
340
|
+
By default, HTM uses:
|
|
341
|
+
- **Embedding Provider**: Ollama (local) with `nomic-embed-text` model (768 dimensions)
|
|
342
|
+
- **Tag Provider**: Ollama (local) with `llama3` model
|
|
343
|
+
- **Logger**: Ruby's standard Logger to STDOUT at INFO level
|
|
344
|
+
- **Token Counter**: Tiktoken with GPT-3.5-turbo encoding
|
|
345
|
+
|
|
346
|
+
```ruby
|
|
347
|
+
require 'htm'
|
|
348
|
+
|
|
349
|
+
# Use defaults
|
|
350
|
+
HTM.configure # or omit this - configuration is lazy-loaded
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Custom Configuration
|
|
354
|
+
|
|
355
|
+
Configure HTM before creating instances:
|
|
356
|
+
|
|
357
|
+
#### Using Ollama (Default)
|
|
358
|
+
|
|
359
|
+
```ruby
|
|
360
|
+
HTM.configure do |config|
|
|
361
|
+
# Embedding configuration
|
|
362
|
+
config.embedding_provider = :ollama
|
|
363
|
+
config.embedding_model = 'nomic-embed-text' # 768 dimensions
|
|
364
|
+
config.embedding_dimensions = 768
|
|
365
|
+
config.ollama_url = 'http://localhost:11434'
|
|
366
|
+
|
|
367
|
+
# Tag extraction configuration
|
|
368
|
+
config.tag_provider = :ollama
|
|
369
|
+
config.tag_model = 'llama3'
|
|
370
|
+
|
|
371
|
+
# Logger configuration
|
|
372
|
+
config.logger = Logger.new($stdout)
|
|
373
|
+
config.logger.level = Logger::INFO
|
|
374
|
+
end
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Ollama Setup:**
|
|
378
|
+
```bash
|
|
379
|
+
# Install Ollama
|
|
380
|
+
curl https://ollama.ai/install.sh | sh
|
|
381
|
+
|
|
382
|
+
# Pull models
|
|
383
|
+
ollama pull nomic-embed-text # For embeddings
|
|
384
|
+
ollama pull llama3 # For tag extraction
|
|
385
|
+
|
|
386
|
+
# Verify Ollama is running
|
|
387
|
+
curl http://localhost:11434/api/version
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
#### Using OpenAI
|
|
391
|
+
|
|
392
|
+
```ruby
|
|
393
|
+
HTM.configure do |config|
|
|
394
|
+
# Embedding configuration (OpenAI)
|
|
395
|
+
config.embedding_provider = :openai
|
|
396
|
+
config.embedding_model = 'text-embedding-3-small' # 1536 dimensions
|
|
397
|
+
config.embedding_dimensions = 1536
|
|
398
|
+
|
|
399
|
+
# Tag extraction (can mix providers)
|
|
400
|
+
config.tag_provider = :openai
|
|
401
|
+
config.tag_model = 'gpt-4'
|
|
402
|
+
|
|
403
|
+
# Logger
|
|
404
|
+
config.logger = Rails.logger # Use Rails logger
|
|
405
|
+
end
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**OpenAI Setup:**
|
|
409
|
+
```bash
|
|
410
|
+
# Set your OpenAI API key
|
|
411
|
+
export OPENAI_API_KEY='sk-your-api-key-here'
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**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.
|
|
415
|
+
|
|
416
|
+
#### Custom Providers
|
|
417
|
+
|
|
418
|
+
Provide your own LLM implementations:
|
|
419
|
+
|
|
420
|
+
```ruby
|
|
421
|
+
HTM.configure do |config|
|
|
422
|
+
# Custom embedding generator
|
|
423
|
+
config.embedding_generator = ->(text) {
|
|
424
|
+
# Call your custom LLM service
|
|
425
|
+
response = MyEmbeddingService.generate(text)
|
|
426
|
+
|
|
427
|
+
# Must return Array<Float>
|
|
428
|
+
response[:embedding] # e.g., [0.123, -0.456, ...]
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
# Custom tag extractor
|
|
432
|
+
config.tag_extractor = ->(text, existing_ontology) {
|
|
433
|
+
# Call your custom LLM service for tag extraction
|
|
434
|
+
# existing_ontology is Array<String> of recent tags for context
|
|
435
|
+
response = MyTagService.extract(text, context: existing_ontology)
|
|
436
|
+
|
|
437
|
+
# Must return Array<String> in hierarchical format
|
|
438
|
+
# e.g., ["ai:llm:embeddings", "database:postgresql"]
|
|
439
|
+
response[:tags]
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
# Custom token counter (optional)
|
|
443
|
+
config.token_counter = ->(text) {
|
|
444
|
+
# Your token counting implementation
|
|
445
|
+
# Must return Integer
|
|
446
|
+
MyTokenizer.count(text)
|
|
447
|
+
}
|
|
448
|
+
end
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### Logger Configuration
|
|
452
|
+
|
|
453
|
+
Customize logging behavior:
|
|
454
|
+
|
|
455
|
+
```ruby
|
|
456
|
+
HTM.configure do |config|
|
|
457
|
+
# Use custom logger
|
|
458
|
+
config.logger = Logger.new('log/htm.log')
|
|
459
|
+
config.logger.level = Logger::DEBUG
|
|
460
|
+
|
|
461
|
+
# Or use Rails logger
|
|
462
|
+
config.logger = Rails.logger
|
|
463
|
+
|
|
464
|
+
# Or disable logging
|
|
465
|
+
config.logger = Logger.new(IO::NULL)
|
|
466
|
+
config.logger.level = Logger::FATAL
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Control log level via environment variable
|
|
470
|
+
ENV['HTM_LOG_LEVEL'] = 'DEBUG' # or INFO, WARN, ERROR
|
|
471
|
+
HTM.configure # Respects HTM_LOG_LEVEL
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
#### Service Layer Architecture
|
|
475
|
+
|
|
476
|
+
HTM uses a service layer to process LLM responses:
|
|
477
|
+
|
|
478
|
+
- **EmbeddingService**: Calls your configured `embedding_generator`, validates responses, handles padding/truncation, and formats for storage
|
|
479
|
+
- **TagService**: Calls your configured `tag_extractor`, parses responses (String or Array), validates format, and filters invalid tags
|
|
480
|
+
|
|
481
|
+
This separation allows you to provide raw LLM access while HTM handles response processing, validation, and storage formatting.
|
|
482
|
+
|
|
483
|
+
### Recall Strategies
|
|
484
|
+
|
|
485
|
+
HTM supports three retrieval strategies:
|
|
486
|
+
|
|
487
|
+
```ruby
|
|
488
|
+
# Vector similarity search (semantic) - uses configured embedding provider
|
|
489
|
+
memories = htm.recall(
|
|
490
|
+
"HTM architecture",
|
|
491
|
+
timeframe: "last week",
|
|
492
|
+
strategy: :vector # Semantic similarity using embeddings
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Full-text search (keyword matching) - PostgreSQL full-text search with trigrams
|
|
496
|
+
memories = htm.recall(
|
|
497
|
+
"database performance",
|
|
498
|
+
timeframe: "last month",
|
|
499
|
+
strategy: :fulltext # Keyword-based matching
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
# Hybrid (combines both) - best of both worlds
|
|
503
|
+
memories = htm.recall(
|
|
504
|
+
"testing strategies",
|
|
505
|
+
timeframe: "yesterday",
|
|
506
|
+
strategy: :hybrid # Weighted combination of vector + full-text
|
|
507
|
+
)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**Strategy Details:**
|
|
511
|
+
- `:vector` - Semantic search using pgvector cosine similarity (requires embeddings)
|
|
512
|
+
- `:fulltext` - PostgreSQL tsvector with pg_trgm fuzzy matching
|
|
513
|
+
- `:hybrid` - Combines both with weighted scoring (default: 70% vector, 30% full-text)
|
|
514
|
+
|
|
515
|
+
## API Reference
|
|
516
|
+
|
|
517
|
+
HTM provides a minimal, focused API with only 3 core instance methods for memory operations:
|
|
518
|
+
|
|
519
|
+
### Core Memory Operations
|
|
520
|
+
|
|
521
|
+
#### `remember(content, source: "")`
|
|
522
|
+
|
|
523
|
+
Store information in memory. Embeddings and tags are automatically generated asynchronously.
|
|
524
|
+
|
|
525
|
+
**Parameters:**
|
|
526
|
+
- `content` (String, required) - The information to remember. Converted to string if nil. Returns ID of last node if empty.
|
|
527
|
+
- `source` (String, optional) - Where the content came from (e.g., "user", "assistant", "system"). Defaults to empty string.
|
|
528
|
+
|
|
529
|
+
**Returns:** Integer - The node ID of the stored memory
|
|
530
|
+
|
|
531
|
+
**Example:**
|
|
532
|
+
```ruby
|
|
533
|
+
# Store with source
|
|
534
|
+
node_id = htm.remember("PostgreSQL is excellent for vector search with pgvector", source: "architect")
|
|
535
|
+
|
|
536
|
+
# Store without source (uses default empty string)
|
|
537
|
+
node_id = htm.remember("HTM uses two-tier memory architecture")
|
|
538
|
+
|
|
539
|
+
# Nil/empty handling
|
|
540
|
+
node_id = htm.remember(nil) # Returns ID of last node without creating duplicate
|
|
541
|
+
node_id = htm.remember("") # Returns ID of last node without creating duplicate
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
#### `recall(topic, timeframe: nil, limit: 20, strategy: :vector, with_relevance: false, query_tags: [])`
|
|
547
|
+
|
|
548
|
+
Retrieve memories using temporal filtering and semantic/keyword search.
|
|
549
|
+
|
|
550
|
+
**Parameters:**
|
|
551
|
+
- `topic` (String, required) - Query text for semantic/keyword matching (first positional argument)
|
|
552
|
+
- `timeframe` (Range, String, optional) - Time range to search within. Default: "last 7 days"
|
|
553
|
+
- Range: `(Time.now - 3600)..Time.now`
|
|
554
|
+
- String: `"last hour"`, `"last week"`, `"yesterday"`
|
|
555
|
+
- `limit` (Integer, optional) - Maximum number of results. Default: 20
|
|
556
|
+
- `strategy` (Symbol, optional) - Search strategy. Default: `:vector`
|
|
557
|
+
- `:vector` - Semantic search using embeddings (cosine similarity)
|
|
558
|
+
- `:fulltext` - Keyword search using PostgreSQL full-text + trigrams
|
|
559
|
+
- `:hybrid` - Weighted combination (70% vector, 30% full-text)
|
|
560
|
+
- `with_relevance` (Boolean, optional) - Include dynamic relevance scores. Default: false
|
|
561
|
+
- `query_tags` (Array<String>, optional) - Filter results by tags. Default: []
|
|
562
|
+
|
|
563
|
+
**Returns:** Array<Hash> - Matching memories with fields: `id`, `content`, `source`, `created_at`, `access_count`, (optionally `relevance`)
|
|
564
|
+
|
|
565
|
+
**Example:**
|
|
566
|
+
```ruby
|
|
567
|
+
# Basic recall with time range
|
|
568
|
+
memories = htm.recall(
|
|
569
|
+
"database architecture",
|
|
570
|
+
timeframe: (Time.now - 86400)..Time.now
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
# Using human-readable timeframe
|
|
574
|
+
memories = htm.recall(
|
|
575
|
+
"PostgreSQL performance",
|
|
576
|
+
timeframe: "last week",
|
|
577
|
+
strategy: :hybrid,
|
|
578
|
+
limit: 10
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# With relevance scoring
|
|
582
|
+
memories = htm.recall(
|
|
583
|
+
"HTM design decisions",
|
|
584
|
+
timeframe: "last month",
|
|
585
|
+
with_relevance: true,
|
|
586
|
+
query_tags: ["architecture"]
|
|
587
|
+
)
|
|
588
|
+
# => [{ "id" => 123, "content" => "...", "relevance" => 0.92, ... }, ...]
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
#### `forget(node_id, confirm: false)`
|
|
594
|
+
|
|
595
|
+
Permanently delete a memory from both working memory and long-term storage.
|
|
596
|
+
|
|
597
|
+
**Parameters:**
|
|
598
|
+
- `node_id` (Integer, required) - The ID of the node to delete
|
|
599
|
+
- `confirm` (Symbol, Boolean, required) - Must be `:confirmed` or `true` to proceed
|
|
600
|
+
|
|
601
|
+
**Returns:** Boolean - `true` if deleted, `false` if not found
|
|
602
|
+
|
|
603
|
+
**Safety:** This is the ONLY way to delete memories. Requires explicit confirmation to prevent accidental deletion.
|
|
604
|
+
|
|
605
|
+
**Example:**
|
|
606
|
+
```ruby
|
|
607
|
+
# Delete with symbol confirmation (recommended)
|
|
608
|
+
htm.forget(node_id, confirm: :confirmed)
|
|
609
|
+
|
|
610
|
+
# Delete with boolean confirmation
|
|
611
|
+
htm.forget(node_id, confirm: true)
|
|
612
|
+
|
|
613
|
+
# Will raise error - confirmation required
|
|
614
|
+
htm.forget(node_id) # ArgumentError: Must confirm deletion
|
|
615
|
+
htm.forget(node_id, confirm: false) # ArgumentError: Must confirm deletion
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
### Complete Usage Example
|
|
621
|
+
|
|
622
|
+
```ruby
|
|
623
|
+
require 'htm'
|
|
624
|
+
|
|
625
|
+
# Configure once globally (optional - uses defaults if not called)
|
|
626
|
+
HTM.configure do |config|
|
|
627
|
+
config.embedding_provider = :ollama
|
|
628
|
+
config.embedding_model = 'nomic-embed-text'
|
|
629
|
+
config.tag_provider = :ollama
|
|
630
|
+
config.tag_model = 'llama3'
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
# Initialize for your robot
|
|
634
|
+
htm = HTM.new(robot_name: "Assistant", working_memory_size: 128_000)
|
|
635
|
+
|
|
636
|
+
# Store information
|
|
637
|
+
htm.remember("PostgreSQL with pgvector for vector search", source: "architect")
|
|
638
|
+
htm.remember("User prefers dark mode", source: "user")
|
|
639
|
+
htm.remember("Use debug_me for debugging, not puts", source: "system")
|
|
640
|
+
|
|
641
|
+
# Retrieve by time + topic
|
|
642
|
+
recent = htm.recall(
|
|
643
|
+
"PostgreSQL",
|
|
644
|
+
timeframe: "last week",
|
|
645
|
+
strategy: :hybrid,
|
|
646
|
+
limit: 5
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
# Delete if needed (requires node ID from remember or recall)
|
|
650
|
+
htm.forget(node_id, confirm: :confirmed)
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## Use with Rails
|
|
654
|
+
|
|
655
|
+
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.
|
|
656
|
+
|
|
657
|
+
### Why HTM Works Well with Rails
|
|
658
|
+
|
|
659
|
+
**1. Uses ActiveRecord**
|
|
660
|
+
- HTM models are standard ActiveRecord classes with associations, validations, and scopes
|
|
661
|
+
- Follows Rails naming conventions and patterns
|
|
662
|
+
- Models are namespaced under `HTM::Models::` to avoid conflicts
|
|
663
|
+
|
|
664
|
+
**2. Standard Rails Configuration**
|
|
665
|
+
- Uses `config/database.yml` (Rails convention)
|
|
666
|
+
- Respects `RAILS_ENV` environment variable
|
|
667
|
+
- Supports ERB in configuration files
|
|
668
|
+
|
|
669
|
+
**3. Separate Database**
|
|
670
|
+
- HTM uses its own PostgreSQL database connection
|
|
671
|
+
- Won't interfere with your Rails app's database
|
|
672
|
+
- Configured with `prepared_statements: false` and `advisory_locks: false` for compatibility
|
|
673
|
+
- Thread-safe for Rails multi-threading
|
|
674
|
+
|
|
675
|
+
**4. Connection Management**
|
|
676
|
+
- Separate connection pool from your Rails app
|
|
677
|
+
- Configurable pool size and timeouts
|
|
678
|
+
- Proper cleanup and disconnection support
|
|
679
|
+
|
|
680
|
+
### Integration Steps
|
|
681
|
+
|
|
682
|
+
#### 1. Add to Gemfile
|
|
683
|
+
|
|
684
|
+
```ruby
|
|
685
|
+
gem 'htm'
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
Then run:
|
|
689
|
+
```bash
|
|
690
|
+
bundle install
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
#### 2. Configure Environment Variables
|
|
694
|
+
|
|
695
|
+
Add HTM database configuration to your Rails environment. You can use `.env` files (with `dotenv-rails`) or set them directly:
|
|
696
|
+
|
|
697
|
+
```ruby
|
|
698
|
+
# .env or config/application.rb
|
|
699
|
+
HTM_DBURL=postgresql://user:pass@localhost:5432/htm_production
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Or use individual variables:
|
|
703
|
+
```ruby
|
|
704
|
+
HTM_DBHOST=localhost
|
|
705
|
+
HTM_DBNAME=htm_production
|
|
706
|
+
HTM_DBUSER=postgres
|
|
707
|
+
HTM_DBPASS=password
|
|
708
|
+
HTM_DBPORT=5432
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
#### 3. Create Initializer
|
|
712
|
+
|
|
713
|
+
Create `config/initializers/htm.rb`:
|
|
714
|
+
|
|
715
|
+
```ruby
|
|
716
|
+
# config/initializers/htm.rb
|
|
717
|
+
|
|
718
|
+
# Configure HTM
|
|
719
|
+
HTM.configure do |config|
|
|
720
|
+
# Use Rails logger
|
|
721
|
+
config.logger = Rails.logger
|
|
722
|
+
|
|
723
|
+
# Embedding configuration (optional - uses Ollama defaults if not set)
|
|
724
|
+
config.embedding_provider = :ollama
|
|
725
|
+
config.embedding_model = 'nomic-embed-text'
|
|
726
|
+
|
|
727
|
+
# Tag extraction configuration (optional - uses Ollama defaults if not set)
|
|
728
|
+
config.tag_provider = :ollama
|
|
729
|
+
config.tag_model = 'llama3'
|
|
730
|
+
|
|
731
|
+
# Custom providers (optional)
|
|
732
|
+
# config.embedding_generator = ->(text) { YourEmbeddingService.call(text) }
|
|
733
|
+
# config.tag_extractor = ->(text, ontology) { YourTagService.call(text, ontology) }
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
# Establish HTM database connection
|
|
737
|
+
HTM::ActiveRecordConfig.establish_connection!
|
|
738
|
+
|
|
739
|
+
# Verify required PostgreSQL extensions are installed
|
|
740
|
+
HTM::ActiveRecordConfig.verify_extensions!
|
|
741
|
+
|
|
742
|
+
Rails.logger.info "HTM initialized with database: #{HTM::Database.default_config[:database]}"
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
#### 4. Set Up Database
|
|
746
|
+
|
|
747
|
+
Run the HTM database setup:
|
|
748
|
+
|
|
749
|
+
```bash
|
|
750
|
+
# Using HTM rake tasks
|
|
751
|
+
rake htm:db:setup
|
|
752
|
+
|
|
753
|
+
# Or manually in Rails console
|
|
754
|
+
rails c
|
|
755
|
+
> HTM::Database.setup
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
#### 5. Use in Your Rails App
|
|
759
|
+
|
|
760
|
+
**In Controllers:**
|
|
761
|
+
|
|
762
|
+
```ruby
|
|
763
|
+
class ChatsController < ApplicationController
|
|
764
|
+
before_action :initialize_memory
|
|
765
|
+
|
|
766
|
+
def create
|
|
767
|
+
# Add user message to memory (embeddings + tags auto-extracted asynchronously)
|
|
768
|
+
@memory.remember(
|
|
769
|
+
params[:message],
|
|
770
|
+
source: "user-#{current_user.id}"
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
# Recall relevant context for the conversation
|
|
774
|
+
context = @memory.create_context(strategy: :balanced)
|
|
775
|
+
|
|
776
|
+
# Use context with your LLM to generate response
|
|
777
|
+
response = generate_llm_response(context, params[:message])
|
|
778
|
+
|
|
779
|
+
# Store assistant's response
|
|
780
|
+
@memory.remember(
|
|
781
|
+
response,
|
|
782
|
+
source: "assistant"
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
render json: { response: response }
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
private
|
|
789
|
+
|
|
790
|
+
def initialize_memory
|
|
791
|
+
@memory = HTM.new(
|
|
792
|
+
robot_name: "ChatBot-#{current_user.id}",
|
|
793
|
+
working_memory_size: 128_000
|
|
794
|
+
)
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
**In Background Jobs:**
|
|
800
|
+
|
|
801
|
+
```ruby
|
|
802
|
+
class ProcessDocumentJob < ApplicationJob
|
|
803
|
+
queue_as :default
|
|
804
|
+
|
|
805
|
+
def perform(document_id)
|
|
806
|
+
document = Document.find(document_id)
|
|
807
|
+
|
|
808
|
+
# Initialize HTM for this job
|
|
809
|
+
memory = HTM.new(robot_name: "DocumentProcessor")
|
|
810
|
+
|
|
811
|
+
# Store document content in memory (embeddings + tags auto-extracted asynchronously)
|
|
812
|
+
memory.remember(
|
|
813
|
+
document.content,
|
|
814
|
+
source: "document-#{document.id}"
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
# Recall related documents (uses vector similarity)
|
|
818
|
+
related = memory.recall(
|
|
819
|
+
document.category,
|
|
820
|
+
timeframe: "last month",
|
|
821
|
+
strategy: :vector
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
# Process with context...
|
|
825
|
+
end
|
|
826
|
+
end
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
**In Models (as a concern):**
|
|
830
|
+
|
|
831
|
+
```ruby
|
|
832
|
+
# app/models/concerns/memorable.rb
|
|
833
|
+
module Memorable
|
|
834
|
+
extend ActiveSupport::Concern
|
|
835
|
+
|
|
836
|
+
included do
|
|
837
|
+
after_create :store_in_memory
|
|
838
|
+
after_update :update_memory
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def store_in_memory
|
|
842
|
+
memory = HTM.new(robot_name: "Rails-#{Rails.env}")
|
|
843
|
+
|
|
844
|
+
memory.remember(
|
|
845
|
+
memory_content,
|
|
846
|
+
source: "#{self.class.name}-#{id}"
|
|
847
|
+
)
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
def update_memory
|
|
851
|
+
# Update existing memory node
|
|
852
|
+
store_in_memory
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
private
|
|
856
|
+
|
|
857
|
+
def memory_content
|
|
858
|
+
# Override in model to customize what gets stored
|
|
859
|
+
attributes.to_json
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
# Then in your model:
|
|
864
|
+
class Article < ApplicationRecord
|
|
865
|
+
include Memorable
|
|
866
|
+
|
|
867
|
+
private
|
|
868
|
+
|
|
869
|
+
def memory_content
|
|
870
|
+
"Article: #{title}\n\n#{body}\n\nCategory: #{category}"
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
### Multi-Database Configuration (Rails 6+)
|
|
876
|
+
|
|
877
|
+
If you want to use Rails' built-in multi-database support, you can configure HTM in your `config/database.yml`:
|
|
878
|
+
|
|
879
|
+
```yaml
|
|
880
|
+
# config/database.yml
|
|
881
|
+
production:
|
|
882
|
+
primary:
|
|
883
|
+
<<: *default
|
|
884
|
+
database: myapp_production
|
|
885
|
+
|
|
886
|
+
htm:
|
|
887
|
+
adapter: postgresql
|
|
888
|
+
encoding: unicode
|
|
889
|
+
database: <%= ENV['HTM_DBNAME'] || 'htm_production' %>
|
|
890
|
+
host: <%= ENV['HTM_DBHOST'] || 'localhost' %>
|
|
891
|
+
port: <%= ENV['HTM_DBPORT'] || 5432 %>
|
|
892
|
+
username: <%= ENV['HTM_DBUSER'] || 'postgres' %>
|
|
893
|
+
password: <%= ENV['HTM_DBPASS'] %>
|
|
894
|
+
pool: <%= ENV['HTM_DB_POOL_SIZE'] || 10 %>
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
Then update your HTM initializer:
|
|
898
|
+
|
|
899
|
+
```ruby
|
|
900
|
+
# config/initializers/htm.rb
|
|
901
|
+
HTM::ActiveRecordConfig.establish_connection!
|
|
902
|
+
|
|
903
|
+
# Or use Rails' connection
|
|
904
|
+
# ActiveRecord::Base.connected_to(role: :writing, shard: :htm) do
|
|
905
|
+
# HTM::ActiveRecordConfig.verify_extensions!
|
|
906
|
+
# end
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
### Testing with Rails
|
|
910
|
+
|
|
911
|
+
**In RSpec:**
|
|
912
|
+
|
|
913
|
+
```ruby
|
|
914
|
+
# spec/rails_helper.rb
|
|
915
|
+
RSpec.configure do |config|
|
|
916
|
+
config.before(:suite) do
|
|
917
|
+
HTM::ActiveRecordConfig.establish_connection!
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
config.after(:suite) do
|
|
921
|
+
HTM::ActiveRecordConfig.disconnect!
|
|
922
|
+
end
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
# spec/features/chat_spec.rb
|
|
926
|
+
RSpec.describe "Chat with memory", type: :feature do
|
|
927
|
+
let(:memory) { HTM.new(robot_name: "TestBot") }
|
|
928
|
+
|
|
929
|
+
before do
|
|
930
|
+
memory.remember("User prefers Ruby over Python", source: "user")
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
it "recalls user preferences" do
|
|
934
|
+
context = memory.create_context(strategy: :balanced)
|
|
935
|
+
expect(context).to include("Ruby")
|
|
936
|
+
end
|
|
937
|
+
end
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
**In Minitest:**
|
|
941
|
+
|
|
942
|
+
```ruby
|
|
943
|
+
# test/test_helper.rb
|
|
944
|
+
class ActiveSupport::TestCase
|
|
945
|
+
setup do
|
|
946
|
+
HTM::ActiveRecordConfig.establish_connection! unless HTM::ActiveRecordConfig.connected?
|
|
947
|
+
end
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
# test/integration/chat_test.rb
|
|
951
|
+
class ChatTest < ActionDispatch::IntegrationTest
|
|
952
|
+
test "stores chat messages in memory" do
|
|
953
|
+
memory = HTM.new(robot_name: "TestBot")
|
|
954
|
+
|
|
955
|
+
post chats_path, params: { message: "Hello!" }
|
|
956
|
+
|
|
957
|
+
assert_response :success
|
|
958
|
+
|
|
959
|
+
# Verify message was stored
|
|
960
|
+
nodes = memory.recall("Hello", timeframe: "last hour")
|
|
961
|
+
assert_not_empty nodes
|
|
962
|
+
end
|
|
963
|
+
end
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
### Production Considerations
|
|
967
|
+
|
|
968
|
+
1. **Connection Pooling**: Set appropriate pool size based on your Rails app's concurrency:
|
|
969
|
+
```ruby
|
|
970
|
+
ENV['HTM_DB_POOL_SIZE'] = '20' # Match or exceed Rails pool
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
2. **Separate Database Server**: For production, use a dedicated PostgreSQL instance for HTM
|
|
974
|
+
|
|
975
|
+
3. **Required Extensions**: Ensure `pgvector` and `pg_trgm` extensions are installed on your PostgreSQL server
|
|
976
|
+
|
|
977
|
+
4. **Memory Management**: HTM's working memory is per-instance. Consider using a singleton pattern or Rails cache for shared instances
|
|
978
|
+
|
|
979
|
+
5. **Background Processing**: Use Sidekiq or similar for embedding generation if processing large amounts of data
|
|
980
|
+
|
|
981
|
+
### Example Rails App Structure
|
|
982
|
+
|
|
983
|
+
```
|
|
984
|
+
app/
|
|
985
|
+
├── controllers/
|
|
986
|
+
│ └── chats_controller.rb # Uses HTM for conversation memory
|
|
987
|
+
├── jobs/
|
|
988
|
+
│ └── process_document_job.rb # Background HTM processing
|
|
989
|
+
├── models/
|
|
990
|
+
│ ├── concerns/
|
|
991
|
+
│ │ └── memorable.rb # HTM integration concern
|
|
992
|
+
│ └── article.rb # Includes Memorable
|
|
993
|
+
└── services/
|
|
994
|
+
└── memory_service.rb # Centralized HTM access
|
|
995
|
+
|
|
996
|
+
config/
|
|
997
|
+
├── initializers/
|
|
998
|
+
│ └── htm.rb # HTM setup
|
|
999
|
+
└── database.yml # Optional: multi-database config
|
|
1000
|
+
|
|
1001
|
+
.env
|
|
1002
|
+
HTM_DBURL=postgresql://... # HTM database connection
|
|
1003
|
+
OPENAI_API_KEY=sk-... # If using OpenAI embeddings
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
## Environment Variables
|
|
1007
|
+
|
|
1008
|
+
HTM uses environment variables for database and service configuration. These can be set in your shell profile, a `.env` file, or exported directly.
|
|
1009
|
+
|
|
1010
|
+
### Database Configuration
|
|
1011
|
+
|
|
1012
|
+
#### HTM_DBURL (Recommended)
|
|
1013
|
+
|
|
1014
|
+
The preferred method for database configuration is a single connection URL:
|
|
1015
|
+
|
|
1016
|
+
```bash
|
|
1017
|
+
export HTM_DBURL="postgresql://username:password@host:port/dbname?sslmode=require"
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
**Format:** `postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=MODE`
|
|
1021
|
+
|
|
1022
|
+
**Example for Local PostgreSQL:**
|
|
1023
|
+
```bash
|
|
1024
|
+
export HTM_DBURL="postgresql://postgres:postgres@localhost:5432/htm_dev?sslmode=disable"
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
#### Individual Database Parameters (Alternative)
|
|
1028
|
+
|
|
1029
|
+
If `HTM_DBURL` is not set, HTM will use individual parameters:
|
|
1030
|
+
|
|
1031
|
+
| Variable | Description | Default | Required |
|
|
1032
|
+
|----------|-------------|---------|----------|
|
|
1033
|
+
| `HTM_DBNAME` | Database name | None | Yes |
|
|
1034
|
+
| `HTM_DBUSER` | Database username | None | Yes |
|
|
1035
|
+
| `HTM_DBPASS` | Database password | None | Yes |
|
|
1036
|
+
| `HTM_DBHOST` | Database hostname or IP | `localhost` | No |
|
|
1037
|
+
| `HTM_DBPORT` | Database port | `5432` | No |
|
|
1038
|
+
| `HTM_SERVICE_NAME` | Service identifier (informational only) | None | No |
|
|
1039
|
+
|
|
1040
|
+
**Example:**
|
|
1041
|
+
```bash
|
|
1042
|
+
export HTM_DBNAME="htm_production"
|
|
1043
|
+
export HTM_DBUSER="postgres"
|
|
1044
|
+
export HTM_DBPASS="secure_password_here"
|
|
1045
|
+
export HTM_DBHOST="localhost"
|
|
1046
|
+
export HTM_DBPORT="5432"
|
|
1047
|
+
export HTM_SERVICE_NAME="production-db"
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
**Configuration Priority:**
|
|
1051
|
+
1. `HTM_DBURL` (if set, this takes precedence)
|
|
1052
|
+
2. Individual parameters (`HTM_DBNAME`, `HTM_DBUSER`, etc.)
|
|
1053
|
+
3. Direct configuration passed to `HTM.new(db_config: {...})`
|
|
1054
|
+
|
|
1055
|
+
### LLM Provider Configuration
|
|
1056
|
+
|
|
1057
|
+
HTM configuration is done programmatically via `HTM.configure` (see [Configuration](#configuration) section). However, a few environment variables are still used:
|
|
1058
|
+
|
|
1059
|
+
#### OPENAI_API_KEY
|
|
1060
|
+
|
|
1061
|
+
Required when using OpenAI as your embedding or tag extraction provider:
|
|
1062
|
+
|
|
1063
|
+
```bash
|
|
1064
|
+
export OPENAI_API_KEY="sk-proj-your-api-key-here"
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
This is required when you configure:
|
|
1068
|
+
```ruby
|
|
1069
|
+
HTM.configure do |config|
|
|
1070
|
+
config.embedding_provider = :openai
|
|
1071
|
+
# or
|
|
1072
|
+
config.tag_provider = :openai
|
|
1073
|
+
end
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
#### OLLAMA_URL
|
|
1077
|
+
|
|
1078
|
+
Custom Ollama server URL (optional):
|
|
1079
|
+
|
|
1080
|
+
```bash
|
|
1081
|
+
export OLLAMA_URL="http://localhost:11434" # Default
|
|
1082
|
+
export OLLAMA_URL="http://ollama-server:11434" # Custom
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
HTM defaults to `http://localhost:11434` if not set.
|
|
1086
|
+
|
|
1087
|
+
#### HTM_LOG_LEVEL
|
|
1088
|
+
|
|
1089
|
+
Control logging verbosity:
|
|
1090
|
+
|
|
1091
|
+
```bash
|
|
1092
|
+
export HTM_LOG_LEVEL="DEBUG" # DEBUG, INFO, WARN, ERROR, FATAL
|
|
1093
|
+
export HTM_LOG_LEVEL="INFO" # Default
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
This is used by the default logger when `HTM.configure` is called without a custom logger.
|
|
1097
|
+
|
|
1098
|
+
### Quick Setup Examples
|
|
1099
|
+
|
|
1100
|
+
#### Local Development (PostgreSQL)
|
|
1101
|
+
|
|
1102
|
+
```bash
|
|
1103
|
+
# Simple local setup
|
|
1104
|
+
export HTM_DBURL="postgresql://postgres:postgres@localhost:5432/htm_dev?sslmode=disable"
|
|
1105
|
+
|
|
1106
|
+
# With custom user
|
|
1107
|
+
export HTM_DBURL="postgresql://myuser:mypass@localhost:5432/htm_dev"
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
#### With OpenAI Embeddings
|
|
1111
|
+
|
|
1112
|
+
```bash
|
|
1113
|
+
# Database + OpenAI
|
|
1114
|
+
export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"
|
|
1115
|
+
export OPENAI_API_KEY="sk-proj-your-api-key-here"
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
### Persistent Configuration
|
|
1119
|
+
|
|
1120
|
+
To make environment variables permanent, add them to your shell profile:
|
|
1121
|
+
|
|
1122
|
+
**For Bash (~/.bashrc or ~/.bash_profile):**
|
|
1123
|
+
```bash
|
|
1124
|
+
echo 'export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"' >> ~/.bashrc
|
|
1125
|
+
source ~/.bashrc
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
**For Zsh (~/.zshrc):**
|
|
1129
|
+
```bash
|
|
1130
|
+
echo 'export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"' >> ~/.zshrc
|
|
1131
|
+
source ~/.zshrc
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
**Or create a dedicated file (~/.bashrc__htm):**
|
|
1135
|
+
```bash
|
|
1136
|
+
# ~/.bashrc__htm
|
|
1137
|
+
export HTM_DBURL="postgresql://user:pass@host:port/db?sslmode=require"
|
|
1138
|
+
export OPENAI_API_KEY="sk-proj-your-api-key"
|
|
1139
|
+
export HTM_SERVICE_NAME="my-service"
|
|
1140
|
+
|
|
1141
|
+
# Then source it from your main profile
|
|
1142
|
+
echo 'source ~/.bashrc__htm' >> ~/.bashrc
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
### Verification
|
|
1146
|
+
|
|
1147
|
+
Verify your configuration:
|
|
1148
|
+
|
|
1149
|
+
```bash
|
|
1150
|
+
# Check database connection
|
|
1151
|
+
ruby test_connection.rb
|
|
1152
|
+
|
|
1153
|
+
# Or in Ruby
|
|
1154
|
+
irb -r ./lib/htm
|
|
1155
|
+
> HTM::Database.default_config
|
|
1156
|
+
> # Should return a hash with your connection parameters
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
## Development
|
|
1160
|
+
|
|
1161
|
+
After checking out the repo, run:
|
|
1162
|
+
|
|
1163
|
+
```bash
|
|
1164
|
+
# Install dependencies
|
|
1165
|
+
bundle install
|
|
1166
|
+
|
|
1167
|
+
# Enable direnv (loads .envrc environment variables)
|
|
1168
|
+
direnv allow
|
|
1169
|
+
|
|
1170
|
+
# Run tests
|
|
1171
|
+
rake test
|
|
1172
|
+
|
|
1173
|
+
# Run example
|
|
1174
|
+
ruby examples/basic_usage.rb
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
### Database Management
|
|
1178
|
+
|
|
1179
|
+
HTM provides comprehensive rake tasks under the `htm:db` namespace for managing the database:
|
|
1180
|
+
|
|
1181
|
+
```bash
|
|
1182
|
+
# List all database tasks
|
|
1183
|
+
rake -T htm:db
|
|
1184
|
+
|
|
1185
|
+
# Set up database (create schema + run migrations)
|
|
1186
|
+
rake htm:db:setup
|
|
1187
|
+
|
|
1188
|
+
# Run pending migrations only
|
|
1189
|
+
rake htm:db:migrate
|
|
1190
|
+
|
|
1191
|
+
# Show migration status (which migrations are applied)
|
|
1192
|
+
rake htm:db:status
|
|
1193
|
+
|
|
1194
|
+
# Show database info (size, tables, extensions, row counts)
|
|
1195
|
+
rake htm:db:info
|
|
1196
|
+
|
|
1197
|
+
# Test database connection
|
|
1198
|
+
rake htm:db:test
|
|
1199
|
+
|
|
1200
|
+
# Open PostgreSQL console (interactive psql session)
|
|
1201
|
+
rake htm:db:console
|
|
1202
|
+
|
|
1203
|
+
# Seed database with sample data
|
|
1204
|
+
rake htm:db:seed
|
|
1205
|
+
|
|
1206
|
+
# Drop all HTM tables (WARNING: destructive!)
|
|
1207
|
+
rake htm:db:drop
|
|
1208
|
+
|
|
1209
|
+
# Drop and recreate database (WARNING: destructive!)
|
|
1210
|
+
rake htm:db:reset
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
**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:
|
|
1214
|
+
|
|
1215
|
+
```bash
|
|
1216
|
+
# Manually export HTM_DBURL
|
|
1217
|
+
export HTM_DBURL="postgresql://user:password@host:port/dbname?sslmode=require"
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
**Common Workflows**:
|
|
1221
|
+
|
|
1222
|
+
```bash
|
|
1223
|
+
# Initial setup (first time)
|
|
1224
|
+
direnv allow
|
|
1225
|
+
rake htm:db:setup # Creates schema and runs migrations
|
|
1226
|
+
|
|
1227
|
+
# After pulling new migrations
|
|
1228
|
+
rake htm:db:migrate # Run pending migrations
|
|
1229
|
+
|
|
1230
|
+
# Check database state
|
|
1231
|
+
rake htm:db:status # See migration status
|
|
1232
|
+
rake htm:db:info # See database details
|
|
1233
|
+
|
|
1234
|
+
# Reset database (development only!)
|
|
1235
|
+
rake htm:db:reset # Drops and recreates everything
|
|
1236
|
+
|
|
1237
|
+
# Open database console for debugging
|
|
1238
|
+
rake htm:db:console # Opens psql
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
### Async Job Management
|
|
1242
|
+
|
|
1243
|
+
HTM provides rake tasks under the `htm:jobs` namespace for managing async embedding and tag extraction jobs:
|
|
1244
|
+
|
|
1245
|
+
```bash
|
|
1246
|
+
# List all job management tasks
|
|
1247
|
+
rake -T htm:jobs
|
|
1248
|
+
|
|
1249
|
+
# Show statistics for async processing
|
|
1250
|
+
rake htm:jobs:stats
|
|
1251
|
+
|
|
1252
|
+
# Process pending jobs
|
|
1253
|
+
rake htm:jobs:process_embeddings # Process nodes without embeddings
|
|
1254
|
+
rake htm:jobs:process_tags # Process nodes without tags
|
|
1255
|
+
rake htm:jobs:process_all # Process both
|
|
1256
|
+
|
|
1257
|
+
# Reprocess all nodes (force regeneration)
|
|
1258
|
+
rake htm:jobs:reprocess_embeddings # WARNING: Prompts for confirmation
|
|
1259
|
+
|
|
1260
|
+
# Debugging and maintenance
|
|
1261
|
+
rake htm:jobs:failed # Show stuck jobs (>1 hour old)
|
|
1262
|
+
rake htm:jobs:clear_all # Clear all embeddings/tags (testing only)
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
**Common Workflows**:
|
|
1266
|
+
|
|
1267
|
+
```bash
|
|
1268
|
+
# Monitor async processing
|
|
1269
|
+
rake htm:jobs:stats
|
|
1270
|
+
|
|
1271
|
+
# Manually process pending jobs (if async job runner is not running)
|
|
1272
|
+
rake htm:jobs:process_all
|
|
1273
|
+
|
|
1274
|
+
# Debug stuck jobs
|
|
1275
|
+
rake htm:jobs:failed
|
|
1276
|
+
rake htm:jobs:process_embeddings # Retry failed embeddings
|
|
1277
|
+
|
|
1278
|
+
# Development/testing: force regeneration
|
|
1279
|
+
rake htm:jobs:reprocess_embeddings # Regenerate all embeddings
|
|
1280
|
+
rake htm:jobs:clear_all # Clear everything and start fresh
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
See the [Async Job Processing](#async-job-processing) section for more details.
|
|
1284
|
+
|
|
1285
|
+
## Testing
|
|
1286
|
+
|
|
1287
|
+
HTM uses Minitest:
|
|
1288
|
+
|
|
1289
|
+
```bash
|
|
1290
|
+
# Run all tests
|
|
1291
|
+
rake test
|
|
1292
|
+
|
|
1293
|
+
# Run specific test file
|
|
1294
|
+
ruby test/htm_test.rb
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
## Architecture
|
|
1298
|
+
|
|
1299
|
+
See [htm_teamwork.md](htm_teamwork.md) for detailed design documentation and planning notes.
|
|
1300
|
+
|
|
1301
|
+
### Key Components
|
|
1302
|
+
|
|
1303
|
+
- **HTM**: Main API class that coordinates all components and provides the primary interface
|
|
1304
|
+
- **Configuration**: Dependency injection for LLM providers (embedding_generator, tag_extractor, token_counter, logger)
|
|
1305
|
+
- **WorkingMemory**: In-memory, token-limited active context for immediate LLM use
|
|
1306
|
+
- **LongTermMemory**: PostgreSQL-backed permanent storage with connection pooling
|
|
1307
|
+
- **EmbeddingService**: Processing and validation layer for vector embeddings (calls configured `embedding_generator`)
|
|
1308
|
+
- **TagService**: Processing and validation layer for hierarchical tags (calls configured `tag_extractor`)
|
|
1309
|
+
- **Database**: Schema setup, migrations, and management
|
|
1310
|
+
- **Background Jobs**: Async processing for embedding generation and tag extraction
|
|
1311
|
+
|
|
1312
|
+
### Database Schema
|
|
1313
|
+
|
|
1314
|
+
- `robots`: Robot registry for all LLM agents using HTM
|
|
1315
|
+
- `nodes`: Main memory storage with vector embeddings (pgvector), full-text search (tsvector), metadata
|
|
1316
|
+
- `tags`: Hierarchical tag ontology (format: `root:level1:level2:level3`)
|
|
1317
|
+
- `node_tags`: Join table implementing many-to-many relationship between nodes and tags
|
|
1318
|
+
|
|
1319
|
+
### Service Architecture
|
|
1320
|
+
|
|
1321
|
+
HTM uses a layered architecture for LLM integration:
|
|
1322
|
+
|
|
1323
|
+
1. **Configuration Layer** (`HTM.configure`): Provides raw LLM access via `embedding_generator` and `tag_extractor` callables
|
|
1324
|
+
2. **Service Layer** (`EmbeddingService`, `TagService`): Processes and validates LLM responses, handles padding/truncation, formats for storage
|
|
1325
|
+
3. **Consumer Layer** (`GenerateEmbeddingJob`, `GenerateTagsJob`, rake tasks): Uses services for async or synchronous processing
|
|
1326
|
+
|
|
1327
|
+
This separation allows you to provide any LLM implementation while HTM handles response processing, validation, and storage.
|
|
1328
|
+
|
|
1329
|
+
## Roadmap
|
|
1330
|
+
|
|
1331
|
+
- [x] Phase 1: Foundation (basic two-tier memory)
|
|
1332
|
+
- [ ] Phase 2: RAG retrieval (semantic search)
|
|
1333
|
+
- [ ] Phase 3: Relationships & tags
|
|
1334
|
+
- [ ] Phase 4: Working memory management
|
|
1335
|
+
- [ ] Phase 5: Hive mind features
|
|
1336
|
+
- [ ] Phase 6: Operations & observability
|
|
1337
|
+
- [ ] Phase 7: Advanced features
|
|
1338
|
+
- [ ] Phase 8: Production-ready gem
|
|
1339
|
+
|
|
1340
|
+
|
|
1341
|
+
## Contributing
|
|
1342
|
+
|
|
1343
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/madbomber/htm.
|
|
1344
|
+
|
|
1345
|
+
## License
|
|
1346
|
+
|
|
1347
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|