robot_lab 0.2.6 → 0.2.7
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/README.md +124 -64
- data/docs/api/core/index.md +41 -15
- data/docs/api/core/memory.md +247 -29
- data/docs/api/core/network.md +255 -33
- data/docs/api/core/result.md +120 -32
- data/docs/api/core/robot.md +551 -61
- data/docs/api/core/state.md +87 -197
- data/docs/api/core/tool.md +165 -20
- data/docs/api/errors.md +110 -17
- data/docs/api/hooks.md +469 -0
- data/docs/api/index.md +80 -7
- data/docs/api/mcp/client.md +129 -35
- data/docs/api/mcp/index.md +164 -23
- data/docs/api/mcp/server.md +27 -3
- data/docs/api/mcp/transports.md +94 -22
- data/docs/api/messages/index.md +26 -3
- data/docs/api/messages/text-message.md +33 -11
- data/docs/api/messages/tool-call-message.md +27 -4
- data/docs/api/messages/tool-result-message.md +23 -4
- data/docs/api/messages/user-message.md +45 -8
- data/docs/api/skills.md +519 -0
- data/docs/api/streaming/context.md +28 -5
- data/docs/api/streaming/index.md +57 -11
- data/docs/api/support.md +846 -0
- data/docs/architecture/core-concepts.md +79 -31
- data/docs/architecture/index.md +86 -11
- data/docs/architecture/message-flow.md +66 -29
- data/docs/architecture/network-orchestration.md +145 -38
- data/docs/architecture/robot-execution.md +172 -90
- data/docs/architecture/state-management.md +31 -12
- data/docs/concepts.md +176 -21
- data/docs/examples/basic-chat.md +72 -19
- data/docs/examples/index.md +117 -31
- data/docs/examples/mcp-server.md +154 -45
- data/docs/examples/multi-robot-network.md +91 -21
- data/docs/examples/tool-usage.md +104 -37
- data/docs/getting-started/configuration.md +284 -97
- data/docs/getting-started/installation.md +53 -41
- data/docs/getting-started/quick-start.md +51 -6
- data/docs/guides/building-robots.md +258 -50
- data/docs/guides/creating-networks.md +214 -30
- data/docs/guides/hooks.md +141 -54
- data/docs/guides/knowledge.md +35 -4
- data/docs/guides/mcp-integration.md +211 -44
- data/docs/guides/memory.md +103 -12
- data/docs/guides/observability.md +95 -47
- data/docs/guides/streaming.md +184 -125
- data/docs/guides/using-tools.md +237 -17
- data/docs/index.md +36 -4
- data/examples/01_simple_robot.rb +2 -2
- data/examples/02_tools.rb +14 -4
- data/examples/03_network.rb +12 -7
- data/examples/04_mcp.rb +11 -4
- data/examples/05_streaming.rb +8 -5
- data/examples/06_prompt_templates.rb +13 -9
- data/examples/07_network_memory.rb +5 -5
- data/examples/08_llm_config.rb +20 -15
- data/examples/09_chaining.rb +4 -4
- data/examples/11_network_introspection.rb +4 -4
- data/examples/12_message_bus.rb +2 -2
- data/examples/13_spawn.rb +2 -2
- data/examples/14_rusty_circuit/README.md +1 -0
- data/examples/14_rusty_circuit/comic.rb +7 -3
- data/examples/14_rusty_circuit/display.rb +14 -0
- data/examples/14_rusty_circuit/heckler.rb +8 -6
- data/examples/14_rusty_circuit/open_mic.rb +17 -6
- data/examples/14_rusty_circuit/scout.rb +17 -10
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +14 -10
- data/examples/15_memory_network_and_bus/linux_writer.rb +2 -2
- data/examples/15_memory_network_and_bus/os_editor.rb +3 -1
- data/examples/15_memory_network_and_bus/os_writer.rb +4 -1
- data/examples/16_writers_room/writer.rb +22 -22
- data/examples/16_writers_room/writers_room.rb +2 -0
- data/examples/17_skills.rb +14 -13
- data/examples/18_rails/README.md +20 -1
- data/examples/18_rails/app/controllers/chat_controller.rb +5 -1
- data/examples/18_rails/app/jobs/robot_run_job.rb +11 -5
- data/examples/18_rails/app/robots/chat_robot.rb +11 -0
- data/examples/18_rails/config/initializers/robot_lab.rb +8 -0
- data/examples/19_token_tracking.rb +25 -9
- data/examples/20_circuit_breaker.rb +10 -7
- data/examples/21_learning_loop.rb +42 -16
- data/examples/22_context_compression.rb +23 -23
- data/examples/23_convergence.rb +24 -17
- data/examples/24_structured_delegation.rb +13 -8
- data/examples/25_history_search.rb +12 -8
- data/examples/27_incident_response/incident_response.rb +31 -13
- data/examples/28_mcp_discovery.rb +17 -13
- data/examples/29_ractor_tools.rb +4 -2
- data/examples/30_ractor_network.rb +22 -17
- data/examples/31_launch_assessment.rb +20 -9
- data/examples/32_newsletter_reader.rb +7 -2
- data/examples/33_stock_predictor.rb +34 -13
- data/examples/34_agentskills.rb +7 -3
- data/examples/35_hooks.rb +18 -8
- data/examples/README.md +199 -45
- data/examples/common.rb +79 -11
- data/examples/xyzzy.rb +8 -1
- data/lib/robot_lab/config.rb +10 -5
- data/lib/robot_lab/names.rb +402 -0
- data/lib/robot_lab/robot/agent_skill_matching.rb +1 -3
- data/lib/robot_lab/robot/bus_messaging.rb +16 -8
- data/lib/robot_lab/robot/template_rendering.rb +16 -3
- data/lib/robot_lab/robot.rb +23 -2
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +21 -15
- data/mkdocs.yml +6 -1
- metadata +7 -2
data/docs/api/core/memory.md
CHANGED
|
@@ -83,14 +83,33 @@ Retrieve one or more values, optionally waiting until they exist.
|
|
|
83
83
|
|
|
84
84
|
**Parameters:**
|
|
85
85
|
|
|
86
|
-
| Name | Type | Description |
|
|
87
|
-
|
|
88
|
-
|
|
|
89
|
-
| `wait` | `Boolean`, `Numeric` | `false`:
|
|
90
|
-
|
|
91
|
-
**Returns:**
|
|
92
|
-
|
|
93
|
-
**Raises:** `AwaitTimeout`
|
|
86
|
+
| Name | Type | Default | Description |
|
|
87
|
+
|------|------|---------|-------------|
|
|
88
|
+
| `*keys` | `Symbol`, `String` | — | One or more keys to retrieve; flattened and symbolized |
|
|
89
|
+
| `wait` | `Boolean`, `Numeric` | `false` | `false`: return immediately (nil if missing). `true`: block indefinitely. `Numeric`: block up to that many seconds |
|
|
90
|
+
|
|
91
|
+
**Returns:** the single value for one key, a `Hash` keyed by symbol for multiple keys.
|
|
92
|
+
|
|
93
|
+
**Raises:** `RobotLab::AwaitTimeout` — `"Timeout waiting for :<key> after <N> seconds"`.
|
|
94
|
+
|
|
95
|
+
!!! warning "Blocking semantics"
|
|
96
|
+
- On expiry `get` **raises `RobotLab::AwaitTimeout`**; it does not return nil.
|
|
97
|
+
Rescue it if a missing value is acceptable.
|
|
98
|
+
- With multiple keys the timeout is applied **per missing key**, not to the
|
|
99
|
+
call as a whole. `get(:a, :b, :c, wait: 60)` can block for up to 180
|
|
100
|
+
seconds if all three are missing.
|
|
101
|
+
- `wait: true` blocks with no deadline and can hang forever. Prefer a numeric
|
|
102
|
+
timeout in production.
|
|
103
|
+
- Waiting is implemented with a pipe (`Waiter`, using `IO#wait_readable`), so
|
|
104
|
+
a blocked reader does not spin.
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
value = begin
|
|
108
|
+
memory.get(:sentiment, wait: 30)
|
|
109
|
+
rescue RobotLab::AwaitTimeout
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
112
|
+
```
|
|
94
113
|
|
|
95
114
|
### key?
|
|
96
115
|
|
|
@@ -114,7 +133,16 @@ Remove a key. Cannot delete reserved keys.
|
|
|
114
133
|
memory.keys # => Array<Symbol>
|
|
115
134
|
```
|
|
116
135
|
|
|
117
|
-
|
|
136
|
+
All keys **excluding** the reserved ones.
|
|
137
|
+
|
|
138
|
+
### all_keys
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
memory.all_keys # => Array<Symbol>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
All keys **including** the reserved ones (`:data`, `:results`, `:messages`,
|
|
145
|
+
`:session_id`, `:cache`).
|
|
118
146
|
|
|
119
147
|
### clear
|
|
120
148
|
|
|
@@ -130,7 +158,14 @@ Clear all non-reserved keys.
|
|
|
130
158
|
memory.reset
|
|
131
159
|
```
|
|
132
160
|
|
|
133
|
-
Reset memory to initial state
|
|
161
|
+
Reset memory to its initial state: clears the backend, restores `:data` to `{}`,
|
|
162
|
+
`:results` and `:messages` to `[]`, `:session_id` to `nil`, and re-installs the
|
|
163
|
+
existing cache object. The `StateProxy` returned by `data` is discarded and
|
|
164
|
+
rebuilt on next access.
|
|
165
|
+
|
|
166
|
+
This resets the **key-value store only**. It has nothing to do with a robot's
|
|
167
|
+
chat history — use `robot.clear_messages(keep_system: true)` for that. The two
|
|
168
|
+
are independent.
|
|
134
169
|
|
|
135
170
|
### subscribe
|
|
136
171
|
|
|
@@ -141,20 +176,41 @@ end
|
|
|
141
176
|
```
|
|
142
177
|
|
|
143
178
|
Subscribe to changes on one or more keys. Callback receives a `MemoryChange` object.
|
|
179
|
+
Returns a subscription ID for [`unsubscribe`](#unsubscribe).
|
|
180
|
+
|
|
181
|
+
**Raises:** `ArgumentError` if no block is given.
|
|
182
|
+
|
|
183
|
+
Only [`set`](#set) notifies subscribers. Writing a reserved key
|
|
184
|
+
(`memory[:data] = ...`, `session_id=`, `append_result`) bypasses notification
|
|
185
|
+
entirely, as does mutating `memory.data`.
|
|
144
186
|
|
|
145
|
-
**
|
|
187
|
+
Callbacks are dispatched through `Async { }`. **Outside a running reactor the
|
|
188
|
+
callback runs synchronously on the writer's thread**, so a slow subscriber blocks
|
|
189
|
+
the `set` that triggered it.
|
|
190
|
+
|
|
191
|
+
**`RobotLab::MemoryChange` attributes:**
|
|
146
192
|
|
|
147
193
|
| Attribute | Type | Description |
|
|
148
194
|
|-----------|------|-------------|
|
|
149
195
|
| `key` | `Symbol` | The changed key |
|
|
150
196
|
| `value` | `Object` | New value |
|
|
151
|
-
| `previous` | `Object` | Previous value |
|
|
152
|
-
| `writer` | `String, nil` |
|
|
197
|
+
| `previous` | `Object, nil` | Previous value |
|
|
198
|
+
| `writer` | `String, nil` | `memory.current_writer` at the time of the write — set to the robot's name for the duration of each `run` |
|
|
153
199
|
| `network_name` | `String, nil` | Network name |
|
|
154
200
|
| `timestamp` | `Time` | When the change occurred |
|
|
155
|
-
| `
|
|
156
|
-
|
|
157
|
-
|
|
201
|
+
| `correlation_id` | `String, nil` | Optional tracing ID |
|
|
202
|
+
|
|
203
|
+
**Predicates** — note that each tests *both* sides, so all three are false when
|
|
204
|
+
`previous` and `value` are both nil, and all three are false for a nil→nil write:
|
|
205
|
+
|
|
206
|
+
| Predicate | Exact definition |
|
|
207
|
+
|-----------|------------------|
|
|
208
|
+
| `created?` | `previous.nil? && !value.nil?` |
|
|
209
|
+
| `updated?` | `!previous.nil? && !value.nil?` |
|
|
210
|
+
| `deleted?` | `value.nil? && !previous.nil?` |
|
|
211
|
+
|
|
212
|
+
Also available: `to_h` (`.compact`ed, `timestamp` rendered as ISO-8601),
|
|
213
|
+
`to_json`, and `MemoryChange.from_hash`.
|
|
158
214
|
|
|
159
215
|
### subscribe_pattern
|
|
160
216
|
|
|
@@ -164,7 +220,8 @@ sub_id = memory.subscribe_pattern("analysis:*") do |change|
|
|
|
164
220
|
end
|
|
165
221
|
```
|
|
166
222
|
|
|
167
|
-
Subscribe to keys matching a glob pattern (`*` and `?` supported).
|
|
223
|
+
Subscribe to keys matching a glob pattern (`*` and `?` supported). Returns a
|
|
224
|
+
subscription ID. **Raises:** `ArgumentError` if no block is given.
|
|
168
225
|
|
|
169
226
|
### unsubscribe
|
|
170
227
|
|
|
@@ -172,15 +229,131 @@ Subscribe to keys matching a glob pattern (`*` and `?` supported).
|
|
|
172
229
|
memory.unsubscribe(sub_id) # => Boolean
|
|
173
230
|
```
|
|
174
231
|
|
|
175
|
-
Remove a subscription by its ID
|
|
232
|
+
Remove a subscription by its ID (works for both `subscribe` and
|
|
233
|
+
`subscribe_pattern`). Returns `true` when something was removed.
|
|
234
|
+
|
|
235
|
+
### unsubscribe_keys
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
memory.unsubscribe_keys(:status, :progress) # => self
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Drop **all** key subscriptions for the named keys at once, without needing their
|
|
242
|
+
IDs. Pattern subscriptions are unaffected.
|
|
243
|
+
|
|
244
|
+
### subscribed?
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
memory.subscribed?(:status) # => Boolean
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Whether any subscriber — key-based or pattern-based — would be notified for `key`.
|
|
176
251
|
|
|
177
252
|
### merge!
|
|
178
253
|
|
|
179
254
|
```ruby
|
|
180
|
-
memory.merge!(key1: "value1", key2: "value2")
|
|
255
|
+
memory.merge!(key1: "value1", key2: "value2") # => self
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Merge multiple key-value pairs into memory. Each pair is assigned through `[]=`,
|
|
259
|
+
so non-reserved keys go through the reactive `set` path and do notify subscribers.
|
|
260
|
+
|
|
261
|
+
## Results and History
|
|
262
|
+
|
|
263
|
+
### append_result
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
memory.append_result(robot_result) # => self
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Push a `RobotResult` onto the accumulated `:results` array. Bypasses subscriber
|
|
270
|
+
notification.
|
|
271
|
+
|
|
272
|
+
### set_results
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
memory.set_results(array_of_results) # => self
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Replace the whole `:results` array (used when loading from persistence).
|
|
279
|
+
|
|
280
|
+
### results_from
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
memory.results_from(5) # => Array<RobotResult>
|
|
181
284
|
```
|
|
182
285
|
|
|
183
|
-
|
|
286
|
+
Results from the given index onward — for incremental saves. Returns `[]` when
|
|
287
|
+
the index is past the end.
|
|
288
|
+
|
|
289
|
+
### format_history
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
memory.format_history(formatter: nil) # => Array<Message>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`messages` followed by every result flat-mapped through `formatter`. Pass a
|
|
296
|
+
`Proc` for `formatter:` to control how a `RobotResult` becomes messages; the
|
|
297
|
+
default formatter is used when omitted.
|
|
298
|
+
|
|
299
|
+
## Backend
|
|
300
|
+
|
|
301
|
+
### redis?
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
memory.redis? # => Boolean
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Whether this memory is backed by Redis rather than the in-process Hash. The
|
|
308
|
+
`backend: :auto` default tries Redis and falls back to a Hash; `backend: :hash`
|
|
309
|
+
forces the Hash.
|
|
310
|
+
|
|
311
|
+
### network_name
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
memory.network_name # => String, nil
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The network this memory belongs to, set once at construction and read-only
|
|
318
|
+
thereafter. `Network` creates its shared memory as `Memory.new(network_name: name)`;
|
|
319
|
+
a standalone robot's inherent memory has `nil`. It is copied onto every
|
|
320
|
+
`MemoryChange` so a subscriber can tell which network a write came from, and it is
|
|
321
|
+
preserved by `clone`/`dup` — but **not** by `from_hash`.
|
|
322
|
+
|
|
323
|
+
### current_writer / current_writer=
|
|
324
|
+
|
|
325
|
+
```ruby
|
|
326
|
+
memory.current_writer # => String, nil
|
|
327
|
+
memory.current_writer = "bot"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The name attributed to writes, surfaced as `MemoryChange#writer`. `Robot#run`
|
|
331
|
+
sets this to the robot's name for the duration of the run and restores the
|
|
332
|
+
previous value in an `ensure` block, so nested and concurrent runs attribute
|
|
333
|
+
correctly.
|
|
334
|
+
|
|
335
|
+
## Document Store
|
|
336
|
+
|
|
337
|
+
These four methods require the **`robot_lab-document_store`** extension gem. Without
|
|
338
|
+
it every one of them raises
|
|
339
|
+
`RobotLab::DependencyError: document storage requires the robot_lab-document_store gem.`
|
|
340
|
+
|
|
341
|
+
| Method | Returns | Description |
|
|
342
|
+
|--------|---------|-------------|
|
|
343
|
+
| `store_document(key, text)` | `self` | Embed `text` and store it under `key` |
|
|
344
|
+
| `search_documents(query, limit: 5)` | `Array<Hash>` | Hits sorted by score descending; each hash has `:key`, `:text`, `:score` |
|
|
345
|
+
| `document_keys` | `Array<Symbol>` | Keys of all stored documents |
|
|
346
|
+
| `delete_document(key)` | `self` | Remove a document |
|
|
347
|
+
|
|
348
|
+
```ruby
|
|
349
|
+
memory.store_document(:readme, File.read("README.md"))
|
|
350
|
+
memory.search_documents("how to configure redis", limit: 3).each do |hit|
|
|
351
|
+
puts "#{hit[:key]} (#{hit[:score].round(3)})"
|
|
352
|
+
end
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Documents live in a separate store, not in the key-value backend — they do not
|
|
356
|
+
appear in `keys`, `all_keys`, or `to_h`.
|
|
184
357
|
|
|
185
358
|
## Reserved Key Accessors
|
|
186
359
|
|
|
@@ -193,7 +366,9 @@ memory.data.user_id # Method access
|
|
|
193
366
|
memory.data[:status] = "active"
|
|
194
367
|
```
|
|
195
368
|
|
|
196
|
-
Runtime data accessed through `StateProxy` for method-style access.
|
|
369
|
+
Runtime data accessed through a [`StateProxy`](state.md) for method-style access.
|
|
370
|
+
Writes made through the proxy are **not** reactive — they bypass `set`, so they
|
|
371
|
+
wake no blocking readers and notify no subscribers.
|
|
197
372
|
|
|
198
373
|
### results
|
|
199
374
|
|
|
@@ -221,10 +396,22 @@ memory.session_id = "abc" # Set session identifier
|
|
|
221
396
|
### cache
|
|
222
397
|
|
|
223
398
|
```ruby
|
|
224
|
-
memory.cache # => RubyLLM::SemanticCache
|
|
399
|
+
memory.cache # => RubyLLM::SemanticCache (the module itself), or nil
|
|
225
400
|
```
|
|
226
401
|
|
|
227
|
-
|
|
402
|
+
Read-only after initialization — assigning it raises
|
|
403
|
+
`ArgumentError: Cannot reassign cache - it is initialized automatically`.
|
|
404
|
+
|
|
405
|
+
!!! warning "`cache` is `nil` when caching is disabled"
|
|
406
|
+
The value stored is the `RubyLLM::SemanticCache` **module**, not an instance.
|
|
407
|
+
When constructed with `enable_cache: false` it is `nil`, so
|
|
408
|
+
`memory.cache.fetch(...)` raises `NoMethodError`. Guard on
|
|
409
|
+
`memory.cache` before use, or leave `enable_cache` at its `true` default.
|
|
410
|
+
|
|
411
|
+
```ruby
|
|
412
|
+
RobotLab::Memory.new.cache # => RubyLLM::SemanticCache
|
|
413
|
+
RobotLab::Memory.new(enable_cache: false).cache # => nil
|
|
414
|
+
```
|
|
228
415
|
|
|
229
416
|
## Serialization
|
|
230
417
|
|
|
@@ -235,27 +422,50 @@ memory.to_h
|
|
|
235
422
|
# => { data: {...}, results: [...], messages: [...], session_id: "...", custom: {...} }
|
|
236
423
|
```
|
|
237
424
|
|
|
425
|
+
Keys: `data` (from the `StateProxy`), `results` (each via `RobotResult#export`),
|
|
426
|
+
`messages` (each via `to_h`), `session_id`, and `custom` (every non-reserved key).
|
|
427
|
+
`cache` is never serialized.
|
|
428
|
+
|
|
429
|
+
!!! warning "`to_h` is `.compact`ed"
|
|
430
|
+
Nil values are dropped, so `session_id` disappears entirely when unset:
|
|
431
|
+
|
|
432
|
+
```ruby
|
|
433
|
+
RobotLab::Memory.new.to_h
|
|
434
|
+
# => { data: {}, results: [], messages: [], custom: {} } -- no :session_id
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Consumers must tolerate the missing key. `custom` is always present (it is
|
|
438
|
+
`{}` rather than nil when there are no custom keys).
|
|
439
|
+
|
|
238
440
|
### to_json
|
|
239
441
|
|
|
240
442
|
```ruby
|
|
241
443
|
memory.to_json # => String
|
|
242
444
|
```
|
|
243
445
|
|
|
446
|
+
Serializes `to_h`.
|
|
447
|
+
|
|
244
448
|
### from_hash
|
|
245
449
|
|
|
246
450
|
```ruby
|
|
247
|
-
memory = Memory.from_hash(hash)
|
|
451
|
+
memory = RobotLab::Memory.from_hash(hash)
|
|
248
452
|
```
|
|
249
453
|
|
|
250
|
-
|
|
454
|
+
Reconstructs `data`, `results`, `messages`, and `session_id`, then re-applies
|
|
455
|
+
every entry from `custom`. A fresh cache is created; subscriptions, backend
|
|
456
|
+
choice, and `network_name` are **not** restored.
|
|
251
457
|
|
|
252
|
-
### clone
|
|
458
|
+
### clone / dup
|
|
253
459
|
|
|
254
460
|
```ruby
|
|
255
461
|
new_memory = memory.clone
|
|
462
|
+
new_memory = memory.dup # alias for clone
|
|
256
463
|
```
|
|
257
464
|
|
|
258
|
-
Deep copy
|
|
465
|
+
Deep copy of `data` plus copies of `results`, `messages`, `session_id`, and all
|
|
466
|
+
non-reserved keys. The `enable_cache` setting and `network_name` are preserved.
|
|
467
|
+
Subscriptions are **not** copied — the clone starts with none. Copying custom
|
|
468
|
+
keys uses the internal non-reactive setter, so no notifications fire.
|
|
259
469
|
|
|
260
470
|
## Examples
|
|
261
471
|
|
|
@@ -297,11 +507,18 @@ network.memory.set(:sentiment, { score: 0.8, confidence: 0.95 })
|
|
|
297
507
|
|
|
298
508
|
# In robot B (reader, may run concurrently)
|
|
299
509
|
result = network.memory.get(:sentiment, wait: true) # Block indefinitely
|
|
300
|
-
result = network.memory.get(:sentiment, wait: 30) # Block up to 30s
|
|
510
|
+
result = network.memory.get(:sentiment, wait: 30) # Block up to 30s, then raise
|
|
301
511
|
|
|
302
|
-
# Multiple keys
|
|
512
|
+
# Multiple keys — the timeout applies PER MISSING KEY, so this can block 180s
|
|
303
513
|
results = network.memory.get(:sentiment, :entities, :keywords, wait: 60)
|
|
304
514
|
# => { sentiment: {...}, entities: [...], keywords: [...] }
|
|
515
|
+
|
|
516
|
+
# Treat a timeout as "not available"
|
|
517
|
+
sentiment = begin
|
|
518
|
+
network.memory.get(:sentiment, wait: 30)
|
|
519
|
+
rescue RobotLab::AwaitTimeout
|
|
520
|
+
nil
|
|
521
|
+
end
|
|
305
522
|
```
|
|
306
523
|
|
|
307
524
|
### Reactive Subscriptions
|
|
@@ -357,4 +574,5 @@ memory = Memory.from_hash(data)
|
|
|
357
574
|
## See Also
|
|
358
575
|
|
|
359
576
|
- [Memory Guide](../../guides/memory.md)
|
|
577
|
+
- [StateProxy](state.md) — the wrapper returned by `memory.data`
|
|
360
578
|
- [State Management Architecture](../../architecture/state-management.md)
|