foobara-agent 0.0.17 → 0.0.18
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 +4 -0
- data/lib/foobara/agent.rb +2 -6
- data/src/foobara/agent/accomplish_goal.rb +0 -42
- data/src/foobara/agent/determine_next_command_name_and_inputs.rb +70 -2
- data/src/foobara/agent.rb +13 -13
- metadata +1 -2
- data/src/foobara/agent/determine_base.rb +0 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff591852377b5821eabf7ff4b49c4cc4bcacbebfdceaed539af14b831aa9cea3
|
4
|
+
data.tar.gz: 35de585c33d57e7f52f91baf95efaccd685e42fb342a0ac407875c8eb1790dad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ba055dccfe9e32a9ec6a9397a47707de6804d43c1e0e99a23f798951f246ea158b08048b939b51278cf0f552d16268cd83fe22a46bc2915f3434de693a1040e
|
7
|
+
data.tar.gz: '091f144c40b376ee7431c9ba4e906258f6d68704ebee1b568383bb66f7708cb40abeb4fab0650aeafd9bc2a9bb2b69f0428014566783f966d2545e07cfd0eb6e'
|
data/CHANGELOG.md
CHANGED
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
|
-
|
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
|
@@ -52,8 +48,6 @@ module Foobara
|
|
52
48
|
increment_command_calls
|
53
49
|
check_if_too_many_calls
|
54
50
|
|
55
|
-
throttle_llm_calls_if_necessary
|
56
|
-
|
57
51
|
determine_next_command_and_inputs
|
58
52
|
|
59
53
|
run_next_command
|
@@ -500,42 +494,6 @@ module Foobara
|
|
500
494
|
llm_call_timestamps.unshift(Time.now)
|
501
495
|
end
|
502
496
|
|
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
|
509
|
-
end
|
510
|
-
|
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
|
524
|
-
end
|
525
|
-
|
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
|
537
|
-
end
|
538
|
-
|
539
497
|
def context
|
540
498
|
agent.context
|
541
499
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
require "foobara/llm_backed_command"
|
2
2
|
|
3
3
|
module Foobara
|
4
4
|
class Agent < CommandConnector
|
5
|
-
class DetermineNextCommandNameAndInputs <
|
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]
|
@@ -61,6 +61,16 @@ module Foobara
|
|
61
61
|
inputs :attributes, :allow_nil
|
62
62
|
end
|
63
63
|
|
64
|
+
inputs do
|
65
|
+
agent Agent, :required
|
66
|
+
pass_aggregates_to_llm :boolean, :allow_nil, "Should we send aggregates to the LLM or " \
|
67
|
+
"require it to fetch what it needs?"
|
68
|
+
llm_model :string,
|
69
|
+
one_of: Foobara::Ai::AnswerBot::Types::ModelEnum,
|
70
|
+
default: Ai.default_llm_model,
|
71
|
+
description: "The model to use for the LLM"
|
72
|
+
end
|
73
|
+
|
64
74
|
def determine_llm_instructions
|
65
75
|
self.llm_instructions = self.class.llm_instructions(
|
66
76
|
computed_user_association_depth,
|
@@ -68,6 +78,64 @@ module Foobara
|
|
68
78
|
previous_goal_and_status_pairs
|
69
79
|
)
|
70
80
|
end
|
81
|
+
|
82
|
+
def association_depth
|
83
|
+
if pass_aggregates_to_llm
|
84
|
+
Foobara::AssociationDepth::AGGREGATE
|
85
|
+
else
|
86
|
+
Foobara::AssociationDepth::ATOM
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_messages
|
91
|
+
p = [
|
92
|
+
{
|
93
|
+
content: llm_instructions,
|
94
|
+
role: :system
|
95
|
+
}
|
96
|
+
]
|
97
|
+
|
98
|
+
context.command_log.each do |command_log_entry|
|
99
|
+
agent_entry = {
|
100
|
+
command: command_log_entry.command_name
|
101
|
+
}
|
102
|
+
|
103
|
+
inputs = command_log_entry.inputs
|
104
|
+
|
105
|
+
if inputs && !inputs.empty?
|
106
|
+
agent_entry[:inputs] = inputs
|
107
|
+
end
|
108
|
+
|
109
|
+
outcome_entry = command_log_entry.outcome
|
110
|
+
|
111
|
+
p << {
|
112
|
+
content: agent_entry,
|
113
|
+
role: :assistant
|
114
|
+
}
|
115
|
+
p << {
|
116
|
+
content: outcome_entry,
|
117
|
+
role: :user
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
p
|
122
|
+
end
|
123
|
+
|
124
|
+
def goal
|
125
|
+
context.current_goal.text
|
126
|
+
end
|
127
|
+
|
128
|
+
def previous_goal_and_status_pairs
|
129
|
+
if context.previous_goals && !context.previous_goals.empty?
|
130
|
+
context.previous_goals.map do |previous_goal|
|
131
|
+
[previous_goal.text, previous_goal.state]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def context
|
137
|
+
agent.context
|
138
|
+
end
|
71
139
|
end
|
72
140
|
end
|
73
141
|
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,9 +38,9 @@ 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
|
@@ -55,9 +55,9 @@ module Foobara
|
|
55
55
|
self.verbose = verbose
|
56
56
|
self.io_out = io_out
|
57
57
|
self.io_err = io_err
|
58
|
-
self.max_llm_calls_per_minute = max_llm_calls_per_minute
|
59
58
|
self.result_entity_depth = result_entity_depth
|
60
59
|
self.pass_aggregates_to_llm = pass_aggregates_to_llm
|
60
|
+
self.maximum_command_calls = maximum_command_calls
|
61
61
|
|
62
62
|
pre_commit_transformer = if pass_aggregates_to_llm
|
63
63
|
CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer
|
@@ -124,7 +124,7 @@ module Foobara
|
|
124
124
|
def accomplish_goal(
|
125
125
|
goal,
|
126
126
|
result_type: nil,
|
127
|
-
|
127
|
+
maximum_command_calls: nil,
|
128
128
|
llm_model: nil
|
129
129
|
)
|
130
130
|
set_context_goal(goal)
|
@@ -150,7 +150,7 @@ module Foobara
|
|
150
150
|
state_machine.perform_transition!(:accomplish_goal)
|
151
151
|
|
152
152
|
begin
|
153
|
-
inputs = accomplish_goal_inputs(goal, result_type:,
|
153
|
+
inputs = accomplish_goal_inputs(goal, result_type:, maximum_command_calls:, llm_model:)
|
154
154
|
|
155
155
|
self.current_accomplish_goal_command = AccomplishGoal.new(inputs)
|
156
156
|
|
@@ -186,7 +186,7 @@ module Foobara
|
|
186
186
|
|
187
187
|
def accomplish_goal_inputs(goal,
|
188
188
|
result_type: nil,
|
189
|
-
|
189
|
+
maximum_command_calls: nil,
|
190
190
|
llm_model: nil)
|
191
191
|
inputs = {
|
192
192
|
goal:,
|
@@ -200,8 +200,12 @@ module Foobara
|
|
200
200
|
inputs[:llm_model] = llm_model
|
201
201
|
end
|
202
202
|
|
203
|
-
|
204
|
-
|
203
|
+
if maximum_command_calls.nil?
|
204
|
+
maximum_command_calls = self.maximum_command_calls
|
205
|
+
end
|
206
|
+
|
207
|
+
unless maximum_command_calls.nil?
|
208
|
+
inputs[:maximum_command_calls] = maximum_command_calls
|
205
209
|
end
|
206
210
|
|
207
211
|
if verbose
|
@@ -220,10 +224,6 @@ module Foobara
|
|
220
224
|
inputs[:include_message_to_user_in_result] = include_message_to_user_in_result
|
221
225
|
end
|
222
226
|
|
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
227
|
if result_entity_depth
|
228
228
|
inputs[:result_entity_depth] = result_entity_depth
|
229
229
|
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.18
|
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
|