htm 0.0.10 → 0.0.14
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/.dictate.toml +46 -0
- data/.envrc +2 -0
- data/CHANGELOG.md +86 -3
- data/README.md +86 -7
- data/Rakefile +14 -2
- data/bin/htm_mcp.rb +621 -0
- data/config/database.yml +20 -13
- data/db/migrate/00010_add_soft_delete_to_associations.rb +29 -0
- data/db/migrate/00011_add_performance_indexes.rb +21 -0
- data/db/migrate/00012_add_tags_trigram_index.rb +18 -0
- data/db/migrate/00013_enable_lz4_compression.rb +43 -0
- data/db/schema.sql +49 -92
- data/docs/api/index.md +1 -1
- data/docs/api/yard/HTM.md +2 -4
- data/docs/architecture/index.md +1 -1
- data/docs/development/index.md +1 -1
- data/docs/getting-started/index.md +1 -1
- data/docs/guides/index.md +1 -1
- data/docs/images/telemetry-architecture.svg +153 -0
- data/docs/telemetry.md +391 -0
- data/examples/README.md +171 -1
- data/examples/cli_app/README.md +1 -1
- data/examples/cli_app/htm_cli.rb +1 -1
- data/examples/mcp_client.rb +529 -0
- data/examples/sinatra_app/app.rb +1 -1
- data/examples/telemetry/README.md +147 -0
- data/examples/telemetry/SETUP_README.md +169 -0
- data/examples/telemetry/demo.rb +498 -0
- data/examples/telemetry/grafana/dashboards/htm-metrics.json +457 -0
- data/lib/htm/configuration.rb +261 -70
- data/lib/htm/database.rb +46 -22
- data/lib/htm/embedding_service.rb +24 -14
- data/lib/htm/errors.rb +15 -1
- data/lib/htm/jobs/generate_embedding_job.rb +19 -0
- data/lib/htm/jobs/generate_propositions_job.rb +103 -0
- data/lib/htm/jobs/generate_tags_job.rb +24 -0
- data/lib/htm/loaders/markdown_chunker.rb +79 -0
- data/lib/htm/loaders/markdown_loader.rb +41 -15
- data/lib/htm/long_term_memory/fulltext_search.rb +138 -0
- data/lib/htm/long_term_memory/hybrid_search.rb +324 -0
- data/lib/htm/long_term_memory/node_operations.rb +209 -0
- data/lib/htm/long_term_memory/relevance_scorer.rb +355 -0
- data/lib/htm/long_term_memory/robot_operations.rb +34 -0
- data/lib/htm/long_term_memory/tag_operations.rb +428 -0
- data/lib/htm/long_term_memory/vector_search.rb +109 -0
- data/lib/htm/long_term_memory.rb +51 -1153
- data/lib/htm/models/node.rb +35 -2
- data/lib/htm/models/node_tag.rb +31 -0
- data/lib/htm/models/robot_node.rb +31 -0
- data/lib/htm/models/tag.rb +44 -0
- data/lib/htm/proposition_service.rb +169 -0
- data/lib/htm/query_cache.rb +214 -0
- data/lib/htm/sql_builder.rb +178 -0
- data/lib/htm/tag_service.rb +16 -6
- data/lib/htm/tasks.rb +8 -2
- data/lib/htm/telemetry.rb +224 -0
- data/lib/htm/version.rb +1 -1
- data/lib/htm.rb +64 -3
- data/lib/tasks/doc.rake +1 -1
- data/lib/tasks/htm.rake +259 -13
- data/mkdocs.yml +96 -96
- metadata +75 -18
- data/.aigcm_msg +0 -1
- data/.claude/settings.local.json +0 -92
- data/CLAUDE.md +0 -603
- data/examples/cli_app/temp.log +0 -93
- data/lib/htm/loaders/paragraph_chunker.rb +0 -112
- data/notes/ARCHITECTURE_REVIEW.md +0 -1167
- data/notes/IMPLEMENTATION_SUMMARY.md +0 -606
- data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +0 -451
- data/notes/next_steps.md +0 -100
- data/notes/plan.md +0 -627
- data/notes/tag_ontology_enhancement_ideas.md +0 -222
- data/notes/timescaledb_removal_summary.md +0 -200
data/CLAUDE.md
DELETED
|
@@ -1,603 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
HTM (Hierarchical Temporary Memory) is a Ruby gem that provides intelligent memory management for LLM-based applications (robots). It implements a two-tier memory architecture:
|
|
8
|
-
|
|
9
|
-
- **Long-term Memory**: Durable PostgreSQL storage with vector embeddings for semantic search
|
|
10
|
-
- **Working Memory**: Token-limited in-memory context for immediate LLM use
|
|
11
|
-
|
|
12
|
-
Key capabilities include RAG-based retrieval, temporal filtering, multi-robot "hive mind" shared memory, and flexible hierarchical tagging for organization.
|
|
13
|
-
|
|
14
|
-
## Development Commands
|
|
15
|
-
|
|
16
|
-
### Setup
|
|
17
|
-
```bash
|
|
18
|
-
# Set database connection URL (required before any database operations)
|
|
19
|
-
export HTM_DBURL="postgresql://postgres@localhost:5432/htm_development"
|
|
20
|
-
|
|
21
|
-
# Install dependencies
|
|
22
|
-
bundle install
|
|
23
|
-
|
|
24
|
-
# Initialize database schema (run once)
|
|
25
|
-
rake db_setup
|
|
26
|
-
# OR
|
|
27
|
-
ruby -r ./lib/htm -e "HTM::Database.setup"
|
|
28
|
-
|
|
29
|
-
# Verify database connection
|
|
30
|
-
rake db_test
|
|
31
|
-
# OR
|
|
32
|
-
ruby test_connection.rb
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Testing
|
|
36
|
-
```bash
|
|
37
|
-
# Run all tests (Minitest)
|
|
38
|
-
rake test
|
|
39
|
-
|
|
40
|
-
# Run specific test file
|
|
41
|
-
ruby test/htm_test.rb
|
|
42
|
-
ruby test/embedding_service_test.rb
|
|
43
|
-
ruby test/integration_test.rb
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Examples
|
|
47
|
-
```bash
|
|
48
|
-
# Run basic usage example
|
|
49
|
-
rake example
|
|
50
|
-
# OR
|
|
51
|
-
ruby examples/basic_usage.rb
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Gem Tasks
|
|
55
|
-
```bash
|
|
56
|
-
# Build gem
|
|
57
|
-
rake build
|
|
58
|
-
|
|
59
|
-
# Install gem locally
|
|
60
|
-
rake install
|
|
61
|
-
|
|
62
|
-
# Release gem (requires proper credentials)
|
|
63
|
-
rake release
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Utilities
|
|
67
|
-
```bash
|
|
68
|
-
# Show code statistics
|
|
69
|
-
rake stats
|
|
70
|
-
|
|
71
|
-
# Show database table record counts and statistics
|
|
72
|
-
rake htm:db:stats
|
|
73
|
-
|
|
74
|
-
# Rebuild all embeddings (clears and regenerates via LLM)
|
|
75
|
-
rake htm:db:rebuild:embeddings
|
|
76
|
-
|
|
77
|
-
# Enable PostgreSQL extensions (if needed)
|
|
78
|
-
ruby enable_extensions.rb
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Architecture
|
|
82
|
-
|
|
83
|
-
### Core Components
|
|
84
|
-
|
|
85
|
-
**HTM** (`lib/htm.rb`): Main API class that coordinates all components. Handles robot registration, memory operations (add, recall, retrieve, forget), context assembly, and hive mind queries.
|
|
86
|
-
|
|
87
|
-
**HTM::Database** (`lib/htm/database.rb`): Database schema setup and configuration. Parses connection URLs from `HTM_DBURL` environment variable, verifies extensions (pgvector, pg_trgm), and runs schema migrations using ActiveRecord.
|
|
88
|
-
|
|
89
|
-
**HTM::LongTermMemory** (`lib/htm/long_term_memory.rb`): PostgreSQL-backed permanent storage with ActiveRecord models. Manages nodes table with many-to-many tagging system, vector search (pgvector), full-text search (PostgreSQL tsvector), and hybrid search capabilities.
|
|
90
|
-
|
|
91
|
-
**HTM::WorkingMemory** (`lib/htm/working_memory.rb`): In-memory token-limited cache. Tracks active nodes with LRU eviction, manages token budgets, and assembles context strings with multiple strategies (recent, important, balanced).
|
|
92
|
-
|
|
93
|
-
**HTM::EmbeddingService** (`lib/htm/embedding_service.rb`): Vector embedding validation and processing service. Wraps the configured embedding generator (via RubyLLM), validates responses, handles dimension padding/truncation. Called by `GenerateEmbeddingJob` background job. See ADR-016 for async workflow.
|
|
94
|
-
|
|
95
|
-
**HTM::TagService** (`lib/htm/tag_service.rb`): Hierarchical tag validation and processing service. Wraps the configured tag extractor (via RubyLLM chat), validates tag format, enforces depth limits. Called by `GenerateTagsJob` background job. Parallel architecture to EmbeddingService. See ADR-016.
|
|
96
|
-
|
|
97
|
-
**HTM::Configuration** (`lib/htm/configuration.rb`): Multi-provider LLM configuration via RubyLLM. Supports OpenAI, Anthropic, Gemini, Azure, Ollama (default), HuggingFace, OpenRouter, Bedrock, and DeepSeek. Manages API keys, model selection, and timeouts.
|
|
98
|
-
|
|
99
|
-
**HTM::Models::FileSource** (`lib/htm/models/file_source.rb`): Tracks source files loaded into memory. Stores file path, mtime for re-sync detection, YAML frontmatter metadata, and links to chunked nodes via `source_id` foreign key.
|
|
100
|
-
|
|
101
|
-
**HTM::Loaders::MarkdownLoader** (`lib/htm/loaders/markdown_loader.rb`): Loads markdown files into long-term memory. Extracts YAML frontmatter, chunks text by paragraph, tracks source file for re-sync, and handles duplicate detection via content hash.
|
|
102
|
-
|
|
103
|
-
**HTM::Loaders::ParagraphChunker** (`lib/htm/loaders/paragraph_chunker.rb`): Splits text into paragraph-based chunks. Preserves fenced code blocks (``` and ~~~), splits by blank lines, and merges very short fragments.
|
|
104
|
-
|
|
105
|
-
### Database Schema
|
|
106
|
-
|
|
107
|
-
Located in `db/schema.sql`:
|
|
108
|
-
|
|
109
|
-
- **robots**: Registry of all LLM agents using the HTM system with metadata
|
|
110
|
-
- **nodes**: Primary memory storage with vector embeddings (up to 2000 dimensions), full-text search, fuzzy matching, JSONB metadata, and soft delete support (`deleted_at` column)
|
|
111
|
-
- **tags**: Hierarchical tag system using colon-separated namespaces (e.g., `database:postgresql:extensions`)
|
|
112
|
-
- **nodes_tags**: Join table implementing many-to-many relationship between nodes and tags
|
|
113
|
-
- **file_sources**: Source file metadata for loaded documents (path, mtime, frontmatter, sync status)
|
|
114
|
-
|
|
115
|
-
**Nodes table key columns:**
|
|
116
|
-
- `content`: The memory content (text)
|
|
117
|
-
- `embedding`: Vector embedding for semantic search (up to 2000 dimensions)
|
|
118
|
-
- `metadata`: JSONB column for arbitrary key-value data with GIN index
|
|
119
|
-
- `content_hash`: SHA-256 hash for deduplication
|
|
120
|
-
- `deleted_at`: Soft delete timestamp (null = active)
|
|
121
|
-
|
|
122
|
-
The schema uses PostgreSQL 17 with pgvector and pg_trgm extensions. ActiveRecord models are available:
|
|
123
|
-
- `HTM::Models::Robot`
|
|
124
|
-
- `HTM::Models::Node`
|
|
125
|
-
- `HTM::Models::Tag`
|
|
126
|
-
- `HTM::Models::NodeTag`
|
|
127
|
-
- `HTM::Models::FileSource`
|
|
128
|
-
|
|
129
|
-
### Memory Types
|
|
130
|
-
|
|
131
|
-
- `:fact` - Immutable facts about the user or system
|
|
132
|
-
- `:context` - Conversational context and state
|
|
133
|
-
- `:code` - Code snippets, patterns, and implementations
|
|
134
|
-
- `:preference` - User preferences and settings
|
|
135
|
-
- `:decision` - Architectural and design decisions
|
|
136
|
-
- `:question` - Unresolved questions or uncertainties
|
|
137
|
-
|
|
138
|
-
### Search Strategies
|
|
139
|
-
|
|
140
|
-
- **Vector search** (`:vector`): Semantic similarity using pgvector with cosine distance on embeddings
|
|
141
|
-
- **Full-text search** (`:fulltext`): Keyword matching using PostgreSQL's tsvector with trigram fuzzy matching
|
|
142
|
-
- **Hybrid search** (`:hybrid`): Combines vector and full-text with weighted scoring
|
|
143
|
-
|
|
144
|
-
### Context Assembly Strategies
|
|
145
|
-
|
|
146
|
-
- **Recent** (`:recent`): Prioritizes newest memories
|
|
147
|
-
- **Important** (`:important`): Prioritizes high importance scores
|
|
148
|
-
- **Balanced** (`:balanced`): Importance × recency weighted combination
|
|
149
|
-
|
|
150
|
-
## Dependencies
|
|
151
|
-
|
|
152
|
-
### Runtime
|
|
153
|
-
- `pg` - PostgreSQL client library
|
|
154
|
-
- `pgvector` - Vector similarity search support
|
|
155
|
-
- `connection_pool` - Database connection pooling
|
|
156
|
-
- `tiktoken_ruby` - Token counting for context management
|
|
157
|
-
- `ruby_llm` - Multi-provider LLM client (OpenAI, Anthropic, Gemini, Azure, Ollama, HuggingFace, OpenRouter, Bedrock, DeepSeek)
|
|
158
|
-
|
|
159
|
-
### Development
|
|
160
|
-
- `rake` - Task automation
|
|
161
|
-
- `minitest` - Testing framework
|
|
162
|
-
- `minitest-reporters` - Enhanced test output
|
|
163
|
-
- `debug_me` - Debugging utilities (preferred over puts)
|
|
164
|
-
|
|
165
|
-
## Environment Variables
|
|
166
|
-
|
|
167
|
-
### Database
|
|
168
|
-
- `HTM_DBURL` - Full PostgreSQL connection URL (preferred)
|
|
169
|
-
- `HTM_SERVICE_NAME` - Service identifier
|
|
170
|
-
- `HTM_DBNAME` - Database name
|
|
171
|
-
- `HTM_DBUSER` - Database username
|
|
172
|
-
- `HTM_DBPASS` - Database password
|
|
173
|
-
- `HTM_DBPORT` - Database port
|
|
174
|
-
- `HTM_DBHOST` - Database host
|
|
175
|
-
|
|
176
|
-
### LLM Providers (via RubyLLM)
|
|
177
|
-
- `OPENAI_API_KEY` - OpenAI API key
|
|
178
|
-
- `OPENAI_ORGANIZATION` - OpenAI organization ID (optional)
|
|
179
|
-
- `ANTHROPIC_API_KEY` - Anthropic API key
|
|
180
|
-
- `GEMINI_API_KEY` - Google Gemini API key
|
|
181
|
-
- `AZURE_OPENAI_API_KEY` - Azure OpenAI API key
|
|
182
|
-
- `AZURE_OPENAI_ENDPOINT` - Azure OpenAI endpoint URL
|
|
183
|
-
- `OLLAMA_URL` - Ollama server URL (default: http://localhost:11434)
|
|
184
|
-
- `HUGGINGFACE_API_KEY` - HuggingFace Inference API key
|
|
185
|
-
- `OPENROUTER_API_KEY` - OpenRouter API key
|
|
186
|
-
- `AWS_ACCESS_KEY_ID` - AWS Bedrock access key
|
|
187
|
-
- `AWS_SECRET_ACCESS_KEY` - AWS Bedrock secret key
|
|
188
|
-
- `AWS_REGION` - AWS region (default: us-east-1)
|
|
189
|
-
- `DEEPSEEK_API_KEY` - DeepSeek API key
|
|
190
|
-
|
|
191
|
-
## External Services
|
|
192
|
-
|
|
193
|
-
### LLM Providers (via RubyLLM)
|
|
194
|
-
|
|
195
|
-
HTM uses RubyLLM for multi-provider LLM support. Configure your preferred provider:
|
|
196
|
-
|
|
197
|
-
```ruby
|
|
198
|
-
# OpenAI (cloud)
|
|
199
|
-
HTM.configure do |config|
|
|
200
|
-
config.embedding_provider = :openai
|
|
201
|
-
config.embedding_model = 'text-embedding-3-small'
|
|
202
|
-
config.tag_provider = :openai
|
|
203
|
-
config.tag_model = 'gpt-4o-mini'
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
# Anthropic (cloud)
|
|
207
|
-
HTM.configure do |config|
|
|
208
|
-
config.tag_provider = :anthropic
|
|
209
|
-
config.tag_model = 'claude-3-haiku-20240307'
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# Ollama (local - default)
|
|
213
|
-
HTM.configure do |config|
|
|
214
|
-
config.embedding_provider = :ollama
|
|
215
|
-
config.embedding_model = 'nomic-embed-text'
|
|
216
|
-
config.tag_provider = :ollama
|
|
217
|
-
config.tag_model = 'llama3'
|
|
218
|
-
end
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Ollama (default local provider)
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
# Install Ollama
|
|
225
|
-
curl https://ollama.ai/install.sh | sh
|
|
226
|
-
|
|
227
|
-
# Pull embedding model
|
|
228
|
-
ollama pull nomic-embed-text
|
|
229
|
-
|
|
230
|
-
# Pull chat model for tag extraction
|
|
231
|
-
ollama pull llama3
|
|
232
|
-
|
|
233
|
-
# Verify Ollama is running
|
|
234
|
-
curl http://localhost:11434/api/version
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### PostgreSQL Database
|
|
238
|
-
The project uses a local PostgreSQL instance with:
|
|
239
|
-
- PostgreSQL 14+ (17.x recommended)
|
|
240
|
-
- pgvector extension for vector similarity search
|
|
241
|
-
- pg_trgm extension for fuzzy text matching
|
|
242
|
-
- ActiveRecord for ORM and migrations
|
|
243
|
-
|
|
244
|
-
**Quick setup on macOS:**
|
|
245
|
-
```bash
|
|
246
|
-
brew install postgresql@17
|
|
247
|
-
brew services start postgresql@17
|
|
248
|
-
createdb htm_development
|
|
249
|
-
psql htm_development -c "CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
## Testing Philosophy
|
|
253
|
-
|
|
254
|
-
- All tests use Minitest framework with spec reporter
|
|
255
|
-
- Test files located in `test/` directory
|
|
256
|
-
- Integration tests require database connection (use test database if available)
|
|
257
|
-
- Mock Ollama embeddings in tests to avoid external dependencies
|
|
258
|
-
|
|
259
|
-
## Code Style
|
|
260
|
-
|
|
261
|
-
- Use `debug_me` gem for debugging, not `puts`
|
|
262
|
-
- Frozen string literals in all files
|
|
263
|
-
- Methods should be testable in isolation
|
|
264
|
-
- Use 2-space indentation
|
|
265
|
-
- Follow Ruby style guide conventions
|
|
266
|
-
|
|
267
|
-
## Common Development Tasks
|
|
268
|
-
|
|
269
|
-
### Adding a New Memory Type
|
|
270
|
-
1. Create ActiveRecord migration in `db/migrate/` if new columns needed
|
|
271
|
-
2. Add type to documentation in `lib/htm.rb` and `CLAUDE.md`
|
|
272
|
-
3. Consider adding specialized methods in `HTM` class
|
|
273
|
-
4. Add tests for the new type
|
|
274
|
-
5. Run `bundle exec rake htm:db:migrate` and `bundle exec rake htm:db:schema:dump`
|
|
275
|
-
|
|
276
|
-
### Using a Different LLM Provider
|
|
277
|
-
HTM uses RubyLLM which supports multiple providers out of the box. To use a different provider:
|
|
278
|
-
|
|
279
|
-
1. Set the appropriate API key environment variable (e.g., `OPENAI_API_KEY`)
|
|
280
|
-
2. Configure HTM with your preferred provider and model:
|
|
281
|
-
```ruby
|
|
282
|
-
HTM.configure do |config|
|
|
283
|
-
config.embedding_provider = :openai # or :anthropic, :gemini, :azure, etc.
|
|
284
|
-
config.embedding_model = 'text-embedding-3-small'
|
|
285
|
-
config.tag_provider = :openai
|
|
286
|
-
config.tag_model = 'gpt-4o-mini'
|
|
287
|
-
end
|
|
288
|
-
```
|
|
289
|
-
3. Supported providers: `:openai`, `:anthropic`, `:gemini`, `:azure`, `:ollama`, `:huggingface`, `:openrouter`, `:bedrock`, `:deepseek`
|
|
290
|
-
|
|
291
|
-
### Modifying Database Schema
|
|
292
|
-
1. Create new ActiveRecord migration in `db/migrate/` with timestamp prefix
|
|
293
|
-
2. Use ActiveRecord DSL for table/column changes (create_table, add_column, add_index, etc.)
|
|
294
|
-
3. Update affected ActiveRecord models in `lib/htm/models/`
|
|
295
|
-
4. Update affected classes (`LongTermMemory`, `Database`)
|
|
296
|
-
5. Run `bundle exec rake htm:db:migrate` to apply changes
|
|
297
|
-
6. Run `bundle exec rake htm:db:schema:dump` to update `db/schema.sql`
|
|
298
|
-
7. Update documentation in `docs/development/schema.md` and `CLAUDE.md`
|
|
299
|
-
8. Update tests
|
|
300
|
-
|
|
301
|
-
### Async Background Processing (ADR-016)
|
|
302
|
-
|
|
303
|
-
HTM uses **async-job** for background processing with two parallel jobs per node:
|
|
304
|
-
|
|
305
|
-
**Node Creation Flow**:
|
|
306
|
-
1. **Save node immediately** (~15ms) - Fast user response
|
|
307
|
-
2. **Enqueue `GenerateEmbeddingJob`** - Adds embedding asynchronously
|
|
308
|
-
3. **Enqueue `GenerateTagsJob`** - Extracts and adds tags asynchronously
|
|
309
|
-
|
|
310
|
-
**Eventual Consistency**:
|
|
311
|
-
- Node available immediately for basic retrieval
|
|
312
|
-
- Embedding added within ~100ms (enables vector search)
|
|
313
|
-
- Tags added within ~1 second (enables hierarchical navigation)
|
|
314
|
-
- Full-text search works immediately (no dependencies)
|
|
315
|
-
|
|
316
|
-
**Current workflow**:
|
|
317
|
-
```ruby
|
|
318
|
-
# 1. Create node (fast response)
|
|
319
|
-
node = htm.add_message("PostgreSQL supports vector search via pgvector")
|
|
320
|
-
# Returns immediately (~15ms)
|
|
321
|
-
|
|
322
|
-
# 2. Background jobs run in parallel (user doesn't wait)
|
|
323
|
-
# - GenerateEmbeddingJob: Uses EmbeddingService to generate embedding
|
|
324
|
-
# - GenerateTagsJob: Uses TagService to extract tags from content
|
|
325
|
-
|
|
326
|
-
# 3. Node eventually enriched (~1 second)
|
|
327
|
-
# - node.embedding populated (enables vector search)
|
|
328
|
-
# - tags created and associated (enables tag navigation)
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### Working with Embeddings
|
|
332
|
-
1. **Async generation**: `GenerateEmbeddingJob` runs after node creation
|
|
333
|
-
2. **Multi-provider support** (via RubyLLM):
|
|
334
|
-
- Ollama (default): `nomic-embed-text` (768-dim)
|
|
335
|
-
- OpenAI: `text-embedding-3-small` (1536-dim)
|
|
336
|
-
- Gemini: `text-embedding-004` (768-dim)
|
|
337
|
-
- Azure, HuggingFace, OpenRouter, Bedrock, DeepSeek also supported
|
|
338
|
-
3. **Dimensions**: Stored in `embedding_dimension` column, max 2000 dimensions (padded automatically)
|
|
339
|
-
4. **Error handling**: Failures logged, node remains without embedding
|
|
340
|
-
5. **Search behavior**: Vector search excludes nodes without embeddings
|
|
341
|
-
|
|
342
|
-
### Working with Metadata
|
|
343
|
-
|
|
344
|
-
Nodes have a `metadata` JSONB column for flexible key-value storage:
|
|
345
|
-
|
|
346
|
-
**Storing metadata:**
|
|
347
|
-
```ruby
|
|
348
|
-
# Store with metadata
|
|
349
|
-
htm.remember("User prefers dark mode", metadata: { category: "preference", priority: "high" })
|
|
350
|
-
|
|
351
|
-
# Combine with tags
|
|
352
|
-
htm.remember(
|
|
353
|
-
"API rate limit is 1000 req/min",
|
|
354
|
-
tags: ["api:rate-limiting"],
|
|
355
|
-
metadata: { environment: "production", version: 2 }
|
|
356
|
-
)
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
**Querying by metadata:**
|
|
360
|
-
```ruby
|
|
361
|
-
# Filter by metadata in recall
|
|
362
|
-
htm.recall("settings", metadata: { priority: "high" })
|
|
363
|
-
htm.recall("API config", metadata: { environment: "production", version: 2 })
|
|
364
|
-
|
|
365
|
-
# Combine with other filters
|
|
366
|
-
htm.recall("database", timeframe: "last month", strategy: :hybrid, metadata: { reviewed: true })
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
**Metadata features:**
|
|
370
|
-
- Stored as JSONB with GIN index for efficient queries
|
|
371
|
-
- Uses PostgreSQL `@>` containment operator (node must contain all specified keys)
|
|
372
|
-
- Supports any JSON type: strings, numbers, booleans, arrays, objects
|
|
373
|
-
- Validation ensures keys are strings or symbols
|
|
374
|
-
|
|
375
|
-
### Working with Tags
|
|
376
|
-
1. **Async extraction**: `GenerateTagsJob` uses LLM to extract hierarchical tags
|
|
377
|
-
2. **Multi-provider support**: Uses RubyLLM chat (same providers as embeddings)
|
|
378
|
-
3. **Hierarchical format**: Colon separators (e.g., `ai:llm:embeddings`)
|
|
379
|
-
4. **Ontology context**: Uses existing tags to maintain consistency
|
|
380
|
-
5. **Query by prefix**: `WHERE tags.name LIKE 'database:%'` finds all database tags
|
|
381
|
-
6. **Error handling**: Failures logged, node remains without tags
|
|
382
|
-
|
|
383
|
-
**Manual tag operations** (if needed):
|
|
384
|
-
```ruby
|
|
385
|
-
ltm.add_tag(node_id: node.id, tag: 'database:postgresql') # Manual tag
|
|
386
|
-
tags = ltm.node_topics(node.id) # Returns array of tag names
|
|
387
|
-
related = ltm.topic_relationships(min_shared_nodes: 2) # Co-occurrence
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
**Tag hierarchy visualization**:
|
|
391
|
-
```ruby
|
|
392
|
-
# Text tree (directory-style)
|
|
393
|
-
puts HTM::Models::Tag.all.tree_string
|
|
394
|
-
|
|
395
|
-
# Mermaid flowchart format
|
|
396
|
-
puts HTM::Models::Tag.all.tree_mermaid # Top-down
|
|
397
|
-
puts HTM::Models::Tag.all.tree_mermaid(direction: 'LR') # Left-to-right
|
|
398
|
-
|
|
399
|
-
# SVG visualization (dark theme, transparent background)
|
|
400
|
-
File.write('tags.svg', HTM::Models::Tag.all.tree_svg)
|
|
401
|
-
File.write('tags.svg', HTM::Models::Tag.all.tree_svg(title: 'My Tags'))
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
**Rake tasks for tag management**:
|
|
405
|
-
```bash
|
|
406
|
-
rake htm:tags:tree # Display text tree (all tags)
|
|
407
|
-
rake 'htm:tags:tree[database]' # Display tags with prefix 'database'
|
|
408
|
-
rake htm:tags:mermaid # Export to tags.md (Mermaid format)
|
|
409
|
-
rake 'htm:tags:mermaid[ai]' # Export tags with prefix 'ai' to tags.md
|
|
410
|
-
rake htm:tags:svg # Export to tags.svg
|
|
411
|
-
rake 'htm:tags:svg[web]' # Export tags with prefix 'web' to tags.svg
|
|
412
|
-
rake htm:tags:export # Export all formats (tags.txt, tags.md, tags.svg)
|
|
413
|
-
rake 'htm:tags:export[database]' # Export filtered tags to all formats
|
|
414
|
-
rake htm:tags:rebuild # Rebuild all tags (clears and regenerates via LLM)
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
### Soft Delete and Memory Recovery
|
|
418
|
-
|
|
419
|
-
HTM uses soft delete by default when forgetting memories, allowing recovery of accidentally deleted nodes.
|
|
420
|
-
|
|
421
|
-
**Soft delete (default - recoverable)**:
|
|
422
|
-
```ruby
|
|
423
|
-
htm.forget(node_id) # Soft delete (sets deleted_at timestamp)
|
|
424
|
-
htm.forget(node_id, soft: true) # Explicit soft delete
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
**Restore soft-deleted nodes**:
|
|
428
|
-
```ruby
|
|
429
|
-
htm.restore(node_id) # Clears deleted_at, node visible again
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
**Permanent delete (requires confirmation)**:
|
|
433
|
-
```ruby
|
|
434
|
-
htm.forget(node_id, soft: false, confirm: :confirmed) # Permanently removes from database
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
**Purge old soft-deleted nodes**:
|
|
438
|
-
```ruby
|
|
439
|
-
htm.purge_deleted(older_than: 30.days.ago, confirm: :confirmed)
|
|
440
|
-
htm.purge_deleted(older_than: Time.new(2024, 1, 1), confirm: :confirmed)
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
**Query soft-deleted nodes directly**:
|
|
444
|
-
```ruby
|
|
445
|
-
HTM::Models::Node.deleted # All soft-deleted nodes
|
|
446
|
-
HTM::Models::Node.with_deleted # All nodes including deleted
|
|
447
|
-
HTM::Models::Node.deleted_before(30.days.ago) # Deleted before a date
|
|
448
|
-
|
|
449
|
-
node = HTM::Models::Node.with_deleted.find(id)
|
|
450
|
-
node.deleted? # Check if soft-deleted
|
|
451
|
-
node.restore! # Restore a specific node
|
|
452
|
-
node.soft_delete! # Soft delete a specific node
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### Loading Files into Memory
|
|
456
|
-
|
|
457
|
-
HTM can load text-based files (currently markdown) into long-term memory with automatic chunking, source tracking, and re-sync support.
|
|
458
|
-
|
|
459
|
-
**Load a single file**:
|
|
460
|
-
```ruby
|
|
461
|
-
htm = HTM.new(robot_name: "Document Loader")
|
|
462
|
-
|
|
463
|
-
# Load a markdown file - chunks by paragraph, extracts frontmatter
|
|
464
|
-
result = htm.load_file("docs/guide.md")
|
|
465
|
-
# => { file_source_id: 1, chunks_created: 5, chunks_updated: 0, chunks_deleted: 0 }
|
|
466
|
-
|
|
467
|
-
# Force re-sync even if file hasn't changed
|
|
468
|
-
result = htm.load_file("docs/guide.md", force: true)
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
**Load a directory**:
|
|
472
|
-
```ruby
|
|
473
|
-
# Load all markdown files in a directory (recursive)
|
|
474
|
-
results = htm.load_directory("docs/")
|
|
475
|
-
# => [{ file_path: "docs/guide.md", ... }, { file_path: "docs/api.md", ... }]
|
|
476
|
-
|
|
477
|
-
# Custom glob pattern
|
|
478
|
-
results = htm.load_directory("content/", pattern: "**/*.md")
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
**Query nodes from a file**:
|
|
482
|
-
```ruby
|
|
483
|
-
# Get all nodes loaded from a specific file
|
|
484
|
-
nodes = htm.nodes_from_file("docs/guide.md")
|
|
485
|
-
# => [#<HTM::Models::Node>, #<HTM::Models::Node>, ...]
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
**Unload a file**:
|
|
489
|
-
```ruby
|
|
490
|
-
# Soft delete all nodes from a file and remove file source
|
|
491
|
-
htm.unload_file("docs/guide.md")
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
**Re-sync behavior**:
|
|
495
|
-
- Files are tracked by path with mtime-based change detection
|
|
496
|
-
- If file hasn't changed, `load_file` returns early (unless `force: true`)
|
|
497
|
-
- Changed files are re-synced: new chunks created, unchanged chunks kept, removed chunks soft-deleted
|
|
498
|
-
- YAML frontmatter is extracted and stored as metadata on the file source
|
|
499
|
-
|
|
500
|
-
**Chunking strategy**:
|
|
501
|
-
- Text is split by paragraph (blank lines)
|
|
502
|
-
- Fenced code blocks (``` and ~~~) are preserved as single chunks
|
|
503
|
-
- Very short fragments (<10 chars) are merged with neighbors
|
|
504
|
-
- Each chunk becomes a node with `source_id` linking back to the file
|
|
505
|
-
|
|
506
|
-
**FileSource model**:
|
|
507
|
-
```ruby
|
|
508
|
-
source = HTM::Models::FileSource.find_by(file_path: "docs/guide.md")
|
|
509
|
-
source.needs_sync? # Check if file changed since last load
|
|
510
|
-
source.chunks # Get all nodes from this file (ordered by position)
|
|
511
|
-
source.frontmatter # Get parsed YAML frontmatter hash
|
|
512
|
-
source.frontmatter_tags # Get tags from frontmatter (if present)
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
**Rake tasks for file loading**:
|
|
516
|
-
```bash
|
|
517
|
-
rake 'htm:files:load[docs/guide.md]' # Load a single file
|
|
518
|
-
rake 'htm:files:load_dir[docs/]' # Load all markdown files from directory
|
|
519
|
-
rake 'htm:files:load_dir[docs/,**/*.md]' # Load with custom glob pattern
|
|
520
|
-
rake htm:files:list # List all loaded file sources
|
|
521
|
-
rake 'htm:files:info[docs/guide.md]' # Show details for a loaded file
|
|
522
|
-
rake 'htm:files:unload[docs/guide.md]' # Unload a file from memory
|
|
523
|
-
rake htm:files:sync # Sync all loaded files (reload changed)
|
|
524
|
-
rake htm:files:stats # Show file loading statistics
|
|
525
|
-
|
|
526
|
-
# Use FORCE=true to reload even if file hasn't changed
|
|
527
|
-
FORCE=true rake 'htm:files:load[docs/guide.md]'
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
## Architecture Framework
|
|
531
|
-
|
|
532
|
-
HTM uses the [ai-software-architect](https://github.com/codenamev/ai-software-architect) framework for managing architectural decisions and reviews.
|
|
533
|
-
|
|
534
|
-
### Architecture Directory Structure
|
|
535
|
-
```
|
|
536
|
-
.architecture/
|
|
537
|
-
├── decisions/adrs/ # Architectural Decision Records
|
|
538
|
-
├── reviews/ # Architecture review documents
|
|
539
|
-
├── recalibration/ # Implementation plans from reviews
|
|
540
|
-
├── comparisons/ # Version comparisons
|
|
541
|
-
├── docs/ # Architecture documentation
|
|
542
|
-
├── templates/ # Document templates
|
|
543
|
-
└── members.yml # Review team roster
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
### Working with Architecture
|
|
547
|
-
|
|
548
|
-
**View existing ADRs**: Browse `.architecture/decisions/adrs/` for documented architectural decisions.
|
|
549
|
-
|
|
550
|
-
**Create new ADR**: Use natural language with Claude Code:
|
|
551
|
-
```
|
|
552
|
-
Create an ADR for [topic]
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
**Start architecture review**: For version, feature, or component reviews:
|
|
556
|
-
```
|
|
557
|
-
Start architecture review for version X.Y.Z
|
|
558
|
-
Start architecture review for [feature name]
|
|
559
|
-
Review architecture for [component description]
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
**Consult specialists**: Invoke specific review perspectives:
|
|
563
|
-
```
|
|
564
|
-
Ask Security Architect to review this API design
|
|
565
|
-
Ask Performance Specialist to review the caching strategy
|
|
566
|
-
Ask AI Engineer to review the RAG implementation
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
**View system analysis**: Comprehensive system overview available at `.architecture/reviews/initial-system-analysis.md`.
|
|
570
|
-
|
|
571
|
-
### Review Team
|
|
572
|
-
|
|
573
|
-
The architecture review team (defined in `.architecture/members.yml`) includes:
|
|
574
|
-
- **Systems Architect**: Distributed systems, scalability, system decomposition
|
|
575
|
-
- **Domain Expert**: Domain-driven design, business logic, semantic modeling
|
|
576
|
-
- **Security Specialist**: Threat modeling, vulnerability assessment, data protection
|
|
577
|
-
- **Maintainability Expert**: Code quality, technical debt, long-term evolution
|
|
578
|
-
- **Performance Specialist**: Optimization, profiling, resource utilization
|
|
579
|
-
- **AI Engineer**: LLM integration, RAG systems, embedding strategies
|
|
580
|
-
- **Ruby Expert**: Ruby idioms, gem development, testing
|
|
581
|
-
- **Database Architect**: PostgreSQL, ActiveRecord, pgvector optimization, schema design
|
|
582
|
-
|
|
583
|
-
## Important Notes
|
|
584
|
-
|
|
585
|
-
- The gem is under active development; see `htm_teamwork.md` for roadmap
|
|
586
|
-
- Database connection requires `HTM_DBURL` environment variable or config/database.yml
|
|
587
|
-
- **Async Processing** (ADR-016): Nodes saved immediately (~15ms), embeddings and tags added via background jobs
|
|
588
|
-
- **Background Jobs**: Uses `async-job` gem with `GenerateEmbeddingJob` and `GenerateTagsJob`
|
|
589
|
-
- **Multi-provider LLM support**: OpenAI, Anthropic, Gemini, Azure, Ollama (default), HuggingFace, OpenRouter, Bedrock, DeepSeek via RubyLLM
|
|
590
|
-
- Database uses PostgreSQL 17 with pgvector and pg_trgm extensions
|
|
591
|
-
- ActiveRecord models provide ORM layer: Robot, Node, Tag, NodeTag (ADR-013)
|
|
592
|
-
- **TagService**: LLM-based hierarchical tag extraction (parallel architecture to EmbeddingService)
|
|
593
|
-
- All robots share global memory (hive mind architecture)
|
|
594
|
-
- **Soft Delete**: `forget()` performs soft delete by default (recoverable via `restore()`); permanent delete requires `soft: false, confirm: :confirmed`
|
|
595
|
-
- Working memory eviction moves nodes to long-term storage, never deletes them
|
|
596
|
-
- **Metadata**: Nodes have a JSONB `metadata` column for flexible key-value storage; filter with `recall(..., metadata: {...})`
|
|
597
|
-
- **Tag Visualization**: Export tag hierarchy as text tree, Mermaid flowchart, or SVG (dark theme)
|
|
598
|
-
- Token counting uses Tiktoken with GPT-3.5-turbo encoding
|
|
599
|
-
- Architecture decisions are documented in ADRs (see `.architecture/decisions/adrs/`)
|
|
600
|
-
- **Key ADRs**: 001 (PostgreSQL), 013 (ActiveRecord+Tags), **016 (Async Jobs)** [supersedes 014, 015]
|
|
601
|
-
- **File Loading**: Load markdown files into memory with `load_file()`, `load_directory()`, `unload_file()` methods
|
|
602
|
-
- backward-compatibility is not necessary.
|
|
603
|
-
- backward compatibility is never a consideration
|