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/docs/api/htm.md
ADDED
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
# HTM Class
|
|
2
|
+
|
|
3
|
+
The main interface for HTM's intelligent memory management system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`HTM` is the primary class that orchestrates the two-tier memory system:
|
|
8
|
+
|
|
9
|
+
- **Working Memory**: Token-limited active context for immediate LLM use
|
|
10
|
+
- **Long-term Memory**: Durable PostgreSQL storage
|
|
11
|
+
|
|
12
|
+
Key features:
|
|
13
|
+
|
|
14
|
+
- Never forgets unless explicitly told (`forget`)
|
|
15
|
+
- RAG-based retrieval (temporal + semantic search)
|
|
16
|
+
- Multi-robot "hive mind" - all robots share global memory
|
|
17
|
+
- Relationship graphs for knowledge connections
|
|
18
|
+
- Time-series optimized with TimescaleDB
|
|
19
|
+
|
|
20
|
+
## Class Definition
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
class HTM
|
|
24
|
+
attr_reader :robot_id, :robot_name, :working_memory, :long_term_memory
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Initialization
|
|
29
|
+
|
|
30
|
+
### `new(**options)` {: #new }
|
|
31
|
+
|
|
32
|
+
Create a new HTM instance.
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
HTM.new(
|
|
36
|
+
working_memory_size: 128_000,
|
|
37
|
+
robot_id: nil,
|
|
38
|
+
robot_name: nil,
|
|
39
|
+
db_config: nil,
|
|
40
|
+
embedding_service: :ollama,
|
|
41
|
+
embedding_model: 'gpt-oss'
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Parameters
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Default | Description |
|
|
48
|
+
|-----------|------|---------|-------------|
|
|
49
|
+
| `working_memory_size` | Integer | `128_000` | Maximum tokens for working memory |
|
|
50
|
+
| `robot_id` | String, nil | Auto-generated UUID | Unique identifier for this robot |
|
|
51
|
+
| `robot_name` | String, nil | `"robot_#{id[0..7]}"` | Human-readable name |
|
|
52
|
+
| `db_config` | Hash, nil | From `ENV['HTM_DBURL']` | Database configuration |
|
|
53
|
+
| `embedding_service` | Symbol | `:ollama` | Embedding provider (`:ollama`, `:openai`, `:cohere`, `:local`) |
|
|
54
|
+
| `embedding_model` | String | `'gpt-oss'` | Model name for embeddings |
|
|
55
|
+
|
|
56
|
+
#### Returns
|
|
57
|
+
|
|
58
|
+
- `HTM` instance
|
|
59
|
+
|
|
60
|
+
#### Examples
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
# Basic initialization
|
|
64
|
+
htm = HTM.new(robot_name: "Code Assistant")
|
|
65
|
+
|
|
66
|
+
# Custom working memory size
|
|
67
|
+
htm = HTM.new(
|
|
68
|
+
robot_name: "Large Context Bot",
|
|
69
|
+
working_memory_size: 256_000
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# OpenAI embeddings
|
|
73
|
+
htm = HTM.new(
|
|
74
|
+
robot_name: "Research Bot",
|
|
75
|
+
embedding_service: :openai,
|
|
76
|
+
embedding_model: 'text-embedding-3-small'
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Custom database
|
|
80
|
+
htm = HTM.new(
|
|
81
|
+
db_config: {
|
|
82
|
+
host: 'localhost',
|
|
83
|
+
port: 5432,
|
|
84
|
+
dbname: 'htm_db',
|
|
85
|
+
user: 'postgres',
|
|
86
|
+
password: 'secret'
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Instance Attributes
|
|
94
|
+
|
|
95
|
+
### `robot_id` {: #robot_id }
|
|
96
|
+
|
|
97
|
+
Unique identifier for this robot instance.
|
|
98
|
+
|
|
99
|
+
- **Type**: String (UUID format)
|
|
100
|
+
- **Read-only**: Yes
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
htm.robot_id # => "a1b2c3d4-e5f6-..."
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `robot_name` {: #robot_name }
|
|
107
|
+
|
|
108
|
+
Human-readable name for this robot.
|
|
109
|
+
|
|
110
|
+
- **Type**: String
|
|
111
|
+
- **Read-only**: Yes
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
htm.robot_name # => "Code Assistant"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `working_memory` {: #working_memory }
|
|
118
|
+
|
|
119
|
+
The working memory instance.
|
|
120
|
+
|
|
121
|
+
- **Type**: `HTM::WorkingMemory`
|
|
122
|
+
- **Read-only**: Yes
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
htm.working_memory.token_count # => 45234
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `long_term_memory` {: #long_term_memory }
|
|
129
|
+
|
|
130
|
+
The long-term memory instance.
|
|
131
|
+
|
|
132
|
+
- **Type**: `HTM::LongTermMemory`
|
|
133
|
+
- **Read-only**: Yes
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
htm.long_term_memory.stats # => {...}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Public Methods
|
|
142
|
+
|
|
143
|
+
### `add_node(key, value, **options)` {: #add_node }
|
|
144
|
+
|
|
145
|
+
Add a new memory node to both working and long-term memory.
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
add_node(key, value,
|
|
149
|
+
type: nil,
|
|
150
|
+
category: nil,
|
|
151
|
+
importance: 1.0,
|
|
152
|
+
related_to: [],
|
|
153
|
+
tags: []
|
|
154
|
+
)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Parameters
|
|
158
|
+
|
|
159
|
+
| Parameter | Type | Default | Description |
|
|
160
|
+
|-----------|------|---------|-------------|
|
|
161
|
+
| `key` | String | *required* | Unique identifier for this node |
|
|
162
|
+
| `value` | String | *required* | Content of the memory |
|
|
163
|
+
| `type` | Symbol, nil | `nil` | Memory type (`:fact`, `:context`, `:code`, `:preference`, `:decision`, `:question`) |
|
|
164
|
+
| `category` | String, nil | `nil` | Optional category for organization |
|
|
165
|
+
| `importance` | Float | `1.0` | Importance score (0.0-10.0) |
|
|
166
|
+
| `related_to` | Array\<String\> | `[]` | Keys of related nodes |
|
|
167
|
+
| `tags` | Array\<String\> | `[]` | Tags for categorization |
|
|
168
|
+
|
|
169
|
+
#### Returns
|
|
170
|
+
|
|
171
|
+
- `Integer` - Database ID of the created node
|
|
172
|
+
|
|
173
|
+
#### Side Effects
|
|
174
|
+
|
|
175
|
+
- Stores node in PostgreSQL with vector embedding
|
|
176
|
+
- Adds node to working memory (evicts if needed)
|
|
177
|
+
- Creates relationships to `related_to` nodes
|
|
178
|
+
- Adds tags to the node
|
|
179
|
+
- Logs operation to `operations_log` table
|
|
180
|
+
- Updates robot activity timestamp
|
|
181
|
+
|
|
182
|
+
#### Examples
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
# Simple fact
|
|
186
|
+
htm.add_node("db_choice", "We chose PostgreSQL for its reliability")
|
|
187
|
+
|
|
188
|
+
# Architectural decision
|
|
189
|
+
htm.add_node(
|
|
190
|
+
"api_gateway_decision",
|
|
191
|
+
"Decided to use Kong as API gateway for rate limiting and auth",
|
|
192
|
+
type: :decision,
|
|
193
|
+
importance: 9.0,
|
|
194
|
+
tags: ["architecture", "api", "gateway"],
|
|
195
|
+
related_to: ["microservices_architecture"]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Code snippet
|
|
199
|
+
code = <<~RUBY
|
|
200
|
+
def calculate_total(items)
|
|
201
|
+
items.sum(&:price)
|
|
202
|
+
end
|
|
203
|
+
RUBY
|
|
204
|
+
|
|
205
|
+
htm.add_node(
|
|
206
|
+
"total_calculation_v1",
|
|
207
|
+
code,
|
|
208
|
+
type: :code,
|
|
209
|
+
category: "helpers",
|
|
210
|
+
importance: 5.0,
|
|
211
|
+
tags: ["ruby", "calculation"]
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# User preference
|
|
215
|
+
htm.add_node(
|
|
216
|
+
"user_123_timezone",
|
|
217
|
+
"User prefers UTC timezone for all timestamps",
|
|
218
|
+
type: :preference,
|
|
219
|
+
category: "user_settings",
|
|
220
|
+
importance: 6.0
|
|
221
|
+
)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Notes
|
|
225
|
+
|
|
226
|
+
- The `key` must be unique across all nodes
|
|
227
|
+
- Embeddings are generated automatically
|
|
228
|
+
- Token count is calculated automatically
|
|
229
|
+
- If working memory is full, less important nodes are evicted
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### `recall(timeframe:, topic:, **options)` {: #recall }
|
|
234
|
+
|
|
235
|
+
Recall memories from a timeframe and topic using RAG-based retrieval.
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
recall(
|
|
239
|
+
timeframe:,
|
|
240
|
+
topic:,
|
|
241
|
+
limit: 20,
|
|
242
|
+
strategy: :vector
|
|
243
|
+
)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
#### Parameters
|
|
247
|
+
|
|
248
|
+
| Parameter | Type | Default | Description |
|
|
249
|
+
|-----------|------|---------|-------------|
|
|
250
|
+
| `timeframe` | String, Range | *required* | Time range (e.g., `"last week"`, `7.days.ago..Time.now`) |
|
|
251
|
+
| `topic` | String | *required* | Topic to search for |
|
|
252
|
+
| `limit` | Integer | `20` | Maximum number of nodes to retrieve |
|
|
253
|
+
| `strategy` | Symbol | `:vector` | Search strategy (`:vector`, `:fulltext`, `:hybrid`) |
|
|
254
|
+
|
|
255
|
+
#### Timeframe Formats
|
|
256
|
+
|
|
257
|
+
String format (natural language):
|
|
258
|
+
|
|
259
|
+
- `"last week"` - Last 7 days
|
|
260
|
+
- `"yesterday"` - Previous day
|
|
261
|
+
- `"last N days"` - Last N days (e.g., "last 30 days")
|
|
262
|
+
- `"this month"` - Current month to now
|
|
263
|
+
- `"last month"` - Previous calendar month
|
|
264
|
+
- Default (unrecognized) - Last 24 hours
|
|
265
|
+
|
|
266
|
+
Range format:
|
|
267
|
+
|
|
268
|
+
- `Time` range: `(Time.now - 7*24*3600)..Time.now`
|
|
269
|
+
- `Date` range: `Date.today-7..Date.today`
|
|
270
|
+
|
|
271
|
+
#### Search Strategies
|
|
272
|
+
|
|
273
|
+
| Strategy | Description | Use Case |
|
|
274
|
+
|----------|-------------|----------|
|
|
275
|
+
| `:vector` | Semantic similarity using embeddings | Find conceptually related content |
|
|
276
|
+
| `:fulltext` | PostgreSQL full-text search | Find exact terms and phrases |
|
|
277
|
+
| `:hybrid` | Fulltext prefilter + vector ranking | Best accuracy + semantic understanding |
|
|
278
|
+
|
|
279
|
+
#### Returns
|
|
280
|
+
|
|
281
|
+
- `Array<Hash>` - Retrieved memory nodes
|
|
282
|
+
|
|
283
|
+
Each hash contains:
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
{
|
|
287
|
+
"id" => 123, # Database ID
|
|
288
|
+
"key" => "node_key", # Node identifier
|
|
289
|
+
"value" => "content...", # Node content
|
|
290
|
+
"type" => "fact", # Node type
|
|
291
|
+
"category" => "architecture", # Category
|
|
292
|
+
"importance" => 8.0, # Importance score
|
|
293
|
+
"created_at" => "2025-01-15...", # Creation timestamp
|
|
294
|
+
"robot_id" => "abc123...", # Robot that created it
|
|
295
|
+
"token_count" => 125, # Token count
|
|
296
|
+
"similarity" => 0.87 # Similarity score (vector/hybrid only)
|
|
297
|
+
# or "rank" => 0.456 # Rank score (fulltext only)
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### Side Effects
|
|
302
|
+
|
|
303
|
+
- Adds recalled nodes to working memory
|
|
304
|
+
- Evicts existing nodes if working memory is full
|
|
305
|
+
- Logs operation to `operations_log` table
|
|
306
|
+
- Updates robot activity timestamp
|
|
307
|
+
|
|
308
|
+
#### Examples
|
|
309
|
+
|
|
310
|
+
```ruby
|
|
311
|
+
# Vector semantic search
|
|
312
|
+
memories = htm.recall(
|
|
313
|
+
timeframe: "last week",
|
|
314
|
+
topic: "database performance optimization"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Fulltext search for exact phrases
|
|
318
|
+
memories = htm.recall(
|
|
319
|
+
timeframe: "last 30 days",
|
|
320
|
+
topic: "PostgreSQL connection pooling",
|
|
321
|
+
strategy: :fulltext,
|
|
322
|
+
limit: 10
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Hybrid search (best of both)
|
|
326
|
+
memories = htm.recall(
|
|
327
|
+
timeframe: "this month",
|
|
328
|
+
topic: "API rate limiting implementation",
|
|
329
|
+
strategy: :hybrid,
|
|
330
|
+
limit: 15
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Custom time range
|
|
334
|
+
start_time = Time.new(2025, 1, 1)
|
|
335
|
+
end_time = Time.now
|
|
336
|
+
|
|
337
|
+
memories = htm.recall(
|
|
338
|
+
timeframe: start_time..end_time,
|
|
339
|
+
topic: "security vulnerabilities",
|
|
340
|
+
limit: 50
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Process results
|
|
344
|
+
memories.each do |memory|
|
|
345
|
+
puts "#{memory['created_at']}: #{memory['value']}"
|
|
346
|
+
puts " Similarity: #{memory['similarity']}" if memory['similarity']
|
|
347
|
+
puts " Robot: #{memory['robot_id']}"
|
|
348
|
+
end
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
#### Performance Notes
|
|
352
|
+
|
|
353
|
+
- Vector search: Best for semantic understanding, requires embedding generation
|
|
354
|
+
- Fulltext search: Fastest for exact matches, no embedding overhead
|
|
355
|
+
- Hybrid search: Slower but most accurate, combines both approaches
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
### `retrieve(key)` {: #retrieve }
|
|
360
|
+
|
|
361
|
+
Retrieve a specific memory node by its key.
|
|
362
|
+
|
|
363
|
+
```ruby
|
|
364
|
+
retrieve(key)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### Parameters
|
|
368
|
+
|
|
369
|
+
| Parameter | Type | Description |
|
|
370
|
+
|-----------|------|-------------|
|
|
371
|
+
| `key` | String | Key of the node to retrieve |
|
|
372
|
+
|
|
373
|
+
#### Returns
|
|
374
|
+
|
|
375
|
+
- `Hash` - Node data if found
|
|
376
|
+
- `nil` - If node doesn't exist
|
|
377
|
+
|
|
378
|
+
#### Side Effects
|
|
379
|
+
|
|
380
|
+
- Updates `last_accessed` timestamp for the node
|
|
381
|
+
- Logs operation to `operations_log` table
|
|
382
|
+
|
|
383
|
+
#### Examples
|
|
384
|
+
|
|
385
|
+
```ruby
|
|
386
|
+
# Retrieve a node
|
|
387
|
+
node = htm.retrieve("api_decision_001")
|
|
388
|
+
|
|
389
|
+
if node
|
|
390
|
+
puts node['value']
|
|
391
|
+
puts "Created: #{node['created_at']}"
|
|
392
|
+
puts "Importance: #{node['importance']}"
|
|
393
|
+
else
|
|
394
|
+
puts "Node not found"
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Use retrieved data
|
|
398
|
+
config = htm.retrieve("database_config")
|
|
399
|
+
db_url = JSON.parse(config['value'])['url'] if config
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
### `forget(key, confirm:)` {: #forget }
|
|
405
|
+
|
|
406
|
+
Explicitly delete a memory node. Requires confirmation to prevent accidental deletion.
|
|
407
|
+
|
|
408
|
+
```ruby
|
|
409
|
+
forget(key, confirm: :confirmed)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### Parameters
|
|
413
|
+
|
|
414
|
+
| Parameter | Type | Description |
|
|
415
|
+
|-----------|------|-------------|
|
|
416
|
+
| `key` | String | Key of the node to delete |
|
|
417
|
+
| `confirm` | Symbol | Must be `:confirmed` to proceed |
|
|
418
|
+
|
|
419
|
+
#### Returns
|
|
420
|
+
|
|
421
|
+
- `true` - If successfully deleted
|
|
422
|
+
|
|
423
|
+
#### Raises
|
|
424
|
+
|
|
425
|
+
- `ArgumentError` - If `confirm` is not `:confirmed`
|
|
426
|
+
|
|
427
|
+
#### Side Effects
|
|
428
|
+
|
|
429
|
+
- Deletes node from PostgreSQL
|
|
430
|
+
- Removes node from working memory
|
|
431
|
+
- Logs operation before deletion
|
|
432
|
+
- Updates robot activity timestamp
|
|
433
|
+
|
|
434
|
+
#### Examples
|
|
435
|
+
|
|
436
|
+
```ruby
|
|
437
|
+
# Correct usage
|
|
438
|
+
htm.forget("temp_note_123", confirm: :confirmed)
|
|
439
|
+
|
|
440
|
+
# This will raise ArgumentError
|
|
441
|
+
htm.forget("temp_note_123") # Missing confirm parameter
|
|
442
|
+
|
|
443
|
+
# Safe deletion with verification
|
|
444
|
+
if htm.retrieve("old_data")
|
|
445
|
+
htm.forget("old_data", confirm: :confirmed)
|
|
446
|
+
puts "Deleted old_data"
|
|
447
|
+
end
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### Notes
|
|
451
|
+
|
|
452
|
+
- This is the **only** way to delete data from HTM
|
|
453
|
+
- Deletion is permanent and cannot be undone
|
|
454
|
+
- Related relationships and tags are also deleted (CASCADE)
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
### `create_context(strategy:, max_tokens:)` {: #create_context }
|
|
459
|
+
|
|
460
|
+
Create a context string from working memory for LLM consumption.
|
|
461
|
+
|
|
462
|
+
```ruby
|
|
463
|
+
create_context(strategy: :balanced, max_tokens: nil)
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
#### Parameters
|
|
467
|
+
|
|
468
|
+
| Parameter | Type | Default | Description |
|
|
469
|
+
|-----------|------|---------|-------------|
|
|
470
|
+
| `strategy` | Symbol | `:balanced` | Assembly strategy |
|
|
471
|
+
| `max_tokens` | Integer, nil | Working memory max | Optional token limit |
|
|
472
|
+
|
|
473
|
+
#### Assembly Strategies
|
|
474
|
+
|
|
475
|
+
| Strategy | Behavior | Use Case |
|
|
476
|
+
|----------|----------|----------|
|
|
477
|
+
| `:recent` | Most recently accessed first | Prioritize latest information |
|
|
478
|
+
| `:important` | Highest importance scores first | Focus on critical information |
|
|
479
|
+
| `:balanced` | Weighted by importance × recency | Best general-purpose strategy |
|
|
480
|
+
|
|
481
|
+
#### Returns
|
|
482
|
+
|
|
483
|
+
- `String` - Assembled context with nodes separated by `"\n\n"`
|
|
484
|
+
|
|
485
|
+
#### Examples
|
|
486
|
+
|
|
487
|
+
```ruby
|
|
488
|
+
# Balanced context (default)
|
|
489
|
+
context = htm.create_context(strategy: :balanced)
|
|
490
|
+
|
|
491
|
+
# Recent context with token limit
|
|
492
|
+
context = htm.create_context(
|
|
493
|
+
strategy: :recent,
|
|
494
|
+
max_tokens: 50_000
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
# Important context only
|
|
498
|
+
context = htm.create_context(strategy: :important)
|
|
499
|
+
|
|
500
|
+
# Use in LLM prompt
|
|
501
|
+
prompt = <<~PROMPT
|
|
502
|
+
You are a helpful assistant.
|
|
503
|
+
|
|
504
|
+
Context from memory:
|
|
505
|
+
#{context}
|
|
506
|
+
|
|
507
|
+
User question: #{user_input}
|
|
508
|
+
PROMPT
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
#### Notes
|
|
512
|
+
|
|
513
|
+
- Nodes are concatenated with double newlines
|
|
514
|
+
- Token limits are respected (stops adding when limit reached)
|
|
515
|
+
- Empty string if working memory is empty
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
### `memory_stats()` {: #memory_stats }
|
|
520
|
+
|
|
521
|
+
Get comprehensive statistics about memory usage.
|
|
522
|
+
|
|
523
|
+
```ruby
|
|
524
|
+
memory_stats()
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
#### Returns
|
|
528
|
+
|
|
529
|
+
- `Hash` - Statistics hash
|
|
530
|
+
|
|
531
|
+
Structure:
|
|
532
|
+
|
|
533
|
+
```ruby
|
|
534
|
+
{
|
|
535
|
+
robot_id: "abc123...",
|
|
536
|
+
robot_name: "Assistant",
|
|
537
|
+
|
|
538
|
+
# Long-term memory stats
|
|
539
|
+
total_nodes: 1234,
|
|
540
|
+
nodes_by_robot: {
|
|
541
|
+
"robot-1" => 500,
|
|
542
|
+
"robot-2" => 734
|
|
543
|
+
},
|
|
544
|
+
nodes_by_type: [
|
|
545
|
+
{"type" => "fact", "count" => 400},
|
|
546
|
+
{"type" => "decision", "count" => 200},
|
|
547
|
+
...
|
|
548
|
+
],
|
|
549
|
+
total_relationships: 567,
|
|
550
|
+
total_tags: 890,
|
|
551
|
+
oldest_memory: "2025-01-01 12:00:00",
|
|
552
|
+
newest_memory: "2025-01-15 14:30:00",
|
|
553
|
+
active_robots: 3,
|
|
554
|
+
robot_activity: [...],
|
|
555
|
+
database_size: 12345678,
|
|
556
|
+
|
|
557
|
+
# Working memory stats
|
|
558
|
+
working_memory: {
|
|
559
|
+
current_tokens: 45234,
|
|
560
|
+
max_tokens: 128000,
|
|
561
|
+
utilization: 35.34,
|
|
562
|
+
node_count: 23
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
#### Examples
|
|
568
|
+
|
|
569
|
+
```ruby
|
|
570
|
+
stats = htm.memory_stats
|
|
571
|
+
|
|
572
|
+
puts "Total memories: #{stats[:total_nodes]}"
|
|
573
|
+
puts "Working memory: #{stats[:working_memory][:utilization]}% full"
|
|
574
|
+
puts "Active robots: #{stats[:active_robots]}"
|
|
575
|
+
|
|
576
|
+
# Check if working memory is getting full
|
|
577
|
+
if stats[:working_memory][:utilization] > 80
|
|
578
|
+
puts "Warning: Working memory is #{stats[:working_memory][:utilization]}% full"
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Display by robot
|
|
582
|
+
stats[:nodes_by_robot].each do |robot_id, count|
|
|
583
|
+
puts "#{robot_id}: #{count} nodes"
|
|
584
|
+
end
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
### `which_robot_said(topic, limit:)` {: #which_robot_said }
|
|
590
|
+
|
|
591
|
+
Find which robots have discussed a specific topic.
|
|
592
|
+
|
|
593
|
+
```ruby
|
|
594
|
+
which_robot_said(topic, limit: 100)
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### Parameters
|
|
598
|
+
|
|
599
|
+
| Parameter | Type | Default | Description |
|
|
600
|
+
|-----------|------|---------|-------------|
|
|
601
|
+
| `topic` | String | *required* | Topic to search for |
|
|
602
|
+
| `limit` | Integer | `100` | Maximum results to consider |
|
|
603
|
+
|
|
604
|
+
#### Returns
|
|
605
|
+
|
|
606
|
+
- `Hash` - Robot IDs mapped to mention counts
|
|
607
|
+
|
|
608
|
+
```ruby
|
|
609
|
+
{
|
|
610
|
+
"robot-abc123" => 15,
|
|
611
|
+
"robot-def456" => 8,
|
|
612
|
+
"robot-ghi789" => 3
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
#### Examples
|
|
617
|
+
|
|
618
|
+
```ruby
|
|
619
|
+
# Find who discussed deployment
|
|
620
|
+
robots = htm.which_robot_said("deployment")
|
|
621
|
+
# => {"robot-1" => 12, "robot-2" => 5}
|
|
622
|
+
|
|
623
|
+
# Top contributor
|
|
624
|
+
top_robot, count = robots.max_by { |robot, count| count }
|
|
625
|
+
puts "#{top_robot} mentioned it #{count} times"
|
|
626
|
+
|
|
627
|
+
# Check if specific robot discussed it
|
|
628
|
+
if robots.key?("robot-123")
|
|
629
|
+
puts "Robot-123 discussed deployment #{robots['robot-123']} times"
|
|
630
|
+
end
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
### `conversation_timeline(topic, limit:)` {: #conversation_timeline }
|
|
636
|
+
|
|
637
|
+
Get a chronological timeline of conversation about a topic.
|
|
638
|
+
|
|
639
|
+
```ruby
|
|
640
|
+
conversation_timeline(topic, limit: 50)
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### Parameters
|
|
644
|
+
|
|
645
|
+
| Parameter | Type | Default | Description |
|
|
646
|
+
|-----------|------|---------|-------------|
|
|
647
|
+
| `topic` | String | *required* | Topic to search for |
|
|
648
|
+
| `limit` | Integer | `50` | Maximum results |
|
|
649
|
+
|
|
650
|
+
#### Returns
|
|
651
|
+
|
|
652
|
+
- `Array<Hash>` - Timeline entries sorted by timestamp
|
|
653
|
+
|
|
654
|
+
Structure:
|
|
655
|
+
|
|
656
|
+
```ruby
|
|
657
|
+
[
|
|
658
|
+
{
|
|
659
|
+
timestamp: "2025-01-15 10:30:00",
|
|
660
|
+
robot: "robot-abc123",
|
|
661
|
+
content: "We should consider PostgreSQL...",
|
|
662
|
+
type: "decision"
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
timestamp: "2025-01-15 11:45:00",
|
|
666
|
+
robot: "robot-def456",
|
|
667
|
+
content: "Agreed, PostgreSQL has better...",
|
|
668
|
+
type: "fact"
|
|
669
|
+
},
|
|
670
|
+
...
|
|
671
|
+
]
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
#### Examples
|
|
675
|
+
|
|
676
|
+
```ruby
|
|
677
|
+
# Get timeline
|
|
678
|
+
timeline = htm.conversation_timeline("API design", limit: 20)
|
|
679
|
+
|
|
680
|
+
# Display timeline
|
|
681
|
+
timeline.each do |entry|
|
|
682
|
+
puts "[#{entry[:timestamp]}] #{entry[:robot]}:"
|
|
683
|
+
puts " #{entry[:content]}"
|
|
684
|
+
puts " (#{entry[:type]})"
|
|
685
|
+
puts
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
# Find first mention
|
|
689
|
+
first = timeline.first
|
|
690
|
+
puts "First discussed by #{first[:robot]} at #{first[:timestamp]}"
|
|
691
|
+
|
|
692
|
+
# Group by robot
|
|
693
|
+
by_robot = timeline.group_by { |e| e[:robot] }
|
|
694
|
+
by_robot.each do |robot, entries|
|
|
695
|
+
puts "#{robot}: #{entries.size} contributions"
|
|
696
|
+
end
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
## Error Handling
|
|
702
|
+
|
|
703
|
+
### ArgumentError
|
|
704
|
+
|
|
705
|
+
```ruby
|
|
706
|
+
# Invalid confirm parameter
|
|
707
|
+
htm.forget("key")
|
|
708
|
+
# => ArgumentError: Must pass confirm: :confirmed to delete
|
|
709
|
+
|
|
710
|
+
# Invalid timeframe
|
|
711
|
+
htm.recall(timeframe: nil, topic: "test")
|
|
712
|
+
# => ArgumentError: Invalid timeframe: nil
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### PG::Error
|
|
716
|
+
|
|
717
|
+
```ruby
|
|
718
|
+
# Database connection issues
|
|
719
|
+
htm = HTM.new(db_config: { host: 'invalid' })
|
|
720
|
+
# => PG::ConnectionBad: could not translate host name...
|
|
721
|
+
|
|
722
|
+
# Duplicate key
|
|
723
|
+
htm.add_node("existing_key", "value")
|
|
724
|
+
# => PG::UniqueViolation: duplicate key value...
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
## Best Practices
|
|
728
|
+
|
|
729
|
+
### Memory Organization
|
|
730
|
+
|
|
731
|
+
```ruby
|
|
732
|
+
# Use consistent key naming
|
|
733
|
+
htm.add_node("decision_20250115_api_gateway", ...)
|
|
734
|
+
htm.add_node("fact_20250115_database_choice", ...)
|
|
735
|
+
|
|
736
|
+
# Use importance strategically
|
|
737
|
+
htm.add_node(key, value, importance: 9.0) # Critical
|
|
738
|
+
htm.add_node(key, value, importance: 5.0) # Normal
|
|
739
|
+
htm.add_node(key, value, importance: 2.0) # Low priority
|
|
740
|
+
|
|
741
|
+
# Build knowledge graphs
|
|
742
|
+
htm.add_node(
|
|
743
|
+
"api_v2_implementation",
|
|
744
|
+
"...",
|
|
745
|
+
related_to: ["api_v1_design", "authentication_decision"]
|
|
746
|
+
)
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
### Search Strategies
|
|
750
|
+
|
|
751
|
+
```ruby
|
|
752
|
+
# Use vector for semantic understanding
|
|
753
|
+
memories = htm.recall(
|
|
754
|
+
timeframe: "last month",
|
|
755
|
+
topic: "performance issues",
|
|
756
|
+
strategy: :vector # Finds "slow queries", "optimization", etc.
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
# Use fulltext for exact terms
|
|
760
|
+
memories = htm.recall(
|
|
761
|
+
timeframe: "this week",
|
|
762
|
+
topic: "PostgreSQL EXPLAIN ANALYZE",
|
|
763
|
+
strategy: :fulltext # Exact match
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
# Use hybrid for best results
|
|
767
|
+
memories = htm.recall(
|
|
768
|
+
timeframe: "last week",
|
|
769
|
+
topic: "security vulnerability",
|
|
770
|
+
strategy: :hybrid # Accurate + semantic
|
|
771
|
+
)
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
### Resource Management
|
|
775
|
+
|
|
776
|
+
```ruby
|
|
777
|
+
# Check working memory before large operations
|
|
778
|
+
stats = htm.memory_stats
|
|
779
|
+
if stats[:working_memory][:utilization] > 90
|
|
780
|
+
# Maybe explicitly recall less
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
# Use appropriate limits
|
|
784
|
+
htm.recall(topic: "common_topic", limit: 10) # Not 1000
|
|
785
|
+
|
|
786
|
+
# Monitor database size
|
|
787
|
+
if stats[:database_size] > 1_000_000_000 # 1GB
|
|
788
|
+
# Consider archival strategy
|
|
789
|
+
end
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
## See Also
|
|
793
|
+
|
|
794
|
+
- [WorkingMemory API](working-memory.md)
|
|
795
|
+
- [LongTermMemory API](long-term-memory.md)
|
|
796
|
+
- [EmbeddingService API](embedding-service.md)
|
|
797
|
+
- [Database API](database.md)
|