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,323 +0,0 @@
1
- # Streaming Responses
2
-
3
- Stream LLM responses in real-time for better user experience.
4
-
5
- RobotLab has exactly two ways to receive streaming content, and both hand you a
6
- `RubyLLM::Chunk`:
7
-
8
- 1. **`on_content:`** — a callback wired at build time (or through `RunConfig`) that fires on *every* `run`
9
- 2. **A block passed to `run`** — `robot.run(msg) { |chunk| ... }`, for one-off streaming
10
-
11
- > [!IMPORTANT]
12
- > Read the content off the chunk with **`chunk.content`**. `RubyLLM::Chunk`
13
- > has no `#text` method — `chunk.text` raises `NoMethodError`.
14
-
15
- ## Streaming via `on_content:`
16
-
17
- Wire the callback once at build time; it fires on every subsequent `run`:
18
-
19
- ```ruby
20
- robot = RobotLab.build(
21
- name: "storyteller",
22
- system_prompt: "You are a creative storyteller.",
23
- on_content: ->(chunk) { print chunk.content }
24
- )
25
-
26
- result = robot.run("Tell me a story about a brave robot")
27
- puts
28
- puts result.last_text_content
29
- ```
30
-
31
- ## Streaming via a Block on `run`
32
-
33
- For one-off streaming, pass a block straight to `run`:
34
-
35
- ```ruby
36
- robot = RobotLab.build(
37
- name: "factbot",
38
- system_prompt: "You are concise. Answer in one sentence."
39
- )
40
-
41
- robot.run("What year was Ruby created?") { |chunk| print chunk.content }
42
- ```
43
-
44
- The block goes to the same place `on_content:` does — no need to reach for
45
- `robot.chat.ask` to get streaming, and reaching for it skips memory resolution,
46
- tool resolution, hooks, and budget accounting.
47
-
48
- ## Using Both Together
49
-
50
- When a robot has a stored `on_content:` *and* a block is passed to `run`, both
51
- fire for every chunk. The **stored callback fires first**, then the block:
52
-
53
- ```ruby
54
- stored_log = []
55
- block_log = []
56
-
57
- robot = RobotLab.build(
58
- name: "combo",
59
- system_prompt: "You are concise.",
60
- on_content: ->(chunk) { stored_log << chunk.content }
61
- )
62
-
63
- robot.run("What is Matz's full name?") { |chunk| block_log << chunk.content }
64
-
65
- stored_log == block_log # => true — both saw the same chunks
66
- ```
67
-
68
- ## Streaming via RunConfig
69
-
70
- `on_content` is a `RunConfig` field, so it participates in the config cascade:
71
-
72
- ```ruby
73
- config = RobotLab::RunConfig.new(
74
- model: "claude-sonnet-4",
75
- on_content: ->(chunk) { print chunk.content }
76
- )
77
-
78
- robot = RobotLab.build(
79
- name: "config_bot",
80
- system_prompt: "You are concise.",
81
- config: config
82
- )
83
-
84
- robot.run("Who designed the Ruby programming language?")
85
- ```
86
-
87
- > [!WARNING]
88
- > `on_content` is read from the **robot's own** config when the robot is
89
- > constructed. A network-level `config:` propagates only `mcp` and `tools` to
90
- > member robots — it will **not** supply `on_content` to them. Put the callback
91
- > on each robot (or on the `config:` you pass to that robot).
92
-
93
- ## Web Integration
94
-
95
- ### Rails Action Cable
96
-
97
- ```ruby
98
- class ChatChannel < ApplicationCable::Channel
99
- def receive(data)
100
- robot = RobotLab.build(
101
- name: "chat_bot",
102
- system_prompt: "You are a helpful chat assistant.",
103
- on_content: ->(chunk) {
104
- transmit({ event: "text.delta", content: chunk.content })
105
- }
106
- )
107
-
108
- robot.run(data["message"])
109
- transmit({ event: "run.completed" })
110
- end
111
- end
112
- ```
113
-
114
- ### Server-Sent Events
115
-
116
- ```ruby
117
- class StreamController < ApplicationController
118
- include ActionController::Live
119
-
120
- def create
121
- response.headers["Content-Type"] = "text/event-stream"
122
-
123
- robot = RobotLab.build(
124
- name: "stream_bot",
125
- system_prompt: "You are helpful."
126
- )
127
-
128
- robot.run(params[:message]) do |chunk|
129
- response.stream.write("data: #{chunk.content}\n\n")
130
- end
131
-
132
- response.stream.write("data: [DONE]\n\n")
133
- ensure
134
- response.stream.close
135
- end
136
- end
137
- ```
138
-
139
- ### WebSocket
140
-
141
- ```ruby
142
- # Using Faye WebSocket
143
- ws.on :message do |msg|
144
- robot.run(msg.data) { |chunk| ws.send(chunk.content) }
145
- end
146
- ```
147
-
148
- ## Progress Tracking
149
-
150
- Chunk callbacks are the right place to count characters. Tool activity is
151
- tracked separately, through the robot's `on_tool_call:` / `on_tool_result:`
152
- callbacks:
153
-
154
- ```ruby
155
- class StreamProgress
156
- attr_reader :chars, :tools
157
-
158
- def initialize
159
- @chars = 0
160
- @tools = 0
161
- end
162
-
163
- # Returns the streaming block to hand to run().
164
- def content_callback
165
- ->(chunk) {
166
- @chars += chunk.content.to_s.length
167
- print "\rReceived #{@chars} characters..."
168
- }
169
- end
170
-
171
- def tool_callback
172
- ->(tool_call) {
173
- @tools += 1
174
- puts "\nTool call ##{@tools}: #{tool_call.name}"
175
- }
176
- end
177
- end
178
-
179
- progress = StreamProgress.new
180
-
181
- robot = RobotLab.build(
182
- name: "assistant",
183
- system_prompt: "You are helpful.",
184
- local_tools: [WeatherTool],
185
- on_content: progress.content_callback,
186
- on_tool_call: progress.tool_callback
187
- )
188
-
189
- robot.run("Process this complex request", tools: :inherit)
190
- puts "\nTotal: #{progress.chars} chars, #{progress.tools} tool calls"
191
- ```
192
-
193
- > [!NOTE]
194
- > `chunk.content` can be `nil` on chunks that carry only metadata (tool-call
195
- > deltas, usage). Guard with `to_s` when accumulating, as above.
196
-
197
- > [!WARNING]
198
- > `run` defaults to `tools: :none`, so the tool callback above never fires
199
- > unless you pass `tools: :inherit` (or an explicit allowlist). See
200
- > [Using Tools](using-tools.md#runtime-tool-filtering).
201
-
202
- ## Without Streaming
203
-
204
- When streaming is not needed, call `run` with no `on_content:` and no block:
205
-
206
- ```ruby
207
- result = robot.run("Hello!")
208
- puts result.last_text_content
209
- ```
210
-
211
- ## Tool Callbacks
212
-
213
- `on_tool_call:` and `on_tool_result:` are constructor/`RunConfig` callbacks
214
- alongside `on_content:`. Each receives **one** argument:
215
-
216
- ```ruby
217
- robot = RobotLab.build(
218
- name: "assistant",
219
- system_prompt: "You are helpful.",
220
- local_tools: [WeatherTool],
221
- on_tool_call: ->(tool_call) { puts "\n[Tool] Calling: #{tool_call.name}" },
222
- on_tool_result: ->(result) { puts "[Tool] returned: #{result}" }
223
- )
224
-
225
- robot.run("What's the weather in Tokyo?", tools: :inherit)
226
- ```
227
-
228
- Note that `on_tool_result` does **not** receive the originating tool call —
229
- only the result. If you need to correlate the two, record the call in
230
- `on_tool_call` and pair them up yourself.
231
-
232
- > [!NOTE]
233
- > RobotLab installs these two callbacks on the underlying chat's
234
- > `on_tool_call` / `on_tool_result` hooks, which RubyLLM 1.16 has deprecated.
235
- > Building a robot with either callback therefore prints a RubyLLM deprecation
236
- > warning. The callbacks themselves work; the warning comes from the layer
237
- > below.
238
-
239
- ## Legacy RubyLLM Chat Callbacks
240
-
241
- `Robot` also exposes RubyLLM's chat-level callbacks
242
- (`on_new_message`, `on_end_message`, `on_tool_call`, `on_tool_result`).
243
-
244
- > [!CAUTION]
245
- > All four are **deprecated in RubyLLM 1.16** and slated for removal in
246
- > RubyLLM 2.0. Registering one emits a deprecation warning naming its
247
- > replacement: `before_message`, `after_message`, `before_tool_call`, and
248
- > `after_tool_result`. Prefer RobotLab's `on_content:` /
249
- > `on_tool_call:` / `on_tool_result:` constructor callbacks instead.
250
-
251
- Their arities are not what you might expect:
252
-
253
- | Callback | Replacement | Arguments received |
254
- |----------|-------------|--------------------|
255
- | `on_new_message` | `before_message` | **none** |
256
- | `on_end_message` | `after_message` | the completed `RubyLLM::Message` |
257
- | `on_tool_call` | `before_tool_call` | the `ToolCall` |
258
- | `on_tool_result` | `after_tool_result` | the **result only** |
259
-
260
- > [!WARNING]
261
- > `on_new_message` is **not** a streaming hook. It fires once when a message
262
- > begins and is invoked with **zero arguments** — a block written as
263
- > `do |message| ... end` receives `nil`, so `message.content` raises
264
- > `NoMethodError: undefined method 'content' for nil`. Use `on_content:` or a
265
- > `run` block for content.
266
-
267
- ```ruby
268
- # Correct usage of the legacy hooks (still deprecated):
269
- robot.chat.on_new_message { puts "--- message starting ---" } # no argument
270
- robot.chat.on_end_message { |message| puts "Length: #{message.content&.length}" }
271
- robot.chat.on_tool_result { |result| puts "Tool returned: #{result}" }
272
- ```
273
-
274
- ## Best Practices
275
-
276
- ### 1. Wire streaming before the run
277
-
278
- `on_content:` belongs in the constructor (or the `config:` you pass it); a
279
- `run` block belongs on the call itself. There is no "register then run" step.
280
-
281
- ### 2. Handle errors inside the callback
282
-
283
- An exception raised in a streaming callback propagates out of `run` and aborts
284
- the turn. Swallow client-side failures you can survive:
285
-
286
- ```ruby
287
- robot = RobotLab.build(
288
- name: "broadcaster",
289
- system_prompt: "You are helpful.",
290
- on_content: ->(chunk) do
291
- broadcast(chunk.content)
292
- rescue BroadcastError => e
293
- # Client disconnected, but let the run finish.
294
- RobotLab.config.logger.warn("Broadcast failed: #{e.message}")
295
- end
296
- )
297
- ```
298
-
299
- ### 3. Clean up resources
300
-
301
- ```ruby
302
- begin
303
- robot.run("Hello") { |chunk| stream_to_client(chunk.content) }
304
- ensure
305
- close_stream_connection
306
- end
307
- ```
308
-
309
- ## What About `RobotLab::Streaming`?
310
-
311
- `RobotLab::Streaming::Context`, `Streaming::Events`, and
312
- `Streaming::SequenceCounter` exist in the codebase and are documented in the
313
- [API reference](../api/streaming/index.md), but **nothing in `Robot` or
314
- `Network` constructs or publishes to them**. They are a standalone event-object
315
- toolkit you would have to drive yourself — not the path that `run` takes. For
316
- actual token streaming, use `on_content:` or a `run` block as shown above.
317
-
318
- ## Next Steps
319
-
320
- - [Building Robots](building-robots.md) - Robot creation
321
- - [Creating Networks](creating-networks.md) - Network patterns
322
- - [examples/05_streaming.rb](https://github.com/MadBomber/robot_lab/blob/main/examples/05_streaming.rb) - Runnable streaming example
323
- - [API Reference: Streaming](../api/streaming/index.md) - The standalone `Streaming::*` event objects