Class: Boxcars::AgentRunner

Inherits:
Object
  • Object
show all
Defined in:
lib/boxcars/agent_runner.rb

Overview

Orchestrator that follows agent-to-agent handoffs.

Starting from a given agent, runs the agent and follows any handoff chain until an agent completes without a handoff or the max_handoffs limit is reached.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(starting_agent:, max_handoffs: 10) ⇒ AgentRunner

Returns a new instance of AgentRunner.

Parameters:

  • starting_agent (Boxcars::StationAgent)

    The first agent to run

  • max_handoffs (Integer) (defaults to: 10)

    Maximum number of handoffs before stopping (default: 10)



13
14
15
16
# File 'lib/boxcars/agent_runner.rb', line 13

def initialize(starting_agent:, max_handoffs: 10)
  @starting_agent = starting_agent
  @max_handoffs = max_handoffs
end

Instance Attribute Details

#max_handoffsObject (readonly)

Returns the value of attribute max_handoffs.



9
10
11
# File 'lib/boxcars/agent_runner.rb', line 9

def max_handoffs
  @max_handoffs
end

#starting_agentObject (readonly)

Returns the value of attribute starting_agent.



9
10
11
# File 'lib/boxcars/agent_runner.rb', line 9

def starting_agent
  @starting_agent
end

Instance Method Details

#run(input) ⇒ Hash

Run the agent chain starting from the starting_agent.

Parameters:

  • input (String)

    The user input/question

Returns:

  • (Hash)

    { answer: String, handoff_chain: Array<Hash> }



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/boxcars/agent_runner.rb', line 21

def run(input)
  current_agent = starting_agent
  handoff_chain = []
  handoffs_remaining = max_handoffs

  loop do
    result = current_agent.conduct(input)
    handoff = result.respond_to?(:output_for) ? result.output_for(:handoff) : result[:handoff]

    unless handoff
      answer = extract_answer(result, current_agent)
      return { answer: answer, handoff_chain: handoff_chain }
    end

    if handoffs_remaining <= 0
      return {
        answer: "Agent stopped due to max handoffs (#{max_handoffs}).",
        handoff_chain: handoff_chain
      }
    end

    handoff_chain << { from: current_agent.name, to: handoff[:agent].name, reason: handoff[:reason] }
    current_agent = handoff[:agent]
    handoffs_remaining -= 1
  end
end

#run_stream(input) {|AgentEvent| ... } ⇒ Hash, Enumerator<AgentEvent>

Run the agent chain while streaming events.

Parameters:

  • input (String)

    The user input/question

Yields:

  • (AgentEvent)

    Each lifecycle event from every agent in the chain

Returns:

  • (Hash)

    { answer: String, handoff_chain: Array<Hash> } when block given

  • (Enumerator<AgentEvent>)

    when no block given



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/boxcars/agent_runner.rb', line 53

def run_stream(input, &block)
  return Enumerator.new { |y| run_stream(input) { |event| y << event } } unless block

  current_agent = starting_agent
  handoff_chain = []
  handoffs_remaining = max_handoffs

  loop do
    result = current_agent.run_stream(input, &block)
    handoff = result.respond_to?(:output_for) ? result.output_for(:handoff) : result[:handoff]

    unless handoff
      answer = extract_answer(result, current_agent)
      return { answer: answer, handoff_chain: handoff_chain }
    end

    if handoffs_remaining <= 0
      return {
        answer: "Agent stopped due to max handoffs (#{max_handoffs}).",
        handoff_chain: handoff_chain
      }
    end

    handoff_chain << { from: current_agent.name, to: handoff[:agent].name, reason: handoff[:reason] }
    current_agent = handoff[:agent]
    handoffs_remaining -= 1
  end
end