robot_lab 0.0.4 → 0.0.7
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/CHANGELOG.md +76 -0
- data/README.md +64 -6
- data/Rakefile +2 -1
- data/docs/api/core/index.md +41 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +38 -26
- data/docs/api/core/state.md +55 -73
- data/docs/api/index.md +7 -28
- 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/architecture/core-concepts.md +10 -15
- data/docs/concepts.md +5 -7
- data/docs/examples/index.md +2 -2
- data/docs/getting-started/configuration.md +80 -0
- data/docs/guides/building-robots.md +10 -9
- data/docs/guides/creating-networks.md +49 -0
- data/docs/guides/index.md +0 -5
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +118 -138
- data/docs/index.md +0 -8
- data/examples/03_network.rb +10 -7
- data/examples/08_llm_config.rb +40 -11
- data/examples/09_chaining.rb +45 -6
- data/examples/11_network_introspection.rb +30 -7
- data/examples/12_message_bus.rb +1 -1
- data/examples/14_rusty_circuit/heckler.rb +14 -8
- data/examples/14_rusty_circuit/open_mic.rb +5 -3
- data/examples/14_rusty_circuit/scout.rb +14 -31
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +1 -1
- data/examples/16_writers_room/display.rb +158 -0
- data/examples/16_writers_room/output/.gitignore +4 -0
- data/examples/16_writers_room/output/README.md +69 -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/output/opus_002.md +245 -0
- data/examples/16_writers_room/output/opus_002_notes.log +546 -0
- data/examples/16_writers_room/output/opus_002_screenplay.md +7989 -0
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +993 -0
- data/examples/16_writers_room/prompts/screenplay_writer.md +66 -0
- data/examples/16_writers_room/prompts/writer.md +37 -0
- data/examples/16_writers_room/room.rb +186 -0
- data/examples/16_writers_room/tools.rb +173 -0
- data/examples/16_writers_room/writer.rb +121 -0
- data/examples/16_writers_room/writers_room.rb +256 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/memory.rb +8 -32
- 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 +56 -420
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +29 -8
- data/mkdocs.yml +0 -11
- metadata +21 -20
- 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 -275
- data/docs/api/history/config.md +0 -284
- data/docs/api/history/index.md +0 -128
- data/docs/api/history/thread-manager.md +0 -194
- data/docs/guides/history.md +0 -359
- 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 -160
- data/lib/robot_lab/adapters/registry.rb +0 -81
- 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,9 +2,9 @@
|
|
|
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
10
|
robot = RobotLab.build(
|
|
@@ -12,89 +12,105 @@ robot = RobotLab.build(
|
|
|
12
12
|
system_prompt: "You are a creative storyteller."
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
# Register streaming callback
|
|
16
|
+
robot.on_new_message do |message|
|
|
17
|
+
print message.content if message.content
|
|
17
18
|
end
|
|
19
|
+
|
|
20
|
+
result = robot.run("Tell me a story about a brave robot")
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
##
|
|
23
|
+
## Available Callbacks
|
|
21
24
|
|
|
22
|
-
###
|
|
25
|
+
### on_new_message
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
Called when the assistant starts generating a new message, with streaming chunks:
|
|
25
28
|
|
|
26
29
|
```ruby
|
|
27
|
-
robot.
|
|
28
|
-
|
|
29
|
-
print event[:data][:content]
|
|
30
|
-
end
|
|
30
|
+
robot.on_new_message do |message|
|
|
31
|
+
print message.content if message.content
|
|
31
32
|
end
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
###
|
|
35
|
+
### on_end_message
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
Called when the assistant finishes a message:
|
|
37
38
|
|
|
38
39
|
```ruby
|
|
39
|
-
robot.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
puts "\nCalling: #{event[:data][:name]}"
|
|
43
|
-
when "tool_call.complete"
|
|
44
|
-
puts "Done: #{event[:data][:result]}"
|
|
45
|
-
end
|
|
40
|
+
robot.on_end_message do |message|
|
|
41
|
+
puts "\n--- Response complete ---"
|
|
42
|
+
puts "Content length: #{message.content&.length}"
|
|
46
43
|
end
|
|
47
44
|
```
|
|
48
45
|
|
|
49
|
-
###
|
|
46
|
+
### on_tool_call
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
Called when the LLM invokes a tool:
|
|
52
49
|
|
|
53
50
|
```ruby
|
|
54
|
-
robot.
|
|
55
|
-
|
|
56
|
-
when "run.started"
|
|
57
|
-
puts "Starting..."
|
|
58
|
-
when "run.completed"
|
|
59
|
-
puts "Completed!"
|
|
60
|
-
when "run.failed"
|
|
61
|
-
puts "Failed: #{event[:data][:error]}"
|
|
62
|
-
end
|
|
51
|
+
robot.on_tool_call do |tool_call|
|
|
52
|
+
puts "Calling tool: #{tool_call.name}"
|
|
63
53
|
end
|
|
64
54
|
```
|
|
65
55
|
|
|
66
|
-
|
|
56
|
+
### on_tool_result
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
| `tool_call.complete` | Tool execution done | `name`, `result` |
|
|
58
|
+
Called when a tool returns its result:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
robot.on_tool_result do |tool_call, result|
|
|
62
|
+
puts "Tool #{tool_call.name} returned: #{result}"
|
|
63
|
+
end
|
|
64
|
+
```
|
|
76
65
|
|
|
77
|
-
## Comprehensive
|
|
66
|
+
## Comprehensive Callback Setup
|
|
78
67
|
|
|
79
|
-
|
|
68
|
+
Register all callbacks for full visibility:
|
|
80
69
|
|
|
81
70
|
```ruby
|
|
82
|
-
robot.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
71
|
+
robot = RobotLab.build(
|
|
72
|
+
name: "assistant",
|
|
73
|
+
system_prompt: "You are helpful.",
|
|
74
|
+
local_tools: [WeatherTool]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
robot.on_new_message do |message|
|
|
78
|
+
print message.content if message.content
|
|
79
|
+
end
|
|
80
|
+
|
|
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}"
|
|
95
91
|
end
|
|
92
|
+
|
|
93
|
+
result = robot.run("What's the weather in Tokyo?")
|
|
96
94
|
```
|
|
97
95
|
|
|
96
|
+
## Streaming via Chat Block
|
|
97
|
+
|
|
98
|
+
For more control, pass a block directly to `chat.ask` (the underlying RubyLLM method):
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
robot = RobotLab.build(
|
|
102
|
+
name: "chat_bot",
|
|
103
|
+
system_prompt: "You are a helpful assistant."
|
|
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
|
|
110
|
+
```
|
|
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
|
+
|
|
98
114
|
## Web Integration
|
|
99
115
|
|
|
100
116
|
### Rails Action Cable
|
|
@@ -107,9 +123,15 @@ class ChatChannel < ApplicationCable::Channel
|
|
|
107
123
|
system_prompt: "You are a helpful chat assistant."
|
|
108
124
|
)
|
|
109
125
|
|
|
110
|
-
robot.
|
|
111
|
-
transmit(event)
|
|
126
|
+
robot.on_new_message do |message|
|
|
127
|
+
transmit({ event: "text.delta", content: message.content }) if message.content
|
|
112
128
|
end
|
|
129
|
+
|
|
130
|
+
robot.on_end_message do |_message|
|
|
131
|
+
transmit({ event: "run.completed" })
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
robot.run(data["message"])
|
|
113
135
|
end
|
|
114
136
|
end
|
|
115
137
|
```
|
|
@@ -128,9 +150,15 @@ class StreamController < ApplicationController
|
|
|
128
150
|
system_prompt: "You are helpful."
|
|
129
151
|
)
|
|
130
152
|
|
|
131
|
-
robot.
|
|
132
|
-
response.stream.write("data: #{
|
|
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")
|
|
133
159
|
end
|
|
160
|
+
|
|
161
|
+
robot.run(params[:message])
|
|
134
162
|
ensure
|
|
135
163
|
response.stream.close
|
|
136
164
|
end
|
|
@@ -142,38 +170,17 @@ end
|
|
|
142
170
|
```ruby
|
|
143
171
|
# Using Faye WebSocket
|
|
144
172
|
ws.on :message do |msg|
|
|
145
|
-
robot.
|
|
146
|
-
ws.send(
|
|
173
|
+
robot.on_new_message do |message|
|
|
174
|
+
ws.send(message.content) if message.content
|
|
147
175
|
end
|
|
148
|
-
end
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## Buffering
|
|
152
176
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```ruby
|
|
156
|
-
buffer = []
|
|
157
|
-
|
|
158
|
-
robot.run("Generate a long response") do |event|
|
|
159
|
-
if event[:event] == "text.delta"
|
|
160
|
-
buffer << event[:data][:content]
|
|
161
|
-
|
|
162
|
-
# Flush every 10 chunks
|
|
163
|
-
if buffer.size >= 10
|
|
164
|
-
process_batch(buffer.join)
|
|
165
|
-
buffer.clear
|
|
166
|
-
end
|
|
167
|
-
end
|
|
177
|
+
robot.run(msg.data)
|
|
168
178
|
end
|
|
169
|
-
|
|
170
|
-
# Final flush
|
|
171
|
-
process_batch(buffer.join) if buffer.any?
|
|
172
179
|
```
|
|
173
180
|
|
|
174
181
|
## Progress Tracking
|
|
175
182
|
|
|
176
|
-
Track streaming progress:
|
|
183
|
+
Track streaming progress with callbacks:
|
|
177
184
|
|
|
178
185
|
```ruby
|
|
179
186
|
class StreamProgress
|
|
@@ -182,49 +189,31 @@ class StreamProgress
|
|
|
182
189
|
@tools = 0
|
|
183
190
|
end
|
|
184
191
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
189
197
|
print "\rReceived #{@chars} characters..."
|
|
190
|
-
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
robot.on_tool_call do |tool_call|
|
|
191
201
|
@tools += 1
|
|
192
|
-
puts "\nTool call ##{@tools}: #{
|
|
202
|
+
puts "\nTool call ##{@tools}: #{tool_call.name}"
|
|
193
203
|
end
|
|
194
204
|
end
|
|
195
205
|
end
|
|
196
206
|
|
|
197
207
|
progress = StreamProgress.new
|
|
208
|
+
progress.attach(robot)
|
|
198
209
|
|
|
199
|
-
robot.run("Process this complex request")
|
|
200
|
-
|
|
201
|
-
end
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Error Handling
|
|
205
|
-
|
|
206
|
-
Handle streaming errors gracefully:
|
|
207
|
-
|
|
208
|
-
```ruby
|
|
209
|
-
robot.run("Analyze this") do |event|
|
|
210
|
-
case event[:event]
|
|
211
|
-
when "run.failed"
|
|
212
|
-
log_error(event[:data][:error])
|
|
213
|
-
notify_user("An error occurred")
|
|
214
|
-
when "text.delta"
|
|
215
|
-
begin
|
|
216
|
-
broadcast(event)
|
|
217
|
-
rescue BroadcastError => e
|
|
218
|
-
# Client disconnected, but continue processing
|
|
219
|
-
logger.warn "Broadcast failed: #{e.message}"
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
end
|
|
210
|
+
result = robot.run("Process this complex request")
|
|
211
|
+
puts "\nTotal: #{progress.chars} chars, #{progress.tools} tool calls"
|
|
223
212
|
```
|
|
224
213
|
|
|
225
214
|
## Without Streaming
|
|
226
215
|
|
|
227
|
-
When streaming is not needed, simply call `run` without
|
|
216
|
+
When streaming is not needed, simply call `run` without registering callbacks:
|
|
228
217
|
|
|
229
218
|
```ruby
|
|
230
219
|
# No streaming - returns RobotResult directly
|
|
@@ -234,33 +223,23 @@ puts result.last_text_content
|
|
|
234
223
|
|
|
235
224
|
## Best Practices
|
|
236
225
|
|
|
237
|
-
### 1.
|
|
226
|
+
### 1. Register Callbacks Before Run
|
|
238
227
|
|
|
239
228
|
```ruby
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
when "tool_call.start" then show_tool_indicator(event)
|
|
244
|
-
when "tool_call.complete" then hide_tool_indicator(event)
|
|
245
|
-
when "run.completed" then finalize_response
|
|
246
|
-
when "run.failed" then show_error(event)
|
|
247
|
-
end
|
|
248
|
-
end
|
|
229
|
+
# Correct: register first, then run
|
|
230
|
+
robot.on_new_message { |msg| print msg.content if msg.content }
|
|
231
|
+
robot.run("Hello")
|
|
249
232
|
```
|
|
250
233
|
|
|
251
|
-
### 2.
|
|
234
|
+
### 2. Handle Errors in Callbacks
|
|
252
235
|
|
|
253
236
|
```ruby
|
|
254
|
-
robot.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
when "tool_call.start"
|
|
261
|
-
show_status("Looking up information...")
|
|
262
|
-
when "run.completed"
|
|
263
|
-
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}"
|
|
264
243
|
end
|
|
265
244
|
end
|
|
266
245
|
```
|
|
@@ -269,9 +248,10 @@ end
|
|
|
269
248
|
|
|
270
249
|
```ruby
|
|
271
250
|
begin
|
|
272
|
-
robot.
|
|
273
|
-
stream_to_client(
|
|
251
|
+
robot.on_new_message do |message|
|
|
252
|
+
stream_to_client(message.content) if message.content
|
|
274
253
|
end
|
|
254
|
+
robot.run("Hello")
|
|
275
255
|
ensure
|
|
276
256
|
close_stream_connection
|
|
277
257
|
end
|
data/docs/index.md
CHANGED
|
@@ -68,14 +68,6 @@ Each robot is backed by a persistent LLM chat, configured with keyword arguments
|
|
|
68
68
|
|
|
69
69
|
[:octicons-arrow-right-24: Memory System](guides/memory.md)
|
|
70
70
|
|
|
71
|
-
- :material-history:{ .lg .middle } **Conversation History**
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
Persist and restore conversation threads for long-running interactions.
|
|
76
|
-
|
|
77
|
-
[:octicons-arrow-right-24: History Guide](guides/history.md)
|
|
78
|
-
|
|
79
71
|
</div>
|
|
80
72
|
|
|
81
73
|
## Quick Example
|
data/examples/03_network.rb
CHANGED
|
@@ -39,33 +39,36 @@ class ClassifierRobot < RobotLab::Robot
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
#
|
|
42
|
+
# Shared RunConfig — all robots in this network use the same model
|
|
43
|
+
shared_config = RobotLab::RunConfig.new(model: "claude-3-haiku-20240307")
|
|
44
|
+
|
|
45
|
+
# Create specialized robots (no model: needed — inherited from RunConfig)
|
|
43
46
|
classifier = ClassifierRobot.new(
|
|
44
47
|
name: "classifier",
|
|
45
48
|
template: :classifier,
|
|
46
|
-
|
|
49
|
+
config: shared_config
|
|
47
50
|
)
|
|
48
51
|
|
|
49
52
|
billing_robot = RobotLab.build(
|
|
50
53
|
name: "billing",
|
|
51
54
|
template: :billing,
|
|
52
|
-
|
|
55
|
+
config: shared_config
|
|
53
56
|
)
|
|
54
57
|
|
|
55
58
|
technical_robot = RobotLab.build(
|
|
56
59
|
name: "technical",
|
|
57
60
|
template: :technical,
|
|
58
|
-
|
|
61
|
+
config: shared_config
|
|
59
62
|
)
|
|
60
63
|
|
|
61
64
|
general_robot = RobotLab.build(
|
|
62
65
|
name: "general",
|
|
63
66
|
template: :general,
|
|
64
|
-
|
|
67
|
+
config: shared_config
|
|
65
68
|
)
|
|
66
69
|
|
|
67
|
-
# Create network with optional task routing
|
|
68
|
-
network = RobotLab.create_network(name: "support_network") do
|
|
70
|
+
# Create network with optional task routing and shared config
|
|
71
|
+
network = RobotLab.create_network(name: "support_network", config: shared_config) do
|
|
69
72
|
task :classifier, classifier, depends_on: :none
|
|
70
73
|
task :billing, billing_robot, depends_on: :optional
|
|
71
74
|
task :technical, technical_robot, depends_on: :optional
|
data/examples/08_llm_config.rb
CHANGED
|
@@ -77,20 +77,43 @@ puts "-" * 70
|
|
|
77
77
|
puts "Configuration hierarchy (highest priority first):"
|
|
78
78
|
puts
|
|
79
79
|
puts " Per-Robot (override global settings for a specific robot):"
|
|
80
|
-
puts "
|
|
81
|
-
puts "
|
|
82
|
-
puts "
|
|
83
|
-
puts "
|
|
80
|
+
puts " 11. Run-time context: kwargs to robot.run re-render template"
|
|
81
|
+
puts " 10. with_* methods: robot.with_temperature(0.9).ask(...)"
|
|
82
|
+
puts " 9. Constructor params: Robot.new(model: ..., temperature: ...)"
|
|
83
|
+
puts " 8. Template front matter: model, temperature, etc. in .md YAML header"
|
|
84
|
+
puts " 7. Robot RunConfig: config: passed to Robot constructor"
|
|
85
|
+
puts
|
|
86
|
+
puts " Network/Task (shared defaults for a group of robots):"
|
|
87
|
+
puts " 6. Task RunConfig: config: passed to task() in network"
|
|
88
|
+
puts " 5. Network RunConfig: config: passed to create_network()"
|
|
84
89
|
puts
|
|
85
90
|
puts " Global (apply to all robots unless overridden):"
|
|
86
|
-
puts "
|
|
87
|
-
puts "
|
|
88
|
-
puts "
|
|
89
|
-
puts "
|
|
90
|
-
puts "
|
|
91
|
+
puts " 4. Environment variables: ROBOT_LAB_RUBY_LLM__MODEL, etc."
|
|
92
|
+
puts " 3. Project config: ./config/robot_lab.yml"
|
|
93
|
+
puts " 2. User config: ~/.config/robot_lab/config.yml"
|
|
94
|
+
puts " 1. Bundled defaults: lib/robot_lab/config/defaults.yml + env overrides"
|
|
95
|
+
puts "-" * 70
|
|
96
|
+
puts
|
|
97
|
+
|
|
98
|
+
# --- RunConfig demonstration ---
|
|
99
|
+
puts "-" * 70
|
|
100
|
+
puts "RunConfig: Shared Operational Defaults"
|
|
91
101
|
puts "-" * 70
|
|
92
102
|
puts
|
|
93
103
|
|
|
104
|
+
shared = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
|
|
105
|
+
puts " shared = RunConfig.new(model: \"claude-sonnet-4\", temperature: 0.5)"
|
|
106
|
+
puts " shared.to_h => #{shared.to_h.inspect}"
|
|
107
|
+
puts
|
|
108
|
+
|
|
109
|
+
creative = RobotLab::RunConfig.new(temperature: 0.9)
|
|
110
|
+
merged = shared.merge(creative)
|
|
111
|
+
puts " creative = RunConfig.new(temperature: 0.9)"
|
|
112
|
+
puts " merged = shared.merge(creative)"
|
|
113
|
+
puts " merged.to_h => #{merged.to_h.inspect}"
|
|
114
|
+
puts " (model inherited from shared, temperature overridden by creative)"
|
|
115
|
+
puts
|
|
116
|
+
|
|
94
117
|
# Create a robot using the configuration
|
|
95
118
|
robot = RobotLab.build(
|
|
96
119
|
name: "config_demo",
|
|
@@ -124,9 +147,15 @@ puts <<~FOOTER
|
|
|
124
147
|
#{"=" * 70}
|
|
125
148
|
Configuration demonstrated successfully!
|
|
126
149
|
|
|
127
|
-
Configuration flows through
|
|
150
|
+
Configuration flows through three layers:
|
|
128
151
|
Global (MywayConfig): YAML defaults, env overrides, XDG, env vars
|
|
129
|
-
|
|
152
|
+
Network/Task (RunConfig): shared defaults for groups of robots
|
|
153
|
+
Per-Robot: RunConfig, template front matter, constructor params, with_*
|
|
154
|
+
|
|
155
|
+
RunConfig lets you express shared defaults that flow through the hierarchy:
|
|
156
|
+
shared = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
|
|
157
|
+
network = RobotLab.create_network(name: "team", config: shared) { ... }
|
|
158
|
+
robot = RobotLab.build(name: "bot", config: shared, temperature: 0.9)
|
|
130
159
|
|
|
131
160
|
Example environment variable overrides:
|
|
132
161
|
ROBOT_LAB_RUBY_LLM__MODEL=gpt-4
|
data/examples/09_chaining.rb
CHANGED
|
@@ -136,10 +136,49 @@ prev2 = show_config(robot2)
|
|
|
136
136
|
puts
|
|
137
137
|
|
|
138
138
|
# =============================================================================
|
|
139
|
-
# Section 5:
|
|
139
|
+
# Section 5: RunConfig as alternative to individual kwargs
|
|
140
140
|
# =============================================================================
|
|
141
141
|
|
|
142
|
-
puts "--- Section 5:
|
|
142
|
+
puts "--- Section 5: RunConfig as Alternative to Individual kwargs ---"
|
|
143
|
+
puts
|
|
144
|
+
|
|
145
|
+
# Instead of passing model:, temperature:, etc. individually,
|
|
146
|
+
# use a RunConfig to express shared defaults.
|
|
147
|
+
shared = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
|
|
148
|
+
|
|
149
|
+
puts "RunConfig: #{shared.to_h.inspect}"
|
|
150
|
+
puts
|
|
151
|
+
|
|
152
|
+
# Robot inherits from RunConfig; constructor kwargs still override
|
|
153
|
+
robot3 = RobotLab.build(
|
|
154
|
+
name: "runconfig_demo",
|
|
155
|
+
template: :configurable,
|
|
156
|
+
context: { task_type: "analysis" },
|
|
157
|
+
config: shared,
|
|
158
|
+
temperature: 0.8 # overrides RunConfig's 0.5
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
puts "Robot with config: shared, temperature: 0.8"
|
|
162
|
+
puts "(RunConfig sets 0.5, constructor overrides to 0.8)"
|
|
163
|
+
prev3 = show_config(robot3)
|
|
164
|
+
puts
|
|
165
|
+
|
|
166
|
+
# RunConfig merge semantics
|
|
167
|
+
creative = RobotLab::RunConfig.new(temperature: 0.9, max_tokens: 2000)
|
|
168
|
+
merged = shared.merge(creative)
|
|
169
|
+
|
|
170
|
+
puts "Merge: shared.merge(creative)"
|
|
171
|
+
puts " shared: #{shared.to_h.inspect}"
|
|
172
|
+
puts " creative: #{creative.to_h.inspect}"
|
|
173
|
+
puts " merged: #{merged.to_h.inspect}"
|
|
174
|
+
puts " (model inherited, temperature overridden, max_tokens added)"
|
|
175
|
+
puts
|
|
176
|
+
|
|
177
|
+
# =============================================================================
|
|
178
|
+
# Section 6: Bare robot with chaining
|
|
179
|
+
# =============================================================================
|
|
180
|
+
|
|
181
|
+
puts "--- Section 6: Bare Robot with Chaining ---"
|
|
143
182
|
puts
|
|
144
183
|
|
|
145
184
|
# A bare robot has no template. Configure entirely via chaining.
|
|
@@ -158,10 +197,10 @@ prev_bare = show_config(bare, prev_bare)
|
|
|
158
197
|
puts
|
|
159
198
|
|
|
160
199
|
# =============================================================================
|
|
161
|
-
# Section
|
|
200
|
+
# Section 7: with_template() on an existing robot
|
|
162
201
|
# =============================================================================
|
|
163
202
|
|
|
164
|
-
puts "--- Section
|
|
203
|
+
puts "--- Section 7: with_template() on Existing Robot ---"
|
|
165
204
|
puts
|
|
166
205
|
|
|
167
206
|
# You can apply a template to a robot after creation.
|
|
@@ -172,10 +211,10 @@ show_config(bare, prev_bare)
|
|
|
172
211
|
puts
|
|
173
212
|
|
|
174
213
|
# =============================================================================
|
|
175
|
-
# Section
|
|
214
|
+
# Section 8: AskUser tool for gathering template parameters
|
|
176
215
|
# =============================================================================
|
|
177
216
|
|
|
178
|
-
puts "--- Section
|
|
217
|
+
puts "--- Section 8: AskUser for Template Parameters ---"
|
|
179
218
|
puts
|
|
180
219
|
puts "The :configurable template declares `task_type: general` in its front"
|
|
181
220
|
puts "matter. That default is offered to the user — they can accept it by"
|
|
@@ -55,16 +55,22 @@ puts "Example 11: Network Visualization & Introspection"
|
|
|
55
55
|
puts "=" * 70
|
|
56
56
|
puts
|
|
57
57
|
|
|
58
|
+
# Shared RunConfig for all robots in this network
|
|
59
|
+
shared_config = RobotLab::RunConfig.new(model: "claude-sonnet-4", temperature: 0.5)
|
|
60
|
+
|
|
61
|
+
# Per-task RunConfig override for the writer (higher creativity)
|
|
62
|
+
creative_config = RobotLab::RunConfig.new(temperature: 0.9)
|
|
63
|
+
|
|
58
64
|
# Build robots (no LLM calls, just instances)
|
|
59
65
|
classifier = RobotLab.build(name: "classifier", system_prompt: "Classify input")
|
|
60
66
|
analyst = RobotLab.build(name: "analyst", system_prompt: "Analyze data")
|
|
61
67
|
writer = RobotLab.build(name: "writer", system_prompt: "Write summary")
|
|
62
68
|
|
|
63
|
-
# Build network with dependencies and per-task config
|
|
64
|
-
network = RobotLab.create_network(name: "demo_pipeline") do
|
|
69
|
+
# Build network with RunConfig, dependencies, and per-task config
|
|
70
|
+
network = RobotLab.create_network(name: "demo_pipeline", config: shared_config) do
|
|
65
71
|
task :classify, classifier, depends_on: :none
|
|
66
72
|
task :analyze, analyst, context: { depth: "deep" }, depends_on: [:classify]
|
|
67
|
-
task :write, writer, depends_on: [:analyze]
|
|
73
|
+
task :write, writer, config: creative_config, depends_on: [:analyze]
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
# =============================================================================
|
|
@@ -179,10 +185,27 @@ ap network["analyze"].to_h
|
|
|
179
185
|
puts
|
|
180
186
|
|
|
181
187
|
# =============================================================================
|
|
182
|
-
# Section 5:
|
|
188
|
+
# Section 5: RunConfig Introspection
|
|
189
|
+
# =============================================================================
|
|
190
|
+
|
|
191
|
+
puts "--- Section 5: RunConfig Introspection ---"
|
|
192
|
+
puts
|
|
193
|
+
|
|
194
|
+
puts "Network RunConfig (shared defaults):"
|
|
195
|
+
ap network.config.to_h
|
|
196
|
+
puts
|
|
197
|
+
|
|
198
|
+
puts "Merged effective config for :write task (network + task override):"
|
|
199
|
+
effective = shared_config.merge(creative_config)
|
|
200
|
+
ap effective.to_h
|
|
201
|
+
puts " model inherited from network, temperature overridden by task"
|
|
202
|
+
puts
|
|
203
|
+
|
|
204
|
+
# =============================================================================
|
|
205
|
+
# Section 6: Broadcast
|
|
183
206
|
# =============================================================================
|
|
184
207
|
|
|
185
|
-
puts "--- Section
|
|
208
|
+
puts "--- Section 6: Broadcast ---"
|
|
186
209
|
puts
|
|
187
210
|
|
|
188
211
|
broadcast_messages = []
|
|
@@ -207,10 +230,10 @@ end
|
|
|
207
230
|
puts
|
|
208
231
|
|
|
209
232
|
# =============================================================================
|
|
210
|
-
# Section
|
|
233
|
+
# Section 7: Shared memory access
|
|
211
234
|
# =============================================================================
|
|
212
235
|
|
|
213
|
-
puts "--- Section
|
|
236
|
+
puts "--- Section 7: Shared Network Memory ---"
|
|
214
237
|
puts
|
|
215
238
|
|
|
216
239
|
puts "network.memory is a #{network.memory.class}"
|
data/examples/12_message_bus.rb
CHANGED
|
@@ -31,7 +31,7 @@ class Comedian < RobotLab::Robot
|
|
|
31
31
|
with_temperature(temp)
|
|
32
32
|
joke = run(message.content.to_s).reply.strip
|
|
33
33
|
puts " Bob [##{@attempts}, t=#{"%.1f" % temp}]: #{joke}"
|
|
34
|
-
|
|
34
|
+
send_reply(to: message.from.to_sym, content: joke, in_reply_to: message.key)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|