Class: SwarmSDK::Workflow::NodeBuilder
- Inherits:
-
Object
- Object
- SwarmSDK::Workflow::NodeBuilder
- Defined in:
- lib/swarm_sdk/workflow/node_builder.rb
Overview
NodeBuilder provides DSL for configuring individual nodes within a workflow
A node represents a stage in a multi-step workflow where a specific set of agents collaborate. Each node creates an independent swarm execution.
Instance Attribute Summary collapse
-
#agent_configs ⇒ Object
readonly
Returns the value of attribute agent_configs.
-
#dependencies ⇒ Object
readonly
Returns the value of attribute dependencies.
-
#input_transformer ⇒ Object
readonly
Returns the value of attribute input_transformer.
-
#input_transformer_command ⇒ Object
readonly
Returns the value of attribute input_transformer_command.
-
#lead_override ⇒ Object
readonly
Returns the value of attribute lead_override.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#output_transformer ⇒ Object
readonly
Returns the value of attribute output_transformer.
-
#output_transformer_command ⇒ Object
readonly
Returns the value of attribute output_transformer_command.
Instance Method Summary collapse
-
#agent(name, reset_context: true) ⇒ AgentConfig
Configure an agent for this node.
-
#agent_less? ⇒ Boolean
Check if this is an agent-less (computation-only) node.
-
#depends_on(*node_names) ⇒ void
Declare dependencies (nodes that must execute before this one).
-
#has_input_transformer? ⇒ Boolean
Check if node has any input transformer (block or command).
-
#has_output_transformer? ⇒ Boolean
Check if node has any output transformer (block or command).
-
#initialize(name) ⇒ NodeBuilder
constructor
A new instance of NodeBuilder.
-
#input {|NodeContext| ... } ⇒ String, Hash
Define input transformer for this node.
-
#input_command(command, timeout: nil) ⇒ void
Set input transformer as bash command (YAML API).
-
#lead(agent_name) ⇒ void
Override the lead agent (first agent is lead by default).
-
#lead_agent ⇒ Symbol
Get the lead agent for this node.
-
#output {|NodeContext| ... } ⇒ String, Hash
Define output transformer for this node.
-
#output_command(command, timeout: nil) ⇒ void
Set output transformer as bash command (YAML API).
-
#register_agent(agent_name, delegates_to, reset_context = true, tools = nil) ⇒ void
Register an agent configuration (called by AgentConfig).
-
#transform_input(context, current_input:) ⇒ String, Hash
Transform input using configured transformer (block or command).
-
#transform_output(context) ⇒ String, Hash
Transform output using configured transformer (block or command).
-
#validate! ⇒ void
Validate node configuration.
Constructor Details
#initialize(name) ⇒ NodeBuilder
Returns a new instance of NodeBuilder.
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 33 def initialize(name) @name = name @agent_configs = [] @dependencies = [] @lead_override = nil @input_transformer = nil # Ruby block @output_transformer = nil # Ruby block @input_transformer_command = nil # Bash command @output_transformer_command = nil # Bash command end |
Instance Attribute Details
#agent_configs ⇒ Object (readonly)
Returns the value of attribute agent_configs.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def agent_configs @agent_configs end |
#dependencies ⇒ Object (readonly)
Returns the value of attribute dependencies.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def dependencies @dependencies end |
#input_transformer ⇒ Object (readonly)
Returns the value of attribute input_transformer.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def input_transformer @input_transformer end |
#input_transformer_command ⇒ Object (readonly)
Returns the value of attribute input_transformer_command.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def input_transformer_command @input_transformer_command end |
#lead_override ⇒ Object (readonly)
Returns the value of attribute lead_override.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def lead_override @lead_override end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def name @name end |
#output_transformer ⇒ Object (readonly)
Returns the value of attribute output_transformer.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def output_transformer @output_transformer end |
#output_transformer_command ⇒ Object (readonly)
Returns the value of attribute output_transformer_command.
24 25 26 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 24 def output_transformer_command @output_transformer_command end |
Instance Method Details
#agent(name, reset_context: true) ⇒ AgentConfig
Configure an agent for this node
Returns an AgentConfig object that supports fluent delegation and tool override syntax. If delegates_to/tools are not called, the agent uses global configuration.
By default, agents get fresh context in each node (reset_context: true). Set reset_context: false to preserve conversation history across nodes.
70 71 72 73 74 75 76 77 78 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 70 def agent(name, reset_context: true) config = AgentConfig.new(name, self, reset_context: reset_context) # Register immediately with empty delegation and no tool override # If delegates_to/tools are called later, they will update this register_agent(name, [], reset_context, nil) config end |
#agent_less? ⇒ Boolean
Check if this is an agent-less (computation-only) node
Agent-less nodes run pure Ruby code without LLM execution. They must have at least one transformer (input or output).
453 454 455 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 453 def agent_less? @agent_configs.empty? end |
#depends_on(*node_names) ⇒ void
This method returns an undefined value.
Declare dependencies (nodes that must execute before this one)
117 118 119 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 117 def depends_on(*node_names) @dependencies.concat(node_names.map(&:to_sym)) end |
#has_input_transformer? ⇒ Boolean
Check if node has any input transformer (block or command)
300 301 302 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 300 def has_input_transformer? @input_transformer || @input_transformer_command end |
#has_output_transformer? ⇒ Boolean
Check if node has any output transformer (block or command)
307 308 309 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 307 def has_output_transformer? @output_transformer || @output_transformer_command end |
#input {|NodeContext| ... } ⇒ String, Hash
The input block is automatically converted to a lambda, which means return statements work safely and only exit the transformer, not the entire program. This allows natural control flow patterns.
Define input transformer for this node
The transformer receives a NodeContext object with access to:
- Previous node's result (convenience: ctx.content)
- Original user prompt (ctx.original_prompt)
- All previous node results (ctx.all_results)
- Current node metadata (ctx.node_name, ctx.dependencies)
Can also be used for side effects (logging, file I/O) since the block runs at execution time, not declaration time.
Control Flow: Return a hash with special keys to control execution:
skip_execution: true- Skip node's LLM execution, return content immediatelyhalt_workflow: true- Halt entire workflow with content as final resultgoto_node: :node_name- Jump to different node with content as input
198 199 200 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 198 def input(&block) @input_transformer = ProcHelpers.to_lambda(block) end |
#input_command(command, timeout: nil) ⇒ void
This method returns an undefined value.
Set input transformer as bash command (YAML API)
The command receives NodeContext as JSON on STDIN and outputs transformed content.
Exit codes:
- 0: Success, use STDOUT as transformed content
- 1: Skip node execution, use current_input unchanged (STDOUT ignored)
- 2: Halt workflow with error, show STDERR (STDOUT ignored)
217 218 219 220 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 217 def input_command(command, timeout: nil) timeout ||= SwarmSDK.config.transformer_command_timeout @input_transformer_command = { command: command, timeout: timeout } end |
#lead(agent_name) ⇒ void
This method returns an undefined value.
Override the lead agent (first agent is lead by default)
130 131 132 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 130 def lead(agent_name) @lead_override = agent_name.to_sym end |
#lead_agent ⇒ Symbol
Get the lead agent for this node
443 444 445 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 443 def lead_agent @lead_override || @agent_configs.first&.dig(:agent) end |
#output {|NodeContext| ... } ⇒ String, Hash
The output block is automatically converted to a lambda, which means return statements work safely and only exit the transformer, not the entire program. This allows natural control flow patterns.
Define output transformer for this node
The transformer receives a NodeContext object with access to:
- Current node's result (convenience: ctx.content)
- Original user prompt (ctx.original_prompt)
- All completed node results (ctx.all_results)
- Current node metadata (ctx.node_name)
Can also be used for side effects (logging, file I/O) since the block runs at execution time, not declaration time.
Control Flow: Return a hash with special keys to control execution:
halt_workflow: true- Halt entire workflow with content as final resultgoto_node: :node_name- Jump to different node with content as input
273 274 275 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 273 def output(&block) @output_transformer = ProcHelpers.to_lambda(block) end |
#output_command(command, timeout: nil) ⇒ void
This method returns an undefined value.
Set output transformer as bash command (YAML API)
The command receives NodeContext as JSON on STDIN and outputs transformed content.
Exit codes:
- 0: Success, use STDOUT as transformed content
- 1: Pass through unchanged, use result.content (STDOUT ignored)
- 2: Halt workflow with error, show STDERR (STDOUT ignored)
292 293 294 295 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 292 def output_command(command, timeout: nil) timeout ||= SwarmSDK.config.transformer_command_timeout @output_transformer_command = { command: command, timeout: timeout } end |
#register_agent(agent_name, delegates_to, reset_context = true, tools = nil) ⇒ void
This method returns an undefined value.
Register an agent configuration (called by AgentConfig)
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 87 def register_agent(agent_name, delegates_to, reset_context = true, tools = nil) # Check if agent already registered existing = @agent_configs.find { |ac| ac[:agent] == agent_name } if existing # Update delegation, reset_context, and tools (happens when methods are called after agent()) existing[:delegates_to] = delegates_to existing[:reset_context] = reset_context existing[:tools] = tools unless tools.nil? else # Add new agent configuration @agent_configs << { agent: agent_name, delegates_to: delegates_to, reset_context: reset_context, tools: tools, } end end |
#transform_input(context, current_input:) ⇒ String, Hash
Transform input using configured transformer (block or command)
Executes either Ruby block or bash command transformer.
Ruby block return values:
- String: Transformed content
- Hash with
skip_execution: true: Skip node execution - Hash with
halt_workflow: true: Halt entire workflow - Hash with
goto_node: :name: Jump to different node
Exit code behavior (bash commands only):
- Exit 0: Use STDOUT as transformed content
- Exit 1: Skip node execution, use current_input unchanged (STDOUT ignored)
- Exit 2: Halt workflow with error (STDOUT ignored)
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 330 def transform_input(context, current_input:) # No transformer configured: return content as-is return context.content unless @input_transformer || @input_transformer_command # Ruby block transformer # Ruby blocks can return String (transformed content) OR Hash (control flow) if @input_transformer result = @input_transformer.call(context) # If hash, validate control flow keys if result.is_a?(Hash) validate_transformer_hash(result, :input) end return result end # Bash command transformer # Bash commands use exit codes to control behavior: # - Exit 0: Success, use STDOUT as transformed content # - Exit 1: Skip node execution, use current_input unchanged (STDOUT ignored) # - Exit 2: Halt workflow with error (STDOUT ignored) if @input_transformer_command result = TransformerExecutor.execute( command: @input_transformer_command[:command], context: context, event: "input", node_name: @name, fallback_content: current_input, # Used for exit 1 (skip) timeout: @input_transformer_command[:timeout], ) # Handle transformer result based on exit code if result.halt? # Exit 2: Halt workflow with error raise ConfigurationError, "Input transformer halted workflow for node '#{@name}': #{result.}" elsif result.skip_execution? # Exit 1: Skip node execution, return skip hash # Content is current_input unchanged (STDOUT was ignored) { skip_execution: true, content: result.content } else # Exit 0: Return transformed content from STDOUT result.content end end end |
#transform_output(context) ⇒ String, Hash
Transform output using configured transformer (block or command)
Executes either Ruby block or bash command transformer.
Ruby block return values:
- String: Transformed content
- Hash with
halt_workflow: true: Halt entire workflow - Hash with
goto_node: :name: Jump to different node
Exit code behavior (bash commands only):
- Exit 0: Use STDOUT as transformed content
- Exit 1: Pass through unchanged, use result.content (STDOUT ignored)
- Exit 2: Halt workflow with error (STDOUT ignored)
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 395 def transform_output(context) # No transformer configured: return content as-is return context.content unless @output_transformer || @output_transformer_command # Ruby block transformer # Ruby blocks can return String (transformed content) OR Hash (control flow) if @output_transformer result = @output_transformer.call(context) # If hash, validate control flow keys if result.is_a?(Hash) validate_transformer_hash(result, :output) end return result end # Bash command transformer # Bash commands use exit codes to control behavior: # - Exit 0: Success, use STDOUT as transformed content # - Exit 1: Pass through unchanged, use result.content (STDOUT ignored) # - Exit 2: Halt workflow with error from STDERR (STDOUT ignored) if @output_transformer_command result = TransformerExecutor.execute( command: @output_transformer_command[:command], context: context, event: "output", node_name: @name, fallback_content: context.content, # result.content for exit 1 timeout: @output_transformer_command[:timeout], ) # Handle transformer result based on exit code if result.halt? # Exit 2: Halt workflow with error raise ConfigurationError, "Output transformer halted workflow for node '#{@name}': #{result.}" else # Exit 0: Return transformed content from STDOUT # Exit 1: Return fallback (result.content unchanged) result.content end end end |
#validate! ⇒ void
This method returns an undefined value.
Validate node configuration
Also auto-adds agents that are referenced in delegates_to but not explicitly declared. This allows writing: agent(:backend).delegates_to(:verifier) without needing: agent(:verifier)
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/swarm_sdk/workflow/node_builder.rb', line 465 def validate! # Auto-add agents mentioned in delegates_to but not explicitly declared auto_add_delegate_agents # Agent-less nodes (pure computation) are allowed but need transformers if @agent_configs.empty? unless has_input_transformer? || has_output_transformer? raise ConfigurationError, "Agent-less node '#{@name}' must have at least one transformer (input or output). " \ "Either add agents with agent(:name) or add input/output transformers." end end # If has agents, validate lead override if @lead_override && !@agent_configs.any? { |ac| ac[:agent] == @lead_override } raise ConfigurationError, "Node '#{@name}' lead agent '#{@lead_override}' not found in node's agents" end end |