foobara-agent 0.0.8 → 0.0.10

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: 12e8facdeb95a000d81ec0ba8a133affa1ea66e470115e978bdc81f05bc51855
4
- data.tar.gz: 294064df166d152c037db13263863b8ccfe32f064a78e77f6f5fe59af0be3829
3
+ metadata.gz: 4c8f095d06fb173bc816e83f19755cfe246f15bf2b8ef1f8f91bce08e5960e74
4
+ data.tar.gz: 15b48a59c43c909070f68bbc58c68ce02635d019f9d3af68f1c3aa4264cd4903
5
5
  SHA512:
6
- metadata.gz: 2c7fcaf044ba9bc821dcc42f9b0fb44b412fb42f9ef77dd4b7f3ed13cb8293cdb60d448d136e4e0567d104ecb022a00accfd05dbcf955e38185884d34e59668e
7
- data.tar.gz: 380d9ae749b6a942b4e33b112f5aae35fd9600fa9ffcaf462fc0a35122354aae0658378190a4b9db3c0d65d2a12c9d49305b1d03a2881ce504d6d268cf039255
6
+ metadata.gz: f558c74920f9759a98b2605fe2dcbbc52678ff5d90a0977182a75d0949cfd400b07d3d17d4dbfe162b37a3f463c3c55b4e25dee6382f2daeb3256b5df9615f73
7
+ data.tar.gz: f0588e94676e579c1fd5d7b3b603452d0c283b2b14217511b74a8d341d3126ff42cf7d0cb1cc881048d356525c7b9250f73c1a7631dae556a589f23b8627e1f8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.0.10] - 2025-06-30
2
+
3
+ - Allow more retries
4
+ - Eliminate ability to select command and inputs separately
5
+
6
+ ## [0.0.9] - 2025-06-28
7
+
8
+ - Relocate some common Determine* behavior into a DetermineBase
9
+ - Move goal to Context and make use of LlmBackedCommand#messages
10
+ - Simulating a DescribeCommand selection on failure
11
+ - Compacting the command log
12
+
1
13
  ## [0.0.8] - 2025-06-27
2
14
 
3
15
  - Improve what is logged and its formatting when verbose
data/lib/foobara/agent.rb CHANGED
@@ -11,7 +11,6 @@ module Foobara
11
11
  class << self
12
12
  def reset_all
13
13
  [
14
- DetermineInputsForNextCommand,
15
14
  NotifyUserThatCurrentGoalHasBeenAccomplished
16
15
  ].each do |command_class|
17
16
  command_class.clear_subclass_cache
@@ -19,7 +19,7 @@ module Foobara
19
19
  io_out :duck
20
20
  io_err :duck
21
21
  agent Agent, :required
22
- current_context Context, :allow_nil, "The current context of the agent"
22
+ context Context, :required, "The current context of the agent"
23
23
  maximum_command_calls :integer,
24
24
  :allow_nil,
25
25
  default: 25,
@@ -29,12 +29,6 @@ module Foobara
29
29
  one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
30
30
  default: "claude-3-7-sonnet-20250219",
31
31
  description: "The model to use for the LLM"
32
- choose_next_command_and_next_inputs_separately :boolean,
33
- default: false,
34
- description:
35
- "By default, asks for next command and inputs together. " \
36
- "You can experiment with getting the separately " \
37
- "with this flag if you wish."
38
32
  max_llm_calls_per_minute :integer, :allow_nil
39
33
  end
40
34
 
@@ -46,8 +40,6 @@ module Foobara
46
40
  depends_on ListCommands
47
41
 
48
42
  def execute
49
- build_initial_context_if_necessary
50
-
51
43
  simulate_describe_list_commands_command
52
44
  simulate_list_commands_run
53
45
  # simulate_describe_command_run_for_all_commands
@@ -58,13 +50,9 @@ module Foobara
58
50
 
59
51
  throttle_llm_calls_if_necessary
60
52
 
61
- if choose_next_command_and_next_inputs_separately?
62
- determine_next_command_then_inputs_separately
63
- else
64
- determine_next_command_and_inputs
65
- end
66
-
53
+ determine_next_command_and_inputs
67
54
  run_next_command
55
+
68
56
  log_last_command_outcome
69
57
  end
70
58
 
@@ -75,16 +63,11 @@ module Foobara
75
63
  build_result
76
64
  end
77
65
 
78
- attr_accessor :context, :next_command_name, :next_command_inputs, :next_command_raw_inputs, :mission_accomplished,
66
+ attr_accessor :next_command_name, :next_command_inputs, :next_command_raw_inputs, :mission_accomplished,
79
67
  :given_up, :next_command_class, :next_command, :command_outcome, :timed_out,
80
68
  :final_result, :final_message, :command_response, :delayed_command_name,
81
69
  :command_calls
82
70
 
83
- def build_initial_context_if_necessary
84
- # TODO: shouldn't have to pass command_log here since it has a default, debug that
85
- self.context = current_context || Context.new(command_log: [])
86
- end
87
-
88
71
  def simulate_list_commands_run
89
72
  self.next_command_name = ListCommands.full_command_name
90
73
  self.next_command_raw_inputs = nil
@@ -105,6 +88,26 @@ module Foobara
105
88
  log_last_command_outcome
106
89
  end
107
90
 
91
+ def simulate_describe_command(command_name = next_command_name)
92
+ old_next_command_name = next_command_name
93
+ old_next_command_inputs = next_command_inputs
94
+ old_next_command_raw_inputs = next_command_raw_inputs
95
+ old_next_command_class = next_command_class
96
+
97
+ self.next_command_name = DescribeCommand.full_command_name
98
+ self.next_command_inputs = { command_name: }
99
+ self.next_command_raw_inputs = next_command_inputs
100
+ fetch_next_command_class
101
+
102
+ run_next_command
103
+ log_last_command_outcome
104
+
105
+ self.next_command_name = old_next_command_name
106
+ self.next_command_inputs = old_next_command_inputs
107
+ self.next_command_raw_inputs = old_next_command_raw_inputs
108
+ self.next_command_class = old_next_command_class
109
+ end
110
+
108
111
  def simulate_describe_command_run_for_all_commands
109
112
  # TODO: currently not using this code path. Unclear if it is worth it.
110
113
  # :nocov:
@@ -124,9 +127,25 @@ module Foobara
124
127
  # :nocov:
125
128
  end
126
129
 
127
- def determine_next_command_and_inputs(retries = 2)
130
+ def determine_next_command_and_inputs(retries = 3, error_outcome = nil)
131
+ if retries == 0
132
+ # TODO: test this path by irreparably breaking the needed commands
133
+ # :nocov:
134
+ self.next_command_name = GiveUp.full_command_name
135
+ self.next_command_inputs = {
136
+ message_to_user: "While trying to choose the next command and inputs, " \
137
+ "I've ran into an error several times that I couldn't figure out how to get past. " \
138
+ "The last error looked like this:\n#{error_outcome.errors_hash}"
139
+ }
140
+ self.next_command_raw_inputs = next_command_inputs
141
+
142
+ return
143
+ # :nocov:
144
+ end
145
+
146
+ compact_command_log
147
+
128
148
  inputs_for_determine = {
129
- goal:,
130
149
  context:,
131
150
  llm_model:
132
151
  }
@@ -143,7 +162,7 @@ module Foobara
143
162
  end
144
163
 
145
164
  if outcome.success?
146
- self.next_command_name = outcome.result[:command_name]
165
+ self.next_command_name = outcome.result[:command]
147
166
  self.next_command_inputs = outcome.result[:inputs]
148
167
  self.next_command_raw_inputs = next_command_inputs
149
168
 
@@ -162,7 +181,9 @@ module Foobara
162
181
  outcome:
163
182
  )
164
183
 
165
- determine_next_command_inputs
184
+ simulate_describe_command
185
+
186
+ determine_next_command_and_inputs(retries - 1, outcome)
166
187
  end
167
188
  else
168
189
  self.next_command_inputs = {}
@@ -176,11 +197,7 @@ module Foobara
176
197
  result: nil
177
198
  )
178
199
 
179
- if retries > 0
180
- determine_next_command_and_inputs(retries - 1)
181
- else
182
- determine_next_command_then_inputs_separately
183
- end
200
+ determine_next_command_and_inputs(retries - 1, outcome)
184
201
  end
185
202
  else
186
203
  log_command_outcome(
@@ -190,23 +207,7 @@ module Foobara
190
207
  result: outcome.result || determine_command.raw_result
191
208
  )
192
209
 
193
- if retries > 0
194
- determine_next_command_and_inputs(retries - 1)
195
- else
196
- determine_next_command_then_inputs_separately
197
- end
198
- end
199
- end
200
-
201
- def determine_next_command_then_inputs_separately
202
- determine_next_command_name
203
-
204
- if command_described?
205
- fetch_next_command_class
206
- determine_next_command_inputs
207
- else
208
- choose_describe_command_instead
209
- fetch_next_command_class
210
+ determine_next_command_and_inputs(retries - 1, outcome)
210
211
  end
211
212
  end
212
213
 
@@ -245,74 +246,6 @@ module Foobara
245
246
  @command_name_type ||= Agent.foobara_type_from_declaration(:string, one_of: all_command_classes)
246
247
  end
247
248
 
248
- def determine_next_command_name(retries = 2)
249
- self.next_command_name = if delayed_command_name
250
- name = delayed_command_name
251
- self.delayed_command_name = nil
252
- name
253
- else
254
- inputs = { goal:, context: }
255
- if llm_model
256
- inputs[:llm_model] = llm_model
257
- end
258
-
259
- command = DetermineNextCommand.new(inputs)
260
- outcome = begin
261
- record_llm_call_timestamp
262
- command.run
263
- rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
264
- # :nocov:
265
- Outcome.errors(e.errors)
266
- # :nocov:
267
- end
268
-
269
- if outcome.success?
270
- self.next_command_name = outcome.result
271
-
272
- outcome = validate_next_command_name
273
-
274
- unless outcome.success?
275
- # TODO: figure out a way to hit this path in the test suite or delete it
276
- # :nocov:
277
- log_command_outcome(
278
- command:,
279
- inputs: command.inputs.except(:context),
280
- outcome:,
281
- result: outcome.result || command.raw_result
282
- )
283
-
284
- if retries > 0
285
- return determine_next_command_name(retries - 1)
286
- end
287
- # :nocov:
288
- end
289
- else
290
- # TODO: either figure out a way to hit this path in the test suite or delete it
291
- # :nocov:
292
- log_command_outcome(
293
- command:,
294
- inputs: command.inputs.except(:context),
295
- outcome:,
296
- result: outcome.result || command.raw_result
297
- )
298
-
299
- if retries > 0
300
- return determine_next_command_name(retries - 1)
301
- end
302
- # :nocov:
303
- end
304
-
305
- outcome.raise!
306
- outcome.result
307
- end
308
- end
309
-
310
- def choose_describe_command_instead
311
- self.delayed_command_name = next_command_name
312
- self.next_command_inputs = { command_name: next_command_name }
313
- self.next_command_name = DescribeCommand.full_command_name
314
- end
315
-
316
249
  def all_command_classes
317
250
  @all_command_classes ||= run_subcommand!(ListCommands, command_connector: agent).values.flatten
318
251
  end
@@ -321,56 +254,11 @@ module Foobara
321
254
  self.next_command_class = agent.transformed_command_from_name(next_command_name)
322
255
  end
323
256
 
324
- def determine_next_command_inputs(retries = 2)
325
- self.next_command_inputs = if next_command_has_inputs?
326
- command_class = command_class_for_determine_inputs_for_next_command
327
-
328
- inputs = { goal:, context: }
329
- if llm_model
330
- inputs[:llm_model] = llm_model
331
- end
332
-
333
- command = command_class.new(inputs)
334
- outcome = begin
335
- record_llm_call_timestamp
336
- command.run
337
- rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
338
- # :nocov:
339
- Outcome.errors(e.errors)
340
- # :nocov:
341
- end
342
-
343
- unless outcome.success?
344
- # TODO: either figure out a way to hit this path in the test suite or delete it
345
- # :nocov:
346
- log_command_outcome(
347
- command_name: next_command_name,
348
- inputs: command.raw_result,
349
- outcome:
350
- )
351
-
352
- if retries > 0
353
- return determine_next_command_inputs(retries - 1)
354
- end
355
- # :nocov:
356
- end
357
-
358
- outcome.raise!
359
- outcome.result
360
- end
361
- end
362
-
363
257
  def next_command_has_inputs?
364
258
  type = next_command_class.inputs_type
365
259
  type && !empty_attributes?(type)
366
260
  end
367
261
 
368
- def command_class_for_determine_inputs_for_next_command
369
- DetermineInputsForNextCommand.for(
370
- command_class: next_command_class, agent_id: agent_name
371
- )
372
- end
373
-
374
262
  def run_next_command
375
263
  if verbose?
376
264
  args = if next_command_inputs.nil? || next_command_inputs.empty?
@@ -386,6 +274,7 @@ module Foobara
386
274
  # :nocov:
387
275
  end
388
276
  end
277
+
389
278
  (io_out || $stdout).puts "#{next_command_name}.run#{args}"
390
279
  end
391
280
 
@@ -412,6 +301,66 @@ module Foobara
412
301
  log_command_outcome(command: command_response.command)
413
302
  end
414
303
 
304
+ def compact_command_log
305
+ # Rules:
306
+ # Delete errors for any command that has succeeded since
307
+ # Delete all but the last DescribeCommand call for a given
308
+ describe_command_call_indexes = {}
309
+ commands = {}
310
+
311
+ describe_command_name = DescribeCommand.full_command_name
312
+ context.command_log.each.with_index do |command_log_entry, index|
313
+ command_name = command_log_entry.command_name
314
+
315
+ if command_name == describe_command_name
316
+ described_command = command_log_entry.inputs[:command_name]
317
+
318
+ if command_log_entry.outcome[:success]
319
+ describe_command_call_indexes[described_command] ||= []
320
+ describe_command_call_indexes[described_command] << index
321
+ end
322
+ end
323
+
324
+ commands[command_name] ||= [[], []]
325
+ bucket_index = command_log_entry.outcome[:success] ? 0 : 1
326
+ commands[command_name][bucket_index] << index
327
+ end
328
+
329
+ indexes_to_delete = []
330
+
331
+ describe_command_call_indexes.each_value do |indexes|
332
+ indexes_to_delete += indexes[0..-2]
333
+ end
334
+
335
+ commands.each_value do |(success_indexes, failure_indexes)|
336
+ last_success = success_indexes.last
337
+ next unless last_success
338
+
339
+ failure_indexes.each do |failure_index|
340
+ if failure_index < last_success
341
+ indexes_to_delete << failure_index
342
+ else
343
+ # :nocov:
344
+ break
345
+ # :nocov:
346
+ end
347
+ end
348
+ end
349
+
350
+ if indexes_to_delete.empty?
351
+ return
352
+ end
353
+
354
+ new_log = []
355
+ context.command_log = context.command_log.each.with_index do |entry, index|
356
+ unless indexes_to_delete.include?(index)
357
+ new_log << entry
358
+ end
359
+ end
360
+
361
+ context.command_log = new_log
362
+ end
363
+
415
364
  def increment_command_calls
416
365
  self.command_calls ||= -1
417
366
  self.command_calls += 1
@@ -435,7 +384,7 @@ module Foobara
435
384
  outcome ||= command.outcome
436
385
  end
437
386
 
438
- if outcome
387
+ if outcome&.success?
439
388
  result ||= outcome.result
440
389
  end
441
390
 
@@ -481,10 +430,6 @@ module Foobara
481
430
  }
482
431
  end
483
432
 
484
- def command_described?
485
- described_commands.include?(next_command_name)
486
- end
487
-
488
433
  def described_commands
489
434
  @described_commands ||= Set.new
490
435
  end
@@ -493,14 +438,6 @@ module Foobara
493
438
  type.extends_type?(BuiltinTypes[:attributes]) && type.element_types.empty?
494
439
  end
495
440
 
496
- def choose_next_command_and_next_inputs_separately?
497
- choose_next_command_and_next_inputs_separately
498
- end
499
-
500
- def agent_name
501
- agent.agent_name
502
- end
503
-
504
441
  def verbose?
505
442
  verbose
506
443
  end
@@ -529,7 +466,7 @@ module Foobara
529
466
  first_to_expire = calls.first
530
467
 
531
468
  if first_to_expire
532
- (first_to_expire + SECONDS_PER_MINUTE) - Time.now
469
+ [0, (first_to_expire + SECONDS_PER_MINUTE) - Time.now].max
533
470
  else
534
471
  # TODO: figure out how to test this code path
535
472
  # :nocov:
@@ -13,7 +13,9 @@ module Foobara
13
13
 
14
14
  def cached_subclass(key)
15
15
  if subclass_cache.key?(key)
16
+ # :nocov:
16
17
  subclass_cache[key]
18
+ # :nocov:
17
19
  else
18
20
  subclass_cache[key] = yield
19
21
  end
@@ -0,0 +1,82 @@
1
+ require "foobara/llm_backed_command"
2
+
3
+ module Foobara
4
+ class Agent < CommandConnector
5
+ class DetermineBase < Foobara::LlmBackedCommand
6
+ inputs do
7
+ context Context, :required, "Context of the progress towards the goal so far"
8
+ llm_model :string,
9
+ one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
10
+ default: "claude-3-7-sonnet-20250219",
11
+ description: "The model to use for the LLM"
12
+ end
13
+
14
+ def association_depth
15
+ Foobara::JsonSchemaGenerator::AssociationDepth::ATOM
16
+ end
17
+
18
+ def build_messages
19
+ p = [
20
+ {
21
+ content: llm_instructions,
22
+ role: :system
23
+ }
24
+ ]
25
+
26
+ context.command_log.each do |command_log_entry|
27
+ agent_entry = {
28
+ command: command_log_entry.command_name
29
+ }
30
+
31
+ inputs = command_log_entry.inputs
32
+
33
+ if inputs && !inputs.empty?
34
+ agent_entry[:inputs] = inputs
35
+ end
36
+
37
+ outcome_entry = command_log_entry.outcome
38
+
39
+ p << {
40
+ content: agent_entry,
41
+ role: :assistant
42
+ }
43
+ p << {
44
+ content: outcome_entry,
45
+ role: :user
46
+ }
47
+ end
48
+
49
+ p
50
+ end
51
+
52
+ def llm_instructions
53
+ return @llm_instructions if defined?(@llm_instructions)
54
+
55
+ description = self.class.description
56
+
57
+ instructions = "You are the implementation of a command called #{self.class.scoped_full_name}"
58
+
59
+ instructions += if description && !description.empty?
60
+ " which has the following description:\n\n#{self.class.description}\n\n"
61
+ else
62
+ # :nocov:
63
+ ". "
64
+ # :nocov:
65
+ end
66
+
67
+ instructions += "You are working towards accomplishing the following goal:\n\n#{goal}\n\n"
68
+ instructions += "Your response should match the following JSON schema: \n\n#{self.class.result_json_schema}\n\n"
69
+ instructions += "You can get more details about the result schema for a specific command by " \
70
+ "choosing the DescribeCommand command. " \
71
+ "You will reply with nothing more than the JSON you've generated so that the calling code " \
72
+ "can successfully parse your answer."
73
+
74
+ @llm_instructions = instructions
75
+ end
76
+
77
+ def goal
78
+ context.current_goal
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,33 +1,17 @@
1
- require "foobara/llm_backed_command"
1
+ require_relative "determine_base"
2
2
 
3
3
  module Foobara
4
4
  class Agent < CommandConnector
5
- class DetermineNextCommandNameAndInputs < Foobara::LlmBackedCommand
6
- description "Accepts the current goal, which might already be accomplished, and context of the work " \
7
- "so far and returns the inputs for " \
8
- "the next command to run to make progress towards " \
9
- "accomplishing the goal. If the goal has already been accomplished then choose the " \
5
+ class DetermineNextCommandNameAndInputs < DetermineBase
6
+ description "Returns the name of the next command to run and its inputs given the progress " \
7
+ "towards accomplishing the current goal. " \
8
+ "If the goal has been accomplished it will choose the " \
10
9
  "NotifyUserThatCurrentGoalHasBeenAccomplished command."
11
10
 
12
- inputs do
13
- goal :string, :required, "The current goal to accomplish. If the goal has already been accomplished " \
14
- "by the previous command runs then choose " \
15
- "NotifyUserThatCurrentGoalHasBeenAccomplished to stop the loop."
16
- context Context, :required, "Context of the progress towards the goal so far"
17
- llm_model :string,
18
- one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
19
- default: "claude-3-7-sonnet-20250219",
20
- description: "The model to use for the LLM"
21
- end
22
-
23
11
  result do
24
- command_name :string, :required
12
+ command :string, :required
25
13
  inputs :attributes, :allow_nil
26
14
  end
27
-
28
- def association_depth
29
- Foobara::JsonSchemaGenerator::AssociationDepth::ATOM
30
- end
31
15
  end
32
16
  end
33
17
  end
@@ -19,6 +19,8 @@ module Foobara
19
19
  "The user might issue a new goal."
20
20
 
21
21
  if result_type
22
+ # TODO: fix this... agent backed command sets these via its own result type.
23
+ # check if message_to_user is already here and also search/fix result_data to be result for consistency.
22
24
  if include_message_to_user_in_result
23
25
  klass.add_inputs do
24
26
  result result_type, :required
@@ -1,11 +1,25 @@
1
1
  module Foobara
2
2
  class Agent < CommandConnector
3
3
  class Context < Foobara::Model
4
+ class << self
5
+ def for(goal)
6
+ new(command_log: [], current_goal: goal)
7
+ end
8
+ end
9
+
4
10
  attributes do
11
+ current_goal :string, :required, "The current goal the agent needs to accomplish"
12
+ previous_goals [:string]
5
13
  # TODO: why doesn't this default of [] work as expected on newly created models?
6
14
  command_log [CommandLogEntry], default: [],
7
15
  description: "Log of all commands run so far and their outcomes"
8
16
  end
17
+
18
+ def set_new_goal(goal)
19
+ self.previous_goals ||= []
20
+ previous_goals << current_goal
21
+ self.current_goal = goal
22
+ end
9
23
  end
10
24
  end
11
25
  end
data/src/foobara/agent.rb CHANGED
@@ -67,8 +67,6 @@ module Foobara
67
67
  # TODO: this should work now, switch to this approach
68
68
  # add_default_inputs_transformer EntityToPrimaryKeyInputsTransformer
69
69
 
70
- build_initial_context
71
-
72
70
  # TODO: push this convenience method up into base class?
73
71
  command_classes&.each do |command_class|
74
72
  connect(command_class)
@@ -116,10 +114,11 @@ module Foobara
116
114
  def accomplish_goal(
117
115
  goal,
118
116
  result_type: nil,
119
- choose_next_command_and_next_inputs_separately: nil,
120
117
  maximum_call_count: nil,
121
118
  llm_model: nil
122
119
  )
120
+ set_context_goal(goal)
121
+
123
122
  if result_type && self.result_type != result_type
124
123
  if self.result_type
125
124
  # :nocov:
@@ -144,7 +143,7 @@ module Foobara
144
143
  inputs = {
145
144
  goal:,
146
145
  final_result_type: self.result_type,
147
- current_context: context,
146
+ context:,
148
147
  agent: self
149
148
  }
150
149
 
@@ -154,10 +153,6 @@ module Foobara
154
153
  inputs[:llm_model] = llm_model
155
154
  end
156
155
 
157
- unless choose_next_command_and_next_inputs_separately.nil?
158
- inputs[:choose_next_command_and_next_inputs_separately] = choose_next_command_and_next_inputs_separately
159
- end
160
-
161
156
  unless maximum_call_count.nil?
162
157
  inputs[:maximum_command_calls] = maximum_call_count
163
158
  end
@@ -201,9 +196,12 @@ module Foobara
201
196
  end
202
197
  end
203
198
 
204
- def build_initial_context
205
- # TODO: shouldn't have to pass command_log here since it has a default, debug that
206
- self.context ||= Context.new(command_log: [])
199
+ def set_context_goal(goal)
200
+ if context
201
+ context.set_new_goal(goal)
202
+ else
203
+ self.context = Context.for(goal)
204
+ end
207
205
  end
208
206
 
209
207
  def mark_mission_accomplished(final_result, message_to_user)
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.8
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -54,8 +54,7 @@ files:
54
54
  - src/foobara/agent/connector/set_command_connector_inputs_transformer.rb
55
55
  - src/foobara/agent/describe_command.rb
56
56
  - src/foobara/agent/describe_type.rb
57
- - src/foobara/agent/determine_inputs_for_next_command.rb
58
- - src/foobara/agent/determine_next_command.rb
57
+ - src/foobara/agent/determine_base.rb
59
58
  - src/foobara/agent/determine_next_command_name_and_inputs.rb
60
59
  - src/foobara/agent/give_up.rb
61
60
  - src/foobara/agent/list_commands.rb
@@ -1,45 +0,0 @@
1
- require "foobara/llm_backed_command"
2
-
3
- module Foobara
4
- class Agent < CommandConnector
5
- class DetermineInputsForNextCommand < Foobara::LlmBackedCommand
6
- extend Concerns::SubclassCacheable
7
-
8
- class << self
9
- def for(command_class:, agent_id:)
10
- cached_subclass([command_class.full_command_name, agent_id]) do
11
- command_short_name = Util.non_full_name(command_class.command_name)
12
- class_name = "Foobara::Agent::#{agent_id}::DetermineInputsForNext#{command_short_name}Command"
13
- klass = Util.make_class_p(class_name, self)
14
-
15
- klass.description "Accepts a goal and context of the work so far and returns the inputs for " \
16
- "the next #{command_short_name} command to run to make progress towards " \
17
- "accomplishing the goal."
18
-
19
- klass.inputs do
20
- goal :string, :required, "The current (possibly already accomplished) goal"
21
- context Context, :required, "Context of the progress towards the goal so far"
22
- llm_model :string,
23
- one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
24
- default: "claude-3-7-sonnet-20250219",
25
- description: "The model to use for the LLM"
26
- end
27
-
28
- if command_class.inputs_type
29
- transformer = CommandConnectors::Transformers::EntityToPrimaryKeyInputsTransformer.new(
30
- to: command_class.inputs_type
31
- )
32
- klass.result transformer.from_type
33
- end
34
-
35
- klass
36
- end
37
- end
38
- end
39
-
40
- def association_depth
41
- Foobara::JsonSchemaGenerator::AssociationDepth::ATOM
42
- end
43
- end
44
- end
45
- end
@@ -1,32 +0,0 @@
1
- require "foobara/llm_backed_command"
2
-
3
- module Foobara
4
- class Agent < CommandConnector
5
- class DetermineNextCommand < Foobara::LlmBackedCommand
6
- description "Accepts the current goal, which might already be accomplished, " \
7
- "and context of the work " \
8
- "so far and returns the name of " \
9
- "the next command to run to make progress towards " \
10
- "accomplishing the goal. If the goal has already been accomplished then choose the " \
11
- "NotifyUserThatCurrentGoalHasBeenAccomplished command."
12
-
13
- inputs do
14
- goal :string, :required, "The current goal to accomplish. If the goal has already been accomplished " \
15
- "by the previous command runs then choose " \
16
- "NotifyUserThatCurrentGoalHasBeenAccomplished to stop the loop."
17
- context Context, :required, "Context of progress so far"
18
- llm_model :string,
19
- one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
20
- default: "claude-3-7-sonnet-20250219",
21
- description: "The model to use for the LLM"
22
- end
23
-
24
- result :string,
25
- description: "Name of the next command to run to make progress " \
26
- "towards accomplishing the mission"
27
- def association_depth
28
- Foobara::JsonSchemaGenerator::AssociationDepth::ATOM
29
- end
30
- end
31
- end
32
- end