robot_lab 0.3.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
- data/lib/robot_lab/version.rb +1 -1
- metadata +3 -253
- data/.architecture/AGENTS.md +0 -32
- data/.architecture/config.yml +0 -8
- data/.architecture/members.yml +0 -60
- data/.architecture/reviews/feature-free-will.md +0 -490
- data/.architecture/reviews/overall-codebase.md +0 -427
- data/.claude/settings.local.json +0 -57
- data/.codex/config.toml +0 -2
- data/.envrc +0 -6
- data/.github/workflows/deploy-github-pages.yml +0 -52
- data/.irbrc +0 -6
- data/.loki +0 -65
- data/.quality/reek_baseline.txt +0 -43
- data/.rubocop.yml +0 -10
- data/Archspec.rb +0 -44
- data/CLAUDE.md +0 -141
- data/COMMITS.md +0 -196
- data/Rakefile +0 -193
- data/_typos.toml +0 -21
- data/agent2agent_review.md +0 -192
- data/agentf_improvements.md +0 -253
- data/agents.md +0 -14
- data/docs/api/core/index.md +0 -147
- data/docs/api/core/memory.md +0 -578
- data/docs/api/core/network.md +0 -553
- data/docs/api/core/result.md +0 -211
- data/docs/api/core/robot.md +0 -1667
- data/docs/api/core/state.md +0 -145
- data/docs/api/core/tool.md +0 -520
- data/docs/api/errors.md +0 -323
- data/docs/api/hooks.md +0 -469
- data/docs/api/index.md +0 -137
- data/docs/api/mcp/client.md +0 -387
- data/docs/api/mcp/index.md +0 -267
- data/docs/api/mcp/server.md +0 -252
- data/docs/api/mcp/transports.md +0 -339
- data/docs/api/messages/index.md +0 -126
- data/docs/api/messages/text-message.md +0 -170
- data/docs/api/messages/tool-call-message.md +0 -206
- data/docs/api/messages/tool-result-message.md +0 -242
- data/docs/api/messages/user-message.md +0 -232
- data/docs/api/skills.md +0 -423
- data/docs/api/streaming/context.md +0 -280
- data/docs/api/streaming/events.md +0 -185
- data/docs/api/streaming/index.md +0 -156
- data/docs/api/support.md +0 -846
- data/docs/architecture/core-concepts.md +0 -575
- data/docs/architecture/index.md +0 -252
- data/docs/architecture/message-flow.md +0 -366
- data/docs/architecture/network-orchestration.md +0 -478
- data/docs/architecture/robot-execution.md +0 -396
- data/docs/architecture/state-management.md +0 -410
- data/docs/assets/css/custom.css +0 -56
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +0 -641
- data/docs/examples/basic-chat.md +0 -258
- data/docs/examples/index.md +0 -425
- data/docs/examples/mcp-server.md +0 -403
- data/docs/examples/multi-robot-network.md +0 -476
- data/docs/examples/tool-usage.md +0 -468
- data/docs/getting-started/configuration.md +0 -645
- data/docs/getting-started/index.md +0 -56
- data/docs/getting-started/installation.md +0 -197
- data/docs/getting-started/quick-start.md +0 -293
- data/docs/guides/building-robots.md +0 -1187
- data/docs/guides/creating-networks.md +0 -736
- data/docs/guides/hooks.md +0 -1206
- data/docs/guides/index.md +0 -78
- data/docs/guides/knowledge.md +0 -219
- data/docs/guides/mcp-integration.md +0 -740
- data/docs/guides/memory.md +0 -458
- data/docs/guides/observability.md +0 -733
- data/docs/guides/streaming.md +0 -323
- data/docs/guides/using-tools.md +0 -780
- data/docs/index.md +0 -280
- data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
- data/examples/.envrc +0 -3
- data/examples/01_simple_robot.rb +0 -31
- data/examples/02_tools.rb +0 -109
- data/examples/03_network.rb +0 -118
- data/examples/04_mcp.rb +0 -217
- data/examples/05_streaming.rb +0 -137
- data/examples/06_prompt_templates.rb +0 -325
- data/examples/07_network_memory.rb +0 -313
- data/examples/08_llm_config.rb +0 -162
- data/examples/09_chaining.rb +0 -251
- data/examples/10_memory.rb +0 -315
- data/examples/11_network_introspection.rb +0 -239
- data/examples/12_message_bus.rb +0 -67
- data/examples/13_spawn.rb +0 -85
- data/examples/14_rusty_circuit/.envrc +0 -1
- data/examples/14_rusty_circuit/.gitignore +0 -1
- data/examples/14_rusty_circuit/README.md +0 -1
- data/examples/14_rusty_circuit/comic.rb +0 -149
- data/examples/14_rusty_circuit/display.rb +0 -217
- data/examples/14_rusty_circuit/heckler.rb +0 -65
- data/examples/14_rusty_circuit/open_mic.rb +0 -132
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
- data/examples/14_rusty_circuit/scout.rb +0 -165
- data/examples/14_rusty_circuit/show.log +0 -234
- data/examples/15_memory_network_and_bus/.envrc +0 -1
- data/examples/15_memory_network_and_bus/README.md +0 -66
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
- data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
- data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
- data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
- data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
- data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/memory.json +0 -13
- data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
- data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
- data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
- data/examples/16_writers_room/.envrc +0 -1
- data/examples/16_writers_room/display.rb +0 -158
- data/examples/16_writers_room/output/.gitignore +0 -4
- data/examples/16_writers_room/output/README.md +0 -69
- data/examples/16_writers_room/output/opus_001.md +0 -263
- data/examples/16_writers_room/output/opus_001_notes.log +0 -470
- data/examples/16_writers_room/output/opus_002.md +0 -245
- data/examples/16_writers_room/output/opus_002_notes.log +0 -546
- data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
- data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
- data/examples/16_writers_room/prompts/writer.md +0 -37
- data/examples/16_writers_room/room.rb +0 -186
- data/examples/16_writers_room/tools.rb +0 -173
- data/examples/16_writers_room/writer.rb +0 -121
- data/examples/16_writers_room/writers_room.rb +0 -256
- data/examples/17_skills.rb +0 -234
- data/examples/18_rails/.envrc +0 -3
- data/examples/18_rails/.gitignore +0 -5
- data/examples/18_rails/Gemfile +0 -11
- data/examples/18_rails/README.md +0 -67
- data/examples/18_rails/Rakefile +0 -4
- data/examples/18_rails/app/controllers/application_controller.rb +0 -4
- data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
- data/examples/18_rails/app/jobs/application_job.rb +0 -4
- data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
- data/examples/18_rails/app/models/application_record.rb +0 -5
- data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
- data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
- data/examples/18_rails/app/robots/chat_robot.rb +0 -25
- data/examples/18_rails/app/tools/time_tool.rb +0 -9
- data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
- data/examples/18_rails/app/views/chat/index.html.erb +0 -67
- data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
- data/examples/18_rails/bin/dev +0 -7
- data/examples/18_rails/bin/rails +0 -6
- data/examples/18_rails/bin/setup +0 -15
- data/examples/18_rails/config/application.rb +0 -33
- data/examples/18_rails/config/cable.yml +0 -2
- data/examples/18_rails/config/database.yml +0 -5
- data/examples/18_rails/config/environment.rb +0 -4
- data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
- data/examples/18_rails/config/routes.rb +0 -6
- data/examples/18_rails/config.ru +0 -4
- data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
- data/examples/19_token_tracking.rb +0 -138
- data/examples/20_circuit_breaker.rb +0 -147
- data/examples/21_learning_loop.rb +0 -181
- data/examples/22_context_compression.rb +0 -172
- data/examples/23_convergence.rb +0 -133
- data/examples/24_structured_delegation.rb +0 -151
- data/examples/25_history_search/conversation.jsonl +0 -30
- data/examples/25_history_search.rb +0 -133
- data/examples/26_document_store/api_versioning_adr.md +0 -52
- data/examples/26_document_store/incident_postmortem.md +0 -46
- data/examples/26_document_store/postgres_runbook.md +0 -49
- data/examples/26_document_store/redis_caching_guide.md +0 -48
- data/examples/26_document_store/sidekiq_guide.md +0 -51
- data/examples/26_document_store.rb +0 -140
- data/examples/27_incident_response/README.md +0 -65
- data/examples/27_incident_response/incident_response.rb +0 -261
- data/examples/28_mcp_discovery.rb +0 -113
- data/examples/29_ractor_tools.rb +0 -240
- data/examples/30_ractor_network.rb +0 -252
- data/examples/31_launch_assessment.rb +0 -246
- data/examples/32_newsletter_reader.rb +0 -193
- data/examples/33_stock_generator.rb +0 -80
- data/examples/33_stock_predictor.rb +0 -327
- data/examples/34_agentskills.rb +0 -76
- data/examples/35_hooks.rb +0 -266
- data/examples/README.md +0 -550
- data/examples/common.rb +0 -180
- data/examples/prompts/assistant.md +0 -5
- data/examples/prompts/audit_trail.md +0 -8
- data/examples/prompts/billing.md +0 -10
- data/examples/prompts/classifier.md +0 -7
- data/examples/prompts/comedian.md +0 -6
- data/examples/prompts/comedy_critic.md +0 -10
- data/examples/prompts/configurable.md +0 -9
- data/examples/prompts/dispatcher.md +0 -12
- data/examples/prompts/entity_extractor.md +0 -14
- data/examples/prompts/escalation.md +0 -42
- data/examples/prompts/frontmatter_mcp_test.md +0 -9
- data/examples/prompts/frontmatter_named_test.md +0 -5
- data/examples/prompts/frontmatter_tools_test.md +0 -6
- data/examples/prompts/general.md +0 -7
- data/examples/prompts/github_assistant.md +0 -14
- data/examples/prompts/helper.md +0 -4
- data/examples/prompts/incident_responder.md +0 -18
- data/examples/prompts/keyword_extractor.md +0 -11
- data/examples/prompts/llm_config_demo.md +0 -20
- data/examples/prompts/order_support.md +0 -35
- data/examples/prompts/os_advocate.md +0 -13
- data/examples/prompts/os_chief.md +0 -13
- data/examples/prompts/os_editor.md +0 -13
- data/examples/prompts/parameterized_main_test.md +0 -6
- data/examples/prompts/pii_redactor.md +0 -7
- data/examples/prompts/product_support.md +0 -37
- data/examples/prompts/runbook_protocol.md +0 -12
- data/examples/prompts/sentiment_analyzer.md +0 -12
- data/examples/prompts/skill_a_test.md +0 -4
- data/examples/prompts/skill_b_test.md +0 -4
- data/examples/prompts/skill_config_test.md +0 -5
- data/examples/prompts/skill_cycle_a_test.md +0 -6
- data/examples/prompts/skill_cycle_b_test.md +0 -6
- data/examples/prompts/skill_description_test.md +0 -4
- data/examples/prompts/skill_leaf_test.md +0 -4
- data/examples/prompts/skill_nested_test.md +0 -6
- data/examples/prompts/skill_refs_main_test.md +0 -6
- data/examples/prompts/skill_self_ref_test.md +0 -6
- data/examples/prompts/skill_with_mcp_test.md +0 -9
- data/examples/prompts/skill_with_params_test.md +0 -6
- data/examples/prompts/skill_with_robot_name_test.md +0 -5
- data/examples/prompts/skill_with_tools_test.md +0 -6
- data/examples/prompts/sre_compliance.md +0 -10
- data/examples/prompts/structured_output.md +0 -7
- data/examples/prompts/synthesizer.md +0 -17
- data/examples/prompts/technical.md +0 -10
- data/examples/prompts/template_with_skills_test.md +0 -6
- data/examples/prompts/triage.md +0 -22
- data/examples/ruboruby.md +0 -423
- data/examples/run_all.rb +0 -60
- data/examples/xyzzy.rb +0 -97
- data/logfile +0 -8
- data/mkdocs.yml +0 -209
- data/robot_concurrency.md +0 -38
- data/simple_acp_review.md +0 -298
- data/temp.md +0 -6
- data/tool_manifest_plan.md +0 -155
data/docs/api/core/memory.md
DELETED
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
# Memory
|
|
2
|
-
|
|
3
|
-
Reactive key-value store for sharing data between robots.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::Memory`
|
|
6
|
-
|
|
7
|
-
```ruby
|
|
8
|
-
memory = robot.memory
|
|
9
|
-
|
|
10
|
-
memory.set(:key, "value")
|
|
11
|
-
value = memory.get(:key)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Constants
|
|
15
|
-
|
|
16
|
-
### RESERVED_KEYS
|
|
17
|
-
|
|
18
|
-
```ruby
|
|
19
|
-
Memory::RESERVED_KEYS # => [:data, :results, :messages, :session_id, :cache]
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Reserved keys with special accessors and behavior.
|
|
23
|
-
|
|
24
|
-
## Constructor
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
memory = Memory.new(
|
|
28
|
-
data: {},
|
|
29
|
-
results: [],
|
|
30
|
-
messages: [],
|
|
31
|
-
session_id: nil,
|
|
32
|
-
backend: :auto,
|
|
33
|
-
enable_cache: true,
|
|
34
|
-
network_name: nil
|
|
35
|
-
)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Parameters:**
|
|
39
|
-
|
|
40
|
-
| Name | Type | Default | Description |
|
|
41
|
-
|------|------|---------|-------------|
|
|
42
|
-
| `data` | `Hash` | `{}` | Initial runtime data |
|
|
43
|
-
| `results` | `Array` | `[]` | Pre-loaded robot results |
|
|
44
|
-
| `messages` | `Array` | `[]` | Pre-loaded conversation messages |
|
|
45
|
-
| `session_id` | `String, nil` | `nil` | Session identifier |
|
|
46
|
-
| `backend` | `Symbol` | `:auto` | Storage backend (`:auto`, `:redis`, `:hash`) |
|
|
47
|
-
| `enable_cache` | `Boolean` | `true` | Whether to enable semantic caching |
|
|
48
|
-
| `network_name` | `String, nil` | `nil` | Network this memory belongs to |
|
|
49
|
-
|
|
50
|
-
## Factory Method
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
memory = RobotLab.create_memory(data: { user_id: 123 })
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Methods
|
|
57
|
-
|
|
58
|
-
### set
|
|
59
|
-
|
|
60
|
-
```ruby
|
|
61
|
-
memory.set(:key, value)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Store a value and notify subscribers asynchronously.
|
|
65
|
-
|
|
66
|
-
**Parameters:**
|
|
67
|
-
|
|
68
|
-
| Name | Type | Description |
|
|
69
|
-
|------|------|-------------|
|
|
70
|
-
| `key` | `Symbol`, `String` | Storage key |
|
|
71
|
-
| `value` | `Object` | Value to store |
|
|
72
|
-
|
|
73
|
-
### get
|
|
74
|
-
|
|
75
|
-
```ruby
|
|
76
|
-
memory.get(:key) # => value or nil
|
|
77
|
-
memory.get(:key, wait: true) # Block until available
|
|
78
|
-
memory.get(:key, wait: 30) # Block up to 30 seconds
|
|
79
|
-
memory.get(:a, :b, :c, wait: 60) # Multiple keys, returns Hash
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Retrieve one or more values, optionally waiting until they exist.
|
|
83
|
-
|
|
84
|
-
**Parameters:**
|
|
85
|
-
|
|
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
|
-
```
|
|
113
|
-
|
|
114
|
-
### key?
|
|
115
|
-
|
|
116
|
-
```ruby
|
|
117
|
-
memory.key?(:key) # => Boolean
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Check if key exists. Aliases: `has_key?`, `include?`.
|
|
121
|
-
|
|
122
|
-
### delete
|
|
123
|
-
|
|
124
|
-
```ruby
|
|
125
|
-
memory.delete(:key) # => deleted value
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Remove a key. Cannot delete reserved keys.
|
|
129
|
-
|
|
130
|
-
### keys
|
|
131
|
-
|
|
132
|
-
```ruby
|
|
133
|
-
memory.keys # => Array<Symbol>
|
|
134
|
-
```
|
|
135
|
-
|
|
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`).
|
|
146
|
-
|
|
147
|
-
### clear
|
|
148
|
-
|
|
149
|
-
```ruby
|
|
150
|
-
memory.clear
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
Clear all non-reserved keys.
|
|
154
|
-
|
|
155
|
-
### reset
|
|
156
|
-
|
|
157
|
-
```ruby
|
|
158
|
-
memory.reset
|
|
159
|
-
```
|
|
160
|
-
|
|
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.
|
|
169
|
-
|
|
170
|
-
### subscribe
|
|
171
|
-
|
|
172
|
-
```ruby
|
|
173
|
-
sub_id = memory.subscribe(:key1, :key2) do |change|
|
|
174
|
-
puts "#{change.key} changed: #{change.value}"
|
|
175
|
-
end
|
|
176
|
-
```
|
|
177
|
-
|
|
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`.
|
|
186
|
-
|
|
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:**
|
|
192
|
-
|
|
193
|
-
| Attribute | Type | Description |
|
|
194
|
-
|-----------|------|-------------|
|
|
195
|
-
| `key` | `Symbol` | The changed key |
|
|
196
|
-
| `value` | `Object` | New value |
|
|
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` |
|
|
199
|
-
| `network_name` | `String, nil` | Network name |
|
|
200
|
-
| `timestamp` | `Time` | When the change occurred |
|
|
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`.
|
|
214
|
-
|
|
215
|
-
### subscribe_pattern
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
sub_id = memory.subscribe_pattern("analysis:*") do |change|
|
|
219
|
-
puts "Analysis key #{change.key} updated"
|
|
220
|
-
end
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Subscribe to keys matching a glob pattern (`*` and `?` supported). Returns a
|
|
224
|
-
subscription ID. **Raises:** `ArgumentError` if no block is given.
|
|
225
|
-
|
|
226
|
-
### unsubscribe
|
|
227
|
-
|
|
228
|
-
```ruby
|
|
229
|
-
memory.unsubscribe(sub_id) # => Boolean
|
|
230
|
-
```
|
|
231
|
-
|
|
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`.
|
|
251
|
-
|
|
252
|
-
### merge!
|
|
253
|
-
|
|
254
|
-
```ruby
|
|
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>
|
|
284
|
-
```
|
|
285
|
-
|
|
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`.
|
|
357
|
-
|
|
358
|
-
## Reserved Key Accessors
|
|
359
|
-
|
|
360
|
-
### data
|
|
361
|
-
|
|
362
|
-
```ruby
|
|
363
|
-
memory.data # => StateProxy
|
|
364
|
-
memory.data[:user_id] # Hash access
|
|
365
|
-
memory.data.user_id # Method access
|
|
366
|
-
memory.data[:status] = "active"
|
|
367
|
-
```
|
|
368
|
-
|
|
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.
|
|
372
|
-
|
|
373
|
-
### results
|
|
374
|
-
|
|
375
|
-
```ruby
|
|
376
|
-
memory.results # => Array<RobotResult>
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
Accumulated robot results (returns a copy).
|
|
380
|
-
|
|
381
|
-
### messages
|
|
382
|
-
|
|
383
|
-
```ruby
|
|
384
|
-
memory.messages # => Array<Message>
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
Conversation messages (returns a copy).
|
|
388
|
-
|
|
389
|
-
### session_id
|
|
390
|
-
|
|
391
|
-
```ruby
|
|
392
|
-
memory.session_id # => String | nil
|
|
393
|
-
memory.session_id = "abc" # Set session identifier
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### cache
|
|
397
|
-
|
|
398
|
-
```ruby
|
|
399
|
-
memory.cache # => RubyLLM::SemanticCache (the module itself), or nil
|
|
400
|
-
```
|
|
401
|
-
|
|
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
|
-
```
|
|
415
|
-
|
|
416
|
-
## Serialization
|
|
417
|
-
|
|
418
|
-
### to_h
|
|
419
|
-
|
|
420
|
-
```ruby
|
|
421
|
-
memory.to_h
|
|
422
|
-
# => { data: {...}, results: [...], messages: [...], session_id: "...", custom: {...} }
|
|
423
|
-
```
|
|
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
|
-
|
|
440
|
-
### to_json
|
|
441
|
-
|
|
442
|
-
```ruby
|
|
443
|
-
memory.to_json # => String
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
Serializes `to_h`.
|
|
447
|
-
|
|
448
|
-
### from_hash
|
|
449
|
-
|
|
450
|
-
```ruby
|
|
451
|
-
memory = RobotLab::Memory.from_hash(hash)
|
|
452
|
-
```
|
|
453
|
-
|
|
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.
|
|
457
|
-
|
|
458
|
-
### clone / dup
|
|
459
|
-
|
|
460
|
-
```ruby
|
|
461
|
-
new_memory = memory.clone
|
|
462
|
-
new_memory = memory.dup # alias for clone
|
|
463
|
-
```
|
|
464
|
-
|
|
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.
|
|
469
|
-
|
|
470
|
-
## Examples
|
|
471
|
-
|
|
472
|
-
### Basic Usage
|
|
473
|
-
|
|
474
|
-
```ruby
|
|
475
|
-
robot.memory.set(:user_name, "Alice")
|
|
476
|
-
robot.memory.set(:order_count, 5)
|
|
477
|
-
|
|
478
|
-
name = robot.memory.get(:user_name) # => "Alice"
|
|
479
|
-
count = robot.memory.get(:order_count) # => 5
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
### Bracket Access
|
|
483
|
-
|
|
484
|
-
```ruby
|
|
485
|
-
robot.memory[:user_id] = 123
|
|
486
|
-
robot.memory[:user_id] # => 123
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
### Storing Objects
|
|
490
|
-
|
|
491
|
-
```ruby
|
|
492
|
-
robot.memory.set(:user, {
|
|
493
|
-
id: 123,
|
|
494
|
-
name: "Alice",
|
|
495
|
-
plan: "pro"
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
user = robot.memory.get(:user)
|
|
499
|
-
user[:plan] # => "pro"
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
### Blocking Reads (Network Parallel Execution)
|
|
503
|
-
|
|
504
|
-
```ruby
|
|
505
|
-
# In robot A (writer)
|
|
506
|
-
network.memory.set(:sentiment, { score: 0.8, confidence: 0.95 })
|
|
507
|
-
|
|
508
|
-
# In robot B (reader, may run concurrently)
|
|
509
|
-
result = network.memory.get(:sentiment, wait: true) # Block indefinitely
|
|
510
|
-
result = network.memory.get(:sentiment, wait: 30) # Block up to 30s, then raise
|
|
511
|
-
|
|
512
|
-
# Multiple keys — the timeout applies PER MISSING KEY, so this can block 180s
|
|
513
|
-
results = network.memory.get(:sentiment, :entities, :keywords, wait: 60)
|
|
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
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### Reactive Subscriptions
|
|
525
|
-
|
|
526
|
-
```ruby
|
|
527
|
-
# Subscribe to a key
|
|
528
|
-
memory.subscribe(:raw_data) do |change|
|
|
529
|
-
enriched = enrich(change.value)
|
|
530
|
-
memory.set(:enriched, enriched)
|
|
531
|
-
end
|
|
532
|
-
|
|
533
|
-
# Subscribe with pattern
|
|
534
|
-
memory.subscribe_pattern("user:*") do |change|
|
|
535
|
-
puts "User key #{change.key} updated by #{change.writer}"
|
|
536
|
-
end
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
### Cross-Robot Communication via Network Memory
|
|
540
|
-
|
|
541
|
-
```ruby
|
|
542
|
-
# In classifier robot
|
|
543
|
-
network.memory.set(:intent, "billing")
|
|
544
|
-
network.memory.set(:entities, ["order", "refund"])
|
|
545
|
-
|
|
546
|
-
# In handler robot
|
|
547
|
-
intent = network.memory.get(:intent)
|
|
548
|
-
entities = network.memory.get(:entities)
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
### Data Proxy
|
|
552
|
-
|
|
553
|
-
```ruby
|
|
554
|
-
memory = RobotLab.create_memory(
|
|
555
|
-
data: { user: { name: "Alice", plan: "pro" } }
|
|
556
|
-
)
|
|
557
|
-
|
|
558
|
-
memory.data[:user][:name] # => "Alice"
|
|
559
|
-
memory.data.to_h # => { user: { name: "Alice", plan: "pro" } }
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
### Serialization
|
|
563
|
-
|
|
564
|
-
```ruby
|
|
565
|
-
# Save memory
|
|
566
|
-
json = memory.to_json
|
|
567
|
-
File.write("memory.json", json)
|
|
568
|
-
|
|
569
|
-
# Restore memory
|
|
570
|
-
data = JSON.parse(File.read("memory.json"))
|
|
571
|
-
memory = Memory.from_hash(data)
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
## See Also
|
|
575
|
-
|
|
576
|
-
- [Memory Guide](../../guides/memory.md)
|
|
577
|
-
- [StateProxy](state.md) — the wrapper returned by `memory.data`
|
|
578
|
-
- [State Management Architecture](../../architecture/state-management.md)
|