Class: Phronomy::Agent::PhaseMachineBuilder Private

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/agent/phase_machine_builder.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Builds the state_machines-backed PhaseTracker class for Agent invocations.

This is the Agent counterpart to Workflow::PhaseMachineBuilder. Unlike the Workflow version (which builds a dynamic graph from user-defined DSL), this always generates the same fixed graph representing the Agent invoke execution phases.

The generated class holds a single :phase state machine with:

- One automatic event (+:state_completed+) that FSMSession fires after
each entry action completes.
- Two external events (+:approve+, +:reject+) for HITL.
- after_transition callbacks for each state's entry actions.

Guard methods (+input_passed?+, tool_call_pending?, etc.) are delegated to the InvocationContext stored in attr_accessor :context.

Instance Method Summary collapse

Constructor Details

#initialize(entry_actions: {}, action_timeouts: {}) ⇒ PhaseMachineBuilder

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of PhaseMachineBuilder.

Parameters:

  • entry_actions (Hash{Symbol => Array<#call>}) (defaults to: {})
  • action_timeouts (Hash{Symbol => Numeric}) (defaults to: {})


28
29
30
31
# File 'lib/phronomy/agent/phase_machine_builder.rb', line 28

def initialize(entry_actions: {}, action_timeouts: {})
  @entry_actions = entry_actions
  @action_timeouts = action_timeouts
end

Instance Method Details

#buildClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds and returns the PhaseTracker class.

Returns:

  • (Class)


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/phronomy/agent/phase_machine_builder.rb', line 36

def build
  entry_acts = @entry_actions
  act_timeouts = @action_timeouts
  build_cb = method(:build_entry_callback)

  Class.new do
    # state_machines requires a class-level state machine definition.
    state_machine :phase, initial: :idle do
      # ----------------------------------------------------------------
      # State declarations
      # ----------------------------------------------------------------
      state :idle
      state :filtering_input
      state :building_context
      state :calling_llm
      state :executing_tool
      state :awaiting_approval  # wait_state: external event required
      state :output_filtering
      state :completed          # terminal
      state :blocked            # terminal

      # ----------------------------------------------------------------
      # Automatic transitions (fired by FSMSession on state_completed)
      # Guards are evaluated on the InvocationContext via #context.
      # ----------------------------------------------------------------
      event :state_completed do
        # idle → filtering_input (unconditional)
        transition idle: :filtering_input

        # filtering_input → building_context | blocked
        transition filtering_input: :building_context, if: ->(m) { m.context&.input_passed? }
        transition filtering_input: :blocked, if: ->(m) { m.context&.input_blocked? }

        # building_context → calling_llm (unconditional)
        transition building_context: :calling_llm

        # calling_llm → executing_tool | output_filtering
        transition calling_llm: :executing_tool, if: ->(m) { m.context&.tool_call_pending? }
        transition calling_llm: :output_filtering

        # executing_tool → awaiting_approval | calling_llm
        transition executing_tool: :awaiting_approval, if: ->(m) { m.context&.approval_required? }
        transition executing_tool: :calling_llm

        # output_filtering → completed | blocked
        transition output_filtering: :completed, if: ->(m) { m.context&.output_passed? }
        transition output_filtering: :blocked, if: ->(m) { m.context&.output_blocked? }
      end

      # ----------------------------------------------------------------
      # External events (human-in-the-loop)
      # ----------------------------------------------------------------
      event :approve do
        transition awaiting_approval: :executing_tool
      end

      event :reject do
        transition awaiting_approval: :blocked
      end

      # ----------------------------------------------------------------
      # Entry action after_transition callbacks
      # Each state's callables are fired after entering that state.
      # ----------------------------------------------------------------
      entry_acts.each do |state_name, callables|
        callables.each do |callable|
          timeout_secs = act_timeouts[state_name]
          cb = build_cb.call(callable, state_name, timeout_secs)
          after_transition to: state_name, do: cb
        end
      end
    end

    # Holds the InvocationContext so guard lambdas can access it.
    attr_accessor :context

    # async_pending flag: set when an entry action returns a Task.
    attr_accessor :async_pending

    # FSM session id — set by FSMSession so async task spawns know the
    # target_id for EventLoop events.
    attr_accessor :session_id

    def initialize
      super
      @context = nil
      @async_pending = false
      @session_id = nil
    end
  end
end