boxcars 0.1.6 → 0.1.7
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/Gemfile.lock +1 -1
- data/README.md +19 -6
- data/lib/boxcars/boxcar/active_record.rb +5 -5
- data/lib/boxcars/boxcar/calculator.rb +1 -1
- data/lib/boxcars/boxcar/engine_boxcar.rb +1 -1
- data/lib/boxcars/boxcar/sql.rb +2 -2
- data/lib/boxcars/boxcar.rb +4 -4
- data/lib/boxcars/ruby_repl.rb +2 -2
- data/lib/boxcars/train.rb +3 -3
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars.rb +53 -18
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f8cfde09f23575d2b54249bd2b6d2c15697092b7c52db4d5f0546bb0113a3949
|
|
4
|
+
data.tar.gz: d557f681bf8a26251608b17038650868563da9d54753789c568331328cfa456b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db5af829a8a6e600cbb402e53aa7aaf1f9374725c5cf1d61ee908cb58ffda305b03ee00a4664e9f1134dc6887be9dab8c7877ad4c6b981e5a5665bfc1cfec2f3
|
|
7
|
+
data.tar.gz: 17930e46fa46b4d1a268367aff6d9b5851fac890f00d5a87e722ed3833552bb851614a5ab606cb9a08afce611a5882c9838094e31c1d34b9d17c05923656fc38
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -17,10 +17,11 @@ This gem was inspired by the popular Python library Langchain. However, we wante
|
|
|
17
17
|
|
|
18
18
|
## Concepts
|
|
19
19
|
All of these concepts are in a module named Boxcars:
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
20
|
+
|
|
21
|
+
- Boxcar - an encapsulation that performs something of interest (such as search, math, SQL or an Active Record Query). A Boxcar can use an Engine (described below) to do its work.
|
|
22
|
+
- Train - Given a list of Boxcars and optionally an Engine, a Train breaks down a problem into pieces for individual Boxcars to solve. The individual results are then combined until a final answer is found. ZeroShot is the only current implementation of Train (but we are adding more soon), and you can either construct it directly or use `Boxcars::train` when you want to build a Train.
|
|
23
|
+
- Prompt - used by an Engine to generate text results. Most of the Boxcars have built-in prompts, so you only need to worry about these if you are extending the system.
|
|
24
|
+
- Engine - an entity that generates text from a Prompt. OpenAI's LLM text generator is the default Engine if no other is specified.
|
|
24
25
|
|
|
25
26
|
## Installation
|
|
26
27
|
|
|
@@ -86,6 +87,7 @@ Here is what we have so far, but please put up a PR with your new ideas.
|
|
|
86
87
|
- GoogleSearch: uses the SERP API to do seaches
|
|
87
88
|
- Calculator: uses an Engine to generate ruby code to do math
|
|
88
89
|
- SQL: given an ActiveRecord connection, it will generate and run sql statments from a prompt.
|
|
90
|
+
- ActiveRecord: given an ActiveRecord connection, it will generate and run ActiveRecord statements from a prompt.
|
|
89
91
|
|
|
90
92
|
### Run a list of Boxcars
|
|
91
93
|
```ruby
|
|
@@ -114,7 +116,18 @@ Final Answer: 201.06
|
|
|
114
116
|
< Exiting Zero Shot#run
|
|
115
117
|
201.06
|
|
116
118
|
```
|
|
119
|
+
### More Examples
|
|
120
|
+
See [this](https://github.com/BoxcarsAI/boxcars/blob/main/notebooks/boxcars_examples.ipynb) Jupyter Notebook for more examples.
|
|
121
|
+
|
|
122
|
+
Note, some folks that we talked to didn't know that you could run Ruby Jupyter notebooks. [You can](https://github.com/SciRuby/iruby).
|
|
123
|
+
|
|
124
|
+
### Logging
|
|
125
|
+
If you use this in a Rails application, or configure `Boxcars.configuration.logger = your_logger`, logging will go to your log file.
|
|
126
|
+
|
|
127
|
+
Also, if you set this flag: `Boxcars.configuration.lop_prompts = true`
|
|
128
|
+
The actual prompts handed to the connected Engine will be logged. This is off by default because it is very wordy, but handy if you are debugging prompts.
|
|
117
129
|
|
|
130
|
+
Otherwise, we print to standard out.
|
|
118
131
|
## Development
|
|
119
132
|
|
|
120
133
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -123,7 +136,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
123
136
|
|
|
124
137
|
## Contributing
|
|
125
138
|
|
|
126
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
139
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/BoxcarsAI/boxcars. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/BoxcarsAI/boxcars/blob/main/CODE_OF_CONDUCT.md).
|
|
127
140
|
|
|
128
141
|
## License
|
|
129
142
|
|
|
@@ -131,4 +144,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
131
144
|
|
|
132
145
|
## Code of Conduct
|
|
133
146
|
|
|
134
|
-
Everyone interacting in the Boxcars project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
|
147
|
+
Everyone interacting in the Boxcars project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/BoxcarsAI/boxcars/blob/main/CODE_OF_CONDUCT.md).
|
|
@@ -50,7 +50,7 @@ module Boxcars
|
|
|
50
50
|
def call(inputs:)
|
|
51
51
|
t = predict(question: inputs[input_key], top_k: 5, model_info: model_info, stop: ["Answer:"]).strip
|
|
52
52
|
answer = get_answer(t)
|
|
53
|
-
|
|
53
|
+
Boxcars.info answer, :magenta
|
|
54
54
|
{ output_key => answer }
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -108,7 +108,7 @@ module Boxcars
|
|
|
108
108
|
|
|
109
109
|
bad_words.each do |w|
|
|
110
110
|
if word_list.include?(w)
|
|
111
|
-
|
|
111
|
+
Boxcars.info "code included destructive instruction: #{w} #{code}", :red
|
|
112
112
|
return false
|
|
113
113
|
end
|
|
114
114
|
end
|
|
@@ -128,7 +128,7 @@ module Boxcars
|
|
|
128
128
|
return 0 unless changes_code
|
|
129
129
|
|
|
130
130
|
rollback_after_running do
|
|
131
|
-
|
|
131
|
+
Boxcars.debug "computing change count with: #{changes_code}", :yellow
|
|
132
132
|
evaluate_input changes_code
|
|
133
133
|
end
|
|
134
134
|
end
|
|
@@ -138,7 +138,7 @@ module Boxcars
|
|
|
138
138
|
changes = change_count(changes_code)
|
|
139
139
|
return true unless changes&.positive?
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
Boxcars.debug "Pending Changes: #{changes}", :yellow, style: :bold
|
|
142
142
|
change_str = "#{changes} change#{'s' if changes.to_i > 1}"
|
|
143
143
|
raise SecurityError, "Can not run code that makes #{change_str} in read-only mode" if read_only?
|
|
144
144
|
|
|
@@ -148,7 +148,7 @@ module Boxcars
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def run_active_record_code(code)
|
|
151
|
-
|
|
151
|
+
Boxcars.debug code, :yellow
|
|
152
152
|
if read_only?
|
|
153
153
|
rollback_after_running do
|
|
154
154
|
evaluate_input code
|
|
@@ -36,7 +36,7 @@ module Boxcars
|
|
|
36
36
|
prompts = []
|
|
37
37
|
input_list.each do |inputs|
|
|
38
38
|
new_prompt = prompt.format(**inputs)
|
|
39
|
-
|
|
39
|
+
Boxcars.debug("Prompt after formatting:\n#{new_prompt}", :cyan) if Boxcars.configuration.log_prompts
|
|
40
40
|
prompts.push(new_prompt)
|
|
41
41
|
end
|
|
42
42
|
engine.generate(prompts: prompts, stop: stop)
|
data/lib/boxcars/boxcar/sql.rb
CHANGED
|
@@ -44,7 +44,7 @@ module Boxcars
|
|
|
44
44
|
def call(inputs:)
|
|
45
45
|
t = predict(question: inputs[input_key], dialect: dialect, top_k: 5, table_info: schema, stop: ["Answer:"]).strip
|
|
46
46
|
answer = get_answer(t)
|
|
47
|
-
|
|
47
|
+
Boxcars.debug answer, :magenta
|
|
48
48
|
{ output_key => answer }
|
|
49
49
|
end
|
|
50
50
|
|
|
@@ -72,7 +72,7 @@ module Boxcars
|
|
|
72
72
|
|
|
73
73
|
def get_embedded_sql_answer(text)
|
|
74
74
|
code = text[/^SQLQuery: (.*)/, 1]
|
|
75
|
-
|
|
75
|
+
Boxcars.debug code, :yellow
|
|
76
76
|
output = connection.exec_query(code).to_a
|
|
77
77
|
"Answer: #{output}"
|
|
78
78
|
end
|
data/lib/boxcars/boxcar.rb
CHANGED
|
@@ -62,9 +62,9 @@ module Boxcars
|
|
|
62
62
|
# you can pass one or the other, but not both.
|
|
63
63
|
# @return [String] The answer to the question.
|
|
64
64
|
def run(*args, **kwargs)
|
|
65
|
-
|
|
65
|
+
Boxcars.info "> Entering #{name}#run", :gray, style: :bold
|
|
66
66
|
rv = do_run(*args, **kwargs)
|
|
67
|
-
|
|
67
|
+
Boxcars.info "< Exiting #{name}#run", :gray, style: :bold
|
|
68
68
|
rv
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -77,7 +77,7 @@ module Boxcars
|
|
|
77
77
|
begin
|
|
78
78
|
output = call(inputs: inputs)
|
|
79
79
|
rescue StandardError => e
|
|
80
|
-
|
|
80
|
+
error "Error in #{name} boxcar#call: #{e}", :red
|
|
81
81
|
raise e
|
|
82
82
|
end
|
|
83
83
|
validate_outputs(outputs: output.keys)
|
|
@@ -101,7 +101,7 @@ module Boxcars
|
|
|
101
101
|
|
|
102
102
|
def our_inputs(inputs)
|
|
103
103
|
if inputs.is_a?(String)
|
|
104
|
-
|
|
104
|
+
Boxcars.info inputs, :blue # the question
|
|
105
105
|
if input_keys.length != 1
|
|
106
106
|
raise Boxcars::ArgumentError, "A single string input was passed in, but this boxcar expects " \
|
|
107
107
|
"multiple inputs (#{input_keys}). When a boxcar expects " \
|
data/lib/boxcars/ruby_repl.rb
CHANGED
|
@@ -6,14 +6,14 @@ module Boxcars
|
|
|
6
6
|
# Execute ruby code
|
|
7
7
|
# @param code [String] The code to run
|
|
8
8
|
def call(code:)
|
|
9
|
-
|
|
9
|
+
Boxcars.debug "RubyREPL: #{code}", :yellow
|
|
10
10
|
output = ""
|
|
11
11
|
IO.popen("ruby", "r+") do |io|
|
|
12
12
|
io.puts code
|
|
13
13
|
io.close_write
|
|
14
14
|
output = io.read
|
|
15
15
|
end
|
|
16
|
-
|
|
16
|
+
Boxcars.debug "Answer: #{output}", :yellow, style: :bold
|
|
17
17
|
output
|
|
18
18
|
end
|
|
19
19
|
|
data/lib/boxcars/train.rb
CHANGED
|
@@ -112,7 +112,7 @@ module Boxcars
|
|
|
112
112
|
# @param intermediate_steps [Array<Hash>] The intermediate steps.
|
|
113
113
|
# @return [Hash] The final output.
|
|
114
114
|
def pre_return(output, intermediate_steps)
|
|
115
|
-
|
|
115
|
+
Boxcars.debug output.log, :yellow, style: :bold
|
|
116
116
|
final_output = output.return_values
|
|
117
117
|
final_output["intermediate_steps"] = intermediate_steps if return_intermediate_steps
|
|
118
118
|
final_output
|
|
@@ -198,14 +198,14 @@ module Boxcars
|
|
|
198
198
|
observation = boxcar.run(output.boxcar_input)
|
|
199
199
|
return_direct = boxcar.return_direct
|
|
200
200
|
rescue StandardError => e
|
|
201
|
-
|
|
201
|
+
error "Error in #{boxcar.name} boxcar#call: #{e}", :red
|
|
202
202
|
raise e
|
|
203
203
|
end
|
|
204
204
|
else
|
|
205
205
|
observation = "#{output.boxcar} is not a valid boxcar, try another one."
|
|
206
206
|
return_direct = false
|
|
207
207
|
end
|
|
208
|
-
|
|
208
|
+
Boxcars.debug "Observation: #{observation}", :green
|
|
209
209
|
intermediate_steps.append([output, observation])
|
|
210
210
|
if return_direct
|
|
211
211
|
output = TrainFinish.new({ return_values[0] => observation }, "")
|
data/lib/boxcars/version.rb
CHANGED
data/lib/boxcars.rb
CHANGED
|
@@ -19,23 +19,6 @@ module Boxcars
|
|
|
19
19
|
# Error class for all Boxcars security errors.
|
|
20
20
|
class SecurityError < Error; end
|
|
21
21
|
|
|
22
|
-
# simple string colorization
|
|
23
|
-
class ::String
|
|
24
|
-
# colorize a string
|
|
25
|
-
# @param color [Symbol] The color to use.
|
|
26
|
-
# @param options [Hash] The options to use.
|
|
27
|
-
def colorize(color, options = {})
|
|
28
|
-
background = options[:background] || options[:bg] || false
|
|
29
|
-
style = options[:style]
|
|
30
|
-
offsets = %i[gray red green yellow blue magenta cyan white]
|
|
31
|
-
styles = %i[normal bold dark italic underline xx xx underline xx strikethrough]
|
|
32
|
-
start = background ? 40 : 30
|
|
33
|
-
color_code = start + (offsets.index(color) || 8)
|
|
34
|
-
style_code = styles.index(style) || 0
|
|
35
|
-
"\e[#{style_code};#{color_code}m#{self}\e[0m"
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
22
|
# Configuration contains gem settings
|
|
40
23
|
class Configuration
|
|
41
24
|
attr_writer :openai_access_token, :serpapi_api_key
|
|
@@ -44,7 +27,6 @@ module Boxcars
|
|
|
44
27
|
def initialize
|
|
45
28
|
@organization_id = nil
|
|
46
29
|
@logger = Rails.logger if defined?(Rails)
|
|
47
|
-
@logger ||= Logger.new($stdout)
|
|
48
30
|
@log_prompts = false
|
|
49
31
|
end
|
|
50
32
|
|
|
@@ -115,6 +97,59 @@ module Boxcars
|
|
|
115
97
|
answer.downcase == 'y'
|
|
116
98
|
end
|
|
117
99
|
end
|
|
100
|
+
|
|
101
|
+
# Return a logger, possibly if set.
|
|
102
|
+
def self.logger
|
|
103
|
+
Boxcars.configuration.logger
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Logging system
|
|
107
|
+
def self.debug(msg, color = nil, **options)
|
|
108
|
+
msg = colorize(msg.to_s, color, **options) if color
|
|
109
|
+
if logger
|
|
110
|
+
logger.debug(msg)
|
|
111
|
+
else
|
|
112
|
+
puts msg
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.info(msg, color = nil, **options)
|
|
117
|
+
msg = colorize(msg.to_s, color, **options) if color
|
|
118
|
+
if logger
|
|
119
|
+
logger.info(msg)
|
|
120
|
+
else
|
|
121
|
+
puts msg
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.warn(msg, color = nil, **options)
|
|
126
|
+
msg = colorize(msg.to_s, color, **options) if color
|
|
127
|
+
if logger
|
|
128
|
+
logger.warn(msg)
|
|
129
|
+
else
|
|
130
|
+
puts msg
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def self.error(msg, color = nil, **options)
|
|
135
|
+
msg = colorize(msg.to_s, color, **options) if color
|
|
136
|
+
if logger
|
|
137
|
+
logger.error(msg)
|
|
138
|
+
else
|
|
139
|
+
puts msg
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.colorize(str, color, **options)
|
|
144
|
+
background = options[:background] || options[:bg] || false
|
|
145
|
+
style = options[:style]
|
|
146
|
+
offsets = %i[gray red green yellow blue magenta cyan white]
|
|
147
|
+
styles = %i[normal bold dark italic underline xx xx underline xx strikethrough]
|
|
148
|
+
start = background ? 40 : 30
|
|
149
|
+
color_code = start + (offsets.index(color) || 8)
|
|
150
|
+
style_code = styles.index(style) || 0
|
|
151
|
+
"\e[#{style_code};#{color_code}m#{str}\e[0m"
|
|
152
|
+
end
|
|
118
153
|
end
|
|
119
154
|
|
|
120
155
|
require "boxcars/version"
|
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.7
|
|
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-02-27 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: debug
|