htm 0.0.11 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dictate.toml +46 -0
- data/.envrc +2 -0
- data/CHANGELOG.md +85 -2
- data/README.md +348 -79
- data/Rakefile +14 -2
- data/bin/htm_mcp.rb +94 -0
- data/config/database.yml +20 -13
- data/db/migrate/00003_create_file_sources.rb +5 -0
- data/db/migrate/00004_create_nodes.rb +17 -0
- data/db/migrate/00005_create_tags.rb +7 -0
- data/db/migrate/00006_create_node_tags.rb +2 -0
- data/db/migrate/00007_create_robot_nodes.rb +7 -0
- data/db/schema.sql +69 -100
- data/docs/api/index.md +1 -1
- data/docs/api/yard/HTM/Configuration.md +54 -0
- data/docs/api/yard/HTM/Database.md +13 -10
- data/docs/api/yard/HTM/EmbeddingService.md +5 -1
- data/docs/api/yard/HTM/LongTermMemory.md +18 -277
- data/docs/api/yard/HTM/PropositionError.md +18 -0
- data/docs/api/yard/HTM/PropositionService.md +66 -0
- data/docs/api/yard/HTM/QueryCache.md +88 -0
- data/docs/api/yard/HTM/RobotGroup.md +481 -0
- data/docs/api/yard/HTM/SqlBuilder.md +108 -0
- data/docs/api/yard/HTM/TagService.md +4 -0
- data/docs/api/yard/HTM/Telemetry/NullInstrument.md +13 -0
- data/docs/api/yard/HTM/Telemetry/NullMeter.md +15 -0
- data/docs/api/yard/HTM/Telemetry.md +109 -0
- data/docs/api/yard/HTM/WorkingMemoryChannel.md +176 -0
- data/docs/api/yard/HTM.md +8 -22
- data/docs/api/yard/index.csv +102 -25
- data/docs/api/yard-reference.md +8 -0
- data/docs/architecture/index.md +1 -1
- data/docs/assets/images/multi-provider-failover.svg +51 -0
- data/docs/assets/images/robot-group-architecture.svg +65 -0
- data/docs/database/README.md +3 -3
- data/docs/database/public.file_sources.svg +29 -21
- data/docs/database/public.node_tags.md +2 -0
- data/docs/database/public.node_tags.svg +53 -41
- data/docs/database/public.nodes.md +2 -0
- data/docs/database/public.nodes.svg +52 -40
- data/docs/database/public.robot_nodes.md +2 -0
- data/docs/database/public.robot_nodes.svg +30 -22
- data/docs/database/public.robots.svg +16 -12
- data/docs/database/public.tags.md +3 -0
- data/docs/database/public.tags.svg +41 -33
- data/docs/database/schema.json +66 -0
- data/docs/database/schema.svg +60 -48
- data/docs/development/index.md +14 -1
- data/docs/development/rake-tasks.md +1068 -0
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/quick-start.md +144 -155
- data/docs/guides/adding-memories.md +2 -3
- data/docs/guides/context-assembly.md +185 -184
- data/docs/guides/getting-started.md +154 -148
- data/docs/guides/index.md +8 -1
- data/docs/guides/long-term-memory.md +60 -92
- data/docs/guides/mcp-server.md +617 -0
- data/docs/guides/multi-robot.md +249 -345
- data/docs/guides/recalling-memories.md +153 -163
- data/docs/guides/robot-groups.md +604 -0
- data/docs/guides/search-strategies.md +61 -58
- data/docs/guides/working-memory.md +103 -136
- data/docs/images/telemetry-architecture.svg +153 -0
- data/docs/index.md +30 -26
- data/docs/telemetry.md +391 -0
- data/examples/README.md +46 -1
- data/examples/cli_app/README.md +1 -1
- data/examples/cli_app/htm_cli.rb +1 -1
- data/examples/robot_groups/robot_worker.rb +1 -2
- data/examples/robot_groups/same_process.rb +1 -4
- data/examples/sinatra_app/app.rb +1 -1
- data/examples/telemetry/README.md +147 -0
- data/examples/telemetry/SETUP_README.md +169 -0
- data/examples/telemetry/demo.rb +498 -0
- data/examples/telemetry/grafana/dashboards/htm-metrics.json +457 -0
- data/lib/htm/configuration.rb +261 -70
- data/lib/htm/database.rb +46 -22
- data/lib/htm/embedding_service.rb +24 -14
- data/lib/htm/errors.rb +15 -1
- data/lib/htm/jobs/generate_embedding_job.rb +19 -0
- data/lib/htm/jobs/generate_propositions_job.rb +103 -0
- data/lib/htm/jobs/generate_tags_job.rb +24 -0
- data/lib/htm/loaders/markdown_chunker.rb +79 -0
- data/lib/htm/loaders/markdown_loader.rb +41 -15
- data/lib/htm/long_term_memory/fulltext_search.rb +138 -0
- data/lib/htm/long_term_memory/hybrid_search.rb +324 -0
- data/lib/htm/long_term_memory/node_operations.rb +209 -0
- data/lib/htm/long_term_memory/relevance_scorer.rb +355 -0
- data/lib/htm/long_term_memory/robot_operations.rb +34 -0
- data/lib/htm/long_term_memory/tag_operations.rb +428 -0
- data/lib/htm/long_term_memory/vector_search.rb +109 -0
- data/lib/htm/long_term_memory.rb +51 -1153
- data/lib/htm/models/node.rb +35 -2
- data/lib/htm/models/node_tag.rb +31 -0
- data/lib/htm/models/robot_node.rb +31 -0
- data/lib/htm/models/tag.rb +44 -0
- data/lib/htm/proposition_service.rb +169 -0
- data/lib/htm/query_cache.rb +214 -0
- data/lib/htm/robot_group.rb +721 -0
- data/lib/htm/sql_builder.rb +178 -0
- data/lib/htm/tag_service.rb +16 -6
- data/lib/htm/tasks.rb +8 -2
- data/lib/htm/telemetry.rb +224 -0
- data/lib/htm/version.rb +1 -1
- data/lib/htm/working_memory_channel.rb +250 -0
- data/lib/htm.rb +66 -3
- data/lib/tasks/doc.rake +1 -1
- data/lib/tasks/htm.rake +259 -13
- data/mkdocs.yml +98 -96
- metadata +55 -20
- data/.aigcm_msg +0 -1
- data/.claude/settings.local.json +0 -95
- data/CLAUDE.md +0 -603
- data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +0 -12
- data/examples/cli_app/temp.log +0 -93
- data/examples/robot_groups/lib/robot_group.rb +0 -419
- data/examples/robot_groups/lib/working_memory_channel.rb +0 -140
- data/lib/htm/loaders/paragraph_chunker.rb +0 -112
- data/notes/ARCHITECTURE_REVIEW.md +0 -1167
- data/notes/IMPLEMENTATION_SUMMARY.md +0 -606
- data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +0 -451
- data/notes/next_steps.md +0 -100
- data/notes/plan.md +0 -627
- data/notes/tag_ontology_enhancement_ideas.md +0 -222
- data/notes/timescaledb_removal_summary.md +0 -200
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# Class: HTM::RobotGroup
|
|
2
|
+
**Inherits:** Object
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Coordinates multiple robots with shared working memory and automatic failover.
|
|
6
|
+
|
|
7
|
+
RobotGroup provides application-level coordination for multiple HTM robots,
|
|
8
|
+
enabling them to share a common working memory context. Key capabilities
|
|
9
|
+
include:
|
|
10
|
+
|
|
11
|
+
* **Shared Working Memory**: All group members have access to the same
|
|
12
|
+
context
|
|
13
|
+
* **Active/Passive Roles**: Active robots participate in conversations;
|
|
14
|
+
passive robots maintain synchronized context for instant failover
|
|
15
|
+
* **Real-time Sync**: PostgreSQL LISTEN/NOTIFY enables immediate
|
|
16
|
+
synchronization
|
|
17
|
+
* **Failover**: When an active robot fails, a passive robot takes over
|
|
18
|
+
instantly
|
|
19
|
+
* **Dynamic Scaling**: Add or remove robots at runtime
|
|
20
|
+
|
|
21
|
+
**`@see`** [] Low-level pub/sub mechanism
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
**`@example`**
|
|
25
|
+
```ruby
|
|
26
|
+
group = HTM::RobotGroup.new(
|
|
27
|
+
name: 'customer-support',
|
|
28
|
+
active: ['primary-agent'],
|
|
29
|
+
passive: ['standby-agent'],
|
|
30
|
+
max_tokens: 8000
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Add shared context
|
|
34
|
+
group.remember('Customer prefers email communication.')
|
|
35
|
+
group.remember('Open ticket #789 regarding billing issue.')
|
|
36
|
+
|
|
37
|
+
# Query shared memory
|
|
38
|
+
results = group.recall('billing', limit: 5)
|
|
39
|
+
|
|
40
|
+
# Simulate failover
|
|
41
|
+
group.failover! # Promotes standby to active
|
|
42
|
+
|
|
43
|
+
# Cleanup
|
|
44
|
+
group.shutdown
|
|
45
|
+
```
|
|
46
|
+
# Attributes
|
|
47
|
+
## channel[RW] {: #attribute-i-channel }
|
|
48
|
+
The pub/sub channel used for real-time synchronization
|
|
49
|
+
|
|
50
|
+
**`@return`** [HTM::WorkingMemoryChannel]
|
|
51
|
+
|
|
52
|
+
## max_tokens[RW] {: #attribute-i-max_tokens }
|
|
53
|
+
Maximum token budget for working memory
|
|
54
|
+
|
|
55
|
+
**`@return`** [Integer]
|
|
56
|
+
|
|
57
|
+
## name[RW] {: #attribute-i-name }
|
|
58
|
+
Name of the robot group
|
|
59
|
+
|
|
60
|
+
**`@return`** [String]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Instance Methods
|
|
64
|
+
## active?(robot_name) {: #method-i-active? }
|
|
65
|
+
Checks if a robot is an active member of this group.
|
|
66
|
+
|
|
67
|
+
**`@param`** [String] Name of the robot to check
|
|
68
|
+
|
|
69
|
+
**`@return`** [Boolean] true if the robot is an active member
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
**`@example`**
|
|
73
|
+
```ruby
|
|
74
|
+
group.active?('primary-agent') # => true
|
|
75
|
+
```
|
|
76
|
+
## active_robot_names() {: #method-i-active_robot_names }
|
|
77
|
+
Returns names of all active robots.
|
|
78
|
+
|
|
79
|
+
**`@return`** [Array<String>] Array of active robot names
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
**`@example`**
|
|
83
|
+
```ruby
|
|
84
|
+
group.active_robot_names # => ['primary-agent', 'secondary-agent']
|
|
85
|
+
```
|
|
86
|
+
## add_active(robot_name) {: #method-i-add_active }
|
|
87
|
+
Adds a robot as an active member of the group.
|
|
88
|
+
|
|
89
|
+
Active robots can add memories and respond to queries. The new robot is
|
|
90
|
+
automatically synchronized with existing shared working memory.
|
|
91
|
+
|
|
92
|
+
**`@param`** [String] Unique name for the robot
|
|
93
|
+
|
|
94
|
+
**`@raise`** [ArgumentError] if robot_name is already a member
|
|
95
|
+
|
|
96
|
+
**`@return`** [Integer] The robot's database ID
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
**`@example`**
|
|
100
|
+
```ruby
|
|
101
|
+
robot_id = group.add_active('new-agent')
|
|
102
|
+
puts "Added robot with ID: #{robot_id}"
|
|
103
|
+
```
|
|
104
|
+
## add_passive(robot_name) {: #method-i-add_passive }
|
|
105
|
+
Adds a robot as a passive (standby) member of the group.
|
|
106
|
+
|
|
107
|
+
Passive robots maintain synchronized working memory but don't actively
|
|
108
|
+
participate in conversations. They serve as warm standbys for failover.
|
|
109
|
+
|
|
110
|
+
**`@param`** [String] Unique name for the robot
|
|
111
|
+
|
|
112
|
+
**`@raise`** [ArgumentError] if robot_name is already a member
|
|
113
|
+
|
|
114
|
+
**`@return`** [Integer] The robot's database ID
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
**`@example`**
|
|
118
|
+
```ruby
|
|
119
|
+
robot_id = group.add_passive('standby-agent')
|
|
120
|
+
```
|
|
121
|
+
## clear_working_memory() {: #method-i-clear_working_memory }
|
|
122
|
+
Clears shared working memory for all group members.
|
|
123
|
+
|
|
124
|
+
Updates database flags and notifies all members to clear their in-memory
|
|
125
|
+
caches.
|
|
126
|
+
|
|
127
|
+
**`@return`** [Integer] Number of robot_node records updated
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
**`@example`**
|
|
131
|
+
```ruby
|
|
132
|
+
cleared_count = group.clear_working_memory
|
|
133
|
+
puts "Cleared #{cleared_count} working memory entries"
|
|
134
|
+
```
|
|
135
|
+
## demote(robot_name) {: #method-i-demote }
|
|
136
|
+
Demotes an active robot to passive status.
|
|
137
|
+
|
|
138
|
+
The robot retains its working memory but stops handling queries. Cannot demote
|
|
139
|
+
the last active robot.
|
|
140
|
+
|
|
141
|
+
**`@param`** [String] Name of the active robot to demote
|
|
142
|
+
|
|
143
|
+
**`@raise`** [ArgumentError] if robot_name is not an active member
|
|
144
|
+
|
|
145
|
+
**`@raise`** [ArgumentError] if this is the last active robot
|
|
146
|
+
|
|
147
|
+
**`@return`** [void]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
**`@example`**
|
|
151
|
+
```ruby
|
|
152
|
+
group.demote('primary-agent')
|
|
153
|
+
group.passive?('primary-agent') # => true
|
|
154
|
+
```
|
|
155
|
+
## failover!() {: #method-i-failover! }
|
|
156
|
+
Performs automatic failover to the first passive robot.
|
|
157
|
+
|
|
158
|
+
Promotes the first passive robot to active status. The promoted robot already
|
|
159
|
+
has synchronized working memory and can immediately handle requests.
|
|
160
|
+
|
|
161
|
+
**`@raise`** [RuntimeError] if no passive robots are available
|
|
162
|
+
|
|
163
|
+
**`@return`** [String] Name of the promoted robot
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
**`@example`**
|
|
167
|
+
```ruby
|
|
168
|
+
promoted = group.failover!
|
|
169
|
+
puts "#{promoted} is now active"
|
|
170
|
+
```
|
|
171
|
+
## in_sync?() {: #method-i-in_sync? }
|
|
172
|
+
Checks if all members have identical working memory.
|
|
173
|
+
|
|
174
|
+
Compares the set of working memory node IDs across all members.
|
|
175
|
+
|
|
176
|
+
**`@return`** [Boolean] true if all members have the same working memory nodes
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
**`@example`**
|
|
180
|
+
```ruby
|
|
181
|
+
if group.in_sync?
|
|
182
|
+
puts "All robots synchronized"
|
|
183
|
+
else
|
|
184
|
+
group.sync_all
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
## initialize(name:, active:[], passive:[], max_tokens:4000, db_config:nil) {: #method-i-initialize }
|
|
188
|
+
Creates a new robot group with optional initial members.
|
|
189
|
+
|
|
190
|
+
Initializes the group, sets up the PostgreSQL pub/sub channel for real-time
|
|
191
|
+
synchronization, and registers initial active and passive robots.
|
|
192
|
+
|
|
193
|
+
**`@param`** [String] Unique name for this robot group
|
|
194
|
+
|
|
195
|
+
**`@param`** [Array<String>] Names of robots to add as active members
|
|
196
|
+
|
|
197
|
+
**`@param`** [Array<String>] Names of robots to add as passive (standby) members
|
|
198
|
+
|
|
199
|
+
**`@param`** [Integer] Maximum token budget for shared working memory
|
|
200
|
+
|
|
201
|
+
**`@param`** [Hash, nil] PostgreSQL connection config (defaults to HTM::Database.default_config)
|
|
202
|
+
|
|
203
|
+
**`@return`** [RobotGroup] a new instance of RobotGroup
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
**`@example`**
|
|
207
|
+
```ruby
|
|
208
|
+
group = HTM::RobotGroup.new(
|
|
209
|
+
name: 'support-team',
|
|
210
|
+
active: ['agent-1'],
|
|
211
|
+
passive: ['agent-2'],
|
|
212
|
+
max_tokens: 4000
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
**`@example`**
|
|
216
|
+
```ruby
|
|
217
|
+
group = HTM::RobotGroup.new(name: 'dynamic-team')
|
|
218
|
+
group.add_active('agent-1')
|
|
219
|
+
group.add_passive('agent-2')
|
|
220
|
+
```
|
|
221
|
+
## member?(robot_name) {: #method-i-member? }
|
|
222
|
+
Checks if a robot is a member of this group.
|
|
223
|
+
|
|
224
|
+
**`@param`** [String] Name of the robot to check
|
|
225
|
+
|
|
226
|
+
**`@return`** [Boolean] true if the robot is an active or passive member
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
**`@example`**
|
|
230
|
+
```ruby
|
|
231
|
+
group.member?('agent-1') # => true
|
|
232
|
+
group.member?('unknown') # => false
|
|
233
|
+
```
|
|
234
|
+
## member_ids() {: #method-i-member_ids }
|
|
235
|
+
Returns database IDs of all group members.
|
|
236
|
+
|
|
237
|
+
**`@return`** [Array<Integer>] Array of robot IDs (both active and passive)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
**`@example`**
|
|
241
|
+
```ruby
|
|
242
|
+
group.member_ids # => [1, 2, 3]
|
|
243
|
+
```
|
|
244
|
+
## passive?(robot_name) {: #method-i-passive? }
|
|
245
|
+
Checks if a robot is a passive member of this group.
|
|
246
|
+
|
|
247
|
+
**`@param`** [String] Name of the robot to check
|
|
248
|
+
|
|
249
|
+
**`@return`** [Boolean] true if the robot is a passive member
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
**`@example`**
|
|
253
|
+
```ruby
|
|
254
|
+
group.passive?('standby-agent') # => true
|
|
255
|
+
```
|
|
256
|
+
## passive_robot_names() {: #method-i-passive_robot_names }
|
|
257
|
+
Returns names of all passive robots.
|
|
258
|
+
|
|
259
|
+
**`@return`** [Array<String>] Array of passive robot names
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
**`@example`**
|
|
263
|
+
```ruby
|
|
264
|
+
group.passive_robot_names # => ['standby-agent']
|
|
265
|
+
```
|
|
266
|
+
## promote(robot_name) {: #method-i-promote }
|
|
267
|
+
Promotes a passive robot to active status.
|
|
268
|
+
|
|
269
|
+
The robot retains its synchronized working memory and becomes eligible to
|
|
270
|
+
handle queries and add memories.
|
|
271
|
+
|
|
272
|
+
**`@param`** [String] Name of the passive robot to promote
|
|
273
|
+
|
|
274
|
+
**`@raise`** [ArgumentError] if robot_name is not a passive member
|
|
275
|
+
|
|
276
|
+
**`@return`** [void]
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
**`@example`**
|
|
280
|
+
```ruby
|
|
281
|
+
group.promote('standby-agent')
|
|
282
|
+
group.active?('standby-agent') # => true
|
|
283
|
+
```
|
|
284
|
+
## recall(query, **options) {: #method-i-recall }
|
|
285
|
+
Recalls memories from shared working memory.
|
|
286
|
+
|
|
287
|
+
Uses the first active robot to perform the query against the shared working
|
|
288
|
+
memory context.
|
|
289
|
+
|
|
290
|
+
**`@option`** []
|
|
291
|
+
|
|
292
|
+
**`@option`** []
|
|
293
|
+
|
|
294
|
+
**`@param`** [String] The search query
|
|
295
|
+
|
|
296
|
+
**`@param`** [Hash] Additional options passed to HTM#recall
|
|
297
|
+
|
|
298
|
+
**`@raise`** [RuntimeError] if no active robots exist in the group
|
|
299
|
+
|
|
300
|
+
**`@return`** [Array] Array of matching memories
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
**`@example`**
|
|
304
|
+
```ruby
|
|
305
|
+
results = group.recall('billing issue', limit: 5, strategy: :fulltext)
|
|
306
|
+
```
|
|
307
|
+
## remember(content, originator:nil, **options) {: #method-i-remember }
|
|
308
|
+
Adds content to shared working memory for all group members.
|
|
309
|
+
|
|
310
|
+
The memory is created by the specified originator (or first active robot) and
|
|
311
|
+
automatically synchronized to all other members via database and real-time
|
|
312
|
+
notifications.
|
|
313
|
+
|
|
314
|
+
**`@param`** [String] The content to remember
|
|
315
|
+
|
|
316
|
+
**`@param`** [String, nil] Name of the robot creating the memory (optional)
|
|
317
|
+
|
|
318
|
+
**`@param`** [Hash] Additional options passed to HTM#remember
|
|
319
|
+
|
|
320
|
+
**`@raise`** [RuntimeError] if no active robots exist in the group
|
|
321
|
+
|
|
322
|
+
**`@return`** [Integer] The node ID of the created memory
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
**`@example`**
|
|
326
|
+
```ruby
|
|
327
|
+
node_id = group.remember('Customer prefers morning appointments.')
|
|
328
|
+
```
|
|
329
|
+
**`@example`**
|
|
330
|
+
```ruby
|
|
331
|
+
node_id = group.remember(
|
|
332
|
+
'Escalated to billing department.',
|
|
333
|
+
originator: 'agent-2'
|
|
334
|
+
)
|
|
335
|
+
```
|
|
336
|
+
## remove(robot_name) {: #method-i-remove }
|
|
337
|
+
Removes a robot from the group.
|
|
338
|
+
|
|
339
|
+
Clears the robot's working memory flags in the database. The robot can be
|
|
340
|
+
either active or passive.
|
|
341
|
+
|
|
342
|
+
**`@param`** [String] Name of the robot to remove
|
|
343
|
+
|
|
344
|
+
**`@return`** [void]
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
**`@example`**
|
|
348
|
+
```ruby
|
|
349
|
+
group.remove('departing-agent')
|
|
350
|
+
```
|
|
351
|
+
## shutdown() {: #method-i-shutdown }
|
|
352
|
+
Shuts down the group by stopping the listener thread.
|
|
353
|
+
|
|
354
|
+
Should be called when the group is no longer needed to release resources and
|
|
355
|
+
close the PostgreSQL listener connection.
|
|
356
|
+
|
|
357
|
+
**`@return`** [void]
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
**`@example`**
|
|
361
|
+
```ruby
|
|
362
|
+
group.shutdown
|
|
363
|
+
```
|
|
364
|
+
## status() {: #method-i-status }
|
|
365
|
+
Returns comprehensive status information about the group.
|
|
366
|
+
|
|
367
|
+
**`@option`** []
|
|
368
|
+
|
|
369
|
+
**`@option`** []
|
|
370
|
+
|
|
371
|
+
**`@option`** []
|
|
372
|
+
|
|
373
|
+
**`@option`** []
|
|
374
|
+
|
|
375
|
+
**`@option`** []
|
|
376
|
+
|
|
377
|
+
**`@option`** []
|
|
378
|
+
|
|
379
|
+
**`@option`** []
|
|
380
|
+
|
|
381
|
+
**`@option`** []
|
|
382
|
+
|
|
383
|
+
**`@option`** []
|
|
384
|
+
|
|
385
|
+
**`@param`** [Hash] a customizable set of options
|
|
386
|
+
|
|
387
|
+
**`@return`** [Hash] Status hash with the following keys:
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
**`@example`**
|
|
391
|
+
```ruby
|
|
392
|
+
status = group.status
|
|
393
|
+
puts "Group: #{status[:name]}"
|
|
394
|
+
puts "Active: #{status[:active].join(', ')}"
|
|
395
|
+
puts "Utilization: #{(status[:token_utilization] * 100).round(1)}%"
|
|
396
|
+
```
|
|
397
|
+
## sync_all() {: #method-i-sync_all }
|
|
398
|
+
Synchronizes all members to a consistent state.
|
|
399
|
+
|
|
400
|
+
Ensures every member has access to all shared working memory nodes.
|
|
401
|
+
|
|
402
|
+
**`@return`** [Hash] Sync results with :synced_nodes and :members_updated counts
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
**`@example`**
|
|
406
|
+
```ruby
|
|
407
|
+
result = group.sync_all
|
|
408
|
+
puts "Synced #{result[:synced_nodes]} nodes to #{result[:members_updated]} members"
|
|
409
|
+
```
|
|
410
|
+
## sync_robot(robot_name) {: #method-i-sync_robot }
|
|
411
|
+
Synchronizes a specific robot to match the group's shared working memory.
|
|
412
|
+
|
|
413
|
+
Copies working memory flags from other members to the specified robot,
|
|
414
|
+
ensuring it has access to all shared context.
|
|
415
|
+
|
|
416
|
+
**`@param`** [String] Name of the robot to synchronize
|
|
417
|
+
|
|
418
|
+
**`@raise`** [ArgumentError] if robot_name is not a member
|
|
419
|
+
|
|
420
|
+
**`@return`** [Integer] Number of nodes synchronized
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
**`@example`**
|
|
424
|
+
```ruby
|
|
425
|
+
synced = group.sync_robot('new-agent')
|
|
426
|
+
puts "Synchronized #{synced} nodes"
|
|
427
|
+
```
|
|
428
|
+
## sync_stats() {: #method-i-sync_stats }
|
|
429
|
+
Returns statistics about real-time synchronization.
|
|
430
|
+
|
|
431
|
+
**`@return`** [Hash] Stats hash with :nodes_synced and :evictions_synced counts
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
**`@example`**
|
|
435
|
+
```ruby
|
|
436
|
+
stats = group.sync_stats
|
|
437
|
+
puts "Nodes synced: #{stats[:nodes_synced]}"
|
|
438
|
+
puts "Evictions synced: #{stats[:evictions_synced]}"
|
|
439
|
+
```
|
|
440
|
+
## transfer_working_memory(from_robot, to_robot, clear_source:true) {: #method-i-transfer_working_memory }
|
|
441
|
+
Transfers working memory from one robot to another.
|
|
442
|
+
|
|
443
|
+
Copies all working memory node references from the source robot to the target
|
|
444
|
+
robot, optionally clearing the source.
|
|
445
|
+
|
|
446
|
+
**`@param`** [String] Name of the source robot
|
|
447
|
+
|
|
448
|
+
**`@param`** [String] Name of the destination robot
|
|
449
|
+
|
|
450
|
+
**`@param`** [Boolean] Whether to clear source's working memory after transfer
|
|
451
|
+
|
|
452
|
+
**`@raise`** [ArgumentError] if either robot is not a member
|
|
453
|
+
|
|
454
|
+
**`@return`** [Integer] Number of nodes transferred
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
**`@example`**
|
|
458
|
+
```ruby
|
|
459
|
+
transferred = group.transfer_working_memory('failing-agent', 'backup-agent')
|
|
460
|
+
```
|
|
461
|
+
**`@example`**
|
|
462
|
+
```ruby
|
|
463
|
+
transferred = group.transfer_working_memory(
|
|
464
|
+
'agent-1', 'agent-2',
|
|
465
|
+
clear_source: false
|
|
466
|
+
)
|
|
467
|
+
```
|
|
468
|
+
## working_memory_contents() {: #method-i-working_memory_contents }
|
|
469
|
+
Returns all nodes currently in shared working memory.
|
|
470
|
+
|
|
471
|
+
Queries the database for the union of all members' working memory, returning
|
|
472
|
+
nodes sorted by creation date (newest first).
|
|
473
|
+
|
|
474
|
+
**`@return`** [ActiveRecord::Relation<HTM::Models::Node>] Collection of nodes
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
**`@example`**
|
|
478
|
+
```ruby
|
|
479
|
+
nodes = group.working_memory_contents
|
|
480
|
+
nodes.each { |n| puts n.content }
|
|
481
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Class: HTM::SqlBuilder
|
|
2
|
+
**Inherits:** Object
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
SQL building utilities for constructing safe, parameterized queries
|
|
6
|
+
|
|
7
|
+
Provides class methods for building SQL conditions for:
|
|
8
|
+
* Timeframe filtering (single range or multiple ranges)
|
|
9
|
+
* Metadata filtering (JSONB containment)
|
|
10
|
+
* Embedding sanitization and padding (SQL injection prevention)
|
|
11
|
+
* LIKE pattern sanitization (wildcard injection prevention)
|
|
12
|
+
|
|
13
|
+
All methods use proper escaping and parameterization to prevent SQL injection.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
**`@example`**
|
|
17
|
+
```ruby
|
|
18
|
+
HTM::SqlBuilder.timeframe_condition(1.week.ago..Time.now)
|
|
19
|
+
# => "(created_at BETWEEN '2024-01-01' AND '2024-01-08')"
|
|
20
|
+
```
|
|
21
|
+
**`@example`**
|
|
22
|
+
```ruby
|
|
23
|
+
HTM::SqlBuilder.metadata_condition({ priority: "high" })
|
|
24
|
+
# => "(metadata @> '{\"priority\":\"high\"}'::jsonb)"
|
|
25
|
+
```
|
|
26
|
+
**`@example`**
|
|
27
|
+
```ruby
|
|
28
|
+
HTM::SqlBuilder.sanitize_embedding([0.1, 0.2, 0.3])
|
|
29
|
+
# => "[0.1,0.2,0.3]"
|
|
30
|
+
```
|
|
31
|
+
**`@example`**
|
|
32
|
+
```ruby
|
|
33
|
+
HTM::SqlBuilder.sanitize_like_pattern("test%pattern")
|
|
34
|
+
# => "test\\%pattern"
|
|
35
|
+
```
|
|
36
|
+
# Class Methods
|
|
37
|
+
## apply_metadata(scope , metadata , column: "metadata") {: #method-c-apply_metadata }
|
|
38
|
+
Apply metadata filter to ActiveRecord scope
|
|
39
|
+
**`@param`** [ActiveRecord::Relation] Base scope
|
|
40
|
+
|
|
41
|
+
**`@param`** [Hash] Metadata to filter by
|
|
42
|
+
|
|
43
|
+
**`@param`** [String] Column name (default: "metadata")
|
|
44
|
+
|
|
45
|
+
**`@return`** [ActiveRecord::Relation] Scoped query
|
|
46
|
+
|
|
47
|
+
## apply_timeframe(scope , timeframe , column: :created_at) {: #method-c-apply_timeframe }
|
|
48
|
+
Apply timeframe filter to ActiveRecord scope
|
|
49
|
+
**`@param`** [ActiveRecord::Relation] Base scope
|
|
50
|
+
|
|
51
|
+
**`@param`** [nil, Range, Array<Range>] Time range(s)
|
|
52
|
+
|
|
53
|
+
**`@param`** [Symbol] Column name (default: :created_at)
|
|
54
|
+
|
|
55
|
+
**`@return`** [ActiveRecord::Relation] Scoped query
|
|
56
|
+
|
|
57
|
+
## metadata_condition(metadata , table_alias: nil, column: "metadata") {: #method-c-metadata_condition }
|
|
58
|
+
Build SQL condition for metadata filtering (JSONB containment)
|
|
59
|
+
**`@param`** [Hash] Metadata to filter by
|
|
60
|
+
|
|
61
|
+
**`@param`** [String, nil] Table alias (default: none)
|
|
62
|
+
|
|
63
|
+
**`@param`** [String] Column name (default: "metadata")
|
|
64
|
+
|
|
65
|
+
**`@return`** [String, nil] SQL condition or nil for no filter
|
|
66
|
+
|
|
67
|
+
## pad_embedding(embedding , target_dimension: MAX_EMBEDDING_DIMENSION) {: #method-c-pad_embedding }
|
|
68
|
+
Pad embedding to target dimension
|
|
69
|
+
|
|
70
|
+
Pads embedding with zeros to reach the target dimension for pgvector
|
|
71
|
+
compatibility.
|
|
72
|
+
**`@param`** [Array<Numeric>] Embedding vector
|
|
73
|
+
|
|
74
|
+
**`@param`** [Integer] Target dimension (default: MAX_EMBEDDING_DIMENSION)
|
|
75
|
+
|
|
76
|
+
**`@return`** [Array<Numeric>] Padded embedding
|
|
77
|
+
|
|
78
|
+
## sanitize_embedding(embedding ) {: #method-c-sanitize_embedding }
|
|
79
|
+
Sanitize embedding for SQL use
|
|
80
|
+
|
|
81
|
+
Validates that all values are numeric and converts to safe PostgreSQL vector
|
|
82
|
+
format. This prevents SQL injection by ensuring only valid numeric values are
|
|
83
|
+
included.
|
|
84
|
+
**`@param`** [Array<Numeric>] Embedding vector
|
|
85
|
+
|
|
86
|
+
**`@raise`** [ArgumentError] If embedding contains non-numeric values
|
|
87
|
+
|
|
88
|
+
**`@return`** [String] Sanitized vector string for PostgreSQL (e.g., "[0.1,0.2,0.3]")
|
|
89
|
+
|
|
90
|
+
## sanitize_like_pattern(pattern ) {: #method-c-sanitize_like_pattern }
|
|
91
|
+
Sanitize a string for use in SQL LIKE patterns
|
|
92
|
+
|
|
93
|
+
Escapes SQL LIKE wildcards (% and _) to prevent pattern injection.
|
|
94
|
+
**`@param`** [String] Pattern to sanitize
|
|
95
|
+
|
|
96
|
+
**`@return`** [String] Sanitized pattern safe for LIKE queries
|
|
97
|
+
|
|
98
|
+
## timeframe_condition(timeframe , table_alias: nil, column: "created_at") {: #method-c-timeframe_condition }
|
|
99
|
+
Build SQL condition for timeframe filtering
|
|
100
|
+
**`@param`** [nil, Range, Array<Range>] Time range(s)
|
|
101
|
+
|
|
102
|
+
**`@param`** [String, nil] Table alias (default: none)
|
|
103
|
+
|
|
104
|
+
**`@param`** [String] Column name (default: "created_at")
|
|
105
|
+
|
|
106
|
+
**`@return`** [String, nil] SQL condition or nil for no filter
|
|
107
|
+
|
|
108
|
+
|
|
@@ -29,6 +29,10 @@ Extract tags with validation and processing
|
|
|
29
29
|
|
|
30
30
|
**`@return`** [Array<String>] Validated tag names
|
|
31
31
|
|
|
32
|
+
## max_depth() {: #method-c-max_depth }
|
|
33
|
+
Maximum tag hierarchy depth (configurable, default 4)
|
|
34
|
+
**`@return`** [Integer] Max depth (3 colons max by default)
|
|
35
|
+
|
|
32
36
|
## parse_hierarchy(tag ) {: #method-c-parse_hierarchy }
|
|
33
37
|
Parse hierarchical structure of a tag
|
|
34
38
|
**`@param`** [String] Hierarchical tag (e.g., "ai:llm:embedding")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Class: HTM::Telemetry::NullInstrument
|
|
2
|
+
**Inherits:** Object
|
|
3
|
+
|
|
4
|
+
**Includes:** Singleton
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Null instrument that accepts but ignores all metric operations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Instance Methods
|
|
12
|
+
## add() {: #method-i-add }
|
|
13
|
+
## record() {: #method-i-record }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Class: HTM::Telemetry::NullMeter
|
|
2
|
+
**Inherits:** Object
|
|
3
|
+
|
|
4
|
+
**Includes:** Singleton
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Null meter that creates null instruments Used when telemetry is disabled or
|
|
8
|
+
SDK unavailable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Instance Methods
|
|
13
|
+
## create_counter() {: #method-i-create_counter }
|
|
14
|
+
## create_histogram() {: #method-i-create_histogram }
|
|
15
|
+
## create_up_down_counter() {: #method-i-create_up_down_counter }
|