foobara-agent 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88666f0c9353fd88d386752d774b3a5abac7bf035b9349769049a1e633fb6a4e
4
- data.tar.gz: 60a09c5cd82d3c4b580ab3224875bc78b3086f04d91e3c28da6708483d4b3806
3
+ metadata.gz: fc0affa9f2a80a45d94dbcde1ad4ba5f173d0e3aa91076d91147058e5e1c6829
4
+ data.tar.gz: 40f0e2ed19b6e95a0b325e54eb624b15d3cde7eeda9cfe325626bee5a347f001
5
5
  SHA512:
6
- metadata.gz: 5590b86e0dbb6baf8feac06ed1dce7a95a066acf3d684203dff92b86adb1fc329a217ec702affa332d9b6b6c2d6d187b5e3c960ee4b22a7263c0412760fd30c2
7
- data.tar.gz: 13e01dd7118c05188ffd6a58ba51923eec81b14b227506a6fdeaf0c731dfef4bc41a14a052f2c17c2e46e3d32438a181ff906a082bf7f123ee7210fca1efc959
6
+ metadata.gz: 716dbb353942408388d6a8e28f87c54911f44d067f398d8c224bbed7e926adc63eae816cea9d40f5acd7ccab478421319c542d2d28e9451996775fcd6ec5f9e7
7
+ data.tar.gz: 0e80baab41484b2843ddbbe49a1a9412cce157ed43758af7f621f8dc7c99d94d02e0767b92cbe069d5396373e561ee03da4b510e94cb1ddcc86b738b413d203c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.0.4] - 2025-05-30
2
+
3
+ - Refactor agent to be a command connector
4
+
1
5
  ## [0.0.3] - 2025-05-30
2
6
 
3
7
  - Increase the retries, attempt to improve command descriptions,
data/lib/foobara/agent.rb CHANGED
@@ -3,7 +3,7 @@ require "foobara/ai"
3
3
  require "foobara/command_connectors"
4
4
 
5
5
  module Foobara
6
- class Agent
6
+ class Agent < CommandConnector
7
7
  foobara_domain!
8
8
 
9
9
  foobara_depends_on Foobara::Ai::AnswerBot
@@ -2,7 +2,7 @@ require_relative "list_commands"
2
2
 
3
3
  module Foobara
4
4
  # TODO: should agent maybe be a command connector? It feels a bit more like a command connector.
5
- class Agent
5
+ class Agent < CommandConnector
6
6
  class AccomplishGoal < Foobara::Command
7
7
  possible_error :gave_up, context: { reason: :string }, message: "Gave up."
8
8
  possible_error :too_many_command_calls,
@@ -115,19 +115,17 @@ module Foobara
115
115
  @command_connector ||= existing_command_connector
116
116
  end
117
117
 
118
+ # Do we really want to support calling AccomplishGoal outside the context of an Agent?
119
+ # If not, just delete this awkward coupling
118
120
  def build_command_connector
119
- self.command_connector ||= Connector.new(
120
- accomplish_goal_command: self,
121
- default_serializers: [
122
- Foobara::CommandConnectors::Serializers::ErrorsSerializer,
123
- Foobara::CommandConnectors::Serializers::AtomicSerializer
124
- ],
121
+ self.command_connector ||= Agent.new(
122
+ current_accomplish_goal_command: self,
125
123
  llm_model:
126
124
  )
127
125
  end
128
126
 
129
127
  def set_accomplished_goal_command
130
- command_connector.accomplish_goal_command = self
128
+ command_connector.current_accomplish_goal_command = self
131
129
  end
132
130
 
133
131
  def connect_agent_commands
@@ -160,7 +158,9 @@ module Foobara
160
158
  outcome = begin
161
159
  determine_command.run
162
160
  rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
161
+ # :nocov:
163
162
  Outcome.errors(e.errors)
163
+ # :nocov:
164
164
  end
165
165
 
166
166
  if outcome.success?
@@ -286,7 +286,9 @@ module Foobara
286
286
  outcome = begin
287
287
  command.run
288
288
  rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
289
+ # :nocov:
289
290
  Outcome.errors(e.errors)
291
+ # :nocov:
290
292
  end
291
293
 
292
294
  if outcome.success?
@@ -345,7 +347,9 @@ module Foobara
345
347
  outcome = begin
346
348
  command.run
347
349
  rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
350
+ # :nocov:
348
351
  Outcome.errors(e.errors)
352
+ # :nocov:
349
353
  end
350
354
 
351
355
  if outcome.success?
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  module Concerns
4
4
  # There's nothing really subclass-specific about this concern, maybe rename it...
5
5
  module SubclassCacheable
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class SetCommandConnectorInputsTransformer < TypeDeclarations::TypedTransformer
4
4
  class << self
5
5
  attr_accessor :command_connector
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class DescribeCommand < Foobara::Command
4
4
  inputs do
5
5
  command_connector :duck, :required, "Connector to find relevant command in"
@@ -52,7 +52,7 @@ module Foobara
52
52
  end
53
53
 
54
54
  def mark_command_as_described
55
- command_connector.accomplish_goal_command.described_commands << command_class.full_command_name
55
+ command_connector.current_accomplish_goal_command.described_commands << command_class.full_command_name
56
56
  end
57
57
  end
58
58
  end
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class DescribeType < Foobara::Command
4
4
  inputs do
5
5
  command_connector :duck, :required, "Connector to find relevant type in"
@@ -1,7 +1,7 @@
1
1
  require "foobara/llm_backed_command"
2
2
 
3
3
  module Foobara
4
- class Agent
4
+ class Agent < CommandConnector
5
5
  class DetermineInputsForNextCommand < Foobara::LlmBackedCommand
6
6
  extend Concerns::SubclassCacheable
7
7
 
@@ -1,7 +1,7 @@
1
1
  require "foobara/llm_backed_command"
2
2
 
3
3
  module Foobara
4
- class Agent
4
+ class Agent < CommandConnector
5
5
  class DetermineNextCommand < Foobara::LlmBackedCommand
6
6
  extend Concerns::SubclassCacheable
7
7
 
@@ -1,7 +1,7 @@
1
1
  require "foobara/llm_backed_command"
2
2
 
3
3
  module Foobara
4
- class Agent
4
+ class Agent < CommandConnector
5
5
  class DetermineNextCommandNameAndInputs < Foobara::LlmBackedCommand
6
6
  description "Accepts the current goal, which might already be accomplished, and context of the work " \
7
7
  "so far and returns the inputs for " \
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class GiveUp < Foobara::Command
4
4
  inputs do
5
5
  command_connector CommandConnector, :required, "Connector to end"
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class ListCommands < Foobara::Command
4
4
  inputs do
5
5
  command_connector :duck, :required, "Connector to end"
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class ListTypes < Foobara::Command
4
4
  inputs do
5
5
  command_connector :duck, :required, "Connector to fetch types from"
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class NotifyUserThatCurrentGoalHasBeenAccomplished < Foobara::Command
4
4
  extend Concerns::SubclassCacheable
5
5
 
@@ -71,6 +71,7 @@ module Foobara
71
71
  data = if result_type
72
72
  inputs[:result_data]
73
73
  end
74
+
74
75
  command_connector.mark_mission_accomplished(data, message_to_user)
75
76
  end
76
77
 
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class CommandLogEntry < Foobara::Model
4
4
  attributes do
5
5
  command_name :string, :required, "Name of the command that was run"
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  class Context < Foobara::Model
4
4
  attributes do
5
5
  # TODO: why doesn't this default of [] work as expected on newly created models?
data/src/foobara/agent.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Agent
2
+ class Agent < CommandConnector
3
3
  StateMachine = Foobara::StateMachine.for(
4
4
  [:initialized, :idle, :error, :failure] => {
5
5
  kill: :killed,
@@ -14,32 +14,54 @@ module Foobara
14
14
  )
15
15
 
16
16
  attr_accessor :context,
17
- :agent_command_connector,
18
17
  :agent_name,
19
18
  :llm_model,
20
19
  :current_accomplish_goal_command,
21
- :result_type
20
+ :result_type,
21
+ :agent_commands_connected
22
22
 
23
23
  def initialize(
24
24
  context: nil,
25
25
  agent_name: nil,
26
26
  command_classes: nil,
27
- agent_command_connector: nil,
28
27
  llm_model: nil,
29
- result_type: nil
28
+ result_type: nil,
29
+ current_accomplish_goal_command: nil,
30
+ **opts
30
31
  )
31
32
  # TODO: shouldn't have to pass command_log here since it has a default, debug that
32
33
  self.context = context
33
- self.agent_command_connector = agent_command_connector
34
34
  self.agent_name = agent_name if agent_name
35
35
  self.llm_model = llm_model
36
36
  self.result_type = result_type
37
+ self.current_accomplish_goal_command = current_accomplish_goal_command
38
+
39
+ unless opts.key?(:default_serializers)
40
+ opts = opts.merge(default_serializers: [
41
+ Foobara::CommandConnectors::Serializers::ErrorsSerializer,
42
+ Foobara::CommandConnectors::Serializers::AtomicSerializer
43
+ ])
44
+ end
45
+
46
+ super(**opts)
37
47
 
38
48
  build_initial_context
39
- build_agent_command_connector
40
49
 
50
+ # TODO: push this convenience method up into base class?
41
51
  command_classes&.each do |command_class|
42
- self.agent_command_connector.connect(command_class)
52
+ connect(command_class)
53
+ end
54
+ end
55
+
56
+ def run(*args, **)
57
+ if args.first.is_a?(::String)
58
+ accomplish_goal(*args, **)
59
+ else
60
+ unless agent_commands_connected?
61
+ connect_agent_commands
62
+ end
63
+
64
+ super
43
65
  end
44
66
  end
45
67
 
@@ -66,7 +88,7 @@ module Foobara
66
88
  # :nocov:
67
89
  raise ArgumentError, "You can only specify a result type once"
68
90
  # :nocov:
69
- elsif agent_command_connector.agent_commands_connected?
91
+ elsif agent_commands_connected?
70
92
  # :nocov:
71
93
  raise ArgumentError, "You can't specify a result type this late in the process"
72
94
  # :nocov:
@@ -82,7 +104,7 @@ module Foobara
82
104
  goal:,
83
105
  final_result_type: self.result_type,
84
106
  current_context: context,
85
- existing_command_connector: agent_command_connector,
107
+ existing_command_connector: self,
86
108
  agent_name:
87
109
  }
88
110
 
@@ -120,15 +142,45 @@ module Foobara
120
142
  self.context ||= Context.new(command_log: [])
121
143
  end
122
144
 
123
- def build_agent_command_connector
124
- self.agent_command_connector ||= Connector.new(
125
- accomplish_goal_command: self,
126
- llm_model:,
127
- default_serializers: [
128
- Foobara::CommandConnectors::Serializers::ErrorsSerializer,
129
- Foobara::CommandConnectors::Serializers::AtomicSerializer
130
- ]
131
- )
145
+ def mark_mission_accomplished(final_result, message_to_user)
146
+ # TODO: this is a pretty awkward way to communicate between commands hmmm...
147
+ # maybe see if there's a less hacky way to pull this off.
148
+ current_accomplish_goal_command.mission_accomplished!(final_result, message_to_user)
149
+ end
150
+
151
+ def give_up(reason)
152
+ current_accomplish_goal_command.give_up!(reason)
153
+ end
154
+
155
+ def agent_commands_connected?
156
+ agent_commands_connected
157
+ end
158
+
159
+ def connect_agent_commands(final_result_type: nil, agent_name: nil)
160
+ command_classes = [
161
+ DescribeCommand,
162
+ DescribeType,
163
+ GiveUp,
164
+ ListCommands,
165
+ ListTypes
166
+ ]
167
+
168
+ command_classes << if final_result_type
169
+ NotifyUserThatCurrentGoalHasBeenAccomplished.for(
170
+ result_type: final_result_type,
171
+ agent_id: agent_name
172
+ )
173
+ else
174
+ NotifyUserThatCurrentGoalHasBeenAccomplished
175
+ end
176
+
177
+ set_command_connector_transformer = SetCommandConnectorInputsTransformer.for(self)
178
+
179
+ command_classes.each do |command_class|
180
+ connect(command_class, inputs: set_command_connector_transformer)
181
+ end
182
+
183
+ self.agent_commands_connected = true
132
184
  end
133
185
  end
134
186
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -51,7 +51,6 @@ files:
51
51
  - src/foobara/agent.rb
52
52
  - src/foobara/agent/accomplish_goal.rb
53
53
  - src/foobara/agent/concerns/subclass_cacheable.rb
54
- - src/foobara/agent/connector/connector.rb
55
54
  - src/foobara/agent/connector/set_command_connector_inputs_transformer.rb
56
55
  - src/foobara/agent/describe_command.rb
57
56
  - src/foobara/agent/describe_type.rb
@@ -1,59 +0,0 @@
1
- require "foobara/command_connectors"
2
-
3
- module Foobara
4
- class Agent
5
- class Connector < Foobara::CommandConnector
6
- attr_accessor :accomplish_goal_command, :agent_commands_connected, :llm_model
7
-
8
- def initialize(*, accomplish_goal_command:, llm_model: nil, **)
9
- self.accomplish_goal_command = accomplish_goal_command
10
- self.llm_model = llm_model
11
-
12
- super(*, **)
13
- end
14
-
15
- def mark_mission_accomplished(final_result, message_to_user)
16
- # TODO: this is a pretty awkward way to communicate between commands hmmm...
17
- # maybe see if there's a less hacky way to pull this off.
18
- accomplish_goal_command.mission_accomplished!(final_result, message_to_user)
19
- end
20
-
21
- def give_up(reason)
22
- accomplish_goal_command.give_up!(reason)
23
- end
24
-
25
- def agent_commands_connected?
26
- agent_commands_connected
27
- end
28
-
29
- def connect_agent_commands(final_result_type: nil, agent_name: nil)
30
- command_classes = [
31
- DescribeCommand,
32
- DescribeType,
33
- GiveUp,
34
- ListCommands,
35
- ListTypes
36
- ]
37
-
38
- command_classes << if final_result_type
39
- NotifyUserThatCurrentGoalHasBeenAccomplished.for(
40
- result_type: final_result_type,
41
- agent_id: agent_name
42
- )
43
- else
44
- NotifyUserThatCurrentGoalHasBeenAccomplished
45
- end
46
-
47
- command_classes.each do |command_class|
48
- connect(command_class, inputs: set_command_connector_transformer)
49
- end
50
-
51
- self.agent_commands_connected = true
52
- end
53
-
54
- def set_command_connector_transformer
55
- @set_command_connector_transformer ||= SetCommandConnectorInputsTransformer.for(self)
56
- end
57
- end
58
- end
59
- end