Class: RobotLab::Network
- Inherits:
-
Object
- Object
- RobotLab::Network
- Includes:
- Utils
- Defined in:
- lib/robot_lab/network.rb
Overview
Orchestrates multiple robots in a pipeline workflow
Network is a thin wrapper around SimpleFlow::Pipeline that provides a clean DSL for defining robot workflows with sequential, parallel, and conditional execution.
Shared Memory
Networks provide a shared reactive memory that all robots can read and write. Robots can subscribe to memory keys and be notified when values change, or use blocking reads to wait for values from other robots.
Broadcast Messages
Networks support a broadcast channel for network-wide announcements. Use ‘broadcast` to send messages to all robots, and `on_broadcast` to register handlers for incoming broadcasts.
Constant Summary collapse
- BROADCAST_KEY =
Reserved key for broadcast messages in memory
:_network_broadcast
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#memory ⇒ Memory
readonly
Shared memory for all robots in the network.
-
#name ⇒ String
readonly
Unique identifier for the network.
-
#parallel_mode ⇒ Object
readonly
Returns the value of attribute parallel_mode.
-
#pipeline ⇒ SimpleFlow::Pipeline
readonly
The underlying pipeline.
-
#robots ⇒ Hash<String, Robot>
readonly
Robots in this network, keyed by name.
Instance Method Summary collapse
-
#add_robot(robot) ⇒ self
Add a robot to the network without adding it as a task.
-
#available_robots ⇒ Array<Robot>
Get all robots in the network.
-
#broadcast(payload) ⇒ self
Broadcast a message to all robots in the network.
-
#execution_plan ⇒ String?
Get the execution plan.
-
#initialize(name:, concurrency: :auto, memory: nil, config: nil, parallel_mode: :async) { ... } ⇒ Network
constructor
Creates a new Network instance.
-
#on_broadcast {|Hash| ... } ⇒ self
Register a handler for broadcast messages.
-
#parallel(name = nil, depends_on: :none) { ... } ⇒ self
Define a parallel execution block.
-
#reset_memory ⇒ self
Reset the shared memory.
-
#robot(name) ⇒ Robot?
(also: #[])
Get a robot by name.
-
#run(**run_context) ⇒ SimpleFlow::Result
Run the network with the given context.
-
#task(name, robot, context: {}, mcp: :none, tools: :none, memory: nil, config: nil, depends_on: :none, poller_group: :default) ⇒ self
Add a robot as a pipeline task with optional per-task configuration.
-
#to_dot ⇒ String?
Export pipeline to DOT format (Graphviz).
-
#to_h ⇒ Hash
Converts the network to a hash representation.
-
#to_mermaid ⇒ String?
Export pipeline to Mermaid format.
-
#visualize ⇒ String?
Visualize the pipeline as ASCII.
Constructor Details
#initialize(name:, concurrency: :auto, memory: nil, config: nil, parallel_mode: :async) { ... } ⇒ Network
Creates a new Network instance.
89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/robot_lab/network.rb', line 89 def initialize(name:, concurrency: :auto, memory: nil, config: nil, parallel_mode: :async, &) @name = name.to_s @robots = {} @tasks = {} @pipeline = SimpleFlow::Pipeline.new(concurrency: concurrency) @memory = memory || Memory.new(network_name: @name) @config = config || RunConfig.new @parallel_mode = parallel_mode @broadcast_handlers = [] @bus_poller = BusPoller.new.start instance_eval(&) if block_given? end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
74 75 76 |
# File 'lib/robot_lab/network.rb', line 74 def config @config end |
#memory ⇒ Memory (readonly)
Returns shared memory for all robots in the network.
74 |
# File 'lib/robot_lab/network.rb', line 74 attr_reader :name, :pipeline, :robots, :memory, :config, :parallel_mode |
#name ⇒ String (readonly)
Returns unique identifier for the network.
74 75 76 |
# File 'lib/robot_lab/network.rb', line 74 def name @name end |
#parallel_mode ⇒ Object (readonly)
Returns the value of attribute parallel_mode.
74 75 76 |
# File 'lib/robot_lab/network.rb', line 74 def parallel_mode @parallel_mode end |
#pipeline ⇒ SimpleFlow::Pipeline (readonly)
Returns the underlying pipeline.
74 |
# File 'lib/robot_lab/network.rb', line 74 attr_reader :name, :pipeline, :robots, :memory, :config, :parallel_mode |
#robots ⇒ Hash<String, Robot> (readonly)
Returns robots in this network, keyed by name.
74 |
# File 'lib/robot_lab/network.rb', line 74 attr_reader :name, :pipeline, :robots, :memory, :config, :parallel_mode |
Instance Method Details
#add_robot(robot) ⇒ self
Add a robot to the network without adding it as a task
Useful for dynamically adding robots that will be referenced later.
297 298 299 300 301 302 303 304 |
# File 'lib/robot_lab/network.rb', line 297 def add_robot(robot) if @robots.key?(robot.name) raise ArgumentError, "Robot '#{robot.name}' already exists in network '#{@name}'" end @robots[robot.name] = robot self end |
#available_robots ⇒ Array<Robot>
Get all robots in the network
285 286 287 |
# File 'lib/robot_lab/network.rb', line 285 def available_robots @robots.values end |
#broadcast(payload) ⇒ self
Broadcast a message to all robots in the network.
This sends a network-wide message that all robots subscribed via ‘on_broadcast` will receive asynchronously.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/robot_lab/network.rb', line 212 def broadcast(payload) = { payload: payload, network: @name, timestamp: Time.now } # Notify handlers asynchronously @broadcast_handlers.each do |handler| dispatch_async { handler.call() } end # Also set in memory so robots can subscribe via memory.subscribe @memory.set(BROADCAST_KEY, ) self end |
#execution_plan ⇒ String?
Get the execution plan
334 335 336 |
# File 'lib/robot_lab/network.rb', line 334 def execution_plan @pipeline.execution_plan end |
#on_broadcast {|Hash| ... } ⇒ self
Register a handler for broadcast messages.
The handler is called asynchronously whenever ‘broadcast` is called.
247 248 249 250 251 252 |
# File 'lib/robot_lab/network.rb', line 247 def on_broadcast(&block) raise ArgumentError, "Block required for on_broadcast" unless block_given? @broadcast_handlers << block self end |
#parallel(name = nil, depends_on: :none) { ... } ⇒ self
Define a parallel execution block
162 163 164 165 |
# File 'lib/robot_lab/network.rb', line 162 def parallel(name = nil, depends_on: :none, &) @pipeline.parallel(name, depends_on: depends_on, &) self end |
#reset_memory ⇒ self
Reset the shared memory.
Clears all values in the network’s shared memory. This is useful between runs if you want to start with a fresh memory state.
261 262 263 264 |
# File 'lib/robot_lab/network.rb', line 261 def reset_memory @memory.reset self end |
#robot(name) ⇒ Robot? Also known as: []
Get a robot by name
271 272 273 |
# File 'lib/robot_lab/network.rb', line 271 def robot(name) @robots[name.to_s] end |
#run(**run_context) ⇒ SimpleFlow::Result
Run the network with the given context
All robots share the network’s memory during execution. The memory is passed to each robot and can be used for inter-robot communication.
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/robot_lab/network.rb', line 180 def run(**run_context) # Include shared memory in run params so robots can access it run_context[:network_memory] = @memory # Pass network's config so robots can inherit it run_context[:network_config] = @config unless @config.empty? if @parallel_mode == :ractor run_with_ractor_scheduler(run_context) else initial_result = SimpleFlow::Result.new( run_context, context: { run_params: run_context } ) @pipeline.call_parallel(initial_result, max_concurrent: @config.max_concurrent_robots) end end |
#task(name, robot, context: {}, mcp: :none, tools: :none, memory: nil, config: nil, depends_on: :none, poller_group: :default) ⇒ self
Add a robot as a pipeline task with optional per-task configuration
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/robot_lab/network.rb', line 126 def task(name, robot, context: {}, mcp: :none, tools: :none, memory: nil, config: nil, depends_on: :none, poller_group: :default) task_wrapper = Task.new( name: name, robot: robot, context: context, mcp: mcp, tools: tools, memory: memory, config: config ) # Register the group and assign the shared poller to the robot @bus_poller.add_group(poller_group) robot.assign_bus_poller(@bus_poller, group: poller_group) if robot.respond_to?(:assign_bus_poller, true) @robots[name.to_s] = robot @tasks[name.to_s] = task_wrapper @pipeline.step(name, task_wrapper, depends_on: depends_on) self end |
#to_dot ⇒ String?
Export pipeline to DOT format (Graphviz)
326 327 328 |
# File 'lib/robot_lab/network.rb', line 326 def to_dot @pipeline.visualize_dot end |
#to_h ⇒ Hash
Converts the network to a hash representation
342 343 344 345 346 347 348 349 350 |
# File 'lib/robot_lab/network.rb', line 342 def to_h { name: name, robots: @robots.keys, tasks: @tasks.keys, optional_tasks: @pipeline.optional_steps.to_a, config: (@config.empty? ? nil : @config.to_json_hash) }.compact end |
#to_mermaid ⇒ String?
Export pipeline to Mermaid format
318 319 320 |
# File 'lib/robot_lab/network.rb', line 318 def to_mermaid @pipeline.visualize_mermaid end |
#visualize ⇒ String?
Visualize the pipeline as ASCII
310 311 312 |
# File 'lib/robot_lab/network.rb', line 310 def visualize @pipeline.visualize_ascii end |