foobara-agent 0.0.9 → 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 +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/foobara/agent.rb +0 -1
- data/src/foobara/agent/accomplish_goal.rb +40 -170
- data/src/foobara/agent/concerns/subclass_cacheable.rb +2 -0
- data/src/foobara/agent/determine_next_command_name_and_inputs.rb +1 -1
- data/src/foobara/agent/notify_user_that_current_goal_has_been_accomplished.rb +2 -0
- data/src/foobara/agent.rb +0 -5
- metadata +1 -3
- data/src/foobara/agent/determine_inputs_for_next_command.rb +0 -33
- data/src/foobara/agent/determine_next_command.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c8f095d06fb173bc816e83f19755cfe246f15bf2b8ef1f8f91bce08e5960e74
|
4
|
+
data.tar.gz: 15b48a59c43c909070f68bbc58c68ce02635d019f9d3af68f1c3aa4264cd4903
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f558c74920f9759a98b2605fe2dcbbc52678ff5d90a0977182a75d0949cfd400b07d3d17d4dbfe162b37a3f463c3c55b4e25dee6382f2daeb3256b5df9615f73
|
7
|
+
data.tar.gz: f0588e94676e579c1fd5d7b3b603452d0c283b2b14217511b74a8d341d3126ff42cf7d0cb1cc881048d356525c7b9250f73c1a7631dae556a589f23b8627e1f8
|
data/CHANGELOG.md
CHANGED
data/lib/foobara/agent.rb
CHANGED
@@ -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
|
|
@@ -56,15 +50,10 @@ module Foobara
|
|
56
50
|
|
57
51
|
throttle_llm_calls_if_necessary
|
58
52
|
|
59
|
-
|
60
|
-
determine_next_command_then_inputs_separately
|
61
|
-
else
|
62
|
-
determine_next_command_and_inputs
|
63
|
-
end
|
64
|
-
|
53
|
+
determine_next_command_and_inputs
|
65
54
|
run_next_command
|
55
|
+
|
66
56
|
log_last_command_outcome
|
67
|
-
compact_command_log
|
68
57
|
end
|
69
58
|
|
70
59
|
if given_up
|
@@ -138,7 +127,24 @@ module Foobara
|
|
138
127
|
# :nocov:
|
139
128
|
end
|
140
129
|
|
141
|
-
def determine_next_command_and_inputs(retries =
|
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
|
+
|
142
148
|
inputs_for_determine = {
|
143
149
|
context:,
|
144
150
|
llm_model:
|
@@ -156,7 +162,7 @@ module Foobara
|
|
156
162
|
end
|
157
163
|
|
158
164
|
if outcome.success?
|
159
|
-
self.next_command_name = outcome.result[:
|
165
|
+
self.next_command_name = outcome.result[:command]
|
160
166
|
self.next_command_inputs = outcome.result[:inputs]
|
161
167
|
self.next_command_raw_inputs = next_command_inputs
|
162
168
|
|
@@ -176,7 +182,8 @@ module Foobara
|
|
176
182
|
)
|
177
183
|
|
178
184
|
simulate_describe_command
|
179
|
-
|
185
|
+
|
186
|
+
determine_next_command_and_inputs(retries - 1, outcome)
|
180
187
|
end
|
181
188
|
else
|
182
189
|
self.next_command_inputs = {}
|
@@ -190,11 +197,7 @@ module Foobara
|
|
190
197
|
result: nil
|
191
198
|
)
|
192
199
|
|
193
|
-
|
194
|
-
determine_next_command_and_inputs(retries - 1)
|
195
|
-
else
|
196
|
-
determine_next_command_then_inputs_separately
|
197
|
-
end
|
200
|
+
determine_next_command_and_inputs(retries - 1, outcome)
|
198
201
|
end
|
199
202
|
else
|
200
203
|
log_command_outcome(
|
@@ -204,23 +207,7 @@ module Foobara
|
|
204
207
|
result: outcome.result || determine_command.raw_result
|
205
208
|
)
|
206
209
|
|
207
|
-
|
208
|
-
determine_next_command_and_inputs(retries - 1)
|
209
|
-
else
|
210
|
-
determine_next_command_then_inputs_separately
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def determine_next_command_then_inputs_separately
|
216
|
-
determine_next_command_name
|
217
|
-
|
218
|
-
if command_described?
|
219
|
-
fetch_next_command_class
|
220
|
-
determine_next_command_inputs
|
221
|
-
else
|
222
|
-
choose_describe_command_instead
|
223
|
-
fetch_next_command_class
|
210
|
+
determine_next_command_and_inputs(retries - 1, outcome)
|
224
211
|
end
|
225
212
|
end
|
226
213
|
|
@@ -259,74 +246,6 @@ module Foobara
|
|
259
246
|
@command_name_type ||= Agent.foobara_type_from_declaration(:string, one_of: all_command_classes)
|
260
247
|
end
|
261
248
|
|
262
|
-
def determine_next_command_name(retries = 2)
|
263
|
-
self.next_command_name = if delayed_command_name
|
264
|
-
name = delayed_command_name
|
265
|
-
self.delayed_command_name = nil
|
266
|
-
name
|
267
|
-
else
|
268
|
-
inputs = { context: }
|
269
|
-
if llm_model
|
270
|
-
inputs[:llm_model] = llm_model
|
271
|
-
end
|
272
|
-
|
273
|
-
command = DetermineNextCommand.new(inputs)
|
274
|
-
outcome = begin
|
275
|
-
record_llm_call_timestamp
|
276
|
-
command.run
|
277
|
-
rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
|
278
|
-
# :nocov:
|
279
|
-
Outcome.errors(e.errors)
|
280
|
-
# :nocov:
|
281
|
-
end
|
282
|
-
|
283
|
-
if outcome.success?
|
284
|
-
self.next_command_name = outcome.result
|
285
|
-
|
286
|
-
outcome = validate_next_command_name
|
287
|
-
|
288
|
-
unless outcome.success?
|
289
|
-
# TODO: figure out a way to hit this path in the test suite or delete it
|
290
|
-
# :nocov:
|
291
|
-
log_command_outcome(
|
292
|
-
command:,
|
293
|
-
inputs: command.inputs.except(:context),
|
294
|
-
outcome:,
|
295
|
-
result: outcome.result || command.raw_result
|
296
|
-
)
|
297
|
-
|
298
|
-
if retries > 0
|
299
|
-
return determine_next_command_name(retries - 1)
|
300
|
-
end
|
301
|
-
# :nocov:
|
302
|
-
end
|
303
|
-
else
|
304
|
-
# TODO: either figure out a way to hit this path in the test suite or delete it
|
305
|
-
# :nocov:
|
306
|
-
log_command_outcome(
|
307
|
-
command:,
|
308
|
-
inputs: command.inputs&.except(:context),
|
309
|
-
outcome:,
|
310
|
-
result: outcome.result || command.raw_result
|
311
|
-
)
|
312
|
-
|
313
|
-
if retries > 0
|
314
|
-
return determine_next_command_name(retries - 1)
|
315
|
-
end
|
316
|
-
# :nocov:
|
317
|
-
end
|
318
|
-
|
319
|
-
outcome.raise!
|
320
|
-
outcome.result
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def choose_describe_command_instead
|
325
|
-
self.delayed_command_name = next_command_name
|
326
|
-
self.next_command_inputs = { command_name: next_command_name }
|
327
|
-
self.next_command_name = DescribeCommand.full_command_name
|
328
|
-
end
|
329
|
-
|
330
249
|
def all_command_classes
|
331
250
|
@all_command_classes ||= run_subcommand!(ListCommands, command_connector: agent).values.flatten
|
332
251
|
end
|
@@ -335,58 +254,11 @@ module Foobara
|
|
335
254
|
self.next_command_class = agent.transformed_command_from_name(next_command_name)
|
336
255
|
end
|
337
256
|
|
338
|
-
def determine_next_command_inputs(retries = 2)
|
339
|
-
self.next_command_inputs = if next_command_has_inputs?
|
340
|
-
command_class = command_class_for_determine_inputs_for_next_command
|
341
|
-
|
342
|
-
inputs = { context: }
|
343
|
-
if llm_model
|
344
|
-
inputs[:llm_model] = llm_model
|
345
|
-
end
|
346
|
-
|
347
|
-
command = command_class.new(inputs)
|
348
|
-
outcome = begin
|
349
|
-
record_llm_call_timestamp
|
350
|
-
command.run
|
351
|
-
rescue CommandPatternImplementation::Concerns::Result::CouldNotProcessResult => e
|
352
|
-
# :nocov:
|
353
|
-
Outcome.errors(e.errors)
|
354
|
-
# :nocov:
|
355
|
-
end
|
356
|
-
|
357
|
-
unless outcome.success?
|
358
|
-
# TODO: either figure out a way to hit this path in the test suite or delete it
|
359
|
-
# :nocov:
|
360
|
-
log_command_outcome(
|
361
|
-
command_name: next_command_name,
|
362
|
-
inputs: command.raw_result,
|
363
|
-
outcome:
|
364
|
-
)
|
365
|
-
|
366
|
-
simulate_describe_command
|
367
|
-
|
368
|
-
if retries > 0
|
369
|
-
return determine_next_command_inputs(retries - 1)
|
370
|
-
end
|
371
|
-
# :nocov:
|
372
|
-
end
|
373
|
-
|
374
|
-
outcome.raise!
|
375
|
-
outcome.result
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
257
|
def next_command_has_inputs?
|
380
258
|
type = next_command_class.inputs_type
|
381
259
|
type && !empty_attributes?(type)
|
382
260
|
end
|
383
261
|
|
384
|
-
def command_class_for_determine_inputs_for_next_command
|
385
|
-
DetermineInputsForNextCommand.for(
|
386
|
-
command_class: next_command_class, agent_id: agent_name
|
387
|
-
)
|
388
|
-
end
|
389
|
-
|
390
262
|
def run_next_command
|
391
263
|
if verbose?
|
392
264
|
args = if next_command_inputs.nil? || next_command_inputs.empty?
|
@@ -402,6 +274,7 @@ module Foobara
|
|
402
274
|
# :nocov:
|
403
275
|
end
|
404
276
|
end
|
277
|
+
|
405
278
|
(io_out || $stdout).puts "#{next_command_name}.run#{args}"
|
406
279
|
end
|
407
280
|
|
@@ -431,8 +304,8 @@ module Foobara
|
|
431
304
|
def compact_command_log
|
432
305
|
# Rules:
|
433
306
|
# Delete errors for any command that has succeeded since
|
434
|
-
# Delete all but the last DescribeCommand call
|
435
|
-
describe_command_call_indexes =
|
307
|
+
# Delete all but the last DescribeCommand call for a given
|
308
|
+
describe_command_call_indexes = {}
|
436
309
|
commands = {}
|
437
310
|
|
438
311
|
describe_command_name = DescribeCommand.full_command_name
|
@@ -440,7 +313,12 @@ module Foobara
|
|
440
313
|
command_name = command_log_entry.command_name
|
441
314
|
|
442
315
|
if command_name == describe_command_name
|
443
|
-
|
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
|
444
322
|
end
|
445
323
|
|
446
324
|
commands[command_name] ||= [[], []]
|
@@ -448,7 +326,11 @@ module Foobara
|
|
448
326
|
commands[command_name][bucket_index] << index
|
449
327
|
end
|
450
328
|
|
451
|
-
indexes_to_delete =
|
329
|
+
indexes_to_delete = []
|
330
|
+
|
331
|
+
describe_command_call_indexes.each_value do |indexes|
|
332
|
+
indexes_to_delete += indexes[0..-2]
|
333
|
+
end
|
452
334
|
|
453
335
|
commands.each_value do |(success_indexes, failure_indexes)|
|
454
336
|
last_success = success_indexes.last
|
@@ -548,10 +430,6 @@ module Foobara
|
|
548
430
|
}
|
549
431
|
end
|
550
432
|
|
551
|
-
def command_described?
|
552
|
-
described_commands.include?(next_command_name)
|
553
|
-
end
|
554
|
-
|
555
433
|
def described_commands
|
556
434
|
@described_commands ||= Set.new
|
557
435
|
end
|
@@ -560,14 +438,6 @@ module Foobara
|
|
560
438
|
type.extends_type?(BuiltinTypes[:attributes]) && type.element_types.empty?
|
561
439
|
end
|
562
440
|
|
563
|
-
def choose_next_command_and_next_inputs_separately?
|
564
|
-
choose_next_command_and_next_inputs_separately
|
565
|
-
end
|
566
|
-
|
567
|
-
def agent_name
|
568
|
-
agent.agent_name
|
569
|
-
end
|
570
|
-
|
571
441
|
def verbose?
|
572
442
|
verbose
|
573
443
|
end
|
@@ -596,7 +466,7 @@ module Foobara
|
|
596
466
|
first_to_expire = calls.first
|
597
467
|
|
598
468
|
if first_to_expire
|
599
|
-
(first_to_expire + SECONDS_PER_MINUTE) - Time.now
|
469
|
+
[0, (first_to_expire + SECONDS_PER_MINUTE) - Time.now].max
|
600
470
|
else
|
601
471
|
# TODO: figure out how to test this code path
|
602
472
|
# :nocov:
|
@@ -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
|
data/src/foobara/agent.rb
CHANGED
@@ -114,7 +114,6 @@ module Foobara
|
|
114
114
|
def accomplish_goal(
|
115
115
|
goal,
|
116
116
|
result_type: nil,
|
117
|
-
choose_next_command_and_next_inputs_separately: nil,
|
118
117
|
maximum_call_count: nil,
|
119
118
|
llm_model: nil
|
120
119
|
)
|
@@ -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
|
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.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Georgi
|
@@ -55,8 +55,6 @@ files:
|
|
55
55
|
- src/foobara/agent/describe_command.rb
|
56
56
|
- src/foobara/agent/describe_type.rb
|
57
57
|
- src/foobara/agent/determine_base.rb
|
58
|
-
- src/foobara/agent/determine_inputs_for_next_command.rb
|
59
|
-
- src/foobara/agent/determine_next_command.rb
|
60
58
|
- src/foobara/agent/determine_next_command_name_and_inputs.rb
|
61
59
|
- src/foobara/agent/give_up.rb
|
62
60
|
- src/foobara/agent/list_commands.rb
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Foobara
|
2
|
-
class Agent < CommandConnector
|
3
|
-
class DetermineInputsForNextCommand < DetermineBase
|
4
|
-
extend Concerns::SubclassCacheable
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def for(command_class:, agent_id:)
|
8
|
-
cached_subclass([command_class.full_command_name, agent_id]) do
|
9
|
-
command_short_name = Util.non_full_name(command_class.command_name)
|
10
|
-
class_name = "Foobara::Agent::#{agent_id}::DetermineInputsForNext#{command_short_name}Command"
|
11
|
-
klass = Util.make_class_p(class_name, self)
|
12
|
-
|
13
|
-
klass.description "Returns the inputs for " \
|
14
|
-
"the next #{command_class.full_command_name} command to run."
|
15
|
-
|
16
|
-
if command_class.inputs_type.nil? || command_class.inputs_type.element_types.empty?
|
17
|
-
# :nocov:
|
18
|
-
raise ArgumentError, "command #{command_class.full_command_name} has no inputs"
|
19
|
-
# :nocov:
|
20
|
-
end
|
21
|
-
|
22
|
-
transformer = CommandConnectors::Transformers::EntityToPrimaryKeyInputsTransformer.new(
|
23
|
-
to: command_class.inputs_type
|
24
|
-
)
|
25
|
-
klass.result transformer.from_type
|
26
|
-
|
27
|
-
klass
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module Foobara
|
2
|
-
class Agent < CommandConnector
|
3
|
-
class DetermineNextCommand < DetermineBase
|
4
|
-
description "Returns the name of the next command to run given the progress " \
|
5
|
-
"towards accomplishing the current goal. " \
|
6
|
-
"If the goal has been accomplished it will choose the " \
|
7
|
-
"NotifyUserThatCurrentGoalHasBeenAccomplished command."
|
8
|
-
|
9
|
-
result :string,
|
10
|
-
description: "Name of the next command to run"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|