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
data/docs/guides/streaming.md
CHANGED
|
@@ -2,107 +2,115 @@
|
|
|
2
2
|
|
|
3
3
|
Stream LLM responses in real-time for better user experience.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Streaming via Callbacks
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
RobotLab robots support streaming through callback methods inherited from RubyLLM::Agent. Register callbacks before calling `run`:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
robot.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
streaming: ->(event) {
|
|
14
|
-
puts event.inspect
|
|
15
|
-
}
|
|
10
|
+
robot = RobotLab.build(
|
|
11
|
+
name: "storyteller",
|
|
12
|
+
system_prompt: "You are a creative storyteller."
|
|
16
13
|
)
|
|
14
|
+
|
|
15
|
+
# Register streaming callback
|
|
16
|
+
robot.on_new_message do |message|
|
|
17
|
+
print message.content if message.content
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
result = robot.run("Tell me a story about a brave robot")
|
|
17
21
|
```
|
|
18
22
|
|
|
19
|
-
##
|
|
23
|
+
## Available Callbacks
|
|
20
24
|
|
|
21
|
-
###
|
|
25
|
+
### on_new_message
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
Called when the assistant starts generating a new message, with streaming chunks:
|
|
24
28
|
|
|
25
29
|
```ruby
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
30
|
-
}
|
|
30
|
+
robot.on_new_message do |message|
|
|
31
|
+
print message.content if message.content
|
|
32
|
+
end
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
###
|
|
35
|
+
### on_end_message
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
Called when the assistant finishes a message:
|
|
36
38
|
|
|
37
39
|
```ruby
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
when "tool_call.complete"
|
|
43
|
-
puts "Done: #{event[:data][:result]}"
|
|
44
|
-
end
|
|
45
|
-
}
|
|
40
|
+
robot.on_end_message do |message|
|
|
41
|
+
puts "\n--- Response complete ---"
|
|
42
|
+
puts "Content length: #{message.content&.length}"
|
|
43
|
+
end
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
###
|
|
46
|
+
### on_tool_call
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
Called when the LLM invokes a tool:
|
|
51
49
|
|
|
52
50
|
```ruby
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
puts "Starting run #{event[:data][:run_id]}"
|
|
57
|
-
when "run.completed"
|
|
58
|
-
puts "Completed!"
|
|
59
|
-
when "run.failed"
|
|
60
|
-
puts "Failed: #{event[:data][:error]}"
|
|
61
|
-
end
|
|
62
|
-
}
|
|
51
|
+
robot.on_tool_call do |tool_call|
|
|
52
|
+
puts "Calling tool: #{tool_call.name}"
|
|
53
|
+
end
|
|
63
54
|
```
|
|
64
55
|
|
|
65
|
-
|
|
56
|
+
### on_tool_result
|
|
66
57
|
|
|
67
|
-
|
|
68
|
-
|-------|-------------|------|
|
|
69
|
-
| `run.started` | Network run began | `run_id`, `network` |
|
|
70
|
-
| `run.completed` | Network run finished | `run_id`, `robot_count` |
|
|
71
|
-
| `run.failed` | Error occurred | `run_id`, `error` |
|
|
72
|
-
| `delta` | Text content chunk | `content` |
|
|
73
|
-
| `tool_call.start` | Tool execution starting | `name`, `input` |
|
|
74
|
-
| `tool_call.complete` | Tool execution done | `name`, `result` |
|
|
58
|
+
Called when a tool returns its result:
|
|
75
59
|
|
|
76
|
-
|
|
60
|
+
```ruby
|
|
61
|
+
robot.on_tool_result do |tool_call, result|
|
|
62
|
+
puts "Tool #{tool_call.name} returned: #{result}"
|
|
63
|
+
end
|
|
64
|
+
```
|
|
77
65
|
|
|
78
|
-
|
|
66
|
+
## Comprehensive Callback Setup
|
|
67
|
+
|
|
68
|
+
Register all callbacks for full visibility:
|
|
79
69
|
|
|
80
70
|
```ruby
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
publish: ->(event) { broadcast_to_client(event) }
|
|
71
|
+
robot = RobotLab.build(
|
|
72
|
+
name: "assistant",
|
|
73
|
+
system_prompt: "You are helpful.",
|
|
74
|
+
local_tools: [WeatherTool]
|
|
86
75
|
)
|
|
87
|
-
```
|
|
88
76
|
|
|
89
|
-
|
|
77
|
+
robot.on_new_message do |message|
|
|
78
|
+
print message.content if message.content
|
|
79
|
+
end
|
|
90
80
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
81
|
+
robot.on_end_message do |_message|
|
|
82
|
+
puts "\n--- Done ---"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
robot.on_tool_call do |tool_call|
|
|
86
|
+
puts "\n[Tool] Calling: #{tool_call.name}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
robot.on_tool_result do |tool_call, result|
|
|
90
|
+
puts "[Tool] #{tool_call.name} returned: #{result}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
result = robot.run("What's the weather in Tokyo?")
|
|
95
94
|
```
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
## Streaming via Chat Block
|
|
97
|
+
|
|
98
|
+
For more control, pass a block directly to `chat.ask` (the underlying RubyLLM method):
|
|
98
99
|
|
|
99
100
|
```ruby
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
robot = RobotLab.build(
|
|
102
|
+
name: "chat_bot",
|
|
103
|
+
system_prompt: "You are a helpful assistant."
|
|
103
104
|
)
|
|
105
|
+
|
|
106
|
+
# Use the underlying chat directly with a streaming block
|
|
107
|
+
robot.chat.ask("Tell me a story") do |chunk|
|
|
108
|
+
print chunk.content if chunk.content
|
|
109
|
+
end
|
|
104
110
|
```
|
|
105
111
|
|
|
112
|
+
Note: Using `chat.ask` directly bypasses Robot's memory resolution and tool hierarchy. Use callbacks with `robot.run` when you need those features.
|
|
113
|
+
|
|
106
114
|
## Web Integration
|
|
107
115
|
|
|
108
116
|
### Rails Action Cable
|
|
@@ -110,14 +118,20 @@ context.publish_event(
|
|
|
110
118
|
```ruby
|
|
111
119
|
class ChatChannel < ApplicationCable::Channel
|
|
112
120
|
def receive(data)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
state: state,
|
|
117
|
-
streaming: ->(event) {
|
|
118
|
-
transmit(event)
|
|
119
|
-
}
|
|
121
|
+
robot = RobotLab.build(
|
|
122
|
+
name: "chat_bot",
|
|
123
|
+
system_prompt: "You are a helpful chat assistant."
|
|
120
124
|
)
|
|
125
|
+
|
|
126
|
+
robot.on_new_message do |message|
|
|
127
|
+
transmit({ event: "text.delta", content: message.content }) if message.content
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
robot.on_end_message do |_message|
|
|
131
|
+
transmit({ event: "run.completed" })
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
robot.run(data["message"])
|
|
121
135
|
end
|
|
122
136
|
end
|
|
123
137
|
```
|
|
@@ -131,14 +145,20 @@ class StreamController < ApplicationController
|
|
|
131
145
|
def create
|
|
132
146
|
response.headers["Content-Type"] = "text/event-stream"
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
state: state,
|
|
138
|
-
streaming: ->(event) {
|
|
139
|
-
response.stream.write("data: #{event.to_json}\n\n")
|
|
140
|
-
}
|
|
148
|
+
robot = RobotLab.build(
|
|
149
|
+
name: "stream_bot",
|
|
150
|
+
system_prompt: "You are helpful."
|
|
141
151
|
)
|
|
152
|
+
|
|
153
|
+
robot.on_new_message do |message|
|
|
154
|
+
response.stream.write("data: #{message.content}\n\n") if message.content
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
robot.on_end_message do |_message|
|
|
158
|
+
response.stream.write("data: [DONE]\n\n")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
robot.run(params[:message])
|
|
142
162
|
ensure
|
|
143
163
|
response.stream.close
|
|
144
164
|
end
|
|
@@ -150,62 +170,17 @@ end
|
|
|
150
170
|
```ruby
|
|
151
171
|
# Using Faye WebSocket
|
|
152
172
|
ws.on :message do |msg|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
network.run(
|
|
156
|
-
state: state,
|
|
157
|
-
streaming: ->(event) {
|
|
158
|
-
ws.send(event.to_json)
|
|
159
|
-
}
|
|
160
|
-
)
|
|
161
|
-
end
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## Event Filtering
|
|
165
|
-
|
|
166
|
-
### Check Event Type
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
streaming: ->(event) {
|
|
170
|
-
return unless RobotLab::Streaming::Events.delta?(event)
|
|
171
|
-
print event[:data][:content]
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Available Predicates
|
|
176
|
-
|
|
177
|
-
```ruby
|
|
178
|
-
Streaming::Events.lifecycle?(event) # run.started, run.completed, etc.
|
|
179
|
-
Streaming::Events.delta?(event) # Text content
|
|
180
|
-
Streaming::Events.valid?(event) # Has required fields
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## Buffering
|
|
184
|
-
|
|
185
|
-
Buffer content for batch processing:
|
|
186
|
-
|
|
187
|
-
```ruby
|
|
188
|
-
buffer = []
|
|
189
|
-
|
|
190
|
-
streaming: ->(event) {
|
|
191
|
-
if event[:event] == "delta"
|
|
192
|
-
buffer << event[:data][:content]
|
|
193
|
-
|
|
194
|
-
# Flush every 10 chunks
|
|
195
|
-
if buffer.size >= 10
|
|
196
|
-
process_batch(buffer.join)
|
|
197
|
-
buffer.clear
|
|
198
|
-
end
|
|
173
|
+
robot.on_new_message do |message|
|
|
174
|
+
ws.send(message.content) if message.content
|
|
199
175
|
end
|
|
200
|
-
}
|
|
201
176
|
|
|
202
|
-
|
|
203
|
-
|
|
177
|
+
robot.run(msg.data)
|
|
178
|
+
end
|
|
204
179
|
```
|
|
205
180
|
|
|
206
181
|
## Progress Tracking
|
|
207
182
|
|
|
208
|
-
Track streaming progress:
|
|
183
|
+
Track streaming progress with callbacks:
|
|
209
184
|
|
|
210
185
|
```ruby
|
|
211
186
|
class StreamProgress
|
|
@@ -214,94 +189,69 @@ class StreamProgress
|
|
|
214
189
|
@tools = 0
|
|
215
190
|
end
|
|
216
191
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
192
|
+
attr_reader :chars, :tools
|
|
193
|
+
|
|
194
|
+
def attach(robot)
|
|
195
|
+
robot.on_new_message do |message|
|
|
196
|
+
@chars += message.content.length if message.content
|
|
197
|
+
print "\rReceived #{@chars} characters..."
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
robot.on_tool_call do |tool_call|
|
|
223
201
|
@tools += 1
|
|
224
|
-
puts "\nTool call ##{@tools}: #{
|
|
202
|
+
puts "\nTool call ##{@tools}: #{tool_call.name}"
|
|
225
203
|
end
|
|
226
204
|
end
|
|
227
205
|
end
|
|
228
206
|
|
|
229
207
|
progress = StreamProgress.new
|
|
230
|
-
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## Error Handling
|
|
234
|
-
|
|
235
|
-
Handle streaming errors gracefully:
|
|
208
|
+
progress.attach(robot)
|
|
236
209
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
case event[:event]
|
|
240
|
-
when "run.failed"
|
|
241
|
-
log_error(event[:data][:error])
|
|
242
|
-
notify_user("An error occurred")
|
|
243
|
-
when "delta"
|
|
244
|
-
begin
|
|
245
|
-
broadcast(event)
|
|
246
|
-
rescue BroadcastError => e
|
|
247
|
-
# Client disconnected, but continue processing
|
|
248
|
-
logger.warn "Broadcast failed: #{e.message}"
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
}
|
|
210
|
+
result = robot.run("Process this complex request")
|
|
211
|
+
puts "\nTotal: #{progress.chars} chars, #{progress.tools} tool calls"
|
|
252
212
|
```
|
|
253
213
|
|
|
254
|
-
##
|
|
214
|
+
## Without Streaming
|
|
255
215
|
|
|
256
|
-
|
|
216
|
+
When streaming is not needed, simply call `run` without registering callbacks:
|
|
257
217
|
|
|
258
218
|
```ruby
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
# Or per-run
|
|
264
|
-
network.run(state: state, streaming: nil)
|
|
219
|
+
# No streaming - returns RobotResult directly
|
|
220
|
+
result = robot.run("Hello!")
|
|
221
|
+
puts result.last_text_content
|
|
265
222
|
```
|
|
266
223
|
|
|
267
224
|
## Best Practices
|
|
268
225
|
|
|
269
|
-
### 1.
|
|
226
|
+
### 1. Register Callbacks Before Run
|
|
270
227
|
|
|
271
228
|
```ruby
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
when "tool_call.start" then show_tool_indicator(event)
|
|
276
|
-
when "tool_call.complete" then hide_tool_indicator(event)
|
|
277
|
-
when "run.completed" then finalize_response
|
|
278
|
-
when "run.failed" then show_error(event)
|
|
279
|
-
end
|
|
280
|
-
}
|
|
229
|
+
# Correct: register first, then run
|
|
230
|
+
robot.on_new_message { |msg| print msg.content if msg.content }
|
|
231
|
+
robot.run("Hello")
|
|
281
232
|
```
|
|
282
233
|
|
|
283
|
-
### 2.
|
|
234
|
+
### 2. Handle Errors in Callbacks
|
|
284
235
|
|
|
285
236
|
```ruby
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
when "tool_call.start"
|
|
293
|
-
show_status("Looking up information...")
|
|
294
|
-
when "run.completed"
|
|
295
|
-
hide_typing_indicator
|
|
237
|
+
robot.on_new_message do |message|
|
|
238
|
+
begin
|
|
239
|
+
broadcast(message.content) if message.content
|
|
240
|
+
rescue BroadcastError => e
|
|
241
|
+
# Client disconnected, but continue processing
|
|
242
|
+
logger.warn "Broadcast failed: #{e.message}"
|
|
296
243
|
end
|
|
297
|
-
|
|
244
|
+
end
|
|
298
245
|
```
|
|
299
246
|
|
|
300
247
|
### 3. Clean Up Resources
|
|
301
248
|
|
|
302
249
|
```ruby
|
|
303
250
|
begin
|
|
304
|
-
|
|
251
|
+
robot.on_new_message do |message|
|
|
252
|
+
stream_to_client(message.content) if message.content
|
|
253
|
+
end
|
|
254
|
+
robot.run("Hello")
|
|
305
255
|
ensure
|
|
306
256
|
close_stream_connection
|
|
307
257
|
end
|