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.
Files changed (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
  5. data/lib/robot_lab/version.rb +1 -1
  6. metadata +3 -253
  7. data/.architecture/AGENTS.md +0 -32
  8. data/.architecture/config.yml +0 -8
  9. data/.architecture/members.yml +0 -60
  10. data/.architecture/reviews/feature-free-will.md +0 -490
  11. data/.architecture/reviews/overall-codebase.md +0 -427
  12. data/.claude/settings.local.json +0 -57
  13. data/.codex/config.toml +0 -2
  14. data/.envrc +0 -6
  15. data/.github/workflows/deploy-github-pages.yml +0 -52
  16. data/.irbrc +0 -6
  17. data/.loki +0 -65
  18. data/.quality/reek_baseline.txt +0 -43
  19. data/.rubocop.yml +0 -10
  20. data/Archspec.rb +0 -44
  21. data/CLAUDE.md +0 -141
  22. data/COMMITS.md +0 -196
  23. data/Rakefile +0 -193
  24. data/_typos.toml +0 -21
  25. data/agent2agent_review.md +0 -192
  26. data/agentf_improvements.md +0 -253
  27. data/agents.md +0 -14
  28. data/docs/api/core/index.md +0 -147
  29. data/docs/api/core/memory.md +0 -578
  30. data/docs/api/core/network.md +0 -553
  31. data/docs/api/core/result.md +0 -211
  32. data/docs/api/core/robot.md +0 -1667
  33. data/docs/api/core/state.md +0 -145
  34. data/docs/api/core/tool.md +0 -520
  35. data/docs/api/errors.md +0 -323
  36. data/docs/api/hooks.md +0 -469
  37. data/docs/api/index.md +0 -137
  38. data/docs/api/mcp/client.md +0 -387
  39. data/docs/api/mcp/index.md +0 -267
  40. data/docs/api/mcp/server.md +0 -252
  41. data/docs/api/mcp/transports.md +0 -339
  42. data/docs/api/messages/index.md +0 -126
  43. data/docs/api/messages/text-message.md +0 -170
  44. data/docs/api/messages/tool-call-message.md +0 -206
  45. data/docs/api/messages/tool-result-message.md +0 -242
  46. data/docs/api/messages/user-message.md +0 -232
  47. data/docs/api/skills.md +0 -423
  48. data/docs/api/streaming/context.md +0 -280
  49. data/docs/api/streaming/events.md +0 -185
  50. data/docs/api/streaming/index.md +0 -156
  51. data/docs/api/support.md +0 -846
  52. data/docs/architecture/core-concepts.md +0 -575
  53. data/docs/architecture/index.md +0 -252
  54. data/docs/architecture/message-flow.md +0 -366
  55. data/docs/architecture/network-orchestration.md +0 -478
  56. data/docs/architecture/robot-execution.md +0 -396
  57. data/docs/architecture/state-management.md +0 -410
  58. data/docs/assets/css/custom.css +0 -56
  59. data/docs/assets/images/robot_lab.jpg +0 -0
  60. data/docs/concepts.md +0 -641
  61. data/docs/examples/basic-chat.md +0 -258
  62. data/docs/examples/index.md +0 -425
  63. data/docs/examples/mcp-server.md +0 -403
  64. data/docs/examples/multi-robot-network.md +0 -476
  65. data/docs/examples/tool-usage.md +0 -468
  66. data/docs/getting-started/configuration.md +0 -645
  67. data/docs/getting-started/index.md +0 -56
  68. data/docs/getting-started/installation.md +0 -197
  69. data/docs/getting-started/quick-start.md +0 -293
  70. data/docs/guides/building-robots.md +0 -1187
  71. data/docs/guides/creating-networks.md +0 -736
  72. data/docs/guides/hooks.md +0 -1206
  73. data/docs/guides/index.md +0 -78
  74. data/docs/guides/knowledge.md +0 -219
  75. data/docs/guides/mcp-integration.md +0 -740
  76. data/docs/guides/memory.md +0 -458
  77. data/docs/guides/observability.md +0 -733
  78. data/docs/guides/streaming.md +0 -323
  79. data/docs/guides/using-tools.md +0 -780
  80. data/docs/index.md +0 -280
  81. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  82. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  83. data/examples/.envrc +0 -3
  84. data/examples/01_simple_robot.rb +0 -31
  85. data/examples/02_tools.rb +0 -109
  86. data/examples/03_network.rb +0 -118
  87. data/examples/04_mcp.rb +0 -217
  88. data/examples/05_streaming.rb +0 -137
  89. data/examples/06_prompt_templates.rb +0 -325
  90. data/examples/07_network_memory.rb +0 -313
  91. data/examples/08_llm_config.rb +0 -162
  92. data/examples/09_chaining.rb +0 -251
  93. data/examples/10_memory.rb +0 -315
  94. data/examples/11_network_introspection.rb +0 -239
  95. data/examples/12_message_bus.rb +0 -67
  96. data/examples/13_spawn.rb +0 -85
  97. data/examples/14_rusty_circuit/.envrc +0 -1
  98. data/examples/14_rusty_circuit/.gitignore +0 -1
  99. data/examples/14_rusty_circuit/README.md +0 -1
  100. data/examples/14_rusty_circuit/comic.rb +0 -149
  101. data/examples/14_rusty_circuit/display.rb +0 -217
  102. data/examples/14_rusty_circuit/heckler.rb +0 -65
  103. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  104. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  105. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  106. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  107. data/examples/14_rusty_circuit/scout.rb +0 -165
  108. data/examples/14_rusty_circuit/show.log +0 -234
  109. data/examples/15_memory_network_and_bus/.envrc +0 -1
  110. data/examples/15_memory_network_and_bus/README.md +0 -66
  111. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  112. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  113. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  114. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  115. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  116. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  117. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  118. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  119. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  120. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  121. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  122. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  123. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  124. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  125. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  126. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  127. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  128. data/examples/16_writers_room/.envrc +0 -1
  129. data/examples/16_writers_room/display.rb +0 -158
  130. data/examples/16_writers_room/output/.gitignore +0 -4
  131. data/examples/16_writers_room/output/README.md +0 -69
  132. data/examples/16_writers_room/output/opus_001.md +0 -263
  133. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  134. data/examples/16_writers_room/output/opus_002.md +0 -245
  135. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  136. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  137. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  138. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  139. data/examples/16_writers_room/prompts/writer.md +0 -37
  140. data/examples/16_writers_room/room.rb +0 -186
  141. data/examples/16_writers_room/tools.rb +0 -173
  142. data/examples/16_writers_room/writer.rb +0 -121
  143. data/examples/16_writers_room/writers_room.rb +0 -256
  144. data/examples/17_skills.rb +0 -234
  145. data/examples/18_rails/.envrc +0 -3
  146. data/examples/18_rails/.gitignore +0 -5
  147. data/examples/18_rails/Gemfile +0 -11
  148. data/examples/18_rails/README.md +0 -67
  149. data/examples/18_rails/Rakefile +0 -4
  150. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  151. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  152. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  153. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  154. data/examples/18_rails/app/models/application_record.rb +0 -5
  155. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  156. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  157. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  158. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  159. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  160. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  161. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  162. data/examples/18_rails/bin/dev +0 -7
  163. data/examples/18_rails/bin/rails +0 -6
  164. data/examples/18_rails/bin/setup +0 -15
  165. data/examples/18_rails/config/application.rb +0 -33
  166. data/examples/18_rails/config/cable.yml +0 -2
  167. data/examples/18_rails/config/database.yml +0 -5
  168. data/examples/18_rails/config/environment.rb +0 -4
  169. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  170. data/examples/18_rails/config/routes.rb +0 -6
  171. data/examples/18_rails/config.ru +0 -4
  172. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  173. data/examples/19_token_tracking.rb +0 -138
  174. data/examples/20_circuit_breaker.rb +0 -147
  175. data/examples/21_learning_loop.rb +0 -181
  176. data/examples/22_context_compression.rb +0 -172
  177. data/examples/23_convergence.rb +0 -133
  178. data/examples/24_structured_delegation.rb +0 -151
  179. data/examples/25_history_search/conversation.jsonl +0 -30
  180. data/examples/25_history_search.rb +0 -133
  181. data/examples/26_document_store/api_versioning_adr.md +0 -52
  182. data/examples/26_document_store/incident_postmortem.md +0 -46
  183. data/examples/26_document_store/postgres_runbook.md +0 -49
  184. data/examples/26_document_store/redis_caching_guide.md +0 -48
  185. data/examples/26_document_store/sidekiq_guide.md +0 -51
  186. data/examples/26_document_store.rb +0 -140
  187. data/examples/27_incident_response/README.md +0 -65
  188. data/examples/27_incident_response/incident_response.rb +0 -261
  189. data/examples/28_mcp_discovery.rb +0 -113
  190. data/examples/29_ractor_tools.rb +0 -240
  191. data/examples/30_ractor_network.rb +0 -252
  192. data/examples/31_launch_assessment.rb +0 -246
  193. data/examples/32_newsletter_reader.rb +0 -193
  194. data/examples/33_stock_generator.rb +0 -80
  195. data/examples/33_stock_predictor.rb +0 -327
  196. data/examples/34_agentskills.rb +0 -76
  197. data/examples/35_hooks.rb +0 -266
  198. data/examples/README.md +0 -550
  199. data/examples/common.rb +0 -180
  200. data/examples/prompts/assistant.md +0 -5
  201. data/examples/prompts/audit_trail.md +0 -8
  202. data/examples/prompts/billing.md +0 -10
  203. data/examples/prompts/classifier.md +0 -7
  204. data/examples/prompts/comedian.md +0 -6
  205. data/examples/prompts/comedy_critic.md +0 -10
  206. data/examples/prompts/configurable.md +0 -9
  207. data/examples/prompts/dispatcher.md +0 -12
  208. data/examples/prompts/entity_extractor.md +0 -14
  209. data/examples/prompts/escalation.md +0 -42
  210. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  211. data/examples/prompts/frontmatter_named_test.md +0 -5
  212. data/examples/prompts/frontmatter_tools_test.md +0 -6
  213. data/examples/prompts/general.md +0 -7
  214. data/examples/prompts/github_assistant.md +0 -14
  215. data/examples/prompts/helper.md +0 -4
  216. data/examples/prompts/incident_responder.md +0 -18
  217. data/examples/prompts/keyword_extractor.md +0 -11
  218. data/examples/prompts/llm_config_demo.md +0 -20
  219. data/examples/prompts/order_support.md +0 -35
  220. data/examples/prompts/os_advocate.md +0 -13
  221. data/examples/prompts/os_chief.md +0 -13
  222. data/examples/prompts/os_editor.md +0 -13
  223. data/examples/prompts/parameterized_main_test.md +0 -6
  224. data/examples/prompts/pii_redactor.md +0 -7
  225. data/examples/prompts/product_support.md +0 -37
  226. data/examples/prompts/runbook_protocol.md +0 -12
  227. data/examples/prompts/sentiment_analyzer.md +0 -12
  228. data/examples/prompts/skill_a_test.md +0 -4
  229. data/examples/prompts/skill_b_test.md +0 -4
  230. data/examples/prompts/skill_config_test.md +0 -5
  231. data/examples/prompts/skill_cycle_a_test.md +0 -6
  232. data/examples/prompts/skill_cycle_b_test.md +0 -6
  233. data/examples/prompts/skill_description_test.md +0 -4
  234. data/examples/prompts/skill_leaf_test.md +0 -4
  235. data/examples/prompts/skill_nested_test.md +0 -6
  236. data/examples/prompts/skill_refs_main_test.md +0 -6
  237. data/examples/prompts/skill_self_ref_test.md +0 -6
  238. data/examples/prompts/skill_with_mcp_test.md +0 -9
  239. data/examples/prompts/skill_with_params_test.md +0 -6
  240. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  241. data/examples/prompts/skill_with_tools_test.md +0 -6
  242. data/examples/prompts/sre_compliance.md +0 -10
  243. data/examples/prompts/structured_output.md +0 -7
  244. data/examples/prompts/synthesizer.md +0 -17
  245. data/examples/prompts/technical.md +0 -10
  246. data/examples/prompts/template_with_skills_test.md +0 -6
  247. data/examples/prompts/triage.md +0 -22
  248. data/examples/ruboruby.md +0 -423
  249. data/examples/run_all.rb +0 -60
  250. data/examples/xyzzy.rb +0 -97
  251. data/logfile +0 -8
  252. data/mkdocs.yml +0 -209
  253. data/robot_concurrency.md +0 -38
  254. data/simple_acp_review.md +0 -298
  255. data/temp.md +0 -6
  256. data/tool_manifest_plan.md +0 -155
@@ -1,251 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 9: Robot Chaining & Reconfiguration
5
- #
6
- # Demonstrates the Robot API surface for runtime configuration without
7
- # making any LLM calls. Covers:
8
- # - with_* method chaining
9
- # - update() for reconfiguration
10
- # - to_h introspection (via amazing_print)
11
- # - Config diffs between steps (via hashdiff)
12
- # - Template front matter config keys
13
- # - Constructor params overriding front matter
14
- # - Config hierarchy in action
15
- # - AskUser tool for gathering template parameters interactively
16
- #
17
- # Usage:
18
- # bundle exec ruby examples/09_chaining.rb
19
-
20
- require_relative "common"
21
- require "amazing_print"
22
- require "hashdiff"
23
- require "stringio"
24
-
25
- # Show a config snapshot and the diff from the previous snapshot.
26
- # Returns the current hash for use as the next "previous" snapshot.
27
- def show_config(robot, previous_config = nil)
28
- current = robot.to_h
29
- ap current
30
-
31
- if previous_config
32
- diff = Hashdiff.diff(previous_config, current)
33
- if diff.empty?
34
- puts " (no changes to to_h)"
35
- else
36
- puts
37
- puts " Diff from previous:"
38
- diff.each do |change|
39
- op, path, *values = change
40
- case op
41
- when "+"
42
- puts " + #{path}: #{values.first.inspect}"
43
- when "-"
44
- puts " - #{path}: #{values.first.inspect}"
45
- when "~"
46
- puts " ~ #{path}: #{values.first.inspect} -> #{values.last.inspect}"
47
- end
48
- end
49
- end
50
- end
51
-
52
- current
53
- end
54
-
55
- banner "Robot Chaining & Reconfiguration"
56
-
57
- # =============================================================================
58
- # Section 1: Template front matter config
59
- # =============================================================================
60
-
61
- section "Section 1: Template Front Matter Config"
62
-
63
- # The 'configurable' template sets temperature: 0.3 and max_tokens: 200
64
- # via YAML front matter. These are applied automatically.
65
- robot = RobotLab.build(
66
- **llm_opts,
67
- name: "chameleon",
68
- template: :configurable,
69
- context: { task_type: "analysis" }
70
- )
71
-
72
- puts "Robot created with :configurable template"
73
- prev = show_config(robot)
74
- puts
75
-
76
- # =============================================================================
77
- # Section 2: with_* method chaining
78
- # =============================================================================
79
-
80
- section "Section 2: with_* Method Chaining"
81
-
82
- # with_* methods return self, enabling fluent chaining.
83
- # These override whatever the template set.
84
- robot.with_temperature(0.9)
85
-
86
- puts "After chaining with_temperature(0.9):"
87
- prev = show_config(robot, prev)
88
- puts
89
-
90
- # =============================================================================
91
- # Section 3: update() for reconfiguration
92
- # =============================================================================
93
-
94
- section "Section 3: update() for Reconfiguration"
95
-
96
- # update() can swap the template, model, temperature, and other settings.
97
- robot.update(template: :assistant, temperature: 0.5)
98
-
99
- puts "After update(template: :assistant, temperature: 0.5):"
100
- prev = show_config(robot, prev)
101
- puts
102
-
103
- # Swap back with context
104
- robot.update(template: :configurable, context: { task_type: "creative" })
105
-
106
- puts "After update(template: :configurable, context: { task_type: 'creative' }):"
107
- prev = show_config(robot, prev)
108
- puts
109
-
110
- # =============================================================================
111
- # Section 4: Constructor params override front matter
112
- # =============================================================================
113
-
114
- section "Section 4: Constructor Params Override Front Matter"
115
-
116
- # The configurable template sets temperature: 0.3 in front matter.
117
- # Passing temperature: 0.9 to the constructor overrides it.
118
- robot2 = RobotLab.build(
119
- **llm_opts,
120
- name: "override_demo",
121
- template: :configurable,
122
- context: { task_type: "creative" },
123
- temperature: 0.9
124
- )
125
-
126
- puts "Template sets temperature: 0.3, constructor passes temperature: 0.9"
127
- prev2 = show_config(robot2)
128
- puts
129
-
130
- # =============================================================================
131
- # Section 5: RunConfig as alternative to individual kwargs
132
- # =============================================================================
133
-
134
- section "Section 5: RunConfig as Alternative to Individual kwargs"
135
-
136
- # Instead of passing model:, temperature:, etc. individually,
137
- # use a RunConfig to express shared defaults.
138
- shared = RobotLab::RunConfig.new(model: LLM[:default].model, temperature: 0.5)
139
-
140
- puts "RunConfig: #{shared.to_h.inspect}"
141
- puts
142
-
143
- # Robot inherits from RunConfig; constructor kwargs still override
144
- robot3 = RobotLab.build(
145
- **llm_opts,
146
- name: "runconfig_demo",
147
- template: :configurable,
148
- context: { task_type: "analysis" },
149
- config: shared,
150
- temperature: 0.8 # overrides RunConfig's 0.5
151
- )
152
-
153
- puts "Robot with config: shared, temperature: 0.8"
154
- puts "(RunConfig sets 0.5, constructor overrides to 0.8)"
155
- prev3 = show_config(robot3)
156
- puts
157
-
158
- # RunConfig merge semantics
159
- creative = RobotLab::RunConfig.new(temperature: 0.9, max_tokens: 2000)
160
- merged = shared.merge(creative)
161
-
162
- puts "Merge: shared.merge(creative)"
163
- puts " shared: #{shared.to_h.inspect}"
164
- puts " creative: #{creative.to_h.inspect}"
165
- puts " merged: #{merged.to_h.inspect}"
166
- puts " (model inherited, temperature overridden, max_tokens added)"
167
- puts
168
-
169
- # =============================================================================
170
- # Section 6: Bare robot with chaining
171
- # =============================================================================
172
-
173
- section "Section 6: Bare Robot with Chaining"
174
-
175
- # A bare robot has no template. Configure entirely via chaining.
176
- bare = RobotLab.build(name: "bare")
177
-
178
- puts "Bare robot before chaining:"
179
- prev_bare = show_config(bare)
180
- puts
181
-
182
- bare
183
- .with_instructions("You are a helpful assistant.")
184
- .with_temperature(0.5)
185
-
186
- puts "After with_instructions(...).with_temperature(0.5):"
187
- prev_bare = show_config(bare, prev_bare)
188
- puts
189
-
190
- # =============================================================================
191
- # Section 7: with_template() on an existing robot
192
- # =============================================================================
193
-
194
- section "Section 7: with_template() on Existing Robot"
195
-
196
- # You can apply a template to a robot after creation.
197
- bare.with_template(:helper)
198
-
199
- puts "After bare.with_template(:helper):"
200
- show_config(bare, prev_bare)
201
- puts
202
-
203
- # =============================================================================
204
- # Section 8: AskUser tool for gathering template parameters
205
- # =============================================================================
206
-
207
- section "Section 8: AskUser for Template Parameters"
208
- puts "The :configurable template declares `task_type: general` in its front"
209
- puts "matter. That default is offered to the user — they can accept it by"
210
- puts "pressing Enter or type something else. Parameters with null values"
211
- puts "have no default and require user input."
212
- puts
213
-
214
- # Build a robot with AskUser — the template's task_type default ("general")
215
- # is offered to the user, who can accept or override it.
216
- interactive = RobotLab.build(
217
- **llm_opts,
218
- name: "interactive_demo",
219
- template: :configurable,
220
- local_tools: [RobotLab::AskUser]
221
- )
222
-
223
- puts "Robot 'interactive_demo' has AskUser in its tools."
224
- puts "The template renders with the default: task_type = \"general\""
225
- puts "When run, the robot can call ask_user to let the user confirm"
226
- puts "or change the value."
227
- puts
228
-
229
- # Simulate what the user would see (no LLM call needed)
230
- output = StringIO.new
231
- demo_tool = RobotLab::AskUser.new(robot: interactive)
232
- interactive.output = output
233
- interactive.input = StringIO.new("2\n")
234
-
235
- result = demo_tool.call(
236
- question: "What type of task should I optimize for?",
237
- choices: %w[general analysis creative coding research],
238
- default: "general"
239
- )
240
-
241
- puts output.string
242
- puts "User selected: #{result}"
243
- puts
244
- puts "The robot would re-render the template with task_type: \"#{result}\""
245
- puts "producing: \"You are a precise assistant optimized for #{result} tasks.\""
246
- puts
247
- puts "If the user had pressed Enter, the default \"general\" would be kept."
248
- puts
249
-
250
- hr
251
- puts "All sections completed without any LLM calls."
@@ -1,315 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 10: Advanced Memory Operations
5
- #
6
- # Demonstrates the Memory API without making any LLM calls. Covers:
7
- # - StateProxy for method-style data access
8
- # - Key subscriptions with MemoryChange objects
9
- # - Pattern subscriptions
10
- # - Unsubscribe
11
- # - Key enumeration (keys, all_keys, key?)
12
- # - Serialization (to_h, from_hash, to_json) with amazing_print
13
- # - Serialization round-trip verification with hashdiff
14
- # - Clone for isolated copies
15
- # - Delete with reserved key protection
16
- # - Clear vs reset
17
- #
18
- # Usage:
19
- # bundle exec ruby examples/10_memory.rb
20
-
21
- require_relative "common"
22
- require "json"
23
- require "amazing_print"
24
- require "hashdiff"
25
-
26
- banner "Advanced Memory Operations"
27
-
28
- # =============================================================================
29
- # Section 1: StateProxy for method-style access
30
- # =============================================================================
31
-
32
- section "Section 1: StateProxy for Method-Style Access"
33
-
34
- memory = RobotLab.create_memory(
35
- data: { category: nil, priority: "low" },
36
- enable_cache: false
37
- )
38
-
39
- puts "Initial data:"
40
- ap memory.data.to_h
41
- puts
42
-
43
- # StateProxy allows method-style access to the :data hash
44
- memory.data.category = "billing"
45
-
46
- puts "After memory.data.category = 'billing':"
47
- ap memory.data.to_h
48
- puts
49
-
50
- # Bracket-style also works
51
- memory.data[:priority] = "high"
52
- puts "After memory.data[:priority] = 'high':"
53
- ap memory.data.to_h
54
- puts
55
-
56
- # =============================================================================
57
- # Section 2: Subscriptions and MemoryChange
58
- # =============================================================================
59
-
60
- section "Section 2: Subscriptions and MemoryChange"
61
-
62
- changes = []
63
-
64
- sub_id = memory.subscribe(:status) do |change|
65
- changes << change
66
- end
67
-
68
- puts "Subscribed to :status (sub_id: #{sub_id[0..7]}...)"
69
-
70
- # Set current_writer so the MemoryChange knows who wrote
71
- memory.current_writer = "classifier"
72
- memory.set(:status, "processing")
73
-
74
- # Give the async callback time to fire
75
- sleep 0.1
76
-
77
- if changes.any?
78
- change = changes.first
79
- puts "Change received:"
80
- ap change.to_h
81
- puts " created?: #{change.created?}"
82
- puts " updated?: #{change.updated?}"
83
- end
84
-
85
- # Update the same key to see an 'updated' change
86
- memory.set(:status, "complete")
87
- sleep 0.1
88
-
89
- if changes.size > 1
90
- puts
91
- puts "Second change (update):"
92
- ap changes.last.to_h
93
- puts " created?: #{changes.last.created?}"
94
- puts " updated?: #{changes.last.updated?}"
95
- end
96
- puts
97
-
98
- # =============================================================================
99
- # Section 3: Pattern subscriptions
100
- # =============================================================================
101
-
102
- section "Section 3: Pattern Subscriptions"
103
-
104
- pattern_changes = []
105
-
106
- pattern_sub_id = memory.subscribe_pattern("analysis:*") do |change|
107
- pattern_changes << change
108
- end
109
-
110
- puts "Subscribed to pattern 'analysis:*'"
111
-
112
- memory.current_writer = "analyst"
113
- memory.set(:"analysis:sentiment", { score: 0.8 })
114
- memory.set(:"analysis:entities", ["Ruby", "LLM"])
115
- memory.set(:unrelated_key, "ignored")
116
-
117
- sleep 0.1
118
-
119
- puts "Pattern matched #{pattern_changes.size} changes (expected 2):"
120
- pattern_changes.each do |change|
121
- ap({ change.key => change.value })
122
- end
123
- puts
124
-
125
- # =============================================================================
126
- # Section 4: Unsubscribe
127
- # =============================================================================
128
-
129
- section "Section 4: Unsubscribe"
130
-
131
- count_before = changes.size
132
- memory.unsubscribe(sub_id)
133
- puts "Unsubscribed from :status"
134
-
135
- memory.set(:status, "archived")
136
- sleep 0.1
137
-
138
- puts "Changes after unsubscribe: #{changes.size} (was #{count_before}, should be same)"
139
- puts
140
-
141
- # =============================================================================
142
- # Section 5: Key management
143
- # =============================================================================
144
-
145
- section "Section 5: Key Management"
146
-
147
- puts "memory.keys (non-reserved):"
148
- ap memory.keys
149
- puts
150
-
151
- puts "memory.all_keys (includes reserved: data, results, messages, session_id, cache):"
152
- ap memory.all_keys
153
- puts
154
-
155
- puts "memory.key?(:status) = #{memory.key?(:status)}"
156
- puts "memory.key?(:nope) = #{memory.key?(:nope)}"
157
- puts
158
-
159
- # =============================================================================
160
- # Section 6: Serialization round-trip
161
- # =============================================================================
162
-
163
- section "Section 6: Serialization Round-Trip"
164
-
165
- hash = memory.to_h
166
- puts "memory.to_h:"
167
- ap hash
168
- puts
169
-
170
- json_str = memory.to_json
171
- puts "memory.to_json length: #{json_str.length} chars"
172
- puts
173
-
174
- # Round-trip via from_hash
175
- restored = RobotLab::Memory.from_hash(hash)
176
-
177
- puts "Restored memory from hash:"
178
- ap restored.to_h
179
- puts
180
-
181
- # Use hashdiff to verify the round-trip preserved everything
182
- diff = Hashdiff.diff(hash, restored.to_h)
183
- if diff.empty?
184
- puts "Round-trip verification: PERFECT (no differences)"
185
- else
186
- puts "Round-trip differences:"
187
- diff.each do |change|
188
- op, path, *values = change
189
- case op
190
- when "+"
191
- puts " + #{path}: #{values.first.inspect}"
192
- when "-"
193
- puts " - #{path}: #{values.first.inspect}"
194
- when "~"
195
- puts " ~ #{path}: #{values.first.inspect} -> #{values.last.inspect}"
196
- end
197
- end
198
- end
199
- puts
200
-
201
- # =============================================================================
202
- # Section 7: Clone for isolation
203
- # =============================================================================
204
-
205
- section "Section 7: Clone for Isolation"
206
-
207
- cloned = memory.clone
208
- cloned.set(:isolated_key, "only in clone")
209
-
210
- puts "cloned.key?(:isolated_key) = #{cloned.key?(:isolated_key)}"
211
- puts "memory.key?(:isolated_key) = #{memory.key?(:isolated_key)} (isolated!)"
212
- puts
213
-
214
- # Show exactly what differs between clone and original
215
- diff = Hashdiff.diff(memory.to_h, cloned.to_h)
216
- puts "Diff (original vs clone):"
217
- diff.each do |change|
218
- op, path, *values = change
219
- case op
220
- when "+"
221
- puts " + #{path}: #{values.first.inspect}"
222
- when "-"
223
- puts " - #{path}: #{values.first.inspect}"
224
- when "~"
225
- puts " ~ #{path}: #{values.first.inspect} -> #{values.last.inspect}"
226
- end
227
- end
228
- puts
229
-
230
- # =============================================================================
231
- # Section 8: Delete and reserved key protection
232
- # =============================================================================
233
-
234
- section "Section 8: Delete and Reserved Key Protection"
235
-
236
- before_delete = memory.to_h
237
- memory.delete(:status)
238
-
239
- puts "After memory.delete(:status):"
240
- diff = Hashdiff.diff(before_delete, memory.to_h)
241
- diff.each do |change|
242
- op, path, *values = change
243
- case op
244
- when "-"
245
- puts " - #{path}: #{values.first.inspect}"
246
- end
247
- end
248
- puts
249
-
250
- begin
251
- memory.delete(:data)
252
- rescue ArgumentError => e
253
- puts "memory.delete(:data) raises: #{e.message}"
254
- end
255
- puts
256
-
257
- # =============================================================================
258
- # Section 9: Clear vs Reset
259
- # =============================================================================
260
-
261
- section "Section 9: Clear vs Reset"
262
-
263
- memory.set(:temp1, "value1")
264
- memory.set(:temp2, "value2")
265
-
266
- puts "Before clear:"
267
- before_clear = memory.to_h
268
- ap before_clear
269
- puts
270
-
271
- memory.clear
272
-
273
- puts "After clear (non-reserved keys removed):"
274
- after_clear = memory.to_h
275
- ap after_clear
276
- puts
277
-
278
- puts "Diff (before clear vs after clear):"
279
- Hashdiff.diff(before_clear, after_clear).each do |change|
280
- op, path, *values = change
281
- case op
282
- when "+"
283
- puts " + #{path}: #{values.first.inspect}"
284
- when "-"
285
- puts " - #{path}: #{values.first.inspect}"
286
- when "~"
287
- puts " ~ #{path}: #{values.first.inspect} -> #{values.last.inspect}"
288
- end
289
- end
290
- puts
291
-
292
- before_reset = memory.to_h
293
- memory.reset
294
-
295
- puts "After reset (full reset to initial state):"
296
- after_reset = memory.to_h
297
- ap after_reset
298
- puts
299
-
300
- puts "Diff (before reset vs after reset):"
301
- Hashdiff.diff(before_reset, after_reset).each do |change|
302
- op, path, *values = change
303
- case op
304
- when "+"
305
- puts " + #{path}: #{values.first.inspect}"
306
- when "-"
307
- puts " - #{path}: #{values.first.inspect}"
308
- when "~"
309
- puts " ~ #{path}: #{values.first.inspect} -> #{values.last.inspect}"
310
- end
311
- end
312
- puts
313
-
314
- hr
315
- puts "All sections completed without any LLM calls."