foobara-agent 0.0.6 → 0.0.7

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: 121474f4e61c1b3a5eebc883794bef41159d48ecc05eba44165bd8d79111c426
4
- data.tar.gz: 50bc4c4aea5bd447f17cf39712b7a86295e4f45708602398f72a61af2f221881
3
+ metadata.gz: 968399c2d338c8f069712d0ad9437b928fe48bc7356e88fd86b9ff6fb7ae235f
4
+ data.tar.gz: 8483b856faf2f559985550cc631764121797f88963c0baf428b7f16646bd8b59
5
5
  SHA512:
6
- metadata.gz: e0cbf20f7fc287bd4a3be5caed0572de59475493ae4bed80d755b5284ead25fa697b455959ca323891bb20f0bfb57a5a01f1e62f335faacc6c99c220821920f3
7
- data.tar.gz: cf85ac42b6ab4a9f8efddd35e533afd32f664b9a9ec8bba17f77abf424a5499e64ba6ad576b54bb24c03dadb5e6bc1c2fe54a502bd505f30caaeeb15e002d655
6
+ metadata.gz: 79be2912847cb407d1ad65166313c8b799b981a2b0da6f8c8e3f8132049c1eb67ec56a9307fdf887f8e659299883f0da4517762abc885fdf8b69949f869f54de
7
+ data.tar.gz: 169c02a8070d5feb4d307b41ff757294ca5a2e3b810ac0766cece386a357db2cf85075ae9ecb1f5ee2e0b1ec92c855eb202ee4166102a88341b8e5b4268eeb92
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.0.7] - 2025-06-23
2
+
3
+ -
4
+
1
5
  ## [0.0.6] - 2025-06-19
2
6
 
3
7
  - Do not run next command with pre-cast inputs
@@ -8,16 +8,14 @@ module Foobara
8
8
  context: { maximum_command_calls: :integer }
9
9
 
10
10
  inputs do
11
- agent_name :string, "Name of the agent"
12
11
  goal :string, :required, "What do you want the agent to attempt to accomplish?"
13
12
  # TODO: we should be able to specify a subclass as a type
14
- command_classes [Class], "Commands that can be ran to accomplish the goal"
15
13
  final_result_type :duck, "Specifies how the result of the goal is to be structured"
14
+ include_message_to_user_in_result :boolean, default: true
16
15
  verbose :boolean, default: false
17
16
  io_out :duck
18
17
  io_err :duck
19
- existing_command_connector CommandConnector, :allow_nil,
20
- "A connector containing already-connected commands for the agent to use"
18
+ agent Agent, :required
21
19
  current_context Context, :allow_nil, "The current context of the agent"
22
20
  maximum_command_calls :integer,
23
21
  :allow_nil,
@@ -44,7 +42,7 @@ module Foobara
44
42
  end
45
43
 
46
44
  result do
47
- message_to_user :string, :required, "Message to the user about successfully accomplishing the goal"
45
+ message_to_user :string, :allow_nil, "Message to the user about successfully accomplishing the goal"
48
46
  result_data :duck, "Optional result data to return to the user if final_result_type was given"
49
47
  end
50
48
 
@@ -53,17 +51,6 @@ module Foobara
53
51
  def execute
54
52
  build_initial_context_if_necessary
55
53
 
56
- if command_connector_passed_in?
57
- set_accomplished_goal_command
58
- else
59
- build_command_connector
60
- connect_user_provided_commands
61
- end
62
-
63
- unless agent_commands_connected?
64
- connect_agent_commands
65
- end
66
-
67
54
  simulate_list_commands_run
68
55
  simulate_describe_command_run_for_all_commands
69
56
 
@@ -88,27 +75,10 @@ module Foobara
88
75
  build_result
89
76
  end
90
77
 
91
- def agent_commands_connected?
92
- command_connector.agent_commands_connected?
93
- end
94
-
95
- def validate
96
- validate_either_command_classes_or_connector_given
97
- end
98
-
99
- def validate_either_command_classes_or_connector_given
100
- # TODO: implement this!
101
- end
102
-
103
78
  attr_accessor :context, :next_command_name, :next_command_inputs, :mission_accomplished, :given_up,
104
79
  :next_command_class, :next_command, :command_outcome, :timed_out,
105
80
  :final_result, :final_message, :command_response, :delayed_command_name,
106
81
  :command_calls
107
- attr_writer :command_connector
108
-
109
- def agent_name
110
- @agent_name ||= inputs[:agent_name] || "Anon#{SecureRandom.hex(2)}"
111
- end
112
82
 
113
83
  def build_initial_context_if_necessary
114
84
  # TODO: shouldn't have to pass command_log here since it has a default, debug that
@@ -129,7 +99,7 @@ module Foobara
129
99
  def simulate_describe_command_run_for_all_commands
130
100
  return if context.command_log.size > 1
131
101
 
132
- ListCommands.run!(command_connector:)[:user_provided_commands].each do |full_command_name|
102
+ ListCommands.run!(command_connector: agent)[:user_provided_commands].each do |full_command_name|
133
103
  next if described_commands.include?(full_command_name)
134
104
 
135
105
  self.next_command_name = DescribeCommand.full_command_name
@@ -141,39 +111,6 @@ module Foobara
141
111
  end
142
112
  end
143
113
 
144
- def command_connector_passed_in?
145
- existing_command_connector
146
- end
147
-
148
- def command_connector
149
- @command_connector ||= existing_command_connector
150
- end
151
-
152
- # Do we really want to support calling AccomplishGoal outside the context of an Agent?
153
- # If not, just delete this awkward coupling
154
- def build_command_connector
155
- self.command_connector ||= Agent.new(
156
- current_accomplish_goal_command: self,
157
- llm_model:,
158
- verbose:,
159
- io_out:
160
- )
161
- end
162
-
163
- def set_accomplished_goal_command
164
- command_connector.current_accomplish_goal_command = self
165
- end
166
-
167
- def connect_agent_commands
168
- command_connector.connect_agent_commands(final_result_type:, agent_name:)
169
- end
170
-
171
- def connect_user_provided_commands
172
- command_classes.each do |command_class|
173
- command_connector.connect(command_class)
174
- end
175
- end
176
-
177
114
  def determine_next_command_and_inputs(retries = 2)
178
115
  inputs_for_determine = {
179
116
  goal:,
@@ -348,11 +285,11 @@ module Foobara
348
285
  end
349
286
 
350
287
  def all_command_classes
351
- @all_command_classes ||= run_subcommand!(ListCommands, command_connector:).values.flatten
288
+ @all_command_classes ||= run_subcommand!(ListCommands, command_connector: agent).values.flatten
352
289
  end
353
290
 
354
291
  def fetch_next_command_class
355
- self.next_command_class = command_connector.transformed_command_from_name(next_command_name)
292
+ self.next_command_class = agent.transformed_command_from_name(next_command_name)
356
293
  end
357
294
 
358
295
  def determine_next_command_inputs(retries = 2)
@@ -417,7 +354,7 @@ module Foobara
417
354
  (io_out || $stdout).puts "Running #{next_command_name} with #{next_command_inputs}"
418
355
  end
419
356
 
420
- self.command_response = command_connector.run(
357
+ self.command_response = agent.run(
421
358
  full_command_name: next_command_name,
422
359
  inputs: next_command_inputs,
423
360
  action: "run"
@@ -528,6 +465,10 @@ module Foobara
528
465
  choose_next_command_and_next_inputs_separately
529
466
  end
530
467
 
468
+ def agent_name
469
+ agent.agent_name
470
+ end
471
+
531
472
  def verbose?
532
473
  verbose
533
474
  end
@@ -6,8 +6,10 @@ module Foobara
6
6
  class << self
7
7
  attr_accessor :command_class
8
8
 
9
- def for(result_type:, agent_id:)
10
- cached_subclass([result_type, agent_id]) do
9
+ def for(agent_id: nil, result_type: nil, include_message_to_user_in_result: true)
10
+ agent_id ||= "Anon#{SecureRandom.hex(2)}"
11
+
12
+ cached_subclass([result_type, agent_id, include_message_to_user_in_result]) do
11
13
  command_name = "Foobara::Agent::#{agent_id}::NotifyUserThatCurrentGoalHasBeenAccomplished"
12
14
  klass = Util.make_class_p(command_name, self)
13
15
 
@@ -16,32 +18,50 @@ module Foobara
16
18
  "result schema and an optional message to the user. " \
17
19
  "The user might issue a new goal."
18
20
 
19
- inputs do
20
- command_connector CommandConnector, :required, "Connector to notify user through"
21
- message_to_user :string, :required, "Message to the user about what was done"
22
- end
23
-
24
21
  if result_type
25
- add_inputs do
26
- result_data result_type, :required
22
+ if include_message_to_user_in_result
23
+ klass.add_inputs do
24
+ result result_type, :required
25
+ message_to_user :string, :required, "Message to the user about what was done"
26
+ end
27
+
28
+ klass.result do
29
+ result result_type, :required
30
+ message_to_user :string, :required, "Message to the user about what was done"
31
+ end
32
+
33
+ klass.description "Notifies the user that the current goal has been accomplished and returns a final " \
34
+ "result formatted according to the " \
35
+ "result schema and a message to the user. " \
36
+ "The user might issue a new goal."
37
+ else
38
+ klass.add_inputs do
39
+ result result_type, :required
40
+ end
41
+
42
+ klass.result result_type
43
+
44
+ klass.description "Notifies the user that the current goal has been accomplished and returns a final " \
45
+ "result formatted according to the " \
46
+ "result schema. " \
47
+ "The user might issue a new goal."
48
+ end
49
+ elsif include_message_to_user_in_result
50
+ klass.add_inputs do
51
+ message_to_user :string, :required, "Message to the user about what was done"
27
52
  end
28
53
 
29
54
  klass.result do
30
- message_to_user :string, :required
31
- result_data result_type, :required
55
+ message_to_user :string, :required, "Message to the user about what was done"
32
56
  end
33
- klass.description "Notifies the user that the current goal has been accomplished and returns a final " \
34
- "result formatted according to the " \
35
- "result schema if relevant and an optional message to the user. " \
36
- "The user might issue a new goal."
37
57
 
58
+ klass.description "Notifies the user that the current goal has been accomplished and results in a " \
59
+ "message to the user. " \
60
+ "The user might issue a new goal."
38
61
  else
39
- # TODO: test this code path
62
+ # This should be unreachable actually
40
63
  # :nocov:
41
- klass.description "Notifies the user that the current goal has been accomplished and, if relevant, " \
42
- "returns a final " \
43
- "result formatted according to the " \
44
- "result schema and an optional message to the user. " \
64
+ klass.description "Notifies the user that the current goal has been accomplished. " \
45
65
  "The user might issue a new goal."
46
66
  # :nocov:
47
67
  end
@@ -55,10 +75,7 @@ module Foobara
55
75
  "result schema if relevant and an optional message to the user."
56
76
 
57
77
  inputs do
58
- # TODO: Are we still not able to uses classes as foobara types??
59
- command_connector :duck, :required, "Connector to end"
60
- message_to_user :string, :required, "Message to the user about what was done"
61
- result_data :duck, "The final result of the work if relevant/expected"
78
+ command_connector CommandConnector, :required, "Connector to notify user through"
62
79
  end
63
80
 
64
81
  def execute
@@ -68,21 +85,18 @@ module Foobara
68
85
  end
69
86
 
70
87
  def mark_mission_accomplished
71
- data = if result_type
72
- inputs[:result_data]
73
- end
74
-
75
- command_connector.mark_mission_accomplished(data, message_to_user)
88
+ command_connector.mark_mission_accomplished(inputs[:result], inputs[:message_to_user])
76
89
  end
77
90
 
78
91
  def parsed_result
79
- h = { message_to_user: }
92
+ inputs_type = self.class.inputs_type
93
+ element_types = inputs_type.element_types
80
94
 
81
- if inputs[:result_data] && result_type
82
- h[:result_data] = result_data
95
+ if element_types.key?(:message_to_user)
96
+ inputs.slice(:result, :message_to_user)
97
+ elsif element_types.key?(:result)
98
+ inputs[:result]
83
99
  end
84
-
85
- h
86
100
  end
87
101
  end
88
102
  end
data/src/foobara/agent.rb CHANGED
@@ -18,6 +18,7 @@ module Foobara
18
18
  :llm_model,
19
19
  :current_accomplish_goal_command,
20
20
  :result_type,
21
+ :include_message_to_user_in_result,
21
22
  :agent_commands_connected,
22
23
  :verbose,
23
24
  :io_out,
@@ -29,7 +30,7 @@ module Foobara
29
30
  command_classes: nil,
30
31
  llm_model: nil,
31
32
  result_type: nil,
32
- current_accomplish_goal_command: nil,
33
+ include_message_to_user_in_result: true,
33
34
  verbose: false,
34
35
  io_out: nil,
35
36
  io_err: nil,
@@ -37,10 +38,10 @@ module Foobara
37
38
  )
38
39
  # TODO: shouldn't have to pass command_log here since it has a default, debug that
39
40
  self.context = context
40
- self.agent_name = agent_name if agent_name
41
+ self.agent_name = agent_name || "Anon#{SecureRandom.hex(2)}"
41
42
  self.llm_model = llm_model
42
43
  self.result_type = result_type
43
- self.current_accomplish_goal_command = current_accomplish_goal_command
44
+ self.include_message_to_user_in_result = include_message_to_user_in_result
44
45
  self.verbose = verbose
45
46
  self.io_out = io_out
46
47
  self.io_err = io_err
@@ -107,7 +108,8 @@ module Foobara
107
108
  goal,
108
109
  result_type: nil,
109
110
  choose_next_command_and_next_inputs_separately: nil,
110
- maximum_call_count: nil
111
+ maximum_call_count: nil,
112
+ llm_model: nil
111
113
  )
112
114
  if result_type && self.result_type != result_type
113
115
  if self.result_type
@@ -123,6 +125,10 @@ module Foobara
123
125
  end
124
126
  end
125
127
 
128
+ unless agent_commands_connected?
129
+ connect_agent_commands
130
+ end
131
+
126
132
  state_machine.perform_transition!(:accomplish_goal)
127
133
 
128
134
  begin
@@ -130,12 +136,10 @@ module Foobara
130
136
  goal:,
131
137
  final_result_type: self.result_type,
132
138
  current_context: context,
133
- existing_command_connector: self
139
+ agent: self
134
140
  }
135
141
 
136
- if agent_name
137
- inputs[:agent_name] = agent_name
138
- end
142
+ llm_model ||= self.llm_model
139
143
 
140
144
  if llm_model
141
145
  inputs[:llm_model] = llm_model
@@ -161,14 +165,20 @@ module Foobara
161
165
  inputs[:io_err] = io_err
162
166
  end
163
167
 
168
+ if include_message_to_user_in_result || include_message_to_user_in_result == false
169
+ inputs[:include_message_to_user_in_result] = include_message_to_user_in_result
170
+ end
171
+
164
172
  self.current_accomplish_goal_command = AccomplishGoal.new(inputs)
165
173
 
166
174
  current_accomplish_goal_command.run.tap do |outcome|
167
- if outcome.success?
168
- state_machine.perform_transition!(:goal_accomplished)
169
- else
170
- state_machine.perform_transition!(:goal_errored)
171
- end
175
+ transition = if outcome.success?
176
+ :goal_accomplished
177
+ else
178
+ :goal_errored
179
+ end
180
+
181
+ state_machine.perform_transition!(transition)
172
182
  end
173
183
  rescue
174
184
  # :nocov:
@@ -197,19 +207,21 @@ module Foobara
197
207
  agent_commands_connected
198
208
  end
199
209
 
200
- def connect_agent_commands(final_result_type: nil, agent_name: nil)
210
+ def connect_agent_commands
201
211
  command_classes = [
202
212
  DescribeCommand,
203
213
  DescribeType,
204
214
  GiveUp,
205
- ListCommands,
206
- ListTypes
215
+ ListCommands
207
216
  ]
208
217
 
209
- command_classes << if final_result_type
218
+ command_classes << if result_type || include_message_to_user_in_result
219
+ # TODO: Support changing the final result type when the goal changes
210
220
  NotifyUserThatCurrentGoalHasBeenAccomplished.for(
211
- result_type: final_result_type,
212
- agent_id: agent_name
221
+ result_type:,
222
+ agent_id: agent_name,
223
+ # TODO: Support changing this flag when the goal changes
224
+ include_message_to_user_in_result:
213
225
  )
214
226
  else
215
227
  NotifyUserThatCurrentGoalHasBeenAccomplished
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.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -59,7 +59,6 @@ files:
59
59
  - src/foobara/agent/determine_next_command_name_and_inputs.rb
60
60
  - src/foobara/agent/give_up.rb
61
61
  - src/foobara/agent/list_commands.rb
62
- - src/foobara/agent/list_types.rb
63
62
  - src/foobara/agent/notify_user_that_current_goal_has_been_accomplished.rb
64
63
  - src/foobara/agent/types/command_log_entry.rb
65
64
  - src/foobara/agent/types/context.rb
@@ -1,23 +0,0 @@
1
- module Foobara
2
- class Agent < CommandConnector
3
- class ListTypes < Foobara::Command
4
- inputs do
5
- command_connector :duck, :required, "Connector to fetch types from"
6
- end
7
-
8
- result [:string]
9
-
10
- def execute
11
- construct_type_list
12
-
13
- type_list
14
- end
15
-
16
- attr_accessor :type_list
17
-
18
- def construct_type_list
19
- self.type_list = command_connector.all_exposed_type_names
20
- end
21
- end
22
- end
23
- end