boxcars 0.1.7 → 0.1.8
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/.env_sample +2 -0
- data/CHANGELOG.md +14 -2
- data/Gemfile.lock +1 -1
- data/lib/boxcars/boxcar/active_record.rb +11 -29
- data/lib/boxcars/boxcar/calculator.rb +5 -28
- data/lib/boxcars/boxcar/engine_boxcar.rb +41 -7
- data/lib/boxcars/boxcar/sql.rb +15 -32
- data/lib/boxcars/boxcar.rb +1 -1
- data/lib/boxcars/engine/openai.rb +20 -0
- data/lib/boxcars/prompt.rb +36 -12
- data/lib/boxcars/train/zero_shot.rb +1 -0
- data/lib/boxcars/train.rb +2 -4
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars.rb +15 -12
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3de375ad403bb62fb6af1045aff6a6115cbb598c3747fe48f882fb40483289e0
|
|
4
|
+
data.tar.gz: 343a3077d72ce21a56d8045701a8a3bd681c5fe91fd8ffbca2cc14270b92f3bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: efc72210fb52599b7c6278502a5b4143d995e671c62c7f6c090cbddf2a8926b231b0d7ab0aa28fa1325324ded1ebac5cac4e10e608ae59077a431a20f9d62f23
|
|
7
|
+
data.tar.gz: eeb00a993d7bbf64ac5c7461a1e31f241407c58a19a919c0853ade7e4a3bf717ab4f69331c5bc5d8c0049a89c53353d311d41616352131379b7e87b8ec3e491d
|
data/.env_sample
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [v0.1.7](https://github.com/BoxcarsAI/boxcars/tree/v0.1.7) (2023-02-27)
|
|
4
4
|
|
|
5
|
-
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.
|
|
5
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.6...v0.1.7)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- figure out logging [\#10](https://github.com/BoxcarsAI/boxcars/issues/10)
|
|
10
|
+
|
|
11
|
+
**Merged pull requests:**
|
|
12
|
+
|
|
13
|
+
- Fix typos in README concepts [\#26](https://github.com/BoxcarsAI/boxcars/pull/26) ([MasterOdin](https://github.com/MasterOdin))
|
|
14
|
+
|
|
15
|
+
## [v0.1.6](https://github.com/BoxcarsAI/boxcars/tree/v0.1.6) (2023-02-24)
|
|
16
|
+
|
|
17
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.5...v0.1.6)
|
|
6
18
|
|
|
7
19
|
**Implemented enhancements:**
|
|
8
20
|
|
data/Gemfile.lock
CHANGED
|
@@ -7,7 +7,7 @@ module Boxcars
|
|
|
7
7
|
# the description of this engine boxcar
|
|
8
8
|
ARDESC = "useful for when you need to query a database for an application named %<name>s."
|
|
9
9
|
LOCKED_OUT_MODELS = %w[ActiveRecord::SchemaMigration ActiveRecord::InternalMetadata ApplicationRecord].freeze
|
|
10
|
-
attr_accessor :connection, :
|
|
10
|
+
attr_accessor :connection, :requested_models, :read_only, :approval_callback
|
|
11
11
|
attr_reader :except_models
|
|
12
12
|
|
|
13
13
|
# @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
|
|
@@ -15,43 +15,25 @@ module Boxcars
|
|
|
15
15
|
# @param read_only [Boolean] Whether to use read only models. Defaults to true unless you pass an approval function.
|
|
16
16
|
# @param approval_callback [Proc] A function to call to approve changes. Defaults to nil.
|
|
17
17
|
# @param kwargs [Hash] Any other keyword arguments. These can include:
|
|
18
|
-
# :name, :description, :prompt, :
|
|
18
|
+
# :name, :description, :prompt, :except_models, :top_k, and :stop
|
|
19
19
|
def initialize(engine: nil, models: nil, read_only: nil, approval_callback: nil, **kwargs)
|
|
20
20
|
check_models(models)
|
|
21
21
|
@except_models = LOCKED_OUT_MODELS + kwargs[:except_models].to_a
|
|
22
22
|
@approval_callback = approval_callback
|
|
23
23
|
@read_only = read_only.nil? ? !approval_callback : read_only
|
|
24
|
-
@input_key = kwargs[:input_key] || :question
|
|
25
|
-
@output_key = kwargs[:output_key] || :answer
|
|
26
24
|
the_prompt = kwargs[prompt] || my_prompt
|
|
27
25
|
name = kwargs[:name] || "Data"
|
|
26
|
+
kwargs[:stop] ||= ["Answer:"]
|
|
28
27
|
super(name: name,
|
|
29
28
|
description: kwargs[:description] || format(ARDESC, name: name),
|
|
30
29
|
engine: engine,
|
|
31
30
|
prompt: the_prompt,
|
|
32
|
-
|
|
31
|
+
**kwargs)
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
[input_key]
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# the output keys for the prompt
|
|
42
|
-
# @return [Array<Symbol>] The output keys for the prompt.
|
|
43
|
-
def output_keys
|
|
44
|
-
[output_key]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# call the boxcar
|
|
48
|
-
# @param inputs [Hash] The inputs to the boxcar.
|
|
49
|
-
# @return [Hash] The outputs from the boxcar.
|
|
50
|
-
def call(inputs:)
|
|
51
|
-
t = predict(question: inputs[input_key], top_k: 5, model_info: model_info, stop: ["Answer:"]).strip
|
|
52
|
-
answer = get_answer(t)
|
|
53
|
-
Boxcars.info answer, :magenta
|
|
54
|
-
{ output_key => answer }
|
|
34
|
+
# @return Hash The additional variables for this boxcar.
|
|
35
|
+
def prediction_additional
|
|
36
|
+
{ model_info: model_info }.merge super
|
|
55
37
|
end
|
|
56
38
|
|
|
57
39
|
private
|
|
@@ -83,7 +65,7 @@ module Boxcars
|
|
|
83
65
|
|
|
84
66
|
def model_info
|
|
85
67
|
models = wanted_models
|
|
86
|
-
models.
|
|
68
|
+
models.inspect
|
|
87
69
|
end
|
|
88
70
|
|
|
89
71
|
# to be safe, we wrap the code in a transaction and rollback
|
|
@@ -167,13 +149,12 @@ module Boxcars
|
|
|
167
149
|
output = 0 if output.is_a?(Array) && output.empty?
|
|
168
150
|
output = output.first if output.is_a?(Array) && output.length == 1
|
|
169
151
|
output = output[output.keys.first] if output.is_a?(Hash) && output.length == 1
|
|
170
|
-
"Answer: #{output.
|
|
152
|
+
"Answer: #{output.to_json}"
|
|
171
153
|
rescue StandardError => e
|
|
172
154
|
"Error: #{e.message}"
|
|
173
155
|
end
|
|
174
156
|
|
|
175
157
|
def get_answer(text)
|
|
176
|
-
# debugger
|
|
177
158
|
case text
|
|
178
159
|
when /^ARCode:/
|
|
179
160
|
get_active_record_answer(text)
|
|
@@ -209,7 +190,8 @@ module Boxcars
|
|
|
209
190
|
|
|
210
191
|
# The prompt to use for the engine.
|
|
211
192
|
def my_prompt
|
|
212
|
-
@my_prompt ||= Prompt.new(input_variables: [:question, :
|
|
193
|
+
@my_prompt ||= Prompt.new(input_variables: [:question], other_inputs: [:top_k], output_variables: [:answer],
|
|
194
|
+
template: TEMPLATE)
|
|
213
195
|
end
|
|
214
196
|
end
|
|
215
197
|
end
|
|
@@ -6,42 +6,19 @@ module Boxcars
|
|
|
6
6
|
class Calculator < EngineBoxcar
|
|
7
7
|
# the description of this engine boxcar
|
|
8
8
|
CALCDESC = "useful for when you need to answer questions about math"
|
|
9
|
-
attr_accessor :input_key
|
|
10
9
|
|
|
11
10
|
# @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
|
|
12
11
|
# @param prompt [Boxcars::Prompt] The prompt to use for this boxcar. Defaults to built-in prompt.
|
|
13
|
-
# @param input_key [Symbol] The key to use for the input. Defaults to :question.
|
|
14
|
-
# @param output_key [Symbol] The key to use for the output. Defaults to :answer.
|
|
15
12
|
# @param kwargs [Hash] Any other keyword arguments to pass to the parent class.
|
|
16
|
-
def initialize(engine: nil, prompt: nil,
|
|
17
|
-
# def initialize(engine:, prompt: my_prompt,
|
|
18
|
-
@input_key = input_key
|
|
13
|
+
def initialize(engine: nil, prompt: nil, **kwargs)
|
|
14
|
+
# def initialize(engine:, prompt: my_prompt, output_key: :answer, **kwargs)
|
|
19
15
|
the_prompt = prompt || my_prompt
|
|
16
|
+
kwargs[:stop] ||= ["```output"]
|
|
20
17
|
super(name: kwargs[:name] || "Calculator",
|
|
21
18
|
description: kwargs[:description] || CALCDESC,
|
|
22
19
|
engine: engine,
|
|
23
20
|
prompt: the_prompt,
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# the prompt input keys
|
|
28
|
-
def input_keys
|
|
29
|
-
[input_key]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# the output keys
|
|
33
|
-
def output_keys
|
|
34
|
-
[output_key]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# call the calculator
|
|
38
|
-
# @param inputs [Hash] The inputs to the boxcar.
|
|
39
|
-
# @return [Hash] The outputs from the boxcar.
|
|
40
|
-
def call(inputs:)
|
|
41
|
-
t = predict(question: inputs[input_key], stop: ["```output"]).strip
|
|
42
|
-
answer = get_answer(t)
|
|
43
|
-
Boxcars.info answer, :magenta
|
|
44
|
-
{ output_key => answer }
|
|
21
|
+
**kwargs)
|
|
45
22
|
end
|
|
46
23
|
|
|
47
24
|
private
|
|
@@ -104,7 +81,7 @@ module Boxcars
|
|
|
104
81
|
|
|
105
82
|
# The prompt to use for the engine.
|
|
106
83
|
def my_prompt
|
|
107
|
-
@my_prompt ||= Prompt.new(input_variables: [:question], template: TEMPLATE)
|
|
84
|
+
@my_prompt ||= Prompt.new(input_variables: [:question], output_variables: [:answer], template: TEMPLATE)
|
|
108
85
|
end
|
|
109
86
|
end
|
|
110
87
|
end
|
|
@@ -4,17 +4,18 @@
|
|
|
4
4
|
module Boxcars
|
|
5
5
|
# For Boxcars that use an engine to do their work.
|
|
6
6
|
class EngineBoxcar < Boxcar
|
|
7
|
-
attr_accessor :prompt, :engine, :
|
|
7
|
+
attr_accessor :prompt, :engine, :top_k, :stop
|
|
8
8
|
|
|
9
9
|
# A Boxcar is a container for a single tool to run.
|
|
10
10
|
# @param prompt [Boxcars::Prompt] The prompt to use for this boxcar with sane defaults.
|
|
11
11
|
# @param name [String] The name of the boxcar. Defaults to classname.
|
|
12
12
|
# @param description [String] A description of the boxcar.
|
|
13
13
|
# @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
|
|
14
|
-
def initialize(prompt:, engine: nil,
|
|
14
|
+
def initialize(prompt:, engine: nil, name: nil, description: nil, **kwargs)
|
|
15
15
|
@prompt = prompt
|
|
16
16
|
@engine = engine || Boxcars.engine.new
|
|
17
|
-
@
|
|
17
|
+
@top_k = kwargs[:top_k] || 5
|
|
18
|
+
@stop = kwargs[:stop] || ["Answer:"]
|
|
18
19
|
super(name: name, description: description)
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -23,9 +24,14 @@ module Boxcars
|
|
|
23
24
|
prompt.input_variables
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
# the first input key for the prompt
|
|
28
|
+
def input_key
|
|
29
|
+
input_keys.first
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
# output keys
|
|
27
33
|
def output_keys
|
|
28
|
-
|
|
34
|
+
prompt.output_variables
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
# generate a response from the engine
|
|
@@ -35,6 +41,7 @@ module Boxcars
|
|
|
35
41
|
stop = input_list[0][:stop]
|
|
36
42
|
prompts = []
|
|
37
43
|
input_list.each do |inputs|
|
|
44
|
+
# prompt.missing_variables?(inputs)
|
|
38
45
|
new_prompt = prompt.format(**inputs)
|
|
39
46
|
Boxcars.debug("Prompt after formatting:\n#{new_prompt}", :cyan) if Boxcars.configuration.log_prompts
|
|
40
47
|
prompts.push(new_prompt)
|
|
@@ -48,7 +55,7 @@ module Boxcars
|
|
|
48
55
|
def apply(input_list:)
|
|
49
56
|
response = generate(input_list: input_list)
|
|
50
57
|
response.generations.to_h do |generation|
|
|
51
|
-
[
|
|
58
|
+
[output_keys.first, generation[0].text]
|
|
52
59
|
end
|
|
53
60
|
end
|
|
54
61
|
|
|
@@ -56,7 +63,7 @@ module Boxcars
|
|
|
56
63
|
# @param kwargs [Hash] A hash of input values to use for the prompt.
|
|
57
64
|
# @return [String] The output value.
|
|
58
65
|
def predict(**kwargs)
|
|
59
|
-
apply(input_list: [kwargs])[
|
|
66
|
+
apply(input_list: [kwargs])[output_keys.first]
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
# predict a response from the engine and parse it
|
|
@@ -77,7 +84,7 @@ module Boxcars
|
|
|
77
84
|
def apply_and_parse(input_list:)
|
|
78
85
|
result = apply(input_list: input_list)
|
|
79
86
|
if prompt.output_parser
|
|
80
|
-
result.map { |r| prompt.output_parser.parse(r[
|
|
87
|
+
result.map { |r| prompt.output_parser.parse(r[output_keys.first]) }
|
|
81
88
|
else
|
|
82
89
|
result
|
|
83
90
|
end
|
|
@@ -90,5 +97,32 @@ module Boxcars
|
|
|
90
97
|
|
|
91
98
|
raise Boxcars::ArgumentError, "run not supported when there is not exactly one output key. Got #{output_keys}."
|
|
92
99
|
end
|
|
100
|
+
|
|
101
|
+
# call the boxcar
|
|
102
|
+
# @param inputs [Hash] The inputs to the boxcar.
|
|
103
|
+
# @return [Hash] The outputs from the boxcar.
|
|
104
|
+
def call(inputs:)
|
|
105
|
+
t = predict(**prediction_variables(inputs)).strip
|
|
106
|
+
answer = get_answer(t)
|
|
107
|
+
Boxcars.debug answer, :magenta
|
|
108
|
+
{ output_keys.first => answer }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @param inputs [Hash] The inputs to the boxcar.
|
|
112
|
+
# @return Hash The input variable for this boxcar.
|
|
113
|
+
def prediction_input(inputs)
|
|
114
|
+
{ input_key => inputs[input_key] }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @return Hash The additional variables for this boxcar.
|
|
118
|
+
def prediction_additional
|
|
119
|
+
{ stop: stop, top_k: top_k }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @param inputs [Hash] The inputs to the boxcar.
|
|
123
|
+
# @return Hash The variables for this boxcar.
|
|
124
|
+
def prediction_variables(inputs)
|
|
125
|
+
prediction_input(inputs).merge(prediction_additional)
|
|
126
|
+
end
|
|
93
127
|
end
|
|
94
128
|
end
|
data/lib/boxcars/boxcar/sql.rb
CHANGED
|
@@ -6,46 +6,26 @@ module Boxcars
|
|
|
6
6
|
class SQL < EngineBoxcar
|
|
7
7
|
# the description of this engine boxcar
|
|
8
8
|
SQLDESC = "useful for when you need to query a database for %<name>s."
|
|
9
|
-
attr_accessor :connection
|
|
9
|
+
attr_accessor :connection
|
|
10
10
|
|
|
11
11
|
# @param connection [ActiveRecord::Connection] The SQL connection to use for this boxcar.
|
|
12
12
|
# @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
|
|
13
|
-
# @param input_key [Symbol] The key to use for the input. Defaults to :question.
|
|
14
|
-
# @param output_key [Symbol] The key to use for the output. Defaults to :answer.
|
|
15
13
|
# @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
|
|
16
|
-
# :name, :description and :
|
|
17
|
-
def initialize(connection: nil, engine: nil,
|
|
14
|
+
# :name, :description, :prompt and :top_k
|
|
15
|
+
def initialize(connection: nil, engine: nil, **kwargs)
|
|
18
16
|
@connection = connection || ::ActiveRecord::Base.connection
|
|
19
|
-
@input_key = input_key
|
|
20
17
|
the_prompt = kwargs[prompt] || my_prompt
|
|
21
|
-
|
|
18
|
+
kwargs[:stop] ||= ["Answer:"]
|
|
19
|
+
name = kwargs[:name] || "database"
|
|
22
20
|
super(name: name,
|
|
23
21
|
description: kwargs[:description] || format(SQLDESC, name: name),
|
|
24
22
|
engine: engine,
|
|
25
|
-
prompt: the_prompt
|
|
26
|
-
output_key: output_key)
|
|
23
|
+
prompt: the_prompt)
|
|
27
24
|
end
|
|
28
25
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
[input_key]
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# the output keys for the prompt
|
|
36
|
-
# @return [Array<Symbol>] The output keys for the prompt.
|
|
37
|
-
def output_keys
|
|
38
|
-
[output_key]
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# call the boxcar
|
|
42
|
-
# @param inputs [Hash] The inputs to the boxcar.
|
|
43
|
-
# @return [Hash] The outputs from the boxcar.
|
|
44
|
-
def call(inputs:)
|
|
45
|
-
t = predict(question: inputs[input_key], dialect: dialect, top_k: 5, table_info: schema, stop: ["Answer:"]).strip
|
|
46
|
-
answer = get_answer(t)
|
|
47
|
-
Boxcars.debug answer, :magenta
|
|
48
|
-
{ output_key => answer }
|
|
26
|
+
# @return Hash The additional variables for this boxcar.
|
|
27
|
+
def prediction_additional
|
|
28
|
+
{ schema: schema, dialect: dialect }.merge super
|
|
49
29
|
end
|
|
50
30
|
|
|
51
31
|
private
|
|
@@ -66,7 +46,6 @@ module Boxcars
|
|
|
66
46
|
end
|
|
67
47
|
|
|
68
48
|
def dialect
|
|
69
|
-
# connection.instance_variable_get "@config"[:adapter]
|
|
70
49
|
connection.class.name.split("::").last.sub("Adapter", "")
|
|
71
50
|
end
|
|
72
51
|
|
|
@@ -107,14 +86,18 @@ module Boxcars
|
|
|
107
86
|
Answer: "Final answer here"
|
|
108
87
|
|
|
109
88
|
Only use the following tables:
|
|
110
|
-
%<
|
|
89
|
+
%<schema>s
|
|
111
90
|
|
|
112
91
|
Question: %<question>s
|
|
113
92
|
IPT
|
|
114
93
|
|
|
115
94
|
# The prompt to use for the engine.
|
|
116
95
|
def my_prompt
|
|
117
|
-
@my_prompt ||= Prompt.new(
|
|
96
|
+
@my_prompt ||= Prompt.new(
|
|
97
|
+
input_variables: [:question],
|
|
98
|
+
other_inputs: [:top_k, :dialect, :table_info],
|
|
99
|
+
output_variables: [:answer],
|
|
100
|
+
template: TEMPLATE)
|
|
118
101
|
end
|
|
119
102
|
end
|
|
120
103
|
end
|
data/lib/boxcars/boxcar.rb
CHANGED
|
@@ -83,6 +83,25 @@ module Boxcars
|
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
# make sure we got a valid response
|
|
87
|
+
# @param response [Hash] The response to check.
|
|
88
|
+
# @param must_haves [Array<String>] The keys that must be in the response. Defaults to %w[choices].
|
|
89
|
+
# @raise [KeyError] if there is an issue with the access token.
|
|
90
|
+
# @raise [ValueError] if the response is not valid.
|
|
91
|
+
def check_response(response, must_haves: %w[choices])
|
|
92
|
+
if response['error']
|
|
93
|
+
code = response.dig('error', 'code')
|
|
94
|
+
msg = response.dig('error', 'message') || 'unknown error'
|
|
95
|
+
raise KeyError, "OPENAI_ACCESS_TOKEN not valid" if code == 'invalid_api_key'
|
|
96
|
+
|
|
97
|
+
raise ValueError, "OpenAI error: #{msg}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
must_haves.each do |key|
|
|
101
|
+
raise ValueError, "Expecting key #{key} in response" unless response.key?(key)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
86
105
|
# Call out to OpenAI's endpoint with k unique prompts.
|
|
87
106
|
# @param prompts [Array<String>] The prompts to pass into the model.
|
|
88
107
|
# @param stop [Array<String>] Optional list of stop words to use when generating.
|
|
@@ -98,6 +117,7 @@ module Boxcars
|
|
|
98
117
|
sub_prompts = prompts.each_slice(batch_size).to_a
|
|
99
118
|
sub_prompts.each do |sprompts|
|
|
100
119
|
response = client(prompt: sprompts, **params)
|
|
120
|
+
check_response(response)
|
|
101
121
|
choices.concat(response["choices"])
|
|
102
122
|
keys_to_use = inkeys & response["usage"].keys
|
|
103
123
|
keys_to_use.each { |key| token_usage[key] = token_usage[key].to_i + response["usage"][key] }
|
data/lib/boxcars/prompt.rb
CHANGED
|
@@ -3,25 +3,44 @@
|
|
|
3
3
|
module Boxcars
|
|
4
4
|
# used by Boxcars that have engine's to create a prompt.
|
|
5
5
|
class Prompt
|
|
6
|
-
attr_reader :template, :input_variables, :output_variables
|
|
6
|
+
attr_reader :template, :input_variables, :other_inputs, :output_variables
|
|
7
7
|
|
|
8
8
|
# @param template [String] The template to use for the prompt.
|
|
9
|
-
# @param input_variables [Array<Symbol>] The input vars to use for the prompt.
|
|
10
|
-
# @param
|
|
11
|
-
|
|
9
|
+
# @param input_variables [Array<Symbol>] The input vars to use for the prompt. Defaults to [:input]
|
|
10
|
+
# @param other_inputs [Array<Symbol>] The other input vars to use for the prompt. Defaults to []
|
|
11
|
+
# @param output_variables [Array<Symbol>] The output vars to use for the prompt. Defaults to [:output]
|
|
12
|
+
def initialize(template:, input_variables: nil, other_inputs: nil, output_variables: nil)
|
|
12
13
|
@template = template
|
|
13
|
-
@input_variables = input_variables
|
|
14
|
-
@
|
|
14
|
+
@input_variables = input_variables || [:input]
|
|
15
|
+
@other_inputs = other_inputs || []
|
|
16
|
+
@output_variables = output_variables || [:output]
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
# format the prompt with the input variables
|
|
20
|
+
# @param inputs [Hash] The inputs to use for the prompt.
|
|
21
|
+
# @return [String] The formatted prompt.
|
|
22
|
+
# @raise [Boxcars::KeyError] if the template has extra keys.
|
|
18
23
|
def format(inputs)
|
|
19
24
|
@template % inputs
|
|
25
|
+
rescue ::KeyError => e
|
|
26
|
+
first_line = e.message.to_s.split("\n").first
|
|
27
|
+
Boxcars.error "Missing prompt input key: #{first_line}"
|
|
28
|
+
raise KeyError, "Prompt format error: #{first_line}"
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
# check if the template is valid
|
|
23
32
|
def template_is_valid?
|
|
24
|
-
|
|
33
|
+
all_vars = (input_variables + other_inputs + output_variables).sort
|
|
34
|
+
template_vars = @template.scan(/%<(\w+)>s/).flatten.map(&:to_sym).sort
|
|
35
|
+
all_vars == template_vars
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# missing variables in the template
|
|
39
|
+
def missing_variables?(inputs)
|
|
40
|
+
input_vars = [input_variables, other_inputs].flatten.sort
|
|
41
|
+
return if inputs.keys.sort == input_vars
|
|
42
|
+
|
|
43
|
+
raise ArgumentError, "Missing expected input keys, got: #{inputs.keys}. Expected: #{input_vars}"
|
|
25
44
|
end
|
|
26
45
|
|
|
27
46
|
# create a prompt template from examples
|
|
@@ -29,17 +48,22 @@ module Boxcars
|
|
|
29
48
|
# @param input_variables [Array<Symbol>] The input variables to use for the prompt.
|
|
30
49
|
# @param example_separator [String] The separator to use between the examples. Defaults to "\n\n"
|
|
31
50
|
# @param prefix [String] The prefix to use for the template. Defaults to ""
|
|
32
|
-
def self.from_examples(examples:, suffix:, input_variables:, example_separator: "\n\n", prefix: "")
|
|
51
|
+
def self.from_examples(examples:, suffix:, input_variables:, example_separator: "\n\n", prefix: "", **kwargs)
|
|
33
52
|
template = [prefix, examples, suffix].join(example_separator)
|
|
34
|
-
|
|
53
|
+
other_inputs = kwargs[:other_inputs] || []
|
|
54
|
+
output_variables = kwargs[:output_variables] || [:output]
|
|
55
|
+
Prompt.new(template: template, input_variables: input_variables, other_inputs: other_inputs,
|
|
56
|
+
output_variables: output_variables)
|
|
35
57
|
end
|
|
36
58
|
|
|
37
59
|
# create a prompt template from a file
|
|
38
60
|
# @param path [String] The path to the file to use for the template.
|
|
39
|
-
# @param input_variables [Array<Symbol>] The input variables to use for the prompt.
|
|
40
|
-
|
|
61
|
+
# @param input_variables [Array<Symbol>] The input variables to use for the prompt. Defaults to [:input]
|
|
62
|
+
# @param output_variables [Array<Symbol>] The output variables to use for the prompt. Defaults to [:output]
|
|
63
|
+
def self.from_file(path:, input_variables: nil, other_inputs: nil, output_variables: nil)
|
|
41
64
|
template = File.read(path)
|
|
42
|
-
Prompt.new(template: template, input_variables: input_variables
|
|
65
|
+
Prompt.new(template: template, input_variables: input_variables, other_inputs: other_inputs,
|
|
66
|
+
output_variables: output_variables)
|
|
43
67
|
end
|
|
44
68
|
end
|
|
45
69
|
end
|
|
@@ -19,6 +19,7 @@ module Boxcars
|
|
|
19
19
|
... (this Thought/Action/Action Input/Observation sequence can repeat N times)
|
|
20
20
|
Thought: I now know the final answer
|
|
21
21
|
Final Answer: the final answer to the original input question
|
|
22
|
+
Next Actions: up to three suggested actions for the user to take next
|
|
22
23
|
FINPUT
|
|
23
24
|
|
|
24
25
|
# default prompt suffix
|
data/lib/boxcars/train.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Boxcars
|
|
|
16
16
|
@name = name || self.class.name
|
|
17
17
|
@return_values = [:output]
|
|
18
18
|
@return_intermediate_steps = kwargs[:return_intermediate_steps] || false
|
|
19
|
-
@max_iterations = kwargs[:max_iterations]
|
|
19
|
+
@max_iterations = kwargs[:max_iterations] || 25
|
|
20
20
|
@early_stopping_method = kwargs[:early_stopping_method] || "force"
|
|
21
21
|
|
|
22
22
|
super(prompt: prompt, engine: engine, name: kwargs[:name], description: kwargs[:description])
|
|
@@ -137,8 +137,6 @@ module Boxcars
|
|
|
137
137
|
case prompt
|
|
138
138
|
when Prompt
|
|
139
139
|
prompt.template += "\n%<agent_scratchpad>s"
|
|
140
|
-
# when FewShotPromptTemplate
|
|
141
|
-
# prompt.suffix += "\n%<agent_scratchpad>s"
|
|
142
140
|
else
|
|
143
141
|
raise ValueError, "Got unexpected prompt type #{type(prompt)}"
|
|
144
142
|
end
|
|
@@ -199,7 +197,7 @@ module Boxcars
|
|
|
199
197
|
return_direct = boxcar.return_direct
|
|
200
198
|
rescue StandardError => e
|
|
201
199
|
error "Error in #{boxcar.name} boxcar#call: #{e}", :red
|
|
202
|
-
|
|
200
|
+
observation = "Error - #{e}, correct and try again."
|
|
203
201
|
end
|
|
204
202
|
else
|
|
205
203
|
observation = "#{output.boxcar} is not a valid boxcar, try another one."
|
data/lib/boxcars/version.rb
CHANGED
data/lib/boxcars.rb
CHANGED
|
@@ -19,6 +19,9 @@ module Boxcars
|
|
|
19
19
|
# Error class for all Boxcars security errors.
|
|
20
20
|
class SecurityError < Error; end
|
|
21
21
|
|
|
22
|
+
# Error class for all Boxcars key errors.
|
|
23
|
+
class KeyError < Error; end
|
|
24
|
+
|
|
22
25
|
# Configuration contains gem settings
|
|
23
26
|
class Configuration
|
|
24
27
|
attr_writer :openai_access_token, :serpapi_api_key
|
|
@@ -50,18 +53,18 @@ module Boxcars
|
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def key_lookup(key, kwargs)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
check_key(key,
|
|
56
|
+
val = if kwargs.key?(key) && !kwargs[key].nil?
|
|
57
|
+
# override with kwargs if present
|
|
58
|
+
kwargs[key]
|
|
59
|
+
elsif (provided_val = instance_variable_get("@#{key}"))
|
|
60
|
+
# use saved value if present. Set using Boxcars::configuration.the_key = "abcde"
|
|
61
|
+
provided_val
|
|
62
|
+
else
|
|
63
|
+
# otherwise, dig out of the environment
|
|
64
|
+
env_val = ENV.fetch(key.to_s.upcase, nil)
|
|
65
|
+
env_val
|
|
66
|
+
end
|
|
67
|
+
check_key(key, val)
|
|
65
68
|
end
|
|
66
69
|
end
|
|
67
70
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: boxcars
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Francis Sullivan
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2023-02
|
|
12
|
+
date: 2023-03-02 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: debug
|
|
@@ -89,6 +89,7 @@ executables: []
|
|
|
89
89
|
extensions: []
|
|
90
90
|
extra_rdoc_files: []
|
|
91
91
|
files:
|
|
92
|
+
- ".env_sample"
|
|
92
93
|
- ".rspec"
|
|
93
94
|
- ".rubocop.yml"
|
|
94
95
|
- CHANGELOG.md
|