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