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
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
# WorkingMemory Class
|
|
2
|
+
|
|
3
|
+
Token-limited active context for immediate LLM use.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`HTM::WorkingMemory` manages the active conversation context within strict token limits. When capacity is reached, it intelligently evicts less important or older nodes back to long-term storage.
|
|
8
|
+
|
|
9
|
+
**Key Features:**
|
|
10
|
+
|
|
11
|
+
- Token-based capacity management
|
|
12
|
+
- LRU (Least Recently Used) tracking
|
|
13
|
+
- Importance-weighted eviction
|
|
14
|
+
- Multiple context assembly strategies
|
|
15
|
+
- Real-time utilization monitoring
|
|
16
|
+
|
|
17
|
+
## Class Definition
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
class HTM::WorkingMemory
|
|
21
|
+
attr_reader :max_tokens
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Initialization
|
|
26
|
+
|
|
27
|
+
### `new(max_tokens:)` {: #new }
|
|
28
|
+
|
|
29
|
+
Create a new working memory instance.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
HTM::WorkingMemory.new(max_tokens: 128_000)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### Parameters
|
|
36
|
+
|
|
37
|
+
| Parameter | Type | Default | Description |
|
|
38
|
+
|-----------|------|---------|-------------|
|
|
39
|
+
| `max_tokens` | Integer | *required* | Maximum tokens allowed in working memory |
|
|
40
|
+
|
|
41
|
+
#### Returns
|
|
42
|
+
|
|
43
|
+
- `HTM::WorkingMemory` instance
|
|
44
|
+
|
|
45
|
+
#### Examples
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# Standard working memory (128K tokens)
|
|
49
|
+
wm = HTM::WorkingMemory.new(max_tokens: 128_000)
|
|
50
|
+
|
|
51
|
+
# Large context window (256K tokens)
|
|
52
|
+
wm = HTM::WorkingMemory.new(max_tokens: 256_000)
|
|
53
|
+
|
|
54
|
+
# Small working memory (32K tokens)
|
|
55
|
+
wm = HTM::WorkingMemory.new(max_tokens: 32_000)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Instance Attributes
|
|
61
|
+
|
|
62
|
+
### `max_tokens` {: #max_tokens }
|
|
63
|
+
|
|
64
|
+
Maximum token capacity for this working memory.
|
|
65
|
+
|
|
66
|
+
- **Type**: Integer
|
|
67
|
+
- **Read-only**: Yes
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
wm.max_tokens # => 128000
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Public Methods
|
|
76
|
+
|
|
77
|
+
### `add(key, value, **options)` {: #add }
|
|
78
|
+
|
|
79
|
+
Add a node to working memory.
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
add(key, value,
|
|
83
|
+
token_count:,
|
|
84
|
+
importance: 1.0,
|
|
85
|
+
from_recall: false
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Parameters
|
|
90
|
+
|
|
91
|
+
| Parameter | Type | Default | Description |
|
|
92
|
+
|-----------|------|---------|-------------|
|
|
93
|
+
| `key` | String | *required* | Node identifier |
|
|
94
|
+
| `value` | String | *required* | Node content |
|
|
95
|
+
| `token_count` | Integer | *required* | Number of tokens in this node |
|
|
96
|
+
| `importance` | Float | `1.0` | Importance score (0.0-10.0) |
|
|
97
|
+
| `from_recall` | Boolean | `false` | Whether this node was recalled from long-term memory |
|
|
98
|
+
|
|
99
|
+
#### Returns
|
|
100
|
+
|
|
101
|
+
- `void`
|
|
102
|
+
|
|
103
|
+
#### Side Effects
|
|
104
|
+
|
|
105
|
+
- Adds node to internal hash
|
|
106
|
+
- Updates access order (LRU tracking)
|
|
107
|
+
- Records timestamp
|
|
108
|
+
|
|
109
|
+
#### Examples
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
# Add a simple node
|
|
113
|
+
wm.add("fact_001", "PostgreSQL is our database",
|
|
114
|
+
token_count: 50,
|
|
115
|
+
importance: 7.0
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Add recalled node
|
|
119
|
+
wm.add("decision_001", "Use microservices architecture",
|
|
120
|
+
token_count: 120,
|
|
121
|
+
importance: 9.0,
|
|
122
|
+
from_recall: true
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Add low-importance temporary note
|
|
126
|
+
wm.add("temp_note", "Check deployment at 3pm",
|
|
127
|
+
token_count: 30,
|
|
128
|
+
importance: 2.0
|
|
129
|
+
)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### Notes
|
|
133
|
+
|
|
134
|
+
- Does **not** check capacity - use `has_space?` first
|
|
135
|
+
- Updates existing node if key already exists
|
|
136
|
+
- Automatically updates access order for LRU
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### `remove(key)` {: #remove }
|
|
141
|
+
|
|
142
|
+
Remove a node from working memory.
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
remove(key)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Parameters
|
|
149
|
+
|
|
150
|
+
| Parameter | Type | Description |
|
|
151
|
+
|-----------|------|-------------|
|
|
152
|
+
| `key` | String | Node identifier |
|
|
153
|
+
|
|
154
|
+
#### Returns
|
|
155
|
+
|
|
156
|
+
- `void`
|
|
157
|
+
|
|
158
|
+
#### Side Effects
|
|
159
|
+
|
|
160
|
+
- Removes node from internal hash
|
|
161
|
+
- Removes from access order tracking
|
|
162
|
+
|
|
163
|
+
#### Examples
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
# Remove a node
|
|
167
|
+
wm.remove("temp_note_123")
|
|
168
|
+
|
|
169
|
+
# Safe removal (checks existence)
|
|
170
|
+
if wm.respond_to?(:has_key?) && wm.has_key?("old_key")
|
|
171
|
+
wm.remove("old_key")
|
|
172
|
+
end
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### `has_space?(token_count)` {: #has_space }
|
|
178
|
+
|
|
179
|
+
Check if there's space for a node.
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
has_space?(token_count)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Parameters
|
|
186
|
+
|
|
187
|
+
| Parameter | Type | Description |
|
|
188
|
+
|-----------|------|-------------|
|
|
189
|
+
| `token_count` | Integer | Number of tokens needed |
|
|
190
|
+
|
|
191
|
+
#### Returns
|
|
192
|
+
|
|
193
|
+
- `Boolean` - `true` if space available, `false` otherwise
|
|
194
|
+
|
|
195
|
+
#### Examples
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
# Check before adding
|
|
199
|
+
if wm.has_space?(500)
|
|
200
|
+
wm.add("new_node", "content...", token_count: 500)
|
|
201
|
+
else
|
|
202
|
+
puts "Working memory full, need to evict"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Check capacity for large addition
|
|
206
|
+
large_content_tokens = 5000
|
|
207
|
+
if wm.has_space?(large_content_tokens)
|
|
208
|
+
wm.add("large_node", large_content, token_count: large_content_tokens)
|
|
209
|
+
else
|
|
210
|
+
# Evict to make space
|
|
211
|
+
evicted = wm.evict_to_make_space(large_content_tokens)
|
|
212
|
+
wm.add("large_node", large_content, token_count: large_content_tokens)
|
|
213
|
+
end
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### `evict_to_make_space(needed_tokens)` {: #evict_to_make_space }
|
|
219
|
+
|
|
220
|
+
Evict nodes to free up space for new content.
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
evict_to_make_space(needed_tokens)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Parameters
|
|
227
|
+
|
|
228
|
+
| Parameter | Type | Description |
|
|
229
|
+
|-----------|------|-------------|
|
|
230
|
+
| `needed_tokens` | Integer | Number of tokens needed |
|
|
231
|
+
|
|
232
|
+
#### Returns
|
|
233
|
+
|
|
234
|
+
- `Array<Hash>` - Evicted nodes
|
|
235
|
+
|
|
236
|
+
Each hash contains:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
{
|
|
240
|
+
key: "node_key",
|
|
241
|
+
value: "node content..."
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Eviction Strategy
|
|
246
|
+
|
|
247
|
+
Nodes are sorted by:
|
|
248
|
+
|
|
249
|
+
1. **Importance** (ascending) - Lower importance evicted first
|
|
250
|
+
2. **Recency** (descending) - Older nodes evicted first
|
|
251
|
+
|
|
252
|
+
Formula: `[importance, -recency_in_seconds]`
|
|
253
|
+
|
|
254
|
+
#### Side Effects
|
|
255
|
+
|
|
256
|
+
- Removes evicted nodes from working memory
|
|
257
|
+
- Updates access order tracking
|
|
258
|
+
|
|
259
|
+
#### Examples
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
# Make space for 10,000 tokens
|
|
263
|
+
evicted = wm.evict_to_make_space(10_000)
|
|
264
|
+
|
|
265
|
+
puts "Evicted #{evicted.length} nodes:"
|
|
266
|
+
evicted.each do |node|
|
|
267
|
+
puts " - #{node[:key]}"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Make space and log to long-term memory
|
|
271
|
+
evicted = wm.evict_to_make_space(needed_tokens)
|
|
272
|
+
evicted_keys = evicted.map { |n| n[:key] }
|
|
273
|
+
long_term_memory.mark_evicted(evicted_keys)
|
|
274
|
+
|
|
275
|
+
# Check how much was freed
|
|
276
|
+
tokens_freed = evicted.sum { |n| n[:token_count] }
|
|
277
|
+
puts "Freed #{tokens_freed} tokens"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### Notes
|
|
281
|
+
|
|
282
|
+
- Evicts just enough to meet `needed_tokens`
|
|
283
|
+
- May evict more than one node
|
|
284
|
+
- Preserves high-importance and recent nodes
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### `assemble_context(strategy:, max_tokens:)` {: #assemble_context }
|
|
289
|
+
|
|
290
|
+
Assemble context string for LLM consumption.
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
assemble_context(strategy:, max_tokens: nil)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### Parameters
|
|
297
|
+
|
|
298
|
+
| Parameter | Type | Default | Description |
|
|
299
|
+
|-----------|------|---------|-------------|
|
|
300
|
+
| `strategy` | Symbol | *required* | Assembly strategy (`:recent`, `:important`, `:balanced`) |
|
|
301
|
+
| `max_tokens` | Integer, nil | `@max_tokens` | Optional token limit |
|
|
302
|
+
|
|
303
|
+
#### Assembly Strategies
|
|
304
|
+
|
|
305
|
+
**`:recent`** - Most recently accessed first
|
|
306
|
+
|
|
307
|
+
```ruby
|
|
308
|
+
# Ordered by access time (newest first)
|
|
309
|
+
# Good for: Conversational continuity
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**`:important`** - Highest importance scores first
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
# Ordered by importance score (highest first)
|
|
316
|
+
# Good for: Critical information prioritization
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**`:balanced`** - Weighted by importance and recency
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
# Formula: importance × (1.0 / (1 + age_in_hours))
|
|
323
|
+
# Good for: General-purpose use (recommended)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### Returns
|
|
327
|
+
|
|
328
|
+
- `String` - Assembled context with nodes separated by `"\n\n"`
|
|
329
|
+
|
|
330
|
+
#### Examples
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
# Balanced context (recommended)
|
|
334
|
+
context = wm.assemble_context(strategy: :balanced)
|
|
335
|
+
|
|
336
|
+
# Recent conversations only
|
|
337
|
+
context = wm.assemble_context(strategy: :recent)
|
|
338
|
+
|
|
339
|
+
# Most important information
|
|
340
|
+
context = wm.assemble_context(strategy: :important)
|
|
341
|
+
|
|
342
|
+
# Limited tokens
|
|
343
|
+
context = wm.assemble_context(
|
|
344
|
+
strategy: :balanced,
|
|
345
|
+
max_tokens: 50_000
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# Use in LLM prompt
|
|
349
|
+
prompt = <<~PROMPT
|
|
350
|
+
Context:
|
|
351
|
+
#{context}
|
|
352
|
+
|
|
353
|
+
Question: #{user_question}
|
|
354
|
+
PROMPT
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### Balanced Strategy Details
|
|
358
|
+
|
|
359
|
+
The balanced strategy uses a decay function:
|
|
360
|
+
|
|
361
|
+
```ruby
|
|
362
|
+
score = importance × (1.0 / (1 + recency_in_hours))
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Examples:
|
|
366
|
+
|
|
367
|
+
| Importance | Age | Score |
|
|
368
|
+
|------------|-----|-------|
|
|
369
|
+
| 10.0 | 1 hour | 5.0 |
|
|
370
|
+
| 10.0 | 5 hours | 1.67 |
|
|
371
|
+
| 5.0 | 1 hour | 2.5 |
|
|
372
|
+
| 5.0 | 5 hours | 0.83 |
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### `token_count()` {: #token_count }
|
|
377
|
+
|
|
378
|
+
Get current total tokens in working memory.
|
|
379
|
+
|
|
380
|
+
```ruby
|
|
381
|
+
token_count()
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Returns
|
|
385
|
+
|
|
386
|
+
- `Integer` - Total tokens across all nodes
|
|
387
|
+
|
|
388
|
+
#### Examples
|
|
389
|
+
|
|
390
|
+
```ruby
|
|
391
|
+
current = wm.token_count
|
|
392
|
+
puts "Using #{current} tokens"
|
|
393
|
+
|
|
394
|
+
# Check if near capacity
|
|
395
|
+
if wm.token_count > wm.max_tokens * 0.9
|
|
396
|
+
puts "Warning: Working memory 90% full"
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Calculate available space
|
|
400
|
+
available = wm.max_tokens - wm.token_count
|
|
401
|
+
puts "#{available} tokens available"
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
### `utilization_percentage()` {: #utilization_percentage }
|
|
407
|
+
|
|
408
|
+
Get working memory utilization as a percentage.
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
utilization_percentage()
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
#### Returns
|
|
415
|
+
|
|
416
|
+
- `Float` - Percentage of capacity used (0.0-100.0, rounded to 2 decimals)
|
|
417
|
+
|
|
418
|
+
#### Examples
|
|
419
|
+
|
|
420
|
+
```ruby
|
|
421
|
+
util = wm.utilization_percentage
|
|
422
|
+
puts "Working memory: #{util}% full"
|
|
423
|
+
|
|
424
|
+
# Capacity warnings
|
|
425
|
+
case util
|
|
426
|
+
when 0..50
|
|
427
|
+
puts "Plenty of space"
|
|
428
|
+
when 51..80
|
|
429
|
+
puts "Getting full"
|
|
430
|
+
when 81..95
|
|
431
|
+
puts "Warning: High utilization"
|
|
432
|
+
else
|
|
433
|
+
puts "Critical: Near capacity"
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Progress bar
|
|
437
|
+
bar_length = (util / 2).round
|
|
438
|
+
bar = "█" * bar_length + "░" * (50 - bar_length)
|
|
439
|
+
puts "[#{bar}] #{util}%"
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
### `node_count()` {: #node_count }
|
|
445
|
+
|
|
446
|
+
Get the number of nodes in working memory.
|
|
447
|
+
|
|
448
|
+
```ruby
|
|
449
|
+
node_count()
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### Returns
|
|
453
|
+
|
|
454
|
+
- `Integer` - Number of nodes currently stored
|
|
455
|
+
|
|
456
|
+
#### Examples
|
|
457
|
+
|
|
458
|
+
```ruby
|
|
459
|
+
count = wm.node_count
|
|
460
|
+
puts "#{count} nodes in working memory"
|
|
461
|
+
|
|
462
|
+
# Average tokens per node
|
|
463
|
+
if count > 0
|
|
464
|
+
avg = wm.token_count / count
|
|
465
|
+
puts "Average: #{avg} tokens per node"
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# Density check
|
|
469
|
+
if wm.node_count > 100
|
|
470
|
+
puts "Warning: Many small nodes, consider consolidation"
|
|
471
|
+
end
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Usage Patterns
|
|
477
|
+
|
|
478
|
+
### Capacity Management
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
# Check capacity before operations
|
|
482
|
+
def add_with_eviction(wm, ltm, key, value, token_count, importance)
|
|
483
|
+
unless wm.has_space?(token_count)
|
|
484
|
+
# Evict and mark in long-term memory
|
|
485
|
+
evicted = wm.evict_to_make_space(token_count)
|
|
486
|
+
evicted_keys = evicted.map { |n| n[:key] }
|
|
487
|
+
ltm.mark_evicted(evicted_keys) unless evicted_keys.empty?
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
wm.add(key, value,
|
|
491
|
+
token_count: token_count,
|
|
492
|
+
importance: importance
|
|
493
|
+
)
|
|
494
|
+
end
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Monitoring
|
|
498
|
+
|
|
499
|
+
```ruby
|
|
500
|
+
# Working memory dashboard
|
|
501
|
+
def print_wm_status(wm)
|
|
502
|
+
puts "Working Memory Status:"
|
|
503
|
+
puts " Nodes: #{wm.node_count}"
|
|
504
|
+
puts " Tokens: #{wm.token_count} / #{wm.max_tokens}"
|
|
505
|
+
puts " Utilization: #{wm.utilization_percentage}%"
|
|
506
|
+
|
|
507
|
+
available = wm.max_tokens - wm.token_count
|
|
508
|
+
puts " Available: #{available} tokens"
|
|
509
|
+
end
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Strategic Context Assembly
|
|
513
|
+
|
|
514
|
+
```ruby
|
|
515
|
+
# Different contexts for different tasks
|
|
516
|
+
class ContextManager
|
|
517
|
+
def initialize(wm)
|
|
518
|
+
@wm = wm
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def for_conversation
|
|
522
|
+
# Recent context for chat continuity
|
|
523
|
+
@wm.assemble_context(strategy: :recent, max_tokens: 8000)
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def for_analysis
|
|
527
|
+
# Important context for deep analysis
|
|
528
|
+
@wm.assemble_context(strategy: :important, max_tokens: 32000)
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def for_general_task
|
|
532
|
+
# Balanced for most tasks
|
|
533
|
+
@wm.assemble_context(strategy: :balanced)
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Importance-Based Retention
|
|
539
|
+
|
|
540
|
+
```ruby
|
|
541
|
+
# Critical memories persist longer
|
|
542
|
+
wm.add("critical_security_alert",
|
|
543
|
+
"SQL injection vulnerability found in user input",
|
|
544
|
+
token_count: 150,
|
|
545
|
+
importance: 10.0 # Maximum importance
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# Temporary notes evicted first
|
|
549
|
+
wm.add("temp_reminder",
|
|
550
|
+
"Check logs after deployment",
|
|
551
|
+
token_count: 50,
|
|
552
|
+
importance: 1.0 # Minimum importance
|
|
553
|
+
)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
## Performance Characteristics
|
|
557
|
+
|
|
558
|
+
### Time Complexity
|
|
559
|
+
|
|
560
|
+
| Operation | Complexity | Notes |
|
|
561
|
+
|-----------|------------|-------|
|
|
562
|
+
| `add` | O(1) | Hash insertion + array append |
|
|
563
|
+
| `remove` | O(n) | Array deletion requires scan |
|
|
564
|
+
| `has_space?` | O(n) | Sums all token counts |
|
|
565
|
+
| `evict_to_make_space` | O(n log n) | Sorting nodes |
|
|
566
|
+
| `assemble_context` | O(n log n) | Sorting + concatenation |
|
|
567
|
+
| `token_count` | O(n) | Sums all nodes |
|
|
568
|
+
| `node_count` | O(1) | Hash size |
|
|
569
|
+
|
|
570
|
+
### Space Complexity
|
|
571
|
+
|
|
572
|
+
- O(n) where n is the number of nodes
|
|
573
|
+
- Each node stores: key, value, token_count, importance, timestamp, from_recall flag
|
|
574
|
+
|
|
575
|
+
### Optimization Tips
|
|
576
|
+
|
|
577
|
+
1. **Batch Operations**: Add multiple nodes before checking space
|
|
578
|
+
2. **Importance Scoring**: Use meaningful scores (1-10) for effective eviction
|
|
579
|
+
3. **Token Limits**: Set `max_tokens` based on your LLM's context window
|
|
580
|
+
4. **Strategy Selection**: Use `:recent` for speed, `:balanced` for quality
|
|
581
|
+
|
|
582
|
+
## Internal Data Structures
|
|
583
|
+
|
|
584
|
+
### Node Storage
|
|
585
|
+
|
|
586
|
+
```ruby
|
|
587
|
+
@nodes = {
|
|
588
|
+
"key1" => {
|
|
589
|
+
value: "content...",
|
|
590
|
+
token_count: 150,
|
|
591
|
+
importance: 7.0,
|
|
592
|
+
added_at: Time.now,
|
|
593
|
+
from_recall: false
|
|
594
|
+
},
|
|
595
|
+
"key2" => { ... }
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Access Order
|
|
600
|
+
|
|
601
|
+
```ruby
|
|
602
|
+
@access_order = ["key1", "key3", "key2"]
|
|
603
|
+
# Most recently accessed at the end
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
## Best Practices
|
|
607
|
+
|
|
608
|
+
### 1. Check Space Before Adding
|
|
609
|
+
|
|
610
|
+
```ruby
|
|
611
|
+
# Good
|
|
612
|
+
if wm.has_space?(tokens)
|
|
613
|
+
wm.add(key, value, token_count: tokens)
|
|
614
|
+
else
|
|
615
|
+
wm.evict_to_make_space(tokens)
|
|
616
|
+
wm.add(key, value, token_count: tokens)
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# Bad - may exceed capacity
|
|
620
|
+
wm.add(key, value, token_count: tokens)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### 2. Use Appropriate Importance Scores
|
|
624
|
+
|
|
625
|
+
```ruby
|
|
626
|
+
# Critical architectural decisions
|
|
627
|
+
importance: 10.0
|
|
628
|
+
|
|
629
|
+
# Important facts
|
|
630
|
+
importance: 7.0-8.0
|
|
631
|
+
|
|
632
|
+
# Normal information
|
|
633
|
+
importance: 4.0-6.0
|
|
634
|
+
|
|
635
|
+
# Temporary notes
|
|
636
|
+
importance: 1.0-3.0
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### 3. Monitor Utilization
|
|
640
|
+
|
|
641
|
+
```ruby
|
|
642
|
+
# Set up alerts
|
|
643
|
+
if wm.utilization_percentage > 90
|
|
644
|
+
warn "Working memory critically full"
|
|
645
|
+
end
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### 4. Choose Right Strategy
|
|
649
|
+
|
|
650
|
+
```ruby
|
|
651
|
+
# For chat/conversation
|
|
652
|
+
context = wm.assemble_context(strategy: :recent)
|
|
653
|
+
|
|
654
|
+
# For analysis/reasoning
|
|
655
|
+
context = wm.assemble_context(strategy: :important)
|
|
656
|
+
|
|
657
|
+
# For general use
|
|
658
|
+
context = wm.assemble_context(strategy: :balanced)
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
## See Also
|
|
662
|
+
|
|
663
|
+
- [HTM API](htm.md) - Main class that uses WorkingMemory
|
|
664
|
+
- [LongTermMemory API](long-term-memory.md) - Persistent storage for evicted nodes
|
|
665
|
+
- [EmbeddingService API](embedding-service.md) - Token counting
|