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.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +140 -0
- data/README.md +263 -48
- data/Rakefile +71 -1
- data/docs/api/core/index.md +53 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +490 -130
- data/docs/api/core/state.md +55 -73
- data/docs/api/core/tool.md +205 -209
- data/docs/api/index.md +7 -28
- data/docs/api/mcp/client.md +119 -48
- data/docs/api/mcp/index.md +75 -60
- data/docs/api/mcp/server.md +120 -136
- data/docs/api/mcp/transports.md +172 -184
- data/docs/api/messages/index.md +35 -20
- data/docs/api/messages/text-message.md +67 -21
- data/docs/api/messages/tool-call-message.md +80 -41
- data/docs/api/messages/tool-result-message.md +119 -50
- data/docs/api/messages/user-message.md +48 -24
- data/docs/api/streaming/context.md +157 -74
- data/docs/api/streaming/events.md +114 -166
- data/docs/api/streaming/index.md +74 -72
- data/docs/architecture/core-concepts.md +360 -116
- data/docs/architecture/index.md +97 -59
- data/docs/architecture/message-flow.md +138 -129
- data/docs/architecture/network-orchestration.md +197 -50
- data/docs/architecture/robot-execution.md +199 -146
- data/docs/architecture/state-management.md +255 -187
- data/docs/concepts.md +311 -49
- data/docs/examples/basic-chat.md +89 -77
- data/docs/examples/index.md +222 -47
- data/docs/examples/mcp-server.md +207 -203
- data/docs/examples/multi-robot-network.md +129 -35
- data/docs/examples/rails-application.md +159 -160
- data/docs/examples/tool-usage.md +295 -204
- data/docs/getting-started/configuration.md +347 -154
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/installation.md +22 -13
- data/docs/getting-started/quick-start.md +166 -121
- data/docs/guides/building-robots.md +418 -212
- data/docs/guides/creating-networks.md +143 -24
- data/docs/guides/index.md +0 -5
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +137 -187
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +46 -41
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +19 -17
- data/examples/04_mcp.rb +5 -8
- data/examples/05_streaming.rb +5 -8
- data/examples/06_prompt_templates.rb +42 -37
- data/examples/07_network_memory.rb +13 -14
- data/examples/08_llm_config.rb +169 -0
- data/examples/09_chaining.rb +262 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +253 -0
- data/examples/12_message_bus.rb +74 -0
- data/examples/13_spawn.rb +90 -0
- data/examples/14_rusty_circuit/comic.rb +143 -0
- data/examples/14_rusty_circuit/display.rb +203 -0
- data/examples/14_rusty_circuit/heckler.rb +63 -0
- data/examples/14_rusty_circuit/open_mic.rb +123 -0
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
- data/examples/14_rusty_circuit/scout.rb +156 -0
- data/examples/14_rusty_circuit/scout_notes.md +89 -0
- data/examples/14_rusty_circuit/show.log +234 -0
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
- data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
- data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
- data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
- data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
- data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
- data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
- data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
- data/examples/15_memory_network_and_bus/output/memory.json +13 -0
- data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
- data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
- data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
- data/examples/16_writers_room/display.rb +158 -0
- data/examples/16_writers_room/output/.gitignore +2 -0
- data/examples/16_writers_room/output/opus_001.md +263 -0
- data/examples/16_writers_room/output/opus_001_notes.log +470 -0
- data/examples/16_writers_room/prompts/writer.md +37 -0
- data/examples/16_writers_room/room.rb +150 -0
- data/examples/16_writers_room/tools.rb +162 -0
- data/examples/16_writers_room/writer.rb +121 -0
- data/examples/16_writers_room/writers_room.rb +162 -0
- data/examples/README.md +197 -0
- data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
- data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
- data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
- data/examples/prompts/comedian.md +6 -0
- data/examples/prompts/comedy_critic.md +10 -0
- data/examples/prompts/configurable.md +9 -0
- data/examples/prompts/dispatcher.md +12 -0
- data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
- data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
- data/examples/prompts/frontmatter_mcp_test.md +9 -0
- data/examples/prompts/frontmatter_named_test.md +5 -0
- data/examples/prompts/frontmatter_tools_test.md +6 -0
- data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
- data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
- data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
- data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
- data/examples/prompts/llm_config_demo.md +20 -0
- data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
- data/examples/prompts/os_advocate.md +13 -0
- data/examples/prompts/os_chief.md +13 -0
- data/examples/prompts/os_editor.md +13 -0
- data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
- data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
- data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
- data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
- data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/ask_user.rb +75 -0
- data/lib/robot_lab/config/defaults.yml +121 -0
- data/lib/robot_lab/config.rb +183 -0
- data/lib/robot_lab/error.rb +6 -0
- data/lib/robot_lab/mcp/client.rb +1 -1
- data/lib/robot_lab/memory.rb +10 -34
- data/lib/robot_lab/network.rb +13 -20
- data/lib/robot_lab/robot/bus_messaging.rb +239 -0
- data/lib/robot_lab/robot/mcp_management.rb +88 -0
- data/lib/robot_lab/robot/template_rendering.rb +130 -0
- data/lib/robot_lab/robot.rb +240 -330
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/streaming/context.rb +1 -1
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/tool.rb +108 -172
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +2 -18
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +89 -57
- data/mkdocs.yml +0 -11
- metadata +121 -135
- data/docs/api/adapters/anthropic.md +0 -121
- data/docs/api/adapters/gemini.md +0 -133
- data/docs/api/adapters/index.md +0 -104
- data/docs/api/adapters/openai.md +0 -134
- data/docs/api/history/active-record-adapter.md +0 -195
- data/docs/api/history/config.md +0 -191
- data/docs/api/history/index.md +0 -132
- data/docs/api/history/thread-manager.md +0 -144
- data/docs/guides/history.md +0 -359
- data/examples/prompts/assistant/user.txt.erb +0 -1
- data/examples/prompts/billing/user.txt.erb +0 -1
- data/examples/prompts/classifier/user.txt.erb +0 -1
- data/examples/prompts/entity_extractor/user.txt.erb +0 -3
- data/examples/prompts/escalation/user.txt.erb +0 -34
- data/examples/prompts/general/user.txt.erb +0 -1
- data/examples/prompts/github_assistant/user.txt.erb +0 -1
- data/examples/prompts/helper/user.txt.erb +0 -1
- data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
- data/examples/prompts/order_support/user.txt.erb +0 -22
- data/examples/prompts/product_support/user.txt.erb +0 -32
- data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
- data/examples/prompts/synthesizer/user.txt.erb +0 -15
- data/examples/prompts/technical/user.txt.erb +0 -1
- data/examples/prompts/triage/user.txt.erb +0 -17
- data/lib/robot_lab/adapters/anthropic.rb +0 -163
- data/lib/robot_lab/adapters/base.rb +0 -85
- data/lib/robot_lab/adapters/gemini.rb +0 -193
- data/lib/robot_lab/adapters/openai.rb +0 -159
- data/lib/robot_lab/adapters/registry.rb +0 -81
- data/lib/robot_lab/configuration.rb +0 -143
- data/lib/robot_lab/errors.rb +0 -70
- data/lib/robot_lab/history/active_record_adapter.rb +0 -146
- data/lib/robot_lab/history/config.rb +0 -115
- data/lib/robot_lab/history/thread_manager.rb +0 -93
- data/lib/robot_lab/robotic_model.rb +0 -324
|
@@ -1,171 +1,254 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Streaming::Context
|
|
2
2
|
|
|
3
|
-
Manages streaming
|
|
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(
|
|
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(
|
|
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
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
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
|
-
###
|
|
44
|
+
### run_id
|
|
28
45
|
|
|
29
46
|
```ruby
|
|
30
|
-
context.
|
|
47
|
+
context.run_id # => String
|
|
31
48
|
```
|
|
32
49
|
|
|
33
|
-
The
|
|
50
|
+
The unique run identifier for this context.
|
|
34
51
|
|
|
35
|
-
###
|
|
52
|
+
### parent_run_id
|
|
36
53
|
|
|
37
54
|
```ruby
|
|
38
|
-
context.
|
|
55
|
+
context.parent_run_id # => String | nil
|
|
39
56
|
```
|
|
40
57
|
|
|
41
|
-
The
|
|
58
|
+
The parent run identifier. Set when this is a child context created by `create_child_context`.
|
|
42
59
|
|
|
43
|
-
###
|
|
60
|
+
### message_id
|
|
44
61
|
|
|
45
62
|
```ruby
|
|
46
|
-
context.
|
|
63
|
+
context.message_id # => String
|
|
47
64
|
```
|
|
48
65
|
|
|
49
|
-
The
|
|
66
|
+
The current message identifier.
|
|
50
67
|
|
|
51
|
-
###
|
|
68
|
+
### scope
|
|
52
69
|
|
|
53
70
|
```ruby
|
|
54
|
-
context.
|
|
71
|
+
context.scope # => String
|
|
55
72
|
```
|
|
56
73
|
|
|
57
|
-
|
|
74
|
+
The context scope (converted to string). Typically `"network"` or `"robot"`.
|
|
75
|
+
|
|
76
|
+
## Methods
|
|
58
77
|
|
|
59
|
-
###
|
|
78
|
+
### publish_event
|
|
60
79
|
|
|
61
80
|
```ruby
|
|
62
|
-
context.
|
|
81
|
+
chunk = context.publish_event(event:, data: {})
|
|
63
82
|
```
|
|
64
83
|
|
|
65
|
-
|
|
84
|
+
Publish a streaming event. The event is wrapped in a chunk with automatic sequencing, timestamping, and context metadata injection.
|
|
66
85
|
|
|
67
|
-
|
|
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
|
-
|
|
95
|
+
The chunk structure:
|
|
70
96
|
|
|
71
97
|
```ruby
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
114
|
+
### create_child_context
|
|
78
115
|
|
|
79
116
|
```ruby
|
|
80
|
-
context.
|
|
117
|
+
child = context.create_child_context(robot_run_id)
|
|
81
118
|
```
|
|
82
119
|
|
|
83
|
-
|
|
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
|
-
###
|
|
135
|
+
### create_context_with_shared_sequence
|
|
86
136
|
|
|
87
137
|
```ruby
|
|
88
|
-
context.
|
|
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
|
-
|
|
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
|
-
###
|
|
157
|
+
### generate_part_id
|
|
94
158
|
|
|
95
159
|
```ruby
|
|
96
|
-
context.
|
|
160
|
+
context.generate_part_id # => "part_run_1234_900123_a1b2c3d4"
|
|
97
161
|
```
|
|
98
162
|
|
|
99
|
-
|
|
163
|
+
Generate an OpenAI-compatible part ID (max 40 characters). Combines a truncated message ID, a timestamp suffix, and random hex.
|
|
100
164
|
|
|
101
|
-
###
|
|
165
|
+
### generate_step_id
|
|
102
166
|
|
|
103
167
|
```ruby
|
|
104
|
-
context.
|
|
168
|
+
context.generate_step_id("text_output") # => "publish-3:text_output"
|
|
105
169
|
```
|
|
106
170
|
|
|
107
|
-
|
|
171
|
+
Generate a step ID for durable execution compatibility. Uses the current sequence number.
|
|
108
172
|
|
|
109
|
-
|
|
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
|
-
|
|
182
|
+
context.generate_message_id # => "a1b2c3d4-..."
|
|
113
183
|
```
|
|
114
184
|
|
|
115
|
-
|
|
185
|
+
Generate a new UUID for use as a message identifier.
|
|
116
186
|
|
|
117
187
|
## Examples
|
|
118
188
|
|
|
119
|
-
###
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
###
|
|
209
|
+
### Network with Child Contexts
|
|
138
210
|
|
|
139
211
|
```ruby
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
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
|
|
1
|
+
# Streaming::Events
|
|
2
2
|
|
|
3
|
-
Event
|
|
3
|
+
Event type constants and classification helpers for the streaming system.
|
|
4
4
|
|
|
5
|
-
##
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
15
|
+
## Event Type Constants
|
|
14
16
|
|
|
15
|
-
###
|
|
17
|
+
### Run Lifecycle Events
|
|
16
18
|
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
case event.type
|
|
21
|
-
when :start
|
|
22
|
-
puts "Starting..."
|
|
23
|
-
end
|
|
24
|
-
```
|
|
26
|
+
### Step Events
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
For durable execution tracking:
|
|
27
29
|
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
| `
|
|
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
|
-
###
|
|
36
|
+
### Part Events
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
For message composition tracking:
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
46
|
+
### Content Delta Events
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|------|------|-------------|
|
|
47
|
-
| `text` | `String` | Text content |
|
|
48
|
-
| `robot_name` | `String`, `nil` | Source robot |
|
|
48
|
+
Token-level streaming events:
|
|
49
49
|
|
|
50
|
-
|
|
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
|
-
|
|
58
|
+
### Human-in-the-Loop Events
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
65
|
+
### Metadata Events
|
|
62
66
|
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
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
|
-
###
|
|
72
|
+
### Terminal Event
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
| Constant | Value | Description |
|
|
75
|
+
|----------|-------|-------------|
|
|
76
|
+
| `STREAM_ENDED` | `"stream.ended"` | The stream has ended; no more events |
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
case event.type
|
|
76
|
-
when :tool_result
|
|
77
|
-
puts "#{event.name} returned: #{event.result}"
|
|
78
|
-
end
|
|
79
|
-
```
|
|
78
|
+
## Event Collections
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
### ALL_EVENTS
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
87
|
+
### LIFECYCLE_EVENTS
|
|
93
88
|
|
|
94
89
|
```ruby
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
94
|
+
### DELTA_EVENTS
|
|
102
95
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
102
|
+
## Classification Methods
|
|
108
103
|
|
|
109
|
-
|
|
104
|
+
### Events.lifecycle?
|
|
110
105
|
|
|
111
106
|
```ruby
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
### Events.delta?
|
|
128
114
|
|
|
129
115
|
```ruby
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
120
|
+
Returns `true` if the event is a content delta (token streaming) event.
|
|
137
121
|
|
|
138
|
-
|
|
122
|
+
### Events.valid?
|
|
139
123
|
|
|
140
124
|
```ruby
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
146
|
+
}
|
|
196
147
|
```
|
|
197
148
|
|
|
198
|
-
###
|
|
149
|
+
### Using Constants for Event Matching
|
|
199
150
|
|
|
200
151
|
```ruby
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
167
|
+
}
|
|
212
168
|
```
|
|
213
169
|
|
|
214
|
-
###
|
|
170
|
+
### Validating Custom Events
|
|
215
171
|
|
|
216
172
|
```ruby
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
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
|
-
- [
|
|
237
|
-
- [Streaming Guide](../../guides/streaming.md)
|
|
185
|
+
- [Context](context.md)
|