robot_lab 0.0.1 → 0.0.6

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +9 -9
  3. data/.irbrc +6 -0
  4. data/CHANGELOG.md +140 -0
  5. data/README.md +263 -48
  6. data/Rakefile +71 -1
  7. data/docs/api/core/index.md +53 -46
  8. data/docs/api/core/memory.md +200 -154
  9. data/docs/api/core/network.md +13 -3
  10. data/docs/api/core/robot.md +490 -130
  11. data/docs/api/core/state.md +55 -73
  12. data/docs/api/core/tool.md +205 -209
  13. data/docs/api/index.md +7 -28
  14. data/docs/api/mcp/client.md +119 -48
  15. data/docs/api/mcp/index.md +75 -60
  16. data/docs/api/mcp/server.md +120 -136
  17. data/docs/api/mcp/transports.md +172 -184
  18. data/docs/api/messages/index.md +35 -20
  19. data/docs/api/messages/text-message.md +67 -21
  20. data/docs/api/messages/tool-call-message.md +80 -41
  21. data/docs/api/messages/tool-result-message.md +119 -50
  22. data/docs/api/messages/user-message.md +48 -24
  23. data/docs/api/streaming/context.md +157 -74
  24. data/docs/api/streaming/events.md +114 -166
  25. data/docs/api/streaming/index.md +74 -72
  26. data/docs/architecture/core-concepts.md +360 -116
  27. data/docs/architecture/index.md +97 -59
  28. data/docs/architecture/message-flow.md +138 -129
  29. data/docs/architecture/network-orchestration.md +197 -50
  30. data/docs/architecture/robot-execution.md +199 -146
  31. data/docs/architecture/state-management.md +255 -187
  32. data/docs/concepts.md +311 -49
  33. data/docs/examples/basic-chat.md +89 -77
  34. data/docs/examples/index.md +222 -47
  35. data/docs/examples/mcp-server.md +207 -203
  36. data/docs/examples/multi-robot-network.md +129 -35
  37. data/docs/examples/rails-application.md +159 -160
  38. data/docs/examples/tool-usage.md +295 -204
  39. data/docs/getting-started/configuration.md +347 -154
  40. data/docs/getting-started/index.md +1 -1
  41. data/docs/getting-started/installation.md +22 -13
  42. data/docs/getting-started/quick-start.md +166 -121
  43. data/docs/guides/building-robots.md +418 -212
  44. data/docs/guides/creating-networks.md +143 -24
  45. data/docs/guides/index.md +0 -5
  46. data/docs/guides/mcp-integration.md +152 -113
  47. data/docs/guides/memory.md +220 -164
  48. data/docs/guides/rails-integration.md +244 -162
  49. data/docs/guides/streaming.md +137 -187
  50. data/docs/guides/using-tools.md +259 -212
  51. data/docs/index.md +46 -41
  52. data/examples/01_simple_robot.rb +6 -9
  53. data/examples/02_tools.rb +6 -9
  54. data/examples/03_network.rb +19 -17
  55. data/examples/04_mcp.rb +5 -8
  56. data/examples/05_streaming.rb +5 -8
  57. data/examples/06_prompt_templates.rb +42 -37
  58. data/examples/07_network_memory.rb +13 -14
  59. data/examples/08_llm_config.rb +169 -0
  60. data/examples/09_chaining.rb +262 -0
  61. data/examples/10_memory.rb +331 -0
  62. data/examples/11_network_introspection.rb +253 -0
  63. data/examples/12_message_bus.rb +74 -0
  64. data/examples/13_spawn.rb +90 -0
  65. data/examples/14_rusty_circuit/comic.rb +143 -0
  66. data/examples/14_rusty_circuit/display.rb +203 -0
  67. data/examples/14_rusty_circuit/heckler.rb +63 -0
  68. data/examples/14_rusty_circuit/open_mic.rb +123 -0
  69. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
  70. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
  71. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
  72. data/examples/14_rusty_circuit/scout.rb +156 -0
  73. data/examples/14_rusty_circuit/scout_notes.md +89 -0
  74. data/examples/14_rusty_circuit/show.log +234 -0
  75. data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
  76. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
  77. data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
  78. data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
  79. data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
  80. data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
  81. data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
  82. data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
  83. data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
  84. data/examples/15_memory_network_and_bus/output/memory.json +13 -0
  85. data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
  86. data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
  87. data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
  88. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
  89. data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
  90. data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
  91. data/examples/16_writers_room/display.rb +158 -0
  92. data/examples/16_writers_room/output/.gitignore +2 -0
  93. data/examples/16_writers_room/output/opus_001.md +263 -0
  94. data/examples/16_writers_room/output/opus_001_notes.log +470 -0
  95. data/examples/16_writers_room/prompts/writer.md +37 -0
  96. data/examples/16_writers_room/room.rb +150 -0
  97. data/examples/16_writers_room/tools.rb +162 -0
  98. data/examples/16_writers_room/writer.rb +121 -0
  99. data/examples/16_writers_room/writers_room.rb +162 -0
  100. data/examples/README.md +197 -0
  101. data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
  102. data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
  103. data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
  104. data/examples/prompts/comedian.md +6 -0
  105. data/examples/prompts/comedy_critic.md +10 -0
  106. data/examples/prompts/configurable.md +9 -0
  107. data/examples/prompts/dispatcher.md +12 -0
  108. data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
  109. data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
  110. data/examples/prompts/frontmatter_mcp_test.md +9 -0
  111. data/examples/prompts/frontmatter_named_test.md +5 -0
  112. data/examples/prompts/frontmatter_tools_test.md +6 -0
  113. data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
  114. data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
  115. data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
  116. data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
  117. data/examples/prompts/llm_config_demo.md +20 -0
  118. data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
  119. data/examples/prompts/os_advocate.md +13 -0
  120. data/examples/prompts/os_chief.md +13 -0
  121. data/examples/prompts/os_editor.md +13 -0
  122. data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
  123. data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
  124. data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
  125. data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
  126. data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
  127. data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
  128. data/lib/robot_lab/ask_user.rb +75 -0
  129. data/lib/robot_lab/config/defaults.yml +121 -0
  130. data/lib/robot_lab/config.rb +183 -0
  131. data/lib/robot_lab/error.rb +6 -0
  132. data/lib/robot_lab/mcp/client.rb +1 -1
  133. data/lib/robot_lab/memory.rb +10 -34
  134. data/lib/robot_lab/network.rb +13 -20
  135. data/lib/robot_lab/robot/bus_messaging.rb +239 -0
  136. data/lib/robot_lab/robot/mcp_management.rb +88 -0
  137. data/lib/robot_lab/robot/template_rendering.rb +130 -0
  138. data/lib/robot_lab/robot.rb +240 -330
  139. data/lib/robot_lab/robot_message.rb +44 -0
  140. data/lib/robot_lab/robot_result.rb +1 -0
  141. data/lib/robot_lab/run_config.rb +184 -0
  142. data/lib/robot_lab/state_proxy.rb +2 -12
  143. data/lib/robot_lab/streaming/context.rb +1 -1
  144. data/lib/robot_lab/task.rb +8 -1
  145. data/lib/robot_lab/tool.rb +108 -172
  146. data/lib/robot_lab/tool_config.rb +1 -1
  147. data/lib/robot_lab/tool_manifest.rb +2 -18
  148. data/lib/robot_lab/utils.rb +39 -0
  149. data/lib/robot_lab/version.rb +1 -1
  150. data/lib/robot_lab.rb +89 -57
  151. data/mkdocs.yml +0 -11
  152. metadata +121 -135
  153. data/docs/api/adapters/anthropic.md +0 -121
  154. data/docs/api/adapters/gemini.md +0 -133
  155. data/docs/api/adapters/index.md +0 -104
  156. data/docs/api/adapters/openai.md +0 -134
  157. data/docs/api/history/active-record-adapter.md +0 -195
  158. data/docs/api/history/config.md +0 -191
  159. data/docs/api/history/index.md +0 -132
  160. data/docs/api/history/thread-manager.md +0 -144
  161. data/docs/guides/history.md +0 -359
  162. data/examples/prompts/assistant/user.txt.erb +0 -1
  163. data/examples/prompts/billing/user.txt.erb +0 -1
  164. data/examples/prompts/classifier/user.txt.erb +0 -1
  165. data/examples/prompts/entity_extractor/user.txt.erb +0 -3
  166. data/examples/prompts/escalation/user.txt.erb +0 -34
  167. data/examples/prompts/general/user.txt.erb +0 -1
  168. data/examples/prompts/github_assistant/user.txt.erb +0 -1
  169. data/examples/prompts/helper/user.txt.erb +0 -1
  170. data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
  171. data/examples/prompts/order_support/user.txt.erb +0 -22
  172. data/examples/prompts/product_support/user.txt.erb +0 -32
  173. data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
  174. data/examples/prompts/synthesizer/user.txt.erb +0 -15
  175. data/examples/prompts/technical/user.txt.erb +0 -1
  176. data/examples/prompts/triage/user.txt.erb +0 -17
  177. data/lib/robot_lab/adapters/anthropic.rb +0 -163
  178. data/lib/robot_lab/adapters/base.rb +0 -85
  179. data/lib/robot_lab/adapters/gemini.rb +0 -193
  180. data/lib/robot_lab/adapters/openai.rb +0 -159
  181. data/lib/robot_lab/adapters/registry.rb +0 -81
  182. data/lib/robot_lab/configuration.rb +0 -143
  183. data/lib/robot_lab/errors.rb +0 -70
  184. data/lib/robot_lab/history/active_record_adapter.rb +0 -146
  185. data/lib/robot_lab/history/config.rb +0 -115
  186. data/lib/robot_lab/history/thread_manager.rb +0 -93
  187. data/lib/robot_lab/robotic_model.rb +0 -324
@@ -1,171 +1,254 @@
1
- # StreamingContext
1
+ # Streaming::Context
2
2
 
3
- Manages streaming state during execution.
3
+ Manages streaming event publishing with automatic sequencing, timestamping, and ID generation.
4
4
 
5
5
  ## Class: `RobotLab::Streaming::Context`
6
6
 
7
7
  ```ruby
8
- context = RobotLab::Streaming::Context.new(callback: ->(e) { handle(e) })
8
+ context = RobotLab::Streaming::Context.new(
9
+ run_id: "run_123",
10
+ message_id: "msg_456",
11
+ scope: "network",
12
+ publish: ->(event) { broadcast(event) }
13
+ )
14
+
15
+ context.publish_event(event: "text.delta", data: { delta: "Hello" })
9
16
  ```
10
17
 
11
18
  ## Constructor
12
19
 
13
20
  ```ruby
14
- Context.new(callback:, robot: nil, network: nil)
21
+ Context.new(
22
+ run_id:,
23
+ message_id:,
24
+ scope:,
25
+ publish:,
26
+ parent_run_id: nil,
27
+ sequence_counter: nil
28
+ )
15
29
  ```
16
30
 
17
31
  **Parameters:**
18
32
 
19
- | Name | Type | Description |
20
- |------|------|-------------|
21
- | `callback` | `Proc` | Event handler |
22
- | `robot` | `Robot`, `nil` | Current robot |
23
- | `network` | `NetworkRun`, `nil` | Network context |
33
+ | Name | Type | Default | Description |
34
+ |------|------|---------|-------------|
35
+ | `run_id` | `String` | required | Unique identifier for this run |
36
+ | `message_id` | `String` | required | Current message identifier |
37
+ | `scope` | `String`, `Symbol` | required | Context scope (e.g., `"network"`, `"robot"`) |
38
+ | `publish` | `Proc` | required | Callback invoked with each event hash |
39
+ | `parent_run_id` | `String`, `nil` | `nil` | Parent run identifier for nested contexts |
40
+ | `sequence_counter` | `SequenceCounter`, `nil` | `nil` | Shared sequence counter (creates new one if nil) |
24
41
 
25
42
  ## Attributes
26
43
 
27
- ### callback
44
+ ### run_id
28
45
 
29
46
  ```ruby
30
- context.callback # => Proc
47
+ context.run_id # => String
31
48
  ```
32
49
 
33
- The event handler callback.
50
+ The unique run identifier for this context.
34
51
 
35
- ### robot
52
+ ### parent_run_id
36
53
 
37
54
  ```ruby
38
- context.robot # => Robot | nil
55
+ context.parent_run_id # => String | nil
39
56
  ```
40
57
 
41
- The currently executing robot.
58
+ The parent run identifier. Set when this is a child context created by `create_child_context`.
42
59
 
43
- ### network
60
+ ### message_id
44
61
 
45
62
  ```ruby
46
- context.network # => NetworkRun | nil
63
+ context.message_id # => String
47
64
  ```
48
65
 
49
- The network run context.
66
+ The current message identifier.
50
67
 
51
- ### buffer
68
+ ### scope
52
69
 
53
70
  ```ruby
54
- context.buffer # => String
71
+ context.scope # => String
55
72
  ```
56
73
 
57
- Accumulated text content.
74
+ The context scope (converted to string). Typically `"network"` or `"robot"`.
75
+
76
+ ## Methods
58
77
 
59
- ### tool_calls
78
+ ### publish_event
60
79
 
61
80
  ```ruby
62
- context.tool_calls # => Array<ToolCallMessage>
81
+ chunk = context.publish_event(event:, data: {})
63
82
  ```
64
83
 
65
- Tool calls received during streaming.
84
+ Publish a streaming event. The event is wrapped in a chunk with automatic sequencing, timestamping, and context metadata injection.
66
85
 
67
- ## Methods
86
+ **Parameters:**
87
+
88
+ | Name | Type | Description |
89
+ |------|------|-------------|
90
+ | `event` | `String` | Event type (e.g., `"text.delta"`, `"run.started"`) |
91
+ | `data` | `Hash` | Event payload (default: `{}`) |
92
+
93
+ **Returns:** The constructed event chunk hash.
68
94
 
69
- ### emit
95
+ The chunk structure:
70
96
 
71
97
  ```ruby
72
- context.emit(event)
98
+ {
99
+ event: "text.delta",
100
+ data: {
101
+ delta: "Hello", # from data parameter
102
+ run_id: "run_123", # injected from context
103
+ message_id: "msg_456", # injected from context
104
+ scope: "robot" # injected from context
105
+ },
106
+ timestamp: 1707900000000, # millisecond Unix timestamp
107
+ sequence_number: 1, # monotonically increasing
108
+ id: "publish-1:text.delta" # unique event ID
109
+ }
73
110
  ```
74
111
 
75
- Send an event to the callback.
112
+ If the publish callback raises an error, it is caught and logged as a warning via `RobotLab.config.logger`. The chunk is still returned.
76
113
 
77
- ### emit_text
114
+ ### create_child_context
78
115
 
79
116
  ```ruby
80
- context.emit_text(text)
117
+ child = context.create_child_context(robot_run_id)
81
118
  ```
82
119
 
83
- Emit a text delta event.
120
+ Create a child context for a nested robot execution. The child shares the same publish callback and sequence counter, ensuring events are ordered globally across the parent and child.
121
+
122
+ **Parameters:**
123
+
124
+ | Name | Type | Description |
125
+ |------|------|-------------|
126
+ | `robot_run_id` | `String` | Run ID for the child context |
127
+
128
+ **Returns:** A new `Context` with:
129
+ - `run_id` set to `robot_run_id`
130
+ - `parent_run_id` set to the current context's `run_id`
131
+ - `scope` set to `"robot"`
132
+ - A new `message_id` (generated UUID)
133
+ - Shared `sequence_counter` and `publish` callback
84
134
 
85
- ### emit_tool_call
135
+ ### create_context_with_shared_sequence
86
136
 
87
137
  ```ruby
88
- context.emit_tool_call(id:, name:, input:)
138
+ sibling = context.create_context_with_shared_sequence(
139
+ run_id: "run_789",
140
+ message_id: "msg_789",
141
+ scope: "robot"
142
+ )
89
143
  ```
90
144
 
91
- Emit a tool call event.
145
+ Create a new context that shares the same sequence counter as this context, but with different identifiers.
146
+
147
+ **Parameters:**
148
+
149
+ | Name | Type | Description |
150
+ |------|------|-------------|
151
+ | `run_id` | `String` | Run ID for the new context |
152
+ | `message_id` | `String` | Message ID for the new context |
153
+ | `scope` | `String` | Scope for the new context |
154
+
155
+ **Returns:** A new `Context` sharing the sequence counter and publish callback.
92
156
 
93
- ### emit_error
157
+ ### generate_part_id
94
158
 
95
159
  ```ruby
96
- context.emit_error(error)
160
+ context.generate_part_id # => "part_run_1234_900123_a1b2c3d4"
97
161
  ```
98
162
 
99
- Emit an error event.
163
+ Generate an OpenAI-compatible part ID (max 40 characters). Combines a truncated message ID, a timestamp suffix, and random hex.
100
164
 
101
- ### complete
165
+ ### generate_step_id
102
166
 
103
167
  ```ruby
104
- context.complete
168
+ context.generate_step_id("text_output") # => "publish-3:text_output"
105
169
  ```
106
170
 
107
- Signal streaming completion.
171
+ Generate a step ID for durable execution compatibility. Uses the current sequence number.
108
172
 
109
- ### for_robot
173
+ **Parameters:**
174
+
175
+ | Name | Type | Description |
176
+ |------|------|-------------|
177
+ | `base_name` | `String` | Base name for the step |
178
+
179
+ ### generate_message_id
110
180
 
111
181
  ```ruby
112
- new_context = context.for_robot(robot)
182
+ context.generate_message_id # => "a1b2c3d4-..."
113
183
  ```
114
184
 
115
- Create a child context for a specific robot.
185
+ Generate a new UUID for use as a message identifier.
116
186
 
117
187
  ## Examples
118
188
 
119
- ### Custom Context
189
+ ### Basic Event Publishing
120
190
 
121
191
  ```ruby
192
+ publish = ->(event) {
193
+ puts "[#{event[:event]}] #{event[:data]}"
194
+ }
195
+
122
196
  context = RobotLab::Streaming::Context.new(
123
- callback: ->(event) {
124
- case event.type
125
- when :text_delta
126
- @output << event.text
127
- when :complete
128
- process_output(@output)
129
- end
130
- }
197
+ run_id: SecureRandom.uuid,
198
+ message_id: SecureRandom.uuid,
199
+ scope: "robot",
200
+ publish: publish
131
201
  )
132
202
 
133
- # Pass to robot
134
- robot.run(state: state, streaming: context)
203
+ context.publish_event(event: "run.started", data: { robot_name: "assistant" })
204
+ context.publish_event(event: "text.delta", data: { delta: "Hello " })
205
+ context.publish_event(event: "text.delta", data: { delta: "world!" })
206
+ context.publish_event(event: "run.completed", data: {})
135
207
  ```
136
208
 
137
- ### Accumulating Content
209
+ ### Network with Child Contexts
138
210
 
139
211
  ```ruby
140
- context = RobotLab::Streaming::Context.new(
141
- callback: ->(event) {
142
- print event.text if event.type == :text_delta
143
- }
212
+ # Network-level context
213
+ network_ctx = RobotLab::Streaming::Context.new(
214
+ run_id: "net_run_1",
215
+ message_id: "net_msg_1",
216
+ scope: "network",
217
+ publish: ->(e) { stream_to_client(e) }
144
218
  )
145
219
 
146
- robot.run(state: state, streaming: context)
220
+ network_ctx.publish_event(event: "run.started", data: {})
221
+
222
+ # Robot 1 executes
223
+ robot1_ctx = network_ctx.create_child_context("robot1_run_1")
224
+ robot1_ctx.publish_event(event: "step.started", data: { robot: "classifier" })
225
+ robot1_ctx.publish_event(event: "text.delta", data: { delta: "Category: billing" })
226
+ robot1_ctx.publish_event(event: "step.completed", data: { robot: "classifier" })
227
+
228
+ # Robot 2 executes (sequence numbers continue from robot 1)
229
+ robot2_ctx = network_ctx.create_child_context("robot2_run_1")
230
+ robot2_ctx.publish_event(event: "step.started", data: { robot: "responder" })
231
+ robot2_ctx.publish_event(event: "text.delta", data: { delta: "I can help with that." })
232
+ robot2_ctx.publish_event(event: "step.completed", data: { robot: "responder" })
147
233
 
148
- # Access accumulated content
149
- puts "Total content: #{context.buffer}"
150
- puts "Tool calls: #{context.tool_calls.size}"
234
+ network_ctx.publish_event(event: "run.completed", data: {})
151
235
  ```
152
236
 
153
- ### Network Context
237
+ ### Error-Safe Publishing
154
238
 
155
239
  ```ruby
240
+ # Errors in the publish callback are caught and logged,
241
+ # so streaming failures do not interrupt execution.
156
242
  context = RobotLab::Streaming::Context.new(
157
- callback: ->(event) {
158
- prefix = event.robot_name ? "[#{event.robot_name}] " : ""
159
- case event.type
160
- when :text_delta
161
- print "#{prefix}#{event.text}"
162
- when :robot_complete
163
- puts "\n#{prefix}Complete"
164
- end
165
- }
243
+ run_id: "run_1",
244
+ message_id: "msg_1",
245
+ scope: "robot",
246
+ publish: ->(e) { raise "connection lost" }
166
247
  )
167
248
 
168
- network.run(state: state, streaming: context)
249
+ # This does not raise -- the error is logged via RobotLab.config.logger
250
+ chunk = context.publish_event(event: "text.delta", data: { delta: "test" })
251
+ # chunk is still returned with the event data
169
252
  ```
170
253
 
171
254
  ## See Also
@@ -1,237 +1,185 @@
1
- # Streaming Events
1
+ # Streaming::Events
2
2
 
3
- Event types for real-time response handling.
3
+ Event type constants and classification helpers for the streaming system.
4
4
 
5
- ## Class: `RobotLab::Streaming::Event`
5
+ ## Module: `RobotLab::Streaming::Events`
6
+
7
+ Defines all recognized event types as string constants and provides helper methods for classifying events.
6
8
 
7
9
  ```ruby
8
- event.type # => Symbol
9
- event.text # => String (for text events)
10
- event.robot_name # => String (for robot events)
10
+ RobotLab::Streaming::Events::TEXT_DELTA # => "text.delta"
11
+ RobotLab::Streaming::Events::RUN_COMPLETED # => "run.completed"
12
+ RobotLab::Streaming::Events.delta?("text.delta") # => true
11
13
  ```
12
14
 
13
- ## Event Types
15
+ ## Event Type Constants
14
16
 
15
- ### :start
17
+ ### Run Lifecycle Events
16
18
 
17
- Streaming has begun.
19
+ | Constant | Value | Description |
20
+ |----------|-------|-------------|
21
+ | `RUN_STARTED` | `"run.started"` | A run has begun |
22
+ | `RUN_COMPLETED` | `"run.completed"` | A run completed successfully |
23
+ | `RUN_FAILED` | `"run.failed"` | A run failed with an error |
24
+ | `RUN_INTERRUPTED` | `"run.interrupted"` | A run was interrupted |
18
25
 
19
- ```ruby
20
- case event.type
21
- when :start
22
- puts "Starting..."
23
- end
24
- ```
26
+ ### Step Events
25
27
 
26
- **Attributes:**
28
+ For durable execution tracking:
27
29
 
28
- | Name | Type | Description |
29
- |------|------|-------------|
30
- | `robot_name` | `String`, `nil` | Robot name |
30
+ | Constant | Value | Description |
31
+ |----------|-------|-------------|
32
+ | `STEP_STARTED` | `"step.started"` | A processing step has begun |
33
+ | `STEP_COMPLETED` | `"step.completed"` | A processing step completed |
34
+ | `STEP_FAILED` | `"step.failed"` | A processing step failed |
31
35
 
32
- ### :text_delta
36
+ ### Part Events
33
37
 
34
- A chunk of text was received.
38
+ For message composition tracking:
35
39
 
36
- ```ruby
37
- case event.type
38
- when :text_delta
39
- print event.text
40
- end
41
- ```
40
+ | Constant | Value | Description |
41
+ |----------|-------|-------------|
42
+ | `PART_CREATED` | `"part.created"` | A message part was created |
43
+ | `PART_COMPLETED` | `"part.completed"` | A message part completed |
44
+ | `PART_FAILED` | `"part.failed"` | A message part failed |
42
45
 
43
- **Attributes:**
46
+ ### Content Delta Events
44
47
 
45
- | Name | Type | Description |
46
- |------|------|-------------|
47
- | `text` | `String` | Text content |
48
- | `robot_name` | `String`, `nil` | Source robot |
48
+ Token-level streaming events:
49
49
 
50
- ### :tool_call
50
+ | Constant | Value | Description |
51
+ |----------|-------|-------------|
52
+ | `TEXT_DELTA` | `"text.delta"` | A chunk of text content was generated |
53
+ | `TOOL_CALL_ARGUMENTS_DELTA` | `"tool_call.arguments.delta"` | A chunk of tool call arguments |
54
+ | `TOOL_CALL_OUTPUT_DELTA` | `"tool_call.output.delta"` | A chunk of tool call output |
55
+ | `REASONING_DELTA` | `"reasoning.delta"` | A chunk of reasoning/thinking content |
56
+ | `DATA_DELTA` | `"data.delta"` | A chunk of structured data |
51
57
 
52
- A tool is being invoked.
58
+ ### Human-in-the-Loop Events
53
59
 
54
- ```ruby
55
- case event.type
56
- when :tool_call
57
- puts "Calling #{event.name} with #{event.input}"
58
- end
59
- ```
60
+ | Constant | Value | Description |
61
+ |----------|-------|-------------|
62
+ | `HITL_REQUESTED` | `"hitl.requested"` | Human input has been requested |
63
+ | `HITL_RESOLVED` | `"hitl.resolved"` | Human input has been provided |
60
64
 
61
- **Attributes:**
65
+ ### Metadata Events
62
66
 
63
- | Name | Type | Description |
64
- |------|------|-------------|
65
- | `id` | `String` | Tool call ID |
66
- | `name` | `String` | Tool name |
67
- | `input` | `Hash` | Tool parameters |
68
- | `robot_name` | `String`, `nil` | Source robot |
67
+ | Constant | Value | Description |
68
+ |----------|-------|-------------|
69
+ | `USAGE_UPDATED` | `"usage.updated"` | Token usage statistics updated |
70
+ | `METADATA_UPDATED` | `"metadata.updated"` | Run metadata updated |
69
71
 
70
- ### :tool_result
72
+ ### Terminal Event
71
73
 
72
- A tool has returned a result.
74
+ | Constant | Value | Description |
75
+ |----------|-------|-------------|
76
+ | `STREAM_ENDED` | `"stream.ended"` | The stream has ended; no more events |
73
77
 
74
- ```ruby
75
- case event.type
76
- when :tool_result
77
- puts "#{event.name} returned: #{event.result}"
78
- end
79
- ```
78
+ ## Event Collections
80
79
 
81
- **Attributes:**
80
+ ### ALL_EVENTS
82
81
 
83
- | Name | Type | Description |
84
- |------|------|-------------|
85
- | `id` | `String` | Tool call ID |
86
- | `name` | `String` | Tool name |
87
- | `result` | `Object` | Tool result |
88
- | `robot_name` | `String`, `nil` | Source robot |
89
-
90
- ### :robot_start
82
+ ```ruby
83
+ RobotLab::Streaming::Events::ALL_EVENTS
84
+ # => Array of all event type strings (frozen)
85
+ ```
91
86
 
92
- A robot has started executing (network only).
87
+ ### LIFECYCLE_EVENTS
93
88
 
94
89
  ```ruby
95
- case event.type
96
- when :robot_start
97
- puts "Robot #{event.robot_name} starting"
98
- end
90
+ RobotLab::Streaming::Events::LIFECYCLE_EVENTS
91
+ # => ["run.started", "run.completed", "run.failed", "run.interrupted"]
99
92
  ```
100
93
 
101
- **Attributes:**
94
+ ### DELTA_EVENTS
102
95
 
103
- | Name | Type | Description |
104
- |------|------|-------------|
105
- | `robot_name` | `String` | Robot name |
96
+ ```ruby
97
+ RobotLab::Streaming::Events::DELTA_EVENTS
98
+ # => ["text.delta", "tool_call.arguments.delta", "tool_call.output.delta",
99
+ # "reasoning.delta", "data.delta"]
100
+ ```
106
101
 
107
- ### :robot_complete
102
+ ## Classification Methods
108
103
 
109
- A robot has finished executing (network only).
104
+ ### Events.lifecycle?
110
105
 
111
106
  ```ruby
112
- case event.type
113
- when :robot_complete
114
- puts "Robot #{event.robot_name} finished"
115
- end
107
+ RobotLab::Streaming::Events.lifecycle?("run.started") # => true
108
+ RobotLab::Streaming::Events.lifecycle?("text.delta") # => false
116
109
  ```
117
110
 
118
- **Attributes:**
119
-
120
- | Name | Type | Description |
121
- |------|------|-------------|
122
- | `robot_name` | `String` | Robot name |
123
- | `result` | `RobotResult` | Execution result |
124
-
125
- ### :complete
111
+ Returns `true` if the event is a run lifecycle event.
126
112
 
127
- All streaming has finished.
113
+ ### Events.delta?
128
114
 
129
115
  ```ruby
130
- case event.type
131
- when :complete
132
- puts "All done!"
133
- end
116
+ RobotLab::Streaming::Events.delta?("text.delta") # => true
117
+ RobotLab::Streaming::Events.delta?("run.completed") # => false
134
118
  ```
135
119
 
136
- ### :error
120
+ Returns `true` if the event is a content delta (token streaming) event.
137
121
 
138
- An error occurred.
122
+ ### Events.valid?
139
123
 
140
124
  ```ruby
141
- case event.type
142
- when :error
143
- puts "Error: #{event.error.message}"
144
- end
125
+ RobotLab::Streaming::Events.valid?("text.delta") # => true
126
+ RobotLab::Streaming::Events.valid?("unknown.event") # => false
145
127
  ```
146
128
 
147
- **Attributes:**
148
-
149
- | Name | Type | Description |
150
- |------|------|-------------|
151
- | `error` | `Exception` | The error |
152
- | `robot_name` | `String`, `nil` | Source robot |
129
+ Returns `true` if the event is a recognized event type.
153
130
 
154
131
  ## Examples
155
132
 
156
- ### Complete Handler
157
-
158
- ```ruby
159
- robot.run(state: state) do |event|
160
- case event.type
161
- when :start
162
- puts "=== Starting ==="
163
- when :text_delta
164
- print event.text
165
- when :tool_call
166
- puts "\n[Tool: #{event.name}]"
167
- when :tool_result
168
- puts "[Result: #{event.result.to_s.truncate(50)}]"
169
- when :complete
170
- puts "\n=== Complete ==="
171
- when :error
172
- puts "\n!!! Error: #{event.error.message}"
173
- end
174
- end
175
- ```
176
-
177
- ### Network Handler
133
+ ### Filtering Events by Category
178
134
 
179
135
  ```ruby
180
- network.run(state: state) do |event|
181
- robot = event.robot_name || "system"
182
-
183
- case event.type
184
- when :robot_start
185
- puts "[#{robot}] Starting..."
186
- when :text_delta
187
- print event.text
188
- when :tool_call
189
- puts "\n[#{robot}] Calling #{event.name}"
190
- when :robot_complete
191
- puts "\n[#{robot}] Complete"
192
- when :error
193
- puts "\n[#{robot}] Error: #{event.error.message}"
136
+ publish = ->(event) {
137
+ event_type = event[:event]
138
+
139
+ if RobotLab::Streaming::Events.delta?(event_type)
140
+ # Handle streaming content
141
+ print event[:data][:delta]
142
+ elsif RobotLab::Streaming::Events.lifecycle?(event_type)
143
+ # Handle lifecycle transitions
144
+ puts "\n[#{event_type}] run_id=#{event[:data][:run_id]}"
194
145
  end
195
- end
146
+ }
196
147
  ```
197
148
 
198
- ### Filtering Events
149
+ ### Using Constants for Event Matching
199
150
 
200
151
  ```ruby
201
- # Only text
202
- robot.run(state: state) do |event|
203
- print event.text if event.type == :text_delta
204
- end
205
-
206
- # Only tools
207
- robot.run(state: state) do |event|
208
- if event.type == :tool_call
209
- log_tool_usage(event.name, event.input)
152
+ include RobotLab::Streaming::Events
153
+
154
+ publish = ->(event) {
155
+ case event[:event]
156
+ when TEXT_DELTA
157
+ print event[:data][:delta]
158
+ when TOOL_CALL_ARGUMENTS_DELTA
159
+ buffer_tool_args(event[:data])
160
+ when RUN_COMPLETED
161
+ puts "\nRun complete"
162
+ when RUN_FAILED
163
+ puts "\nRun failed: #{event[:data][:error]}"
164
+ when STREAM_ENDED
165
+ cleanup
210
166
  end
211
- end
167
+ }
212
168
  ```
213
169
 
214
- ### Async Processing
170
+ ### Validating Custom Events
215
171
 
216
172
  ```ruby
217
- queue = Queue.new
218
-
219
- Thread.new do
220
- loop do
221
- event = queue.pop
222
- break if event == :done
223
- process_event(event)
173
+ def emit(event_type, data)
174
+ unless RobotLab::Streaming::Events.valid?(event_type)
175
+ raise ArgumentError, "Unknown event type: #{event_type}"
224
176
  end
225
- end
226
177
 
227
- robot.run(state: state) do |event|
228
- queue << event
229
- queue << :done if event.type == :complete
178
+ context.publish_event(event: event_type, data: data)
230
179
  end
231
180
  ```
232
181
 
233
182
  ## See Also
234
183
 
235
184
  - [Streaming Overview](index.md)
236
- - [StreamingContext](context.md)
237
- - [Streaming Guide](../../guides/streaming.md)
185
+ - [Context](context.md)