foobara-agent 0.0.5 → 0.0.6
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 121474f4e61c1b3a5eebc883794bef41159d48ecc05eba44165bd8d79111c426
|
4
|
+
data.tar.gz: 50bc4c4aea5bd447f17cf39712b7a86295e4f45708602398f72a61af2f221881
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0cbf20f7fc287bd4a3be5caed0572de59475493ae4bed80d755b5284ead25fa697b455959ca323891bb20f0bfb57a5a01f1e62f335faacc6c99c220821920f3
|
7
|
+
data.tar.gz: cf85ac42b6ab4a9f8efddd35e533afd32f664b9a9ec8bba17f77abf424a5499e64ba6ad576b54bb24c03dadb5e6bc1c2fe54a502bd505f30caaeeb15e002d655
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## [0.0.6] - 2025-06-19
|
2
|
+
|
3
|
+
- Do not run next command with pre-cast inputs
|
4
|
+
- Make result_data and message_to_user required (probably unwise for message_to_user?)
|
5
|
+
- Simulate ListCommands and DescribeCommand being at start
|
6
|
+
- Add a verbose flag for debugging help
|
7
|
+
- Give each AccomplishGoal its own command call count
|
8
|
+
- Convert entities to their aggregates as input and primary keys as output
|
9
|
+
|
1
10
|
## [0.0.5] - 2025-05-30
|
2
11
|
|
3
12
|
- Don't require agents to have a name
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require_relative "list_commands"
|
2
2
|
|
3
3
|
module Foobara
|
4
|
-
# TODO: should agent maybe be a command connector? It feels a bit more like a command connector.
|
5
4
|
class Agent < CommandConnector
|
6
5
|
class AccomplishGoal < Foobara::Command
|
7
6
|
possible_error :gave_up, context: { reason: :string }, message: "Gave up."
|
@@ -14,6 +13,9 @@ module Foobara
|
|
14
13
|
# TODO: we should be able to specify a subclass as a type
|
15
14
|
command_classes [Class], "Commands that can be ran to accomplish the goal"
|
16
15
|
final_result_type :duck, "Specifies how the result of the goal is to be structured"
|
16
|
+
verbose :boolean, default: false
|
17
|
+
io_out :duck
|
18
|
+
io_err :duck
|
17
19
|
existing_command_connector CommandConnector, :allow_nil,
|
18
20
|
"A connector containing already-connected commands for the agent to use"
|
19
21
|
current_context Context, :allow_nil, "The current context of the agent"
|
@@ -62,8 +64,13 @@ module Foobara
|
|
62
64
|
connect_agent_commands
|
63
65
|
end
|
64
66
|
|
67
|
+
simulate_list_commands_run
|
68
|
+
simulate_describe_command_run_for_all_commands
|
69
|
+
|
65
70
|
until mission_accomplished or given_up
|
71
|
+
increment_command_calls
|
66
72
|
check_if_too_many_calls
|
73
|
+
|
67
74
|
if choose_next_command_and_next_inputs_separately?
|
68
75
|
determine_next_command_then_inputs_separately
|
69
76
|
else
|
@@ -95,7 +102,8 @@ module Foobara
|
|
95
102
|
|
96
103
|
attr_accessor :context, :next_command_name, :next_command_inputs, :mission_accomplished, :given_up,
|
97
104
|
:next_command_class, :next_command, :command_outcome, :timed_out,
|
98
|
-
:final_result, :final_message, :command_response, :delayed_command_name
|
105
|
+
:final_result, :final_message, :command_response, :delayed_command_name,
|
106
|
+
:command_calls
|
99
107
|
attr_writer :command_connector
|
100
108
|
|
101
109
|
def agent_name
|
@@ -107,6 +115,32 @@ module Foobara
|
|
107
115
|
self.context = current_context || Context.new(command_log: [])
|
108
116
|
end
|
109
117
|
|
118
|
+
def simulate_list_commands_run
|
119
|
+
return unless context.command_log.empty?
|
120
|
+
|
121
|
+
self.next_command_name = ListCommands.full_command_name
|
122
|
+
self.next_command_inputs = nil
|
123
|
+
fetch_next_command_class
|
124
|
+
|
125
|
+
run_next_command
|
126
|
+
log_last_command_outcome
|
127
|
+
end
|
128
|
+
|
129
|
+
def simulate_describe_command_run_for_all_commands
|
130
|
+
return if context.command_log.size > 1
|
131
|
+
|
132
|
+
ListCommands.run!(command_connector:)[:user_provided_commands].each do |full_command_name|
|
133
|
+
next if described_commands.include?(full_command_name)
|
134
|
+
|
135
|
+
self.next_command_name = DescribeCommand.full_command_name
|
136
|
+
self.next_command_inputs = { command_name: full_command_name }
|
137
|
+
fetch_next_command_class
|
138
|
+
|
139
|
+
run_next_command
|
140
|
+
log_last_command_outcome
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
110
144
|
def command_connector_passed_in?
|
111
145
|
existing_command_connector
|
112
146
|
end
|
@@ -120,7 +154,9 @@ module Foobara
|
|
120
154
|
def build_command_connector
|
121
155
|
self.command_connector ||= Agent.new(
|
122
156
|
current_accomplish_goal_command: self,
|
123
|
-
llm_model
|
157
|
+
llm_model:,
|
158
|
+
verbose:,
|
159
|
+
io_out:
|
124
160
|
)
|
125
161
|
end
|
126
162
|
|
@@ -139,13 +175,6 @@ module Foobara
|
|
139
175
|
end
|
140
176
|
|
141
177
|
def determine_next_command_and_inputs(retries = 2)
|
142
|
-
if context.command_log.empty?
|
143
|
-
self.next_command_name = ListCommands.full_command_name
|
144
|
-
self.next_command_inputs = nil
|
145
|
-
fetch_next_command_class
|
146
|
-
return
|
147
|
-
end
|
148
|
-
|
149
178
|
inputs_for_determine = {
|
150
179
|
goal:,
|
151
180
|
context:,
|
@@ -250,15 +279,9 @@ module Foobara
|
|
250
279
|
def validate_next_command_inputs
|
251
280
|
inputs_type = next_command_class.inputs_type
|
252
281
|
|
253
|
-
|
282
|
+
NestedTransactionable.with_needed_transactions_for_type(inputs_type) do
|
254
283
|
inputs_type.process_value(next_command_inputs)
|
255
284
|
end
|
256
|
-
|
257
|
-
if outcome.success?
|
258
|
-
self.next_command_inputs = outcome.result
|
259
|
-
end
|
260
|
-
|
261
|
-
outcome
|
262
285
|
end
|
263
286
|
|
264
287
|
def command_name_type
|
@@ -266,9 +289,7 @@ module Foobara
|
|
266
289
|
end
|
267
290
|
|
268
291
|
def determine_next_command_name(retries = 2)
|
269
|
-
self.next_command_name = if
|
270
|
-
ListCommands.full_command_name
|
271
|
-
elsif delayed_command_name
|
292
|
+
self.next_command_name = if delayed_command_name
|
272
293
|
name = delayed_command_name
|
273
294
|
self.delayed_command_name = nil
|
274
295
|
name
|
@@ -392,6 +413,10 @@ module Foobara
|
|
392
413
|
end
|
393
414
|
|
394
415
|
def run_next_command
|
416
|
+
if verbose?
|
417
|
+
(io_out || $stdout).puts "Running #{next_command_name} with #{next_command_inputs}"
|
418
|
+
end
|
419
|
+
|
395
420
|
self.command_response = command_connector.run(
|
396
421
|
full_command_name: next_command_name,
|
397
422
|
inputs: next_command_inputs,
|
@@ -399,14 +424,31 @@ module Foobara
|
|
399
424
|
)
|
400
425
|
|
401
426
|
self.command_outcome = command_response.outcome
|
427
|
+
|
428
|
+
if verbose?
|
429
|
+
if command_outcome.success?
|
430
|
+
(io_out || $stdout).puts "Command #{command_response.command.class.full_command_name} succeeded"
|
431
|
+
else
|
432
|
+
# :nocov:
|
433
|
+
(io_err || $stderr).puts(
|
434
|
+
"Command #{command_response.command.class.full_command_name} failed #{command_outcome.errors_hash}"
|
435
|
+
)
|
436
|
+
# :nocov:
|
437
|
+
end
|
438
|
+
end
|
402
439
|
end
|
403
440
|
|
404
441
|
def log_last_command_outcome
|
405
442
|
log_command_outcome(command: command_response.command)
|
406
443
|
end
|
407
444
|
|
445
|
+
def increment_command_calls
|
446
|
+
self.command_calls ||= -1
|
447
|
+
self.command_calls += 1
|
448
|
+
end
|
449
|
+
|
408
450
|
def check_if_too_many_calls
|
409
|
-
if
|
451
|
+
if command_calls > maximum_command_calls
|
410
452
|
add_runtime_error(
|
411
453
|
:too_many_command_calls,
|
412
454
|
"Too many command calls. " \
|
@@ -485,6 +527,10 @@ module Foobara
|
|
485
527
|
def choose_next_command_and_next_inputs_separately?
|
486
528
|
choose_next_command_and_next_inputs_separately
|
487
529
|
end
|
530
|
+
|
531
|
+
def verbose?
|
532
|
+
verbose
|
533
|
+
end
|
488
534
|
end
|
489
535
|
end
|
490
536
|
end
|
@@ -18,17 +18,17 @@ module Foobara
|
|
18
18
|
|
19
19
|
inputs do
|
20
20
|
command_connector CommandConnector, :required, "Connector to notify user through"
|
21
|
-
message_to_user :string, "
|
21
|
+
message_to_user :string, :required, "Message to the user about what was done"
|
22
22
|
end
|
23
23
|
|
24
24
|
if result_type
|
25
25
|
add_inputs do
|
26
|
-
result_data result_type
|
26
|
+
result_data result_type, :required
|
27
27
|
end
|
28
28
|
|
29
29
|
klass.result do
|
30
|
-
message_to_user :string
|
31
|
-
result_data result_type
|
30
|
+
message_to_user :string, :required
|
31
|
+
result_data result_type, :required
|
32
32
|
end
|
33
33
|
klass.description "Notifies the user that the current goal has been accomplished and returns a final " \
|
34
34
|
"result formatted according to the " \
|
@@ -57,7 +57,7 @@ module Foobara
|
|
57
57
|
inputs do
|
58
58
|
# TODO: Are we still not able to uses classes as foobara types??
|
59
59
|
command_connector :duck, :required, "Connector to end"
|
60
|
-
message_to_user :string, "
|
60
|
+
message_to_user :string, :required, "Message to the user about what was done"
|
61
61
|
result_data :duck, "The final result of the work if relevant/expected"
|
62
62
|
end
|
63
63
|
|
data/src/foobara/agent.rb
CHANGED
@@ -18,7 +18,10 @@ module Foobara
|
|
18
18
|
:llm_model,
|
19
19
|
:current_accomplish_goal_command,
|
20
20
|
:result_type,
|
21
|
-
:agent_commands_connected
|
21
|
+
:agent_commands_connected,
|
22
|
+
:verbose,
|
23
|
+
:io_out,
|
24
|
+
:io_err
|
22
25
|
|
23
26
|
def initialize(
|
24
27
|
context: nil,
|
@@ -27,6 +30,9 @@ module Foobara
|
|
27
30
|
llm_model: nil,
|
28
31
|
result_type: nil,
|
29
32
|
current_accomplish_goal_command: nil,
|
33
|
+
verbose: false,
|
34
|
+
io_out: nil,
|
35
|
+
io_err: nil,
|
30
36
|
**opts
|
31
37
|
)
|
32
38
|
# TODO: shouldn't have to pass command_log here since it has a default, debug that
|
@@ -35,16 +41,22 @@ module Foobara
|
|
35
41
|
self.llm_model = llm_model
|
36
42
|
self.result_type = result_type
|
37
43
|
self.current_accomplish_goal_command = current_accomplish_goal_command
|
44
|
+
self.verbose = verbose
|
45
|
+
self.io_out = io_out
|
46
|
+
self.io_err = io_err
|
38
47
|
|
39
48
|
unless opts.key?(:default_serializers)
|
40
49
|
opts = opts.merge(default_serializers: [
|
41
50
|
Foobara::CommandConnectors::Serializers::ErrorsSerializer,
|
42
|
-
Foobara::CommandConnectors::Serializers::
|
51
|
+
Foobara::CommandConnectors::Serializers::AggregateSerializer
|
43
52
|
])
|
44
53
|
end
|
45
54
|
|
46
55
|
super(**opts)
|
47
56
|
|
57
|
+
# TODO: this should work now, switch to this approach
|
58
|
+
# add_default_inputs_transformer EntityToPrimaryKeyInputsTransformer
|
59
|
+
|
48
60
|
build_initial_context
|
49
61
|
|
50
62
|
# TODO: push this convenience method up into base class?
|
@@ -53,6 +65,20 @@ module Foobara
|
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
68
|
+
def connect(*args, **opts)
|
69
|
+
args, opts = desugarize_connect_args(args, opts)
|
70
|
+
|
71
|
+
inputs_transformers = opts[:inputs_transformers] || []
|
72
|
+
inputs_transformers = Util.array(inputs_transformers)
|
73
|
+
inputs_transformers << CommandConnectors::Transformers::EntityToPrimaryKeyInputsTransformer
|
74
|
+
|
75
|
+
unless opts.key?(:aggregate_entities)
|
76
|
+
opts = opts.merge(aggregate_entities: true)
|
77
|
+
end
|
78
|
+
|
79
|
+
super(*args, **opts.merge(inputs_transformers:))
|
80
|
+
end
|
81
|
+
|
56
82
|
def run(*args, **)
|
57
83
|
if args.first.is_a?(::String)
|
58
84
|
accomplish_goal(*args, **)
|
@@ -123,6 +149,18 @@ module Foobara
|
|
123
149
|
inputs[:maximum_command_calls] = maximum_call_count
|
124
150
|
end
|
125
151
|
|
152
|
+
if verbose
|
153
|
+
inputs[:verbose] = verbose
|
154
|
+
end
|
155
|
+
|
156
|
+
if io_out
|
157
|
+
inputs[:io_out] = io_out
|
158
|
+
end
|
159
|
+
|
160
|
+
if io_err
|
161
|
+
inputs[:io_err] = io_err
|
162
|
+
end
|
163
|
+
|
126
164
|
self.current_accomplish_goal_command = AccomplishGoal.new(inputs)
|
127
165
|
|
128
166
|
current_accomplish_goal_command.run.tap do |outcome|
|
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.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Georgi
|
@@ -85,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: '0'
|
87
87
|
requirements: []
|
88
|
-
rubygems_version: 3.6.
|
88
|
+
rubygems_version: 3.6.9
|
89
89
|
specification_version: 4
|
90
90
|
summary: An agent that uses whatever Foobara commands you wish to accomplish goals
|
91
91
|
of your choosing!
|