Class: Foobara::Agent

Inherits:
CommandConnector show all
Defined in:
foobara-agent-0.0.5/src/foobara/agent/accomplish_goal.rb,
foobara-agent-0.0.5/src/foobara/agent.rb,
foobara-agent-0.0.5/src/foobara/agent/give_up.rb,
foobara-agent-0.0.5/src/foobara/agent/list_types.rb,
foobara-agent-0.0.5/src/foobara/agent/describe_type.rb,
foobara-agent-0.0.5/src/foobara/agent/list_commands.rb,
foobara-agent-0.0.5/src/foobara/agent/types/context.rb,
foobara-agent-0.0.5/src/foobara/agent/describe_command.rb,
foobara-agent-0.0.5/src/foobara/agent/determine_next_command.rb,
foobara-agent-0.0.5/src/foobara/agent/types/command_log_entry.rb,
foobara-agent-0.0.5/src/foobara/agent/concerns/subclass_cacheable.rb,
foobara-agent-0.0.5/src/foobara/agent/determine_inputs_for_next_command.rb,
foobara-agent-0.0.5/src/foobara/agent/determine_next_command_name_and_inputs.rb,
foobara-agent-0.0.5/src/foobara/agent/connector/set_command_connector_inputs_transformer.rb,
foobara-agent-0.0.5/src/foobara/agent/notify_user_that_current_goal_has_been_accomplished.rb,
foobara-agent-0.0.5/lib/foobara/agent.rb

Overview

TODO: should agent maybe be a command connector? It feels a bit more like a command connector.

Defined Under Namespace

Modules: Concerns Classes: AccomplishGoal, CommandLogEntry, Context, DescribeCommand, DescribeType, DetermineInputsForNextCommand, DetermineNextCommand, DetermineNextCommandNameAndInputs, GiveUp, ListCommands, ListTypes, NotifyUserThatCurrentGoalHasBeenAccomplished, SetCommandConnectorInputsTransformer

Constant Summary collapse

StateMachine =
Foobara::StateMachine.for(
  [:initialized, :idle, :error, :failure] => {
    kill: :killed,
    accomplish_goal: :accomplishing_goal
  },
  accomplishing_goal: {
    goal_accomplished: :idle,
    goal_errored: :error,
    goal_failed: :failure,
    kill: :killed
  }
)

Instance Attribute Summary collapse

Attributes inherited from CommandConnector

#authenticator, #capture_unknown_error, #command_registry

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from CommandConnector

#all_exposed_commands, #all_exposed_type_names, allowed_rules_to_register, authenticator_registry, #build_command_instance, #build_request, #build_response, #connect, #connect_delayed, #delayed_connections, #desugarize_connect_args, #determine_command_class, find_builtin_command_class, #find_builtin_command_class, #foobara_manifest, #foobara_manifest_in_current_namespace, #lookup_command, #mutate_response, #normalize_manifest, #patch_up_broken_parents_for_errors_with_missing_command_parents, #process_delayed_connections, register_allowed_rule, register_authenticator, #request_to_command_class, #request_to_command_inputs, #request_to_command_instance, #request_to_response, #run_command, #run_request, #serialize_response_body, #set_response_body, #set_response_status, to_authenticator, #transform_command_class, #type_from_name

Constructor Details

#initialize(context: nil, agent_name: nil, command_classes: nil, llm_model: nil, result_type: nil, current_accomplish_goal_command: nil, **opts) ⇒ Agent

Returns a new instance of Agent.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 23

def initialize(
  context: nil,
  agent_name: nil,
  command_classes: nil,
  llm_model: nil,
  result_type: nil,
  current_accomplish_goal_command: nil,
  **opts
)
  # TODO: shouldn't have to pass command_log here since it has a default, debug that
  self.context = context
  self.agent_name = agent_name if agent_name
  self.llm_model = llm_model
  self.result_type = result_type
  self.current_accomplish_goal_command = current_accomplish_goal_command

  unless opts.key?(:default_serializers)
    opts = opts.merge(default_serializers: [
                        Foobara::CommandConnectors::Serializers::ErrorsSerializer,
                        Foobara::CommandConnectors::Serializers::AtomicSerializer
                      ])
  end

  super(**opts)

  build_initial_context

  # TODO: push this convenience method up into base class?
  command_classes&.each do |command_class|
    connect(command_class)
  end
end

Instance Attribute Details

#agent_commands_connectedObject

Returns the value of attribute agent_commands_connected.



16
17
18
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 16

def agent_commands_connected
  @agent_commands_connected
end

#agent_nameObject

Returns the value of attribute agent_name.



16
17
18
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 16

def agent_name
  @agent_name
end

#contextObject

Returns the value of attribute context.



16
17
18
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 16

def context
  @context
end

#current_accomplish_goal_commandObject

Returns the value of attribute current_accomplish_goal_command.



16
17
18
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 16

def current_accomplish_goal_command
  @current_accomplish_goal_command
end

#llm_modelObject

Returns the value of attribute llm_model.



16
17
18
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 16

def llm_model
  @llm_model
end

#result_typeObject

Returns the value of attribute result_type.



16
17
18
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 16

def result_type
  @result_type
end

Class Method Details

.reset_allObject



12
13
14
15
16
17
18
19
20
21
# File 'foobara-agent-0.0.5/lib/foobara/agent.rb', line 12

def reset_all
  [
    DetermineInputsForNextCommand,
    DetermineNextCommand,
    NotifyUserThatCurrentGoalHasBeenAccomplished
  ].each do |command_class|
    command_class.clear_subclass_cache
    Util.descendants(command_class).each(&:clear_subclass_cache)
  end
end

Instance Method Details

#accomplish_goal(goal, result_type: nil, choose_next_command_and_next_inputs_separately: nil, maximum_call_count: nil) ⇒ Object



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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 80

def accomplish_goal(
  goal,
  result_type: nil,
  choose_next_command_and_next_inputs_separately: nil,
  maximum_call_count: nil
)
  if result_type && self.result_type != result_type
    if self.result_type
      # :nocov:
      raise ArgumentError, "You can only specify a result type once"
      # :nocov:
    elsif agent_commands_connected?
      # :nocov:
      raise ArgumentError, "You can't specify a result type this late in the process"
      # :nocov:
    else
      self.result_type = result_type
    end
  end

  state_machine.perform_transition!(:accomplish_goal)

  begin
    inputs = {
      goal:,
      final_result_type: self.result_type,
      current_context: context,
      existing_command_connector: self
    }

    if agent_name
      inputs[:agent_name] = agent_name
    end

    if llm_model
      inputs[:llm_model] = llm_model
    end

    unless choose_next_command_and_next_inputs_separately.nil?
      inputs[:choose_next_command_and_next_inputs_separately] = choose_next_command_and_next_inputs_separately
    end

    unless maximum_call_count.nil?
      inputs[:maximum_command_calls] = maximum_call_count
    end

    self.current_accomplish_goal_command = AccomplishGoal.new(inputs)

    current_accomplish_goal_command.run.tap do |outcome|
      if outcome.success?
        state_machine.perform_transition!(:goal_accomplished)
      else
        state_machine.perform_transition!(:goal_errored)
      end
    end
  rescue
    # :nocov:
    state_machine.perform_transition!(:goal_failed)
    raise
    # :nocov:
  end
end

#agent_commands_connected?Boolean

Returns:

  • (Boolean)


158
159
160
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 158

def agent_commands_connected?
  agent_commands_connected
end

#build_initial_contextObject



143
144
145
146
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 143

def build_initial_context
  # TODO: shouldn't have to pass command_log here since it has a default, debug that
  self.context ||= Context.new(command_log: [])
end

#connect_agent_commands(final_result_type: nil, agent_name: nil) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 162

def connect_agent_commands(final_result_type: nil, agent_name: nil)
  command_classes = [
    DescribeCommand,
    DescribeType,
    GiveUp,
    ListCommands,
    ListTypes
  ]

  command_classes << if final_result_type
                       NotifyUserThatCurrentGoalHasBeenAccomplished.for(
                         result_type: final_result_type,
                         agent_id: agent_name
                       )
                     else
                       NotifyUserThatCurrentGoalHasBeenAccomplished
                     end

  set_command_connector_transformer = SetCommandConnectorInputsTransformer.for(self)

  command_classes.each do |command_class|
    connect(command_class, inputs: set_command_connector_transformer)
  end

  self.agent_commands_connected = true
end

#give_up(reason) ⇒ Object



154
155
156
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 154

def give_up(reason)
  current_accomplish_goal_command.give_up!(reason)
end

#kill!Object



72
73
74
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 72

def kill!
  state_machine.perform_transition!(:kill)
end

#killed?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 76

def killed?
  state_machine.current_state == :killed
end

#mark_mission_accomplished(final_result, message_to_user) ⇒ Object



148
149
150
151
152
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 148

def mark_mission_accomplished(final_result, message_to_user)
  # TODO: this is a pretty awkward way to communicate between commands hmmm...
  # maybe see if there's a less hacky way to pull this off.
  current_accomplish_goal_command.mission_accomplished!(final_result, message_to_user)
end

#run(*args) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 56

def run(*args, **)
  if args.first.is_a?(::String)
    accomplish_goal(*args, **)
  else
    unless agent_commands_connected?
      connect_agent_commands
    end

    super
  end
end

#state_machineObject



68
69
70
# File 'foobara-agent-0.0.5/src/foobara/agent.rb', line 68

def state_machine
  @state_machine ||= StateMachine.new
end