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,239 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 11: Network Visualization & Introspection
5
- #
6
- # Demonstrates network inspection and visualization without making any
7
- # LLM calls. Covers:
8
- # - to_mermaid() — Mermaid diagram export
9
- # - to_dot() — Graphviz DOT export
10
- # - execution_plan() — text execution order
11
- # - visualize() — ASCII pipeline visualization
12
- # - robot(name) / [name] — access individual robots
13
- # - available_robots() — list all robots
14
- # - add_robot() — dynamically add a robot
15
- # - to_h() — network introspection hash (via amazing_print)
16
- # - Task-specific config (context:, depends_on:)
17
- # - broadcast() and on_broadcast
18
- #
19
- # Usage:
20
- # bundle exec ruby examples/11_network_introspection.rb
21
-
22
- require_relative "common"
23
- require "amazing_print"
24
- require "tempfile"
25
-
26
- # On macOS + iTerm2, render DOT as a PNG and display inline via imgcat.
27
- # Returns true if the image was displayed, false otherwise.
28
- def render_dot_image(dot_source)
29
- return false unless RUBY_PLATFORM.include?("darwin")
30
-
31
- dot_cmd = `which dot 2>/dev/null`.chomp
32
- imgcat = File.expand_path("~/.iterm2/imgcat")
33
- imgcat = `which imgcat 2>/dev/null`.chomp unless File.executable?(imgcat)
34
-
35
- return false unless File.executable?(dot_cmd) && File.executable?(imgcat)
36
- return false unless ENV["TERM_PROGRAM"] == "iTerm.app"
37
-
38
- Tempfile.create(["pipeline", ".png"]) do |png|
39
- IO.popen([dot_cmd, "-Tpng", "-o", png.path], "w") { |io| io.write(dot_source) }
40
-
41
- if $?.success? && File.size(png.path) > 0
42
- system(imgcat, png.path)
43
- true
44
- else
45
- false
46
- end
47
- end
48
- end
49
-
50
- banner "Network Visualization & Introspection"
51
-
52
- # Shared RunConfig for all robots in this network
53
- shared_config = RobotLab::RunConfig.new(model: LLM[:default].model, temperature: 0.5)
54
-
55
- # Per-task RunConfig override for the writer (higher creativity)
56
- creative_config = RobotLab::RunConfig.new(temperature: 0.9)
57
-
58
- # Build robots (no LLM calls, just instances)
59
- classifier = RobotLab.build(**llm_opts, name: "classifier", system_prompt: "Classify input")
60
- analyst = RobotLab.build(**llm_opts, name: "analyst", system_prompt: "Analyze data")
61
- writer = RobotLab.build(**llm_opts, name: "writer", system_prompt: "Write summary")
62
-
63
- # Build network with RunConfig, dependencies, and per-task config
64
- network = RobotLab.create_network(name: "demo_pipeline", config: shared_config) do
65
- task :classify, classifier, depends_on: :none
66
- task :analyze, analyst, context: { depth: "deep" }, depends_on: [:classify]
67
- task :write, writer, config: creative_config, depends_on: [:analyze]
68
- end
69
-
70
- # =============================================================================
71
- # Section 1: Visualization outputs
72
- # =============================================================================
73
-
74
- section "Section 1: Visualization"
75
-
76
- mermaid = network.to_mermaid
77
- if mermaid
78
- puts "Mermaid diagram:"
79
- puts mermaid
80
- puts
81
- end
82
-
83
- dot = network.to_dot
84
- if dot
85
- puts "Graphviz DOT:"
86
- puts dot
87
- puts
88
-
89
- if render_dot_image(dot)
90
- puts "(Rendered pipeline graph above via Graphviz + imgcat)"
91
- puts
92
- end
93
- end
94
-
95
- plan = network.execution_plan
96
- if plan
97
- puts "Execution plan:"
98
- puts plan
99
- puts
100
- end
101
-
102
- ascii = network.visualize
103
- if ascii
104
- puts "ASCII visualization:"
105
- puts ascii
106
- puts
107
- end
108
-
109
- # If none of the visualization methods returned output, note it
110
- unless mermaid || dot || plan || ascii
111
- puts "(Visualization methods returned nil — depends on simple_flow version)"
112
- puts
113
- end
114
-
115
- # =============================================================================
116
- # Section 2: Robot access
117
- # =============================================================================
118
-
119
- section "Section 2: Robot Access"
120
-
121
- # Access by task name with robot() method
122
- # Note: robots are keyed by task name (the first arg to task()), not robot.name
123
- puts "network.robot('classify').name = #{network.robot('classify').name.inspect}"
124
-
125
- # Access with [] shorthand
126
- puts "network['analyze'].name = #{network['analyze'].name.inspect}"
127
-
128
- # Also works with symbols
129
- puts "network[:write].name = #{network[:write].name.inspect}"
130
- puts
131
-
132
- # List all robots
133
- puts "available_robots:"
134
- ap network.available_robots.map(&:name)
135
- puts
136
-
137
- # =============================================================================
138
- # Section 3: Dynamic robot addition
139
- # =============================================================================
140
-
141
- section "Section 3: Dynamic Robot Addition"
142
-
143
- reviewer = RobotLab.build(**llm_opts, name: "reviewer", system_prompt: "Review output")
144
- network.add_robot(reviewer)
145
-
146
- puts "After add_robot(reviewer):"
147
- ap network.available_robots.map(&:name)
148
- puts
149
-
150
- # Attempting to add a duplicate raises an error
151
- begin
152
- network.add_robot(reviewer)
153
- rescue ArgumentError => e
154
- puts "Duplicate add_robot raises: #{e.message}"
155
- end
156
- puts
157
-
158
- # =============================================================================
159
- # Section 4: Network introspection
160
- # =============================================================================
161
-
162
- section "Section 4: Network Introspection"
163
-
164
- puts "network.to_h:"
165
- ap network.to_h
166
- puts
167
-
168
- # Individual robot introspection
169
- puts "network['classify'].to_h:"
170
- ap network["classify"].to_h
171
- puts
172
-
173
- puts "network['analyze'].to_h:"
174
- ap network["analyze"].to_h
175
- puts
176
-
177
- # =============================================================================
178
- # Section 5: RunConfig Introspection
179
- # =============================================================================
180
-
181
- section "Section 5: RunConfig Introspection"
182
-
183
- puts "Network RunConfig (shared defaults):"
184
- ap network.config.to_h
185
- puts
186
-
187
- puts "Merged effective config for :write task (network + task override):"
188
- effective = shared_config.merge(creative_config)
189
- ap effective.to_h
190
- puts " model inherited from network, temperature overridden by task"
191
- puts
192
-
193
- # =============================================================================
194
- # Section 6: Broadcast
195
- # =============================================================================
196
-
197
- section "Section 6: Broadcast"
198
-
199
- broadcast_messages = []
200
-
201
- network.on_broadcast do |msg|
202
- broadcast_messages << msg
203
- end
204
-
205
- puts "Registered broadcast handler"
206
-
207
- network.broadcast(event: :demo, message: "Hello from network!")
208
-
209
- # Give async handler time to fire
210
- sleep 0.1
211
-
212
- if broadcast_messages.any?
213
- puts "Broadcast received:"
214
- ap broadcast_messages.first
215
- else
216
- puts "(No broadcast received — handler may be async)"
217
- end
218
- puts
219
-
220
- # =============================================================================
221
- # Section 7: Shared memory access
222
- # =============================================================================
223
-
224
- section "Section 7: Shared Network Memory"
225
-
226
- puts "network.memory is a #{network.memory.class}"
227
- puts "network.memory.network_name = #{network.memory.network_name.inspect}"
228
-
229
- # Robots in a network share this memory during run()
230
- network.memory.set(:demo_key, "shared value")
231
- puts "network.memory.get(:demo_key) = #{network.memory.get(:demo_key).inspect}"
232
- puts
233
-
234
- puts "network.memory.to_h:"
235
- ap network.memory.to_h
236
- puts
237
-
238
- hr
239
- puts "All sections completed without any LLM calls."
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 12: Message Bus — Converging on a Funny Robot Joke
5
- #
6
- # Alice tasks Bob to tell a robot joke. Alice uses her LLM to
7
- # evaluate each joke. If she's not impressed she asks Bob to try
8
- # again. The loop continues until Alice approves or MAX_ATTEMPTS.
9
- #
10
- # Usage:
11
- # bundle exec ruby examples/12_message_bus.rb
12
-
13
- require_relative "common"
14
-
15
- MAX_ATTEMPTS = 5
16
-
17
- class Comedian < RobotLab::Robot
18
- TEMP_START = 0.2
19
- TEMP_STEP = 0.2
20
-
21
- def initialize(bus:)
22
- super(name: "bob", **llm_opts, template: :comedian, bus: bus, temperature: TEMP_START)
23
- @attempts = 0
24
- on_message do |message|
25
- @attempts += 1
26
- temp = [TEMP_START + TEMP_STEP * (@attempts - 1), 1.0].min
27
- with_temperature(temp)
28
- joke = run(message.content.to_s).reply.strip
29
- puts " Bob [##{@attempts}, t=#{"%.1f" % temp}]: #{joke}"
30
- send_reply(to: message.from.to_sym, content: joke, in_reply_to: message.key)
31
- end
32
- end
33
-
34
- attr_reader :attempts
35
- end
36
-
37
- class ComedyCritic < RobotLab::Robot
38
- def initialize(bus:)
39
- super(name: "alice", **llm_opts, template: :comedy_critic, bus: bus)
40
- @accepted = false
41
- @rounds = 0
42
- on_message do |message|
43
- @rounds += 1
44
- verdict = run("Evaluate this joke:\n\n#{message.content}").reply.strip
45
- puts " Alice: #{verdict}"
46
- puts
47
- @accepted = verdict.start_with?("FUNNY")
48
- send_message(to: :bob, content: "Not funny enough. Try again.") unless @accepted || @rounds >= MAX_ATTEMPTS
49
- end
50
- end
51
-
52
- attr_reader :accepted
53
- end
54
-
55
- bus = TypedBus::MessageBus.new
56
- bob = Comedian.new(bus: bus)
57
- alice = ComedyCritic.new(bus: bus)
58
-
59
- banner "Tell Me a Funny Robot Joke"
60
-
61
- puts "Alice: Tell me a funny robot joke."
62
- puts
63
- alice.send_message(to: :bob, content: "Tell me a funny robot joke.")
64
-
65
- hr
66
- puts "Attempts: #{bob.attempts} / #{MAX_ATTEMPTS}"
67
- puts "Accepted: #{alice.accepted}"
data/examples/13_spawn.rb DELETED
@@ -1,85 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 13: Spawning Robots — Dynamic Specialist Creation
5
- #
6
- # A dispatcher robot receives a question, decides what kind of
7
- # specialist is needed, spawns one on the fly, and hands off
8
- # the work. The spawned robot replies via the shared message bus.
9
- #
10
- # This demonstrates Werner's "objects that create new objects to
11
- # deal with problems nobody anticipated."
12
- #
13
- # Usage:
14
- # bundle exec ruby examples/13_spawn.rb
15
-
16
- require_relative "common"
17
-
18
- QUESTIONS = [
19
- "Why did the Roman Empire fall?",
20
- "Write a haiku about recursion.",
21
- "What is the square root of 144?",
22
- ].freeze
23
-
24
- class Dispatcher < RobotLab::Robot
25
- attr_reader :spawned
26
-
27
- def initialize(bus: nil)
28
- super(name: "dispatcher", **llm_opts, template: :dispatcher, bus: bus)
29
- @spawned = {}
30
- @pending = {}
31
-
32
- on_message do |message|
33
- puts " Dispatcher <- :#{message.from} replied"
34
- puts " | #{message.content.to_s.lines.first&.strip}"
35
- @pending.delete(message.from)
36
- end
37
- end
38
-
39
- def dispatch(question)
40
- # Ask the LLM which specialist to spawn
41
- plan = run(question).reply.strip
42
- role, instruction = plan.split("\n", 2)
43
- role = role.strip.downcase.gsub(/\s+/, "_")
44
- instruction = instruction&.strip || "You are a helpful #{role}."
45
-
46
- puts " Dispatcher -> spawn :#{role}"
47
- puts " | #{instruction}"
48
-
49
- # Spawn the specialist (reuse if already spawned)
50
- specialist = @spawned[role] ||= spawn(
51
- name: role,
52
- **llm_opts,
53
- system_prompt: instruction
54
- )
55
-
56
- # Record what we're waiting for
57
- @pending[role] = question
58
-
59
- # Ask the specialist to work on the question
60
- specialist.send_message(to: :dispatcher, content:
61
- specialist.run(question).reply.strip
62
- )
63
- end
64
-
65
- def done?
66
- @pending.empty?
67
- end
68
- end
69
-
70
- # ── main ──────────────────────────────────────────────────────
71
-
72
- dispatcher = Dispatcher.new
73
-
74
- banner "Spawning Specialist Robots"
75
-
76
- QUESTIONS.each_with_index do |question, i|
77
- puts
78
- puts "Question #{i + 1}: #{question}"
79
- dispatcher.dispatch(question)
80
- end
81
-
82
- puts
83
- hr
84
- puts "Specialists spawned: #{dispatcher.spawned.keys.join(', ')}"
85
- puts "Total robots on bus: #{dispatcher.spawned.size + 1}"
@@ -1 +0,0 @@
1
- export ROBOT_LAB_TEMPLATE_PATH="${PWD}/prompts"
@@ -1 +0,0 @@
1
- /output/
@@ -1 +0,0 @@
1
- execute the open_mic.rb file
@@ -1,149 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # ── Comic Tools ─────────────────────────────────────────────
4
- #
5
- # Each tool accesses the owning robot via the `robot` accessor
6
- # inherited from RobotLab::Tool.
7
-
8
- class ReinventStyle < RobotLab::Tool
9
- description "Completely rewrite your comedy persona and approach. " \
10
- "Use when your current style is clearly not working. " \
11
- "Be bold — try something totally different. " \
12
- "The new style takes effect on your next bit."
13
-
14
- parameter :new_persona, type: "string",
15
- description: "Your new comedy persona, style, and approach. " \
16
- "Be specific: what kind of humor, what voice, what attitude."
17
-
18
- def execute(new_persona:)
19
- robot.pending_reinvention = new_persona
20
- robot.style_changes += 1
21
- robot.display&.comic_tool("[reinvent_style] -> #{new_persona[0..70]}...")
22
- "Style reinvention accepted: #{new_persona}. " \
23
- "Commit to this new approach starting now."
24
- end
25
- end
26
-
27
- class AdjustEnergy < RobotLab::Tool
28
- description "Adjust your creative energy level. " \
29
- "Higher (0.8-1.0) = wilder, riskier, more unpredictable. " \
30
- "Lower (0.2-0.4) = tighter, more controlled, precise."
31
-
32
- parameter :level, type: "number",
33
- description: "Energy level from 0.1 (very controlled) to 1.0 (unhinged)"
34
- parameter :reason, type: "string",
35
- description: "Why you're adjusting", required: false
36
-
37
- def execute(level:, reason: "tactical adjustment")
38
- clamped = [[level.to_f, 0.1].max, 1.0].min
39
- robot.with_temperature(clamped)
40
- robot.display&.comic_tool("[adjust_energy] -> %.1f (%s)" % [clamped, reason])
41
- "Energy adjusted to #{clamped}. Reason: #{reason}"
42
- end
43
- end
44
-
45
- class GetCoaching < RobotLab::Tool
46
- description "Summon a comedy coach backstage for quick advice. " \
47
- "Use when you're struggling with the crowd and need " \
48
- "an outside perspective on what to try next."
49
-
50
- parameter :situation, type: "string",
51
- description: "Describe what's happening and what you need help with"
52
-
53
- def execute(situation:)
54
- @coaches ||= {}
55
- coach = @coaches["comedy_coach"] ||= begin
56
- robot.coaches_spawned += 1
57
- robot.spawn(
58
- name: "comedy_coach",
59
- **llm_opts,
60
- system_prompt:
61
- "You are a veteran comedy coach backstage at a live show. " \
62
- "A comedian is struggling and needs quick, actionable advice. " \
63
- "Be direct and specific. One paragraph max. Tell them " \
64
- "exactly what to do differently in their next bit."
65
- )
66
- end
67
- robot.display&.working("Comic", "backstage with the coach")
68
- advice = coach.run(situation).reply.strip
69
- robot.display&.comic_tool("[get_coaching] -> #{advice[0..70]}...")
70
- advice
71
- rescue => e
72
- robot.display&.comic_tool("[get_coaching] ERROR: #{e.message}")
73
- "Coach unavailable right now. Trust your instincts."
74
- end
75
- end
76
-
77
-
78
- # ── The Comic ────────────────────────────────────────────────
79
- #
80
- # Has three tools, all with side effects on the robot itself:
81
- #
82
- # reinvent_style — queues a system prompt rewrite (applied next round)
83
- # adjust_energy — changes the comic's temperature immediately
84
- # get_coaching — spawns a comedy coach on the shared bus
85
- #
86
- # The LLM decides when to call them. The developer provides the
87
- # mechanism; the robot provides the judgment.
88
- #
89
- # Listens on personal :comic channel for heckler feedback.
90
- # Publishes performances to the shared :room channel.
91
- #
92
- class Comic < RobotLab::Robot
93
- attr_accessor :round, :style_changes, :coaches_spawned,
94
- :pending_reinvention, :display
95
-
96
- def initialize(bus:, display:)
97
- @round = 0
98
- @style_changes = 0
99
- @coaches_spawned = 0
100
- @pending_reinvention = nil
101
- @display = display
102
-
103
- super(
104
- name: "comic",
105
- **llm_opts,
106
- template: :open_mic_comic,
107
- bus: bus,
108
- local_tools: [
109
- ReinventStyle.new(robot: self),
110
- AdjustEnergy.new(robot: self),
111
- GetCoaching.new(robot: self)
112
- ]
113
- )
114
-
115
- # Listen on personal channel for heckler feedback
116
- on_message do |message|
117
- next unless message.from == "heckler"
118
-
119
- @round += 1
120
-
121
- # Build the prompt, injecting any queued style reinvention.
122
- # This embeds self-modification in the user prompt rather than
123
- # rewriting system messages, avoiding chat message ordering issues.
124
- prompt = "Round #{@round}."
125
-
126
- if @pending_reinvention
127
- prompt += "\n\nSTYLE REINVENTION: You are now #{@pending_reinvention}. " \
128
- "Commit fully to this new style. Abandon your previous approach."
129
- @pending_reinvention = nil
130
- end
131
-
132
- prompt += "\n\nThe heckler just shouted: \"#{message.content}\"\n\n" \
133
- "Process their feedback. If your material isn't landing, " \
134
- "use your tools to adapt — reinvent your style, adjust " \
135
- "your energy, or get coaching. Then deliver your next bit."
136
-
137
- # tools: :inherit — without it run() defaults to :none and the comic's
138
- # three self-modification tools are never offered to the model, which
139
- # kills the whole point of the demo.
140
- result = run(prompt, tools: :inherit)
141
- bit = result.reply.strip
142
-
143
- @display.comic("Comic [Round #{@round}]", bit)
144
-
145
- # Publish to the room — heckler and scout both pick it up
146
- send_message(to: :room, content: "ROUND #{@round}: #{bit}")
147
- end
148
- end
149
- end