robot_lab 0.3.0 → 0.5.0

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.
Files changed (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +29 -19
  4. data/lib/robot_lab/errors.rb +4 -0
  5. data/lib/robot_lab/hook_context.rb +9 -2
  6. data/lib/robot_lab/mcp/registry.rb +99 -0
  7. data/lib/robot_lab/mcp/server.rb +47 -83
  8. data/lib/robot_lab/mcp/server_discovery.rb +22 -9
  9. data/lib/robot_lab/mcp.rb +259 -0
  10. data/lib/robot_lab/robot/mcp_management.rb +159 -105
  11. data/lib/robot_lab/robot/tool_call_gating.rb +144 -0
  12. data/lib/robot_lab/robot.rb +72 -99
  13. data/lib/robot_lab/run_config.rb +3 -2
  14. data/lib/robot_lab/tool.rb +8 -23
  15. data/lib/robot_lab/version.rb +1 -1
  16. data/lib/robot_lab.rb +1 -4
  17. metadata +16 -292
  18. data/.architecture/AGENTS.md +0 -32
  19. data/.architecture/config.yml +0 -8
  20. data/.architecture/members.yml +0 -60
  21. data/.architecture/reviews/feature-free-will.md +0 -490
  22. data/.architecture/reviews/overall-codebase.md +0 -427
  23. data/.claude/settings.local.json +0 -57
  24. data/.codex/config.toml +0 -2
  25. data/.envrc +0 -6
  26. data/.github/workflows/deploy-github-pages.yml +0 -52
  27. data/.irbrc +0 -6
  28. data/.loki +0 -65
  29. data/.quality/reek_baseline.txt +0 -43
  30. data/.rubocop.yml +0 -10
  31. data/Archspec.rb +0 -44
  32. data/CLAUDE.md +0 -141
  33. data/COMMITS.md +0 -196
  34. data/Rakefile +0 -193
  35. data/_typos.toml +0 -21
  36. data/agent2agent_review.md +0 -192
  37. data/agentf_improvements.md +0 -253
  38. data/agents.md +0 -14
  39. data/docs/api/core/index.md +0 -147
  40. data/docs/api/core/memory.md +0 -578
  41. data/docs/api/core/network.md +0 -553
  42. data/docs/api/core/result.md +0 -211
  43. data/docs/api/core/robot.md +0 -1667
  44. data/docs/api/core/state.md +0 -145
  45. data/docs/api/core/tool.md +0 -520
  46. data/docs/api/errors.md +0 -323
  47. data/docs/api/hooks.md +0 -469
  48. data/docs/api/index.md +0 -137
  49. data/docs/api/mcp/client.md +0 -387
  50. data/docs/api/mcp/index.md +0 -267
  51. data/docs/api/mcp/server.md +0 -252
  52. data/docs/api/mcp/transports.md +0 -339
  53. data/docs/api/messages/index.md +0 -126
  54. data/docs/api/messages/text-message.md +0 -170
  55. data/docs/api/messages/tool-call-message.md +0 -206
  56. data/docs/api/messages/tool-result-message.md +0 -242
  57. data/docs/api/messages/user-message.md +0 -232
  58. data/docs/api/skills.md +0 -423
  59. data/docs/api/streaming/context.md +0 -280
  60. data/docs/api/streaming/events.md +0 -185
  61. data/docs/api/streaming/index.md +0 -156
  62. data/docs/api/support.md +0 -846
  63. data/docs/architecture/core-concepts.md +0 -575
  64. data/docs/architecture/index.md +0 -252
  65. data/docs/architecture/message-flow.md +0 -366
  66. data/docs/architecture/network-orchestration.md +0 -478
  67. data/docs/architecture/robot-execution.md +0 -396
  68. data/docs/architecture/state-management.md +0 -410
  69. data/docs/assets/css/custom.css +0 -56
  70. data/docs/assets/images/robot_lab.jpg +0 -0
  71. data/docs/concepts.md +0 -641
  72. data/docs/examples/basic-chat.md +0 -258
  73. data/docs/examples/index.md +0 -425
  74. data/docs/examples/mcp-server.md +0 -403
  75. data/docs/examples/multi-robot-network.md +0 -476
  76. data/docs/examples/tool-usage.md +0 -468
  77. data/docs/getting-started/configuration.md +0 -645
  78. data/docs/getting-started/index.md +0 -56
  79. data/docs/getting-started/installation.md +0 -197
  80. data/docs/getting-started/quick-start.md +0 -293
  81. data/docs/guides/building-robots.md +0 -1187
  82. data/docs/guides/creating-networks.md +0 -736
  83. data/docs/guides/hooks.md +0 -1206
  84. data/docs/guides/index.md +0 -78
  85. data/docs/guides/knowledge.md +0 -219
  86. data/docs/guides/mcp-integration.md +0 -740
  87. data/docs/guides/memory.md +0 -458
  88. data/docs/guides/observability.md +0 -733
  89. data/docs/guides/streaming.md +0 -323
  90. data/docs/guides/using-tools.md +0 -780
  91. data/docs/index.md +0 -280
  92. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  93. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  94. data/examples/.envrc +0 -3
  95. data/examples/01_simple_robot.rb +0 -31
  96. data/examples/02_tools.rb +0 -109
  97. data/examples/03_network.rb +0 -118
  98. data/examples/04_mcp.rb +0 -217
  99. data/examples/05_streaming.rb +0 -137
  100. data/examples/06_prompt_templates.rb +0 -325
  101. data/examples/07_network_memory.rb +0 -313
  102. data/examples/08_llm_config.rb +0 -162
  103. data/examples/09_chaining.rb +0 -251
  104. data/examples/10_memory.rb +0 -315
  105. data/examples/11_network_introspection.rb +0 -239
  106. data/examples/12_message_bus.rb +0 -67
  107. data/examples/13_spawn.rb +0 -85
  108. data/examples/14_rusty_circuit/.envrc +0 -1
  109. data/examples/14_rusty_circuit/.gitignore +0 -1
  110. data/examples/14_rusty_circuit/README.md +0 -1
  111. data/examples/14_rusty_circuit/comic.rb +0 -149
  112. data/examples/14_rusty_circuit/display.rb +0 -217
  113. data/examples/14_rusty_circuit/heckler.rb +0 -65
  114. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  115. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  116. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  117. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  118. data/examples/14_rusty_circuit/scout.rb +0 -165
  119. data/examples/14_rusty_circuit/show.log +0 -234
  120. data/examples/15_memory_network_and_bus/.envrc +0 -1
  121. data/examples/15_memory_network_and_bus/README.md +0 -66
  122. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  123. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  124. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  125. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  126. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  127. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  128. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  129. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  130. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  131. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  132. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  133. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  134. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  135. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  136. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  137. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  138. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  139. data/examples/16_writers_room/.envrc +0 -1
  140. data/examples/16_writers_room/display.rb +0 -158
  141. data/examples/16_writers_room/output/.gitignore +0 -4
  142. data/examples/16_writers_room/output/README.md +0 -69
  143. data/examples/16_writers_room/output/opus_001.md +0 -263
  144. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  145. data/examples/16_writers_room/output/opus_002.md +0 -245
  146. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  147. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  148. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  149. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  150. data/examples/16_writers_room/prompts/writer.md +0 -37
  151. data/examples/16_writers_room/room.rb +0 -186
  152. data/examples/16_writers_room/tools.rb +0 -173
  153. data/examples/16_writers_room/writer.rb +0 -121
  154. data/examples/16_writers_room/writers_room.rb +0 -256
  155. data/examples/17_skills.rb +0 -234
  156. data/examples/18_rails/.envrc +0 -3
  157. data/examples/18_rails/.gitignore +0 -5
  158. data/examples/18_rails/Gemfile +0 -11
  159. data/examples/18_rails/README.md +0 -67
  160. data/examples/18_rails/Rakefile +0 -4
  161. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  162. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  163. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  164. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  165. data/examples/18_rails/app/models/application_record.rb +0 -5
  166. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  167. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  168. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  169. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  170. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  171. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  172. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  173. data/examples/18_rails/bin/dev +0 -7
  174. data/examples/18_rails/bin/rails +0 -6
  175. data/examples/18_rails/bin/setup +0 -15
  176. data/examples/18_rails/config/application.rb +0 -33
  177. data/examples/18_rails/config/cable.yml +0 -2
  178. data/examples/18_rails/config/database.yml +0 -5
  179. data/examples/18_rails/config/environment.rb +0 -4
  180. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  181. data/examples/18_rails/config/routes.rb +0 -6
  182. data/examples/18_rails/config.ru +0 -4
  183. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  184. data/examples/19_token_tracking.rb +0 -138
  185. data/examples/20_circuit_breaker.rb +0 -147
  186. data/examples/21_learning_loop.rb +0 -181
  187. data/examples/22_context_compression.rb +0 -172
  188. data/examples/23_convergence.rb +0 -133
  189. data/examples/24_structured_delegation.rb +0 -151
  190. data/examples/25_history_search/conversation.jsonl +0 -30
  191. data/examples/25_history_search.rb +0 -133
  192. data/examples/26_document_store/api_versioning_adr.md +0 -52
  193. data/examples/26_document_store/incident_postmortem.md +0 -46
  194. data/examples/26_document_store/postgres_runbook.md +0 -49
  195. data/examples/26_document_store/redis_caching_guide.md +0 -48
  196. data/examples/26_document_store/sidekiq_guide.md +0 -51
  197. data/examples/26_document_store.rb +0 -140
  198. data/examples/27_incident_response/README.md +0 -65
  199. data/examples/27_incident_response/incident_response.rb +0 -261
  200. data/examples/28_mcp_discovery.rb +0 -113
  201. data/examples/29_ractor_tools.rb +0 -240
  202. data/examples/30_ractor_network.rb +0 -252
  203. data/examples/31_launch_assessment.rb +0 -246
  204. data/examples/32_newsletter_reader.rb +0 -193
  205. data/examples/33_stock_generator.rb +0 -80
  206. data/examples/33_stock_predictor.rb +0 -327
  207. data/examples/34_agentskills.rb +0 -76
  208. data/examples/35_hooks.rb +0 -266
  209. data/examples/README.md +0 -550
  210. data/examples/common.rb +0 -180
  211. data/examples/prompts/assistant.md +0 -5
  212. data/examples/prompts/audit_trail.md +0 -8
  213. data/examples/prompts/billing.md +0 -10
  214. data/examples/prompts/classifier.md +0 -7
  215. data/examples/prompts/comedian.md +0 -6
  216. data/examples/prompts/comedy_critic.md +0 -10
  217. data/examples/prompts/configurable.md +0 -9
  218. data/examples/prompts/dispatcher.md +0 -12
  219. data/examples/prompts/entity_extractor.md +0 -14
  220. data/examples/prompts/escalation.md +0 -42
  221. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  222. data/examples/prompts/frontmatter_named_test.md +0 -5
  223. data/examples/prompts/frontmatter_tools_test.md +0 -6
  224. data/examples/prompts/general.md +0 -7
  225. data/examples/prompts/github_assistant.md +0 -14
  226. data/examples/prompts/helper.md +0 -4
  227. data/examples/prompts/incident_responder.md +0 -18
  228. data/examples/prompts/keyword_extractor.md +0 -11
  229. data/examples/prompts/llm_config_demo.md +0 -20
  230. data/examples/prompts/order_support.md +0 -35
  231. data/examples/prompts/os_advocate.md +0 -13
  232. data/examples/prompts/os_chief.md +0 -13
  233. data/examples/prompts/os_editor.md +0 -13
  234. data/examples/prompts/parameterized_main_test.md +0 -6
  235. data/examples/prompts/pii_redactor.md +0 -7
  236. data/examples/prompts/product_support.md +0 -37
  237. data/examples/prompts/runbook_protocol.md +0 -12
  238. data/examples/prompts/sentiment_analyzer.md +0 -12
  239. data/examples/prompts/skill_a_test.md +0 -4
  240. data/examples/prompts/skill_b_test.md +0 -4
  241. data/examples/prompts/skill_config_test.md +0 -5
  242. data/examples/prompts/skill_cycle_a_test.md +0 -6
  243. data/examples/prompts/skill_cycle_b_test.md +0 -6
  244. data/examples/prompts/skill_description_test.md +0 -4
  245. data/examples/prompts/skill_leaf_test.md +0 -4
  246. data/examples/prompts/skill_nested_test.md +0 -6
  247. data/examples/prompts/skill_refs_main_test.md +0 -6
  248. data/examples/prompts/skill_self_ref_test.md +0 -6
  249. data/examples/prompts/skill_with_mcp_test.md +0 -9
  250. data/examples/prompts/skill_with_params_test.md +0 -6
  251. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  252. data/examples/prompts/skill_with_tools_test.md +0 -6
  253. data/examples/prompts/sre_compliance.md +0 -10
  254. data/examples/prompts/structured_output.md +0 -7
  255. data/examples/prompts/synthesizer.md +0 -17
  256. data/examples/prompts/technical.md +0 -10
  257. data/examples/prompts/template_with_skills_test.md +0 -6
  258. data/examples/prompts/triage.md +0 -22
  259. data/examples/ruboruby.md +0 -423
  260. data/examples/run_all.rb +0 -60
  261. data/examples/xyzzy.rb +0 -97
  262. data/lib/robot_lab/mcp/client.rb +0 -223
  263. data/lib/robot_lab/mcp/connection_poller.rb +0 -195
  264. data/lib/robot_lab/mcp/transports/base.rb +0 -64
  265. data/lib/robot_lab/mcp/transports/sse.rb +0 -120
  266. data/lib/robot_lab/mcp/transports/stdio.rb +0 -170
  267. data/lib/robot_lab/mcp/transports/streamable_http.rb +0 -141
  268. data/lib/robot_lab/mcp/transports/websocket.rb +0 -111
  269. data/logfile +0 -8
  270. data/mkdocs.yml +0 -209
  271. data/robot_concurrency.md +0 -38
  272. data/simple_acp_review.md +0 -298
  273. data/temp.md +0 -6
  274. data/tool_manifest_plan.md +0 -155
@@ -1,410 +0,0 @@
1
- # Memory Management
2
-
3
- Memory in RobotLab is a reactive key-value store that provides persistent storage for runtime data, conversation history, and arbitrary user-defined values. A single unified `Memory` class serves both standalone robot usage and shared network execution.
4
-
5
- ## Memory Structure
6
-
7
- The `Memory` class holds:
8
-
9
- ```ruby
10
- memory = RobotLab.create_memory(data: { user_id: "123" })
11
-
12
- memory.data # StateProxy - custom key-value data with method-style access
13
- memory.results # Array<RobotResult> - execution history
14
- memory.messages # Array<Message> - conversation history
15
- memory.session_id # String - optional persistence identifier
16
- memory.cache # RubyLLM::SemanticCache when the optional gem is installed, else nil
17
- ```
18
-
19
- ## Standalone Robot Memory
20
-
21
- Every robot has its own inherent memory instance, accessible via `robot.memory`:
22
-
23
- ```ruby
24
- robot = RobotLab.build(
25
- name: "assistant",
26
- system_prompt: "You are helpful."
27
- )
28
-
29
- # Access the robot's memory
30
- robot.memory[:user_name] = "Alice"
31
- robot.memory[:user_name] #=> "Alice"
32
-
33
- # Run the robot
34
- result = robot.run("Hello!")
35
-
36
- # Memory persists between runs on the same robot instance
37
- robot.memory[:preference] = "dark_mode"
38
- result2 = robot.run("What are my preferences?")
39
-
40
- # Reset memory to initial state
41
- robot.reset_memory
42
- ```
43
-
44
- `reset_memory` resets only the key-value store. It does **not** touch the robot's chat history — that is `robot.clear_messages(keep_system: true)`. The two are independent.
45
-
46
- ## Network Shared Memory
47
-
48
- When robots execute within a network, they share the network's memory instead of using their own inherent memory. This enables inter-robot communication.
49
-
50
- ```ruby
51
- classifier = RobotLab.build(name: "classifier", system_prompt: "Classify requests.")
52
- handler = RobotLab.build(name: "handler", system_prompt: "Handle requests.")
53
-
54
- network = RobotLab.create_network(name: "support") do
55
- task :classifier, classifier, depends_on: :none
56
- task :handler, handler, depends_on: [:classifier]
57
- end
58
-
59
- # The network has its own shared memory
60
- network.memory[:customer_tier] = "premium"
61
-
62
- # All robots in the network read/write from network.memory during execution
63
- result = network.run(message: "I need help with billing")
64
-
65
- # Reset network memory between runs if needed
66
- network.reset_memory
67
- ```
68
-
69
- The memory resolution logic is:
70
-
71
- 1. If `network_memory` is provided at runtime, use that
72
- 2. If the robot is in a network, use the network's shared memory
73
- 3. Otherwise, use the robot's own inherent memory (`robot.memory`)
74
-
75
- ## Creating Memory
76
-
77
- ### Basic Creation
78
-
79
- ```ruby
80
- memory = RobotLab.create_memory
81
- ```
82
-
83
- ### With Initial Data
84
-
85
- ```ruby
86
- memory = RobotLab.create_memory(
87
- data: {
88
- user_id: "user_123",
89
- order_id: "ord_456",
90
- priority: "high"
91
- }
92
- )
93
- ```
94
-
95
- ### With Caching Disabled
96
-
97
- ```ruby
98
- memory = RobotLab.create_memory(data: {}, enable_cache: false)
99
- ```
100
-
101
- ## Reserved Keys
102
-
103
- Memory has five reserved keys with special behavior and dedicated accessors:
104
-
105
- | Key | Type | Description |
106
- |-----|------|-------------|
107
- | `:data` | `StateProxy` | Runtime data with method-style access |
108
- | `:results` | `Array<RobotResult>` | Accumulated robot execution results |
109
- | `:messages` | `Array<Message>` | Conversation history |
110
- | `:session_id` | `String` | Conversation session identifier |
111
- | `:cache` | `RubyLLM::SemanticCache` or `nil` | Semantic cache module when the optional ruby_llm-semantic_cache gem is installed (read-only after init) |
112
-
113
- Reserved keys are accessed through dedicated methods and are excluded from `memory.keys`:
114
-
115
- ```ruby
116
- memory.data[:category] = "billing"
117
- memory.data.category #=> "billing" (method-style via StateProxy)
118
-
119
- memory.results #=> []
120
- memory.session_id #=> nil
121
- memory.cache #=> RubyLLM::SemanticCache (the module itself), or nil when enable_cache: false or the optional gem is absent
122
- ```
123
-
124
- ## StateProxy
125
-
126
- The `data` attribute is a `StateProxy` that provides convenient hash-style and method-style access:
127
-
128
- ```ruby
129
- memory.data[:user_id] # Hash-style access
130
- memory.data[:user_id] = "456" # Assignment
131
-
132
- memory.data.user_id # Method-style access
133
- memory.data.user_id = "456" # Method-style assignment
134
-
135
- memory.data.key?(:user_id) # Check existence
136
- memory.data.keys # Get all keys
137
- memory.data.to_h # Convert to plain hash
138
- ```
139
-
140
- ## Reactive Features
141
-
142
- Memory supports pub/sub semantics where robots can subscribe to key changes and optionally block until values become available.
143
-
144
- ### Setting Values
145
-
146
- Use `memory.set(key, value)` to write a value, wake any waiters, and notify subscribers. Subscriber callbacks are dispatched through `Async { }`: inside a running reactor they run as fibers, but **outside** one they run synchronously on the writer's thread.
147
-
148
- ```ruby
149
- memory.set(:sentiment, { score: 0.8, confidence: 0.95 })
150
- ```
151
-
152
- The `[]=` operator also triggers reactive notifications for non-reserved keys:
153
-
154
- ```ruby
155
- memory[:sentiment] = { score: 0.8 } # Equivalent to memory.set(:sentiment, ...)
156
- ```
157
-
158
- ### Blocking Reads
159
-
160
- Use `memory.get(key, wait:)` to block until a value becomes available. This is useful for concurrent pipeline execution where one robot needs to wait for another's output:
161
-
162
- ```ruby
163
- # Immediate read (returns nil if missing)
164
- memory.get(:sentiment)
165
-
166
- # Block indefinitely until value exists
167
- memory.get(:sentiment, wait: true)
168
-
169
- # Block up to 30 seconds, then raise RobotLab::AwaitTimeout
170
- memory.get(:sentiment, wait: 30)
171
-
172
- # Wait for multiple keys at once
173
- results = memory.get(:sentiment, :entities, :keywords, wait: 60)
174
- #=> { sentiment: {...}, entities: [...], keywords: [...] }
175
- ```
176
-
177
- Two things to be careful with:
178
-
179
- - On expiry a blocking read **raises** `RobotLab::AwaitTimeout`; it does not return `nil`. Rescue it if a missing value is acceptable.
180
- - With multiple keys the timeout is applied **per missing key**, not to the call as a whole. `get_multiple` waits on the missing keys one at a time in order, so the call raises as soon as the *first* one expires — it does not spend the full 60 s on each. The 180 s worst case is only reached when the earlier keys arrive just before their own deadlines.
181
-
182
- ### Subscriptions
183
-
184
- Subscribe to key changes with async callbacks. The callback receives a `MemoryChange` object:
185
-
186
- ```ruby
187
- memory.subscribe(:raw_data) do |change|
188
- puts "#{change.key} changed from #{change.previous} to #{change.value}"
189
- puts "Written by: #{change.writer} at #{change.timestamp}"
190
- end
191
-
192
- # Subscribe to multiple keys
193
- memory.subscribe(:sentiment, :entities) do |change|
194
- update_dashboard(change.key, change.value)
195
- end
196
-
197
- # Pattern-based subscriptions (glob-style matching)
198
- memory.subscribe_pattern("analysis:*") do |change|
199
- puts "Analysis key #{change.key} updated"
200
- end
201
-
202
- # Unsubscribe
203
- sub_id = memory.subscribe(:status) { |c| puts c.value }
204
- memory.unsubscribe(sub_id)
205
-
206
- # Check if key has subscribers
207
- memory.subscribed?(:status) #=> true/false
208
- ```
209
-
210
- ### MemoryChange
211
-
212
- The `MemoryChange` object provides context about what changed:
213
-
214
- ```ruby
215
- change.key #=> :sentiment
216
- change.value #=> { score: 0.8 }
217
- change.previous #=> nil (or previous value)
218
- change.writer #=> "classifier" (robot name)
219
- change.network_name #=> "support_pipeline"
220
- change.timestamp #=> Time
221
- change.created? #=> true (new key, no previous value)
222
- change.updated? #=> false
223
- change.deleted? #=> false
224
- ```
225
-
226
- ## Memory Lifecycle
227
-
228
- ### Results
229
-
230
- Results track the history of robot executions:
231
-
232
- ```ruby
233
- # Append a result
234
- memory.append_result(robot_result)
235
-
236
- # Get all results (returns a copy)
237
- memory.results
238
-
239
- # Get results from a specific index (for incremental persistence)
240
- memory.results_from(5)
241
- ```
242
-
243
- Each `RobotResult` contains:
244
-
245
- ```ruby
246
- result.robot_name # Which robot produced this
247
- result.output # [TextMessage] synthesized from the final response text
248
- result.tool_calls # Array<ToolResultMessage> - effectively always empty (see Message Flow)
249
- result.stop_reason # always nil for a robot.run result (see Message Flow)
250
- result.last_text_content # Convenience: last text content string
251
- result.input_tokens # Prompt tokens for the run
252
- result.output_tokens # Completion tokens for the run
253
- result.created_at # When it was created
254
- ```
255
-
256
- ### Format History
257
-
258
- The `format_history` method prepares messages for LLM consumption:
259
-
260
- ```ruby
261
- formatted = memory.format_history
262
- # Returns combined messages + formatted results
263
- ```
264
-
265
- ### Merge
266
-
267
- Merge additional values into memory:
268
-
269
- ```ruby
270
- memory.merge!(user_id: 123, category: "billing")
271
- ```
272
-
273
- ### Key Management
274
-
275
- ```ruby
276
- memory.key?(:user_id) # Check existence
277
- memory.keys # Get all non-reserved keys
278
- memory.all_keys # Get all keys including reserved
279
- memory.delete(:temp_data) # Delete a specific key
280
- memory.clear # Clear all non-reserved keys
281
- memory.reset # Reset to initial state (preserves cache)
282
- ```
283
-
284
- ## Cloning
285
-
286
- Create independent copies of memory for isolated execution. Subscriptions are not cloned:
287
-
288
- ```ruby
289
- original = RobotLab.create_memory(data: { count: 1 })
290
- cloned = original.clone
291
-
292
- cloned[:count] = 2
293
- original[:count] #=> still 1
294
- ```
295
-
296
- ## Serialization
297
-
298
- Convert memory to and from hash for persistence:
299
-
300
- ```ruby
301
- # To hash
302
- hash = memory.to_h
303
- #=> {
304
- # data: { ... },
305
- # results: [...],
306
- # messages: [...],
307
- # session_id: "abc123",
308
- # custom: { my_key: "value" }
309
- # }
310
-
311
- # To JSON
312
- json = memory.to_json
313
-
314
- # From hash
315
- memory = Memory.from_hash(hash)
316
- ```
317
-
318
- `to_h` is compacted, so nil-valued reserved keys (a missing `session_id`, for instance) are omitted entirely rather than serialized as `nil`.
319
-
320
- ## Semantic Cache
321
-
322
- Memory includes a semantic cache via `RubyLLM::SemanticCache` (optional ruby_llm-semantic_cache gem; not yet ruby_llm 2.0-compatible, so `memory.cache` is `nil` without it) that reduces costs and latency by returning cached responses for semantically equivalent queries:
323
-
324
- ```ruby
325
- # Using the cache with fetch
326
- response = memory.cache.fetch("What is Ruby?") do
327
- RubyLLM.chat.ask("What is Ruby?")
328
- end
329
-
330
- # Wrapping a chat instance
331
- chat = memory.cache.wrap(RubyLLM.chat(model: "gpt-4o"))
332
- chat.ask("What is Ruby?") # Cached on semantic similarity
333
- ```
334
-
335
- Caching can be disabled per-memory or per-robot:
336
-
337
- ```ruby
338
- memory = RobotLab.create_memory(enable_cache: false)
339
- robot = RobotLab.build(name: "bot", system_prompt: "...", enable_cache: false)
340
- ```
341
-
342
- ## Backend Options
343
-
344
- Memory defaults to a Hash-based backend but can use Redis for distributed scenarios:
345
-
346
- ```ruby
347
- # Try Redis, fall back to Hash
348
- memory = Memory.new(backend: :auto)
349
-
350
- # Hash backend — the only value that is honoured literally
351
- memory = Memory.new(backend: :hash)
352
-
353
- # Same behaviour as :auto — try Redis, fall back to Hash
354
- memory = Memory.new(backend: :redis)
355
-
356
- # Check what you actually got
357
- memory.redis? #=> true/false
358
- ```
359
-
360
- !!! note "`backend: :redis` does not force Redis"
361
- `select_backend` special-cases only `:hash`; every other value (`:redis`,
362
- `:auto`, anything else) takes the same branch —
363
- `create_redis_backend || create_hash_backend`. `create_redis_backend`
364
- returns `nil` when `redis` is unavailable and rescues `StandardError` to
365
- `nil` on a connection failure, so an unreachable Redis silently downgrades
366
- to a Hash with no exception and no warning. Always check `memory.redis?` if
367
- the distinction matters.
368
-
369
- Redis is configured via `RobotLab.config.redis` or the `REDIS_URL` environment variable.
370
-
371
- ## Best Practices
372
-
373
- ### 1. Use Memory for Cross-Robot Data
374
-
375
- ```ruby
376
- # In a network, robots share memory automatically.
377
- # Robot A writes:
378
- memory.set(:classification, "billing")
379
-
380
- # Robot B reads:
381
- category = memory.get(:classification)
382
- ```
383
-
384
- ### 2. Use Blocking Reads for Concurrent Pipelines
385
-
386
- ```ruby
387
- # When robots run in parallel, use blocking reads
388
- # to synchronize on shared data:
389
- results = memory.get(:sentiment, :entities, wait: 60)
390
- ```
391
-
392
- ### 3. Keep Data Minimal
393
-
394
- ```ruby
395
- # Store references instead of large objects
396
- memory[:response_id] = response.id # Preferred
397
- # memory[:huge_response] = api_response # Avoid
398
- ```
399
-
400
- ### 4. Reset Between Independent Runs
401
-
402
- ```ruby
403
- network.reset_memory
404
- result = network.run(message: "New conversation")
405
- ```
406
-
407
- ## Next Steps
408
-
409
- - [Network Orchestration](network-orchestration.md) - How networks share memory
410
- - [Message Flow](message-flow.md) - How messages are processed
@@ -1,56 +0,0 @@
1
- /* Custom styles for RobotLab documentation */
2
-
3
- /* Robot-themed accent colors */
4
- :root {
5
- --md-primary-fg-color: #673ab7;
6
- --md-primary-fg-color--light: #9575cd;
7
- --md-primary-fg-color--dark: #512da8;
8
- --md-accent-fg-color: #ffc107;
9
- }
10
-
11
- /* Code block styling */
12
- .md-typeset code {
13
- background-color: rgba(103, 58, 183, 0.1);
14
- }
15
-
16
- /* Admonition customization */
17
- .md-typeset .admonition.robot,
18
- .md-typeset details.robot {
19
- border-color: #673ab7;
20
- }
21
-
22
- .md-typeset .robot > .admonition-title,
23
- .md-typeset .robot > summary {
24
- background-color: rgba(103, 58, 183, 0.1);
25
- }
26
-
27
- .md-typeset .robot > .admonition-title::before,
28
- .md-typeset .robot > summary::before {
29
- background-color: #673ab7;
30
- -webkit-mask-image: var(--md-admonition-icon--robot);
31
- mask-image: var(--md-admonition-icon--robot);
32
- }
33
-
34
- /* Grid cards spacing */
35
- .md-typeset .grid.cards > ul > li {
36
- margin-bottom: 1em;
37
- }
38
-
39
- /* API reference table styling */
40
- .md-typeset table:not([class]) th {
41
- background-color: rgba(103, 58, 183, 0.1);
42
- }
43
-
44
- /* Mermaid diagram styling */
45
- .mermaid {
46
- background: transparent !important;
47
- }
48
-
49
- /* Dark mode adjustments */
50
- [data-md-color-scheme="slate"] {
51
- --md-code-bg-color: #1e1e2e;
52
- }
53
-
54
- [data-md-color-scheme="slate"] .md-typeset code {
55
- background-color: rgba(149, 117, 205, 0.2);
56
- }
Binary file