foobara-agent 0.0.17 → 0.0.19

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: 786fe4f10e3355da241847e573ff5d80abfb174d5afdedc0cb936d0a08b583ab
4
- data.tar.gz: f6ae8472f81c4a7ba98f8daa6dd9998118d3d0dbf0728d5710b0d1b0575264ab
3
+ metadata.gz: a8033849f6ba2e137ede7ef837ba585f2e6705149e1a65c1282c971b524efbc7
4
+ data.tar.gz: 59bfa9001aa1badda383fc35a73dafa7fbf3d5adfec50bba217c291e74e25751
5
5
  SHA512:
6
- metadata.gz: d4f150fb79b86ea3e02413bbd2d08e186f08888dd462fd722037d7741d65f7ccfc04d080ac751abcf04cbd2ba3455c240a3a0695699b3b01fd9e3d5ffba96948
7
- data.tar.gz: 759813642a132a0f874506af6d5692f820fc62893e76e14bc759b33ebd353541917ad72b8589efa74141dee0fa8866638dd188e8860d7af4a9a412c69c344571
6
+ metadata.gz: 5625b3b2d52a6193b387c1031ca4950273a81eb872f349ccd43f2c63607e9f15615f17a26b1852d2d4a2d86585ffbb0ada7a816e23303babe2fc05bc86e4df41
7
+ data.tar.gz: ba10c928d42bce2d8cd20e5a98d6652ec8241b3e1a10cda7625e2b299307a1aa08561c36185733c65b2795ec08aa61024e6b7f87097d3bf08ff73d90d181746b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.0.19] - 2025-07-23
2
+
3
+ - Skip result: input if result is a Model and properly cast result
4
+ - Add include_message_to_user_in_result option to #accomplish_goal
5
+ - Include the llm_model in the verbose output
6
+ - Print out info about retries if verbose
7
+ - Change the language of the instructions to see what happens
8
+
9
+ ## [0.0.18] - 2025-07-21
10
+
11
+ - Add maximum_command_calls option to Agent#initialize
12
+
1
13
  ## [0.0.17] - 2025-07-10
2
14
 
3
15
  - Prefix verbose output with the agent name if there is one
data/lib/foobara/agent.rb CHANGED
@@ -10,12 +10,8 @@ module Foobara
10
10
 
11
11
  class << self
12
12
  def reset_all
13
- [
14
- NotifyUserThatCurrentGoalHasBeenAccomplished
15
- ].each do |command_class|
16
- command_class.clear_subclass_cache
17
- Util.descendants(command_class).each(&:clear_subclass_cache)
18
- end
13
+ NotifyUserThatCurrentGoalHasBeenAccomplished.clear_subclass_cache
14
+ Util.descendants(NotifyUserThatCurrentGoalHasBeenAccomplished).each(&:clear_subclass_cache)
19
15
  end
20
16
  end
21
17
  end
@@ -3,9 +3,6 @@ require_relative "list_commands"
3
3
  module Foobara
4
4
  class Agent < CommandConnector
5
5
  class AccomplishGoal < Foobara::Command
6
- # Using a const here so we can stub it in the test suite to speed things up
7
- SECONDS_PER_MINUTE = 60
8
-
9
6
  possible_error :gave_up, context: { reason: :string }, message: "Gave up."
10
7
  possible_error :too_many_command_calls,
11
8
  context: { maximum_command_calls: :integer }
@@ -28,7 +25,6 @@ module Foobara
28
25
  one_of: Ai::AnswerBot::Types::ModelEnum,
29
26
  default: Ai.default_llm_model,
30
27
  description: "The model to use for the LLM"
31
- max_llm_calls_per_minute :integer, :allow_nil
32
28
  user_association_depth :symbol, :allow_nil, one_of: Foobara::AssociationDepth
33
29
  result_entity_depth :symbol, :allow_nil, one_of: Foobara::AssociationDepth
34
30
  pass_aggregates_to_llm :boolean, :allow_nil
@@ -46,20 +42,17 @@ module Foobara
46
42
  simulate_describe_list_commands_command
47
43
  simulate_list_commands_run
48
44
  end
49
- # simulate_describe_command_run_for_all_commands
50
45
 
51
- until mission_accomplished or given_up or killed
46
+ until mission_accomplished? or given_up? or killed?
52
47
  increment_command_calls
53
48
  check_if_too_many_calls
54
49
 
55
- throttle_llm_calls_if_necessary
56
-
57
50
  determine_next_command_and_inputs
58
51
 
59
52
  run_next_command
60
53
  end
61
54
 
62
- if given_up
55
+ if given_up?
63
56
  add_given_up_error
64
57
  end
65
58
 
@@ -130,9 +123,17 @@ module Foobara
130
123
  # :nocov:
131
124
  end
132
125
 
133
- def determine_next_command_and_inputs(retries = 3, error_outcome = nil)
126
+ RETRY_COUNT = 3
127
+
128
+ def determine_next_command_and_inputs(retries = RETRY_COUNT, error_outcome = nil)
134
129
  return if killed
135
130
 
131
+ if verbose? && retries != RETRY_COUNT
132
+ # :nocov:
133
+ (io_err || $stderr).puts " !!! Retrying to determine next command and inputs. Retries left: #{retries}"
134
+ # :nocov:
135
+ end
136
+
136
137
  if retries == 0
137
138
  # TODO: test this path by irreparably breaking the needed commands
138
139
  # :nocov:
@@ -250,7 +251,8 @@ module Foobara
250
251
  inputs_type = next_command_class.inputs_type
251
252
 
252
253
  NestedTransactionable.with_needed_transactions_for_type(inputs_type) do
253
- inputs_type.process_value(next_command_inputs)
254
+ inputs = next_command_inputs.nil? ? {} : next_command_inputs
255
+ inputs_type.process_value(inputs)
254
256
  end
255
257
  end
256
258
 
@@ -431,7 +433,16 @@ module Foobara
431
433
  end
432
434
 
433
435
  agent_name = agent.agent_name
434
- prefix = agent_name && !agent_name.empty? ? "#{agent_name}: " : ""
436
+ prefix = if agent_name && !agent_name.empty?
437
+ "#{agent_name}<#{llm_model}>: "
438
+ else
439
+ # Not setting an agent_name results in non-determinism in NotifyUser... command
440
+ # namespace. This results in problems in the test suite making this code path a little
441
+ # harder to test indirectly.
442
+ # :nocov:
443
+ ""
444
+ # :nocov:
445
+ end
435
446
 
436
447
  (io_out || $stdout).puts "#{prefix}#{next_command_name}.run#{args}"
437
448
  end
@@ -457,10 +468,40 @@ module Foobara
457
468
  add_runtime_error(:gave_up, reason: final_message)
458
469
  end
459
470
 
471
+ def final_result_type_from_declaration
472
+ return @final_result_type_from_declaration if defined?(@final_result_type_from_declaration)
473
+
474
+ @final_result_type_from_declaration = if final_result_type
475
+ if final_result_type.is_a?(Types::Type)
476
+ final_result_type
477
+ else
478
+ domain = result_type.created_in_namespace.foobara_domain
479
+ domain.foobara_type_from_declaration(final_result_type)
480
+ end
481
+ end
482
+ end
483
+
460
484
  def build_result
485
+ result_data = if final_result_type_from_declaration
486
+ outcome = final_result_type_from_declaration.process_value(final_result)
487
+
488
+ if outcome.success?
489
+ outcome.result
490
+ elsif given_up? || killed?
491
+ final_result
492
+ else
493
+ # :nocov:
494
+ raise CommandPatternImplementation::Concerns::Result::CouldNotProcessResult,
495
+ outcome.errors
496
+ # :nocov:
497
+ end
498
+ else
499
+ final_result
500
+ end
501
+
461
502
  {
462
503
  message_to_user: final_message,
463
- result_data: final_result
504
+ result_data:
464
505
  }
465
506
  end
466
507
 
@@ -500,44 +541,20 @@ module Foobara
500
541
  llm_call_timestamps.unshift(Time.now)
501
542
  end
502
543
 
503
- def llm_calls_in_last_minute
504
- llm_call_timestamps.select { |t| t > (Time.now - 60) }
505
- end
506
-
507
- def llm_call_count_in_last_minute
508
- llm_calls_in_last_minute.size
544
+ def context
545
+ agent.context
509
546
  end
510
547
 
511
- def time_until_llm_call_count_in_last_minute_changes
512
- calls = llm_calls_in_last_minute
513
-
514
- first_to_expire = calls.first
515
-
516
- if first_to_expire
517
- [0, (first_to_expire + SECONDS_PER_MINUTE) - Time.now].max
518
- else
519
- # TODO: figure out how to test this code path
520
- # :nocov:
521
- 0
522
- # :nocov:
523
- end
548
+ def mission_accomplished?
549
+ mission_accomplished
524
550
  end
525
551
 
526
- def throttle_llm_calls_if_necessary
527
- return unless max_llm_calls_per_minute && max_llm_calls_per_minute > 0
528
-
529
- if llm_call_count_in_last_minute >= max_llm_calls_per_minute
530
- seconds = time_until_llm_call_count_in_last_minute_changes
531
- if verbose?
532
- (io_out || $stdout).puts "Sleeping for #{seconds} seconds to avoid LLM calls per minute limit"
533
- end
534
-
535
- sleep seconds
536
- end
552
+ def given_up?
553
+ given_up
537
554
  end
538
555
 
539
- def context
540
- agent.context
556
+ def killed?
557
+ killed
541
558
  end
542
559
  end
543
560
  end
@@ -1,8 +1,8 @@
1
- require_relative "determine_base"
1
+ require "foobara/llm_backed_command"
2
2
 
3
3
  module Foobara
4
4
  class Agent < CommandConnector
5
- class DetermineNextCommandNameAndInputs < DetermineBase
5
+ class DetermineNextCommandNameAndInputs < Foobara::LlmBackedCommand
6
6
  class << self
7
7
  def llm_instructions(assistant_association_depth, goal, previous_goals = nil)
8
8
  key = [assistant_association_depth, goal, previous_goals]
@@ -17,15 +17,8 @@ module Foobara
17
17
  end
18
18
 
19
19
  def build_llm_instructions(assistant_association_depth, goal, previous_goals)
20
- instructions = "You are the implementation of a command called #{scoped_full_name}"
21
-
22
- instructions += if description && !description.empty?
23
- " which has the following description:\n\n#{description}\n\n"
24
- else
25
- # :nocov:
26
- ". "
27
- # :nocov:
28
- end
20
+ instructions = "You are part of an agent implementation. " \
21
+ "Your task is to decide which command to run next.\n\n"
29
22
 
30
23
  result_schema = result_json_schema(assistant_association_depth)
31
24
 
@@ -38,13 +31,25 @@ module Foobara
38
31
  end
39
32
  end
40
33
 
41
- instructions += "You are working towards accomplishing the following goal:\n\n#{goal}\n\n"
34
+ instructions += "You are working towards accomplishing the following goal: #{goal}\n\n"
35
+
36
+ instructions += "You will answer with the name of the next command to run and, if needed, its inputs. " \
37
+ "Choose whichever command is best to make progress towards accomplishing the current goal " \
38
+ "based on the progress made so far. " \
39
+ "If the goal has been accomplished then choose the " \
40
+ "NotifyUserThatCurrentGoalHasBeenAccomplished command. " \
41
+ "If you are stuck either due to errors " \
42
+ "or because you do not have the command you need to accomplish the " \
43
+ "goal, then choose GiveUp.\n\n"
42
44
 
43
- instructions += "Your response of which command to run next should match the following JSON schema:"
44
- instructions += "\n\n#{result_schema}\n\n"
45
45
  instructions += "You can get more details about the inputs and result schemas for a specific command by " \
46
- "choosing the DescribeCommand command. " \
47
- "You will reply with nothing more than the JSON you've generated so that the calling code " \
46
+ "choosing the DescribeCommand command.\n\n"
47
+
48
+ instructions += "Your result type is described by the following JSON schema:" \
49
+ "\n\n#{result_schema}\n\n" \
50
+ "You will reply with nothing more than the JSON that you've " \
51
+ "generated that adheres to this result type schema " \
52
+ "so that the calling code " \
48
53
  "can successfully parse your answer."
49
54
 
50
55
  instructions
@@ -54,13 +59,24 @@ module Foobara
54
59
  description "Returns the name of the next command to run and its inputs given the progress " \
55
60
  "towards accomplishing the current goal. " \
56
61
  "If the goal has been accomplished it will choose the " \
57
- "NotifyUserThatCurrentGoalHasBeenAccomplished command."
62
+ "NotifyUserThatCurrentGoalHasBeenAccomplished command. It will choose GiveUp if it runs into an " \
63
+ "error it cannot get around or does not believe it has the proper commands to accomplish its goal."
58
64
 
59
65
  result do
60
66
  command :string, :required
61
67
  inputs :attributes, :allow_nil
62
68
  end
63
69
 
70
+ inputs do
71
+ agent Agent, :required
72
+ pass_aggregates_to_llm :boolean, :allow_nil, "Should we send aggregates to the LLM or " \
73
+ "require it to fetch what it needs?"
74
+ llm_model :string,
75
+ one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
76
+ default: Ai.default_llm_model,
77
+ description: "The model to use for the LLM"
78
+ end
79
+
64
80
  def determine_llm_instructions
65
81
  self.llm_instructions = self.class.llm_instructions(
66
82
  computed_user_association_depth,
@@ -68,6 +84,64 @@ module Foobara
68
84
  previous_goal_and_status_pairs
69
85
  )
70
86
  end
87
+
88
+ def association_depth
89
+ if pass_aggregates_to_llm
90
+ Foobara::AssociationDepth::AGGREGATE
91
+ else
92
+ Foobara::AssociationDepth::ATOM
93
+ end
94
+ end
95
+
96
+ def build_messages
97
+ p = [
98
+ {
99
+ content: llm_instructions,
100
+ role: :system
101
+ }
102
+ ]
103
+
104
+ context.command_log.each do |command_log_entry|
105
+ agent_entry = {
106
+ command: command_log_entry.command_name
107
+ }
108
+
109
+ inputs = command_log_entry.inputs
110
+
111
+ if inputs && !inputs.empty?
112
+ agent_entry[:inputs] = inputs
113
+ end
114
+
115
+ outcome_entry = command_log_entry.outcome
116
+
117
+ p << {
118
+ content: agent_entry,
119
+ role: :assistant
120
+ }
121
+ p << {
122
+ content: outcome_entry,
123
+ role: :user
124
+ }
125
+ end
126
+
127
+ p
128
+ end
129
+
130
+ def goal
131
+ context.current_goal.text
132
+ end
133
+
134
+ def previous_goal_and_status_pairs
135
+ if context.previous_goals && !context.previous_goals.empty?
136
+ context.previous_goals.map do |previous_goal|
137
+ [previous_goal.text, previous_goal.state]
138
+ end
139
+ end
140
+ end
141
+
142
+ def context
143
+ agent.context
144
+ end
71
145
  end
72
146
  end
73
147
  end
@@ -7,7 +7,7 @@ module Foobara
7
7
 
8
8
  class << self
9
9
  attr_accessor :command_class, :returns_message_to_user, :returns_result_data, :result_is_attributes,
10
- :built_result_type
10
+ :built_result_type, :result_is_model
11
11
 
12
12
  def for(agent_id: nil, result_type: nil, include_message_to_user_in_result: true, result_entity_depth: nil)
13
13
  agent_id ||= "Anon#{SecureRandom.hex(2)}"
@@ -46,20 +46,38 @@ module Foobara
46
46
  "result schema and a message to the user. " \
47
47
  "The user might issue a new goal."
48
48
  else
49
- if result_type.extends?(BuiltinTypes[:attributes])
50
- klass.built_result_type = result_type
51
- klass.result_is_attributes = true
52
- klass.add_inputs result_type
53
- else
54
- klass.built_result_type = domain.foobara_type_from_declaration do
55
- result result_type, :required
49
+ if result_type.extends?(BuiltinTypes[:model]) &&
50
+ !result_type.extends?(BuiltinTypes[:detached_entity]) && result_type != BuiltinTypes[:model]
51
+ # TODO: Create a ModelToAttributesInputsTransformer in foobara gem and move this logic there
52
+ model_declaration = result_type.declaration_data
53
+
54
+ # Distinguishing between nil and no inputs might be tricky so if :allow_nil then
55
+ # we will prefer `result: nil` over `nil`
56
+ # if there's a description we'll also prefer result: so that the description will
57
+ # make it through to the LLM on that input.
58
+ # TODO: handle the model's description via the command's description instead.
59
+ if !model_declaration.key?(:allow_nil) && !model_declaration.key?(:description)
60
+ klass.built_result_type = result_type
61
+ klass.result_is_model = true
62
+ klass.add_inputs result_type.target_class.attributes_type
56
63
  end
57
- klass.add_inputs klass.built_result_type
58
64
  end
59
65
 
60
- klass.description "Notifies the user that the current goal has been accomplished and returns a final " \
61
- "result formatted according to the " \
62
- "result schema. " \
66
+ unless klass.result_is_model
67
+ if result_type.extends?(BuiltinTypes[:attributes])
68
+ klass.built_result_type = result_type
69
+ klass.result_is_attributes = true
70
+ klass.add_inputs result_type
71
+ else
72
+ klass.built_result_type = domain.foobara_type_from_declaration do
73
+ result result_type, :required
74
+ end
75
+ klass.add_inputs klass.built_result_type
76
+ end
77
+ end
78
+
79
+ klass.description "Notifies the user that the current goal has been accomplished and accepts a final " \
80
+ "result value as inputs. " \
63
81
  "The user might issue a new goal."
64
82
  end
65
83
  elsif include_message_to_user_in_result
@@ -112,7 +130,7 @@ module Foobara
112
130
  DetachedEntity.contains_associations?(inputs_type)
113
131
  command_klass.before_commit_transaction do |command:, **|
114
132
  # TODO: why can't we just pass in the command??
115
- built_result = command.built_result
133
+ built_result = command.final_result_data
116
134
 
117
135
  if built_result
118
136
  transformer.process_value!(built_result)
@@ -130,34 +148,35 @@ module Foobara
130
148
  end
131
149
 
132
150
  def execute
133
- build_result
151
+ extract_result_data_and_message_to_user
134
152
  mark_mission_accomplished
135
153
 
136
154
  nil
137
155
  end
138
156
 
139
- attr_accessor :built_result
140
-
141
- def mark_mission_accomplished
142
- result, message_to_user = if returns_message_to_user?
143
- [built_result[:result], built_result[:message_to_user]]
144
- elsif returns_result_data?
145
- [built_result, nil]
146
- end
157
+ attr_accessor :final_result_data, :final_message_to_user
158
+
159
+ def extract_result_data_and_message_to_user
160
+ result, message = if returns_message_to_user?
161
+ inputs.values_at(:result, :message_to_user)
162
+ elsif returns_result_data?
163
+ if result_is_attributes?
164
+ inputs.slice(*self.class.built_result_type.element_types.keys)
165
+ elsif result_is_model?
166
+ inputs.slice(
167
+ *self.class.built_result_type.target_class.attributes_type.element_types.keys
168
+ )
169
+ else
170
+ inputs[:result]
171
+ end
172
+ end
147
173
 
148
- command_connector.mark_mission_accomplished(result, message_to_user)
174
+ self.final_result_data = result
175
+ self.final_message_to_user = message
149
176
  end
150
177
 
151
- def build_result
152
- self.built_result = if returns_message_to_user?
153
- inputs.slice(:result, :message_to_user)
154
- elsif returns_result_data?
155
- if result_is_attributes?
156
- inputs.slice(*self.class.built_result_type.element_types.keys)
157
- else
158
- inputs[:result]
159
- end
160
- end
178
+ def mark_mission_accomplished
179
+ command_connector.mark_mission_accomplished(final_result_data, final_message_to_user)
161
180
  end
162
181
 
163
182
  def returns_message_to_user?
@@ -171,6 +190,10 @@ module Foobara
171
190
  def result_is_attributes?
172
191
  self.class.result_is_attributes
173
192
  end
193
+
194
+ def result_is_model?
195
+ self.class.result_is_model
196
+ end
174
197
  end
175
198
  end
176
199
  end
@@ -3,12 +3,6 @@ require_relative "goal"
3
3
  module Foobara
4
4
  class Agent < CommandConnector
5
5
  class Context < Foobara::Model
6
- class << self
7
- def for(goal)
8
- new(command_log: [], current_goal: Goal.new(text: goal))
9
- end
10
- end
11
-
12
6
  attributes do
13
7
  current_goal Goal, :required, "The current goal the agent needs to accomplish"
14
8
  previous_goals [Goal]
@@ -19,7 +13,11 @@ module Foobara
19
13
 
20
14
  def set_new_goal(goal)
21
15
  self.previous_goals ||= []
22
- previous_goals << current_goal
16
+
17
+ if current_goal
18
+ previous_goals << current_goal
19
+ end
20
+
23
21
  self.current_goal = Goal.new(text: goal)
24
22
  end
25
23
  end
data/src/foobara/agent.rb CHANGED
@@ -24,9 +24,9 @@ module Foobara
24
24
  :verbose,
25
25
  :io_out,
26
26
  :io_err,
27
- :max_llm_calls_per_minute,
28
27
  :pass_aggregates_to_llm,
29
- :result_entity_depth
28
+ :result_entity_depth,
29
+ :maximum_command_calls
30
30
 
31
31
  def initialize(
32
32
  context: nil,
@@ -38,13 +38,14 @@ module Foobara
38
38
  verbose: false,
39
39
  io_out: nil,
40
40
  io_err: nil,
41
- max_llm_calls_per_minute: nil,
42
41
  result_entity_depth: AssociationDepth::AGGREGATE,
43
42
  pass_aggregates_to_llm: nil,
43
+ maximum_command_calls: nil,
44
44
  **opts
45
45
  )
46
46
  # TODO: shouldn't have to pass command_log here since it has a default, debug that
47
- self.context = context
47
+ self.context = context || Context.new(command_log: [])
48
+
48
49
  if agent_name
49
50
  self.agent_name = agent_name
50
51
  end
@@ -55,9 +56,9 @@ module Foobara
55
56
  self.verbose = verbose
56
57
  self.io_out = io_out
57
58
  self.io_err = io_err
58
- self.max_llm_calls_per_minute = max_llm_calls_per_minute
59
59
  self.result_entity_depth = result_entity_depth
60
60
  self.pass_aggregates_to_llm = pass_aggregates_to_llm
61
+ self.maximum_command_calls = maximum_command_calls
61
62
 
62
63
  pre_commit_transformer = if pass_aggregates_to_llm
63
64
  CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer
@@ -124,9 +125,14 @@ module Foobara
124
125
  def accomplish_goal(
125
126
  goal,
126
127
  result_type: nil,
127
- maximum_call_count: nil,
128
+ maximum_command_calls: nil,
129
+ include_message_to_user_in_result: nil,
128
130
  llm_model: nil
129
131
  )
132
+ unless include_message_to_user_in_result.nil?
133
+ self.include_message_to_user_in_result = include_message_to_user_in_result
134
+ end
135
+
130
136
  set_context_goal(goal)
131
137
 
132
138
  if result_type && self.result_type != result_type
@@ -150,7 +156,10 @@ module Foobara
150
156
  state_machine.perform_transition!(:accomplish_goal)
151
157
 
152
158
  begin
153
- inputs = accomplish_goal_inputs(goal, result_type:, maximum_call_count:, llm_model:)
159
+ inputs = accomplish_goal_inputs(goal,
160
+ result_type:,
161
+ maximum_command_calls:,
162
+ llm_model:)
154
163
 
155
164
  self.current_accomplish_goal_command = AccomplishGoal.new(inputs)
156
165
 
@@ -186,7 +195,7 @@ module Foobara
186
195
 
187
196
  def accomplish_goal_inputs(goal,
188
197
  result_type: nil,
189
- maximum_call_count: nil,
198
+ maximum_command_calls: nil,
190
199
  llm_model: nil)
191
200
  inputs = {
192
201
  goal:,
@@ -200,8 +209,12 @@ module Foobara
200
209
  inputs[:llm_model] = llm_model
201
210
  end
202
211
 
203
- unless maximum_call_count.nil?
204
- inputs[:maximum_command_calls] = maximum_call_count
212
+ if maximum_command_calls.nil?
213
+ maximum_command_calls = self.maximum_command_calls
214
+ end
215
+
216
+ unless maximum_command_calls.nil?
217
+ inputs[:maximum_command_calls] = maximum_command_calls
205
218
  end
206
219
 
207
220
  if verbose
@@ -220,10 +233,6 @@ module Foobara
220
233
  inputs[:include_message_to_user_in_result] = include_message_to_user_in_result
221
234
  end
222
235
 
223
- if max_llm_calls_per_minute && max_llm_calls_per_minute > 0
224
- inputs[:max_llm_calls_per_minute] = max_llm_calls_per_minute
225
- end
226
-
227
236
  if result_entity_depth
228
237
  inputs[:result_entity_depth] = result_entity_depth
229
238
  end
@@ -236,11 +245,7 @@ module Foobara
236
245
  end
237
246
 
238
247
  def set_context_goal(goal)
239
- if context
240
- context.set_new_goal(goal)
241
- else
242
- self.context = Context.for(goal)
243
- end
248
+ context.set_new_goal(goal)
244
249
  end
245
250
 
246
251
  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.17
4
+ version: 0.0.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -66,7 +66,6 @@ files:
66
66
  - src/foobara/agent/connector/set_command_connector_inputs_transformer.rb
67
67
  - src/foobara/agent/describe_command.rb
68
68
  - src/foobara/agent/describe_type.rb
69
- - src/foobara/agent/determine_base.rb
70
69
  - src/foobara/agent/determine_next_command_name_and_inputs.rb
71
70
  - src/foobara/agent/give_up.rb
72
71
  - src/foobara/agent/list_commands.rb
@@ -1,76 +0,0 @@
1
- require "foobara/llm_backed_command"
2
-
3
- module Foobara
4
- class Agent < CommandConnector
5
- # TODO: just move this back to DetermineNextCommandAndInputs since it's now the only base class
6
- class DetermineBase < Foobara::LlmBackedCommand
7
- inputs do
8
- pass_aggregates_to_llm :boolean, :allow_nil, "Should we send aggregates to the LLM or " \
9
- "require it to fetch what it needs?"
10
- agent Agent, :required
11
- llm_model :string,
12
- one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
13
- default: Ai.default_llm_model,
14
- description: "The model to use for the LLM"
15
- end
16
-
17
- def association_depth
18
- if pass_aggregates_to_llm
19
- Foobara::AssociationDepth::AGGREGATE
20
- else
21
- Foobara::AssociationDepth::ATOM
22
- end
23
- end
24
-
25
- def build_messages
26
- p = [
27
- {
28
- content: llm_instructions,
29
- role: :system
30
- }
31
- ]
32
-
33
- context.command_log.each do |command_log_entry|
34
- agent_entry = {
35
- command: command_log_entry.command_name
36
- }
37
-
38
- inputs = command_log_entry.inputs
39
-
40
- if inputs && !inputs.empty?
41
- agent_entry[:inputs] = inputs
42
- end
43
-
44
- outcome_entry = command_log_entry.outcome
45
-
46
- p << {
47
- content: agent_entry,
48
- role: :assistant
49
- }
50
- p << {
51
- content: outcome_entry,
52
- role: :user
53
- }
54
- end
55
-
56
- p
57
- end
58
-
59
- def goal
60
- context.current_goal.text
61
- end
62
-
63
- def previous_goal_and_status_pairs
64
- if context.previous_goals && !context.previous_goals.empty?
65
- context.previous_goals.map do |previous_goal|
66
- [previous_goal.text, previous_goal.state]
67
- end
68
- end
69
- end
70
-
71
- def context
72
- agent.context
73
- end
74
- end
75
- end
76
- end