boxcars 0.2.3 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -2
- data/Gemfile.lock +27 -27
- data/README.md +25 -13
- data/lib/boxcars/boxcar/active_record.rb +32 -10
- data/lib/boxcars/boxcar/engine_boxcar.rb +1 -1
- data/lib/boxcars/boxcar/google_search.rb +2 -2
- data/lib/boxcars/train/zero_shot.rb +1 -1
- data/lib/boxcars/train.rb +2 -0
- data/lib/boxcars/version.rb +1 -1
- 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: a3593a9df2d9d8a867729e0b6081b300125933566923ca905e2eefb16a933394
|
4
|
+
data.tar.gz: 90c03ea9b328b8cff10f828f8b9bf4375cce0fd574dd8a26c690776e58a58c1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5de41be1f154b2c21fcd6602159a6428d8702dc318904e6e03db9de1b0ed1788b03aa10365203557b39fe268f04b4594cf6915faa941b2c64e475cd6cbb55d09
|
7
|
+
data.tar.gz: 2ad84e5f416b19759807d658d739d1abb8405ba6ded3b38bdf5ce9406efeadccbc2102b7b477a90df67beaa287a9810b09e59b5843090fbe4619b03500d0a4f4
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [v0.2.4](https://github.com/BoxcarsAI/boxcars/tree/v0.2.4) (2023-03-28)
|
4
4
|
|
5
|
-
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.2
|
5
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.3...v0.2.4)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- security [\#40](https://github.com/BoxcarsAI/boxcars/issues/40)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Fix regex action input [\#41](https://github.com/BoxcarsAI/boxcars/pull/41) ([makevoid](https://github.com/makevoid))
|
14
|
+
|
15
|
+
## [v0.2.3](https://github.com/BoxcarsAI/boxcars/tree/v0.2.3) (2023-03-20)
|
16
|
+
|
17
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.2...v0.2.3)
|
6
18
|
|
7
19
|
**Merged pull requests:**
|
8
20
|
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
boxcars (0.2.
|
4
|
+
boxcars (0.2.5)
|
5
5
|
google_search_results (~> 2.2)
|
6
6
|
ruby-openai (~> 3.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activemodel (7.0.4.
|
12
|
-
activesupport (= 7.0.4.
|
13
|
-
activerecord (7.0.4.
|
14
|
-
activemodel (= 7.0.4.
|
15
|
-
activesupport (= 7.0.4.
|
16
|
-
activesupport (7.0.4.
|
11
|
+
activemodel (7.0.4.3)
|
12
|
+
activesupport (= 7.0.4.3)
|
13
|
+
activerecord (7.0.4.3)
|
14
|
+
activemodel (= 7.0.4.3)
|
15
|
+
activesupport (= 7.0.4.3)
|
16
|
+
activesupport (7.0.4.3)
|
17
17
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
18
|
i18n (>= 1.6, < 2)
|
19
19
|
minitest (>= 5.1)
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
addressable (2.8.1)
|
22
22
|
public_suffix (>= 2.0.2, < 6.0)
|
23
23
|
ast (2.4.2)
|
24
|
-
async (1.
|
24
|
+
async (1.31.0)
|
25
25
|
console (~> 1.10)
|
26
26
|
nio4r (~> 2.3)
|
27
27
|
timers (~> 4.1)
|
@@ -38,14 +38,14 @@ GEM
|
|
38
38
|
faraday
|
39
39
|
async-io (1.34.3)
|
40
40
|
async
|
41
|
-
async-pool (0.
|
41
|
+
async-pool (0.4.0)
|
42
42
|
async (>= 1.25)
|
43
|
-
concurrent-ruby (1.2.
|
43
|
+
concurrent-ruby (1.2.2)
|
44
44
|
console (1.16.2)
|
45
45
|
fiber-local
|
46
46
|
crack (0.4.5)
|
47
47
|
rexml
|
48
|
-
debug (1.7.
|
48
|
+
debug (1.7.2)
|
49
49
|
irb (>= 1.5.0)
|
50
50
|
reline (>= 0.3.1)
|
51
51
|
diff-lcs (1.5.0)
|
@@ -56,7 +56,7 @@ GEM
|
|
56
56
|
faraday-http-cache (2.4.1)
|
57
57
|
faraday (>= 0.8)
|
58
58
|
faraday-net_http (3.0.2)
|
59
|
-
faraday-retry (2.
|
59
|
+
faraday-retry (2.1.0)
|
60
60
|
faraday (~> 2.0)
|
61
61
|
fiber-local (1.0.0)
|
62
62
|
github_changelog_generator (1.16.4)
|
@@ -77,13 +77,13 @@ GEM
|
|
77
77
|
concurrent-ruby (~> 1.0)
|
78
78
|
io-console (0.6.0)
|
79
79
|
io-console (0.6.0-java)
|
80
|
-
irb (1.6.
|
80
|
+
irb (1.6.3)
|
81
81
|
reline (>= 0.3.0)
|
82
82
|
json (2.6.3)
|
83
83
|
json (2.6.3-java)
|
84
84
|
mini_mime (1.1.2)
|
85
85
|
mini_portile2 (2.8.1)
|
86
|
-
minitest (5.
|
86
|
+
minitest (5.18.0)
|
87
87
|
multi_json (1.15.0)
|
88
88
|
multi_xml (0.6.0)
|
89
89
|
nio4r (2.5.8)
|
@@ -92,7 +92,7 @@ GEM
|
|
92
92
|
faraday (>= 1, < 3)
|
93
93
|
sawyer (~> 0.9)
|
94
94
|
parallel (1.22.1)
|
95
|
-
parser (3.2.1.
|
95
|
+
parser (3.2.1.1)
|
96
96
|
ast (~> 2.4.1)
|
97
97
|
protocol-hpack (1.4.2)
|
98
98
|
protocol-http (0.24.1)
|
@@ -105,7 +105,7 @@ GEM
|
|
105
105
|
rainbow (3.1.1)
|
106
106
|
rake (13.0.6)
|
107
107
|
regexp_parser (2.7.0)
|
108
|
-
reline (0.3.
|
108
|
+
reline (0.3.3)
|
109
109
|
io-console (~> 0.5)
|
110
110
|
rexml (3.2.5)
|
111
111
|
rspec (3.12.0)
|
@@ -117,42 +117,42 @@ GEM
|
|
117
117
|
rspec-expectations (3.12.2)
|
118
118
|
diff-lcs (>= 1.2.0, < 2.0)
|
119
119
|
rspec-support (~> 3.12.0)
|
120
|
-
rspec-mocks (3.12.
|
120
|
+
rspec-mocks (3.12.5)
|
121
121
|
diff-lcs (>= 1.2.0, < 2.0)
|
122
122
|
rspec-support (~> 3.12.0)
|
123
123
|
rspec-support (3.12.0)
|
124
|
-
rubocop (1.
|
124
|
+
rubocop (1.48.1)
|
125
125
|
json (~> 2.3)
|
126
126
|
parallel (~> 1.10)
|
127
127
|
parser (>= 3.2.0.0)
|
128
128
|
rainbow (>= 2.2.2, < 4.0)
|
129
129
|
regexp_parser (>= 1.8, < 3.0)
|
130
130
|
rexml (>= 3.2.5, < 4.0)
|
131
|
-
rubocop-ast (>= 1.
|
131
|
+
rubocop-ast (>= 1.26.0, < 2.0)
|
132
132
|
ruby-progressbar (~> 1.7)
|
133
133
|
unicode-display_width (>= 2.4.0, < 3.0)
|
134
|
-
rubocop-ast (1.
|
134
|
+
rubocop-ast (1.28.0)
|
135
135
|
parser (>= 3.2.1.0)
|
136
136
|
rubocop-capybara (2.17.1)
|
137
137
|
rubocop (~> 1.41)
|
138
138
|
rubocop-rake (0.6.0)
|
139
139
|
rubocop (~> 1.0)
|
140
|
-
rubocop-rspec (2.
|
140
|
+
rubocop-rspec (2.19.0)
|
141
141
|
rubocop (~> 1.33)
|
142
142
|
rubocop-capybara (~> 2.17)
|
143
|
-
ruby-openai (3.
|
143
|
+
ruby-openai (3.7.0)
|
144
144
|
httparty (>= 0.18.1)
|
145
|
-
ruby-progressbar (1.
|
145
|
+
ruby-progressbar (1.13.0)
|
146
146
|
ruby2_keywords (0.0.5)
|
147
147
|
sawyer (0.9.2)
|
148
148
|
addressable (>= 2.3.5)
|
149
149
|
faraday (>= 0.17.3, < 3)
|
150
|
-
sqlite3 (1.6.
|
150
|
+
sqlite3 (1.6.2)
|
151
151
|
mini_portile2 (~> 2.8.0)
|
152
|
-
sqlite3 (1.6.
|
153
|
-
sqlite3 (1.6.
|
152
|
+
sqlite3 (1.6.2-x86_64-darwin)
|
153
|
+
sqlite3 (1.6.2-x86_64-linux)
|
154
154
|
timers (4.3.5)
|
155
|
-
traces (0.
|
155
|
+
traces (0.9.1)
|
156
156
|
tzinfo (2.0.6)
|
157
157
|
concurrent-ruby (~> 1.0)
|
158
158
|
unicode-display_width (2.4.2)
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
<a href="https://github.com/BoxcarsAI/boxcars/blob/main/LICENSE.txt"><img src="https://img.shields.io/badge/license-MIT-informational" alt="License"></a>
|
12
12
|
</p>
|
13
13
|
|
14
|
-
Boxcars is a gem that enables you to create new systems with AI composability, using various concepts such as OpenAI, Search, SQL, Rails Active Record and more. This can even be extended with your concepts as well
|
14
|
+
Boxcars is a gem that enables you to create new systems with AI composability, using various concepts such as OpenAI, Search, SQL, Rails Active Record and more. This can even be extended with your concepts as well (including your concepts).
|
15
15
|
|
16
16
|
This gem was inspired by the popular Python library Langchain. However, we wanted to give it a Ruby spin and make it more user-friendly for beginners to get started.
|
17
17
|
|
@@ -23,6 +23,11 @@ All of these concepts are in a module named Boxcars:
|
|
23
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
24
|
- Engine - an entity that generates text from a Prompt. OpenAI's LLM text generator is the default Engine if no other is specified.
|
25
25
|
|
26
|
+
## Security
|
27
|
+
Currently, our system is designed for individuals who already possess administrative privileges for their project. It is likely possible to manipulate the system's prompts to carry out malicious actions, but if you already have administrative access, you can perform such actions without requiring boxcars in the first place.
|
28
|
+
|
29
|
+
*Note:* We are actively seeking ways to improve our system's ability to identify and prevent any nefarious attempts from occurring. If you have any suggestions or recommendations, please feel free to share them with us by either finding an existing issue or creating a new one and providing us with your feedback.
|
30
|
+
|
26
31
|
## Installation
|
27
32
|
|
28
33
|
Add this line to your application's Gemfile:
|
@@ -66,10 +71,10 @@ Produces:
|
|
66
71
|
```text
|
67
72
|
> Entering Calculator#run
|
68
73
|
what is pi to the forth power divided by 22.1?
|
69
|
-
RubyREPL: puts(Math::PI**4
|
74
|
+
RubyREPL: puts (Math::PI**4)/22.1
|
70
75
|
Answer: 4.407651178009159
|
71
76
|
|
72
|
-
4.407651178009159
|
77
|
+
{"status":"ok","answer":"4.407651178009159","explanation":"Answer: 4.407651178009159","code":"puts (Math::PI**4)/22.1"}
|
73
78
|
< Exiting Calculator#run
|
74
79
|
4.407651178009159
|
75
80
|
```
|
@@ -94,27 +99,34 @@ Here is what we have so far, but please put up a PR with your new ideas.
|
|
94
99
|
# run a Train for a calculator, and search using default Engine
|
95
100
|
boxcars = [Boxcars::Calculator.new, Boxcars::Serp.new]
|
96
101
|
train = Boxcars.train.new(boxcars: boxcars)
|
97
|
-
|
102
|
+
train.run "What is pi times the square root of the average temperature in Austin TX in January?"
|
98
103
|
```
|
99
104
|
Produces:
|
100
105
|
```text
|
101
106
|
> Entering Zero Shot#run
|
102
107
|
What is pi times the square root of the average temperature in Austin TX in January?
|
108
|
+
Thought: We need to find the average temperature in Austin TX in January and then multiply it by pi and the square root of that value. We can use a search engine to find the average temperature and a calculator to perform the multiplication.
|
103
109
|
Question: Average temperature in Austin TX in January
|
104
110
|
Answer: increase from 62°F to 64°F
|
105
|
-
|
111
|
+
Observation: increase from 62°F to 64°F
|
112
|
+
Thought: The average temperature in Austin TX in January is around 63°F.
|
106
113
|
> Entering Calculator#run
|
107
|
-
|
108
|
-
RubyREPL: puts
|
109
|
-
Answer:
|
114
|
+
pi * sqrt(63)
|
115
|
+
RubyREPL: puts(Math::PI * Math.sqrt(63))
|
116
|
+
Answer: 24.935618646198247
|
110
117
|
|
111
|
-
|
118
|
+
{"status":"ok","answer":"24.935618646198247","explanation":"Answer: 24.935618646198247","code":"puts(Math::PI * Math.sqrt(63))"}
|
112
119
|
< Exiting Calculator#run
|
113
|
-
|
114
|
-
|
115
|
-
|
120
|
+
Observation: 24.935618646198247
|
121
|
+
The result of pi times the square root of the average temperature in Austin TX in January is approximately 24.94.
|
122
|
+
|
123
|
+
Final Answer: 24.94
|
124
|
+
|
125
|
+
Next Actions:
|
126
|
+
1. What is the average temperature in Austin TX in July?
|
127
|
+
2. What is the formula for calculating the area of a circle?
|
128
|
+
3. What is the value of pi to 10 decimal places?
|
116
129
|
< Exiting Zero Shot#run
|
117
|
-
201.06
|
118
130
|
```
|
119
131
|
### More Examples
|
120
132
|
See [this](https://github.com/BoxcarsAI/boxcars/blob/main/notebooks/boxcars_examples.ipynb) Jupyter Notebook for more examples.
|
@@ -76,7 +76,7 @@ module Boxcars
|
|
76
76
|
::ActiveRecord::Base.transaction do
|
77
77
|
begin
|
78
78
|
result = yield
|
79
|
-
rescue ::NameError, ::Error => e
|
79
|
+
rescue SecurityError, ::NameError, ::Error => e
|
80
80
|
Boxcars.error("Error while running code: #{e.message[0..60]} ...", :red)
|
81
81
|
runtime_exception = e
|
82
82
|
end
|
@@ -93,13 +93,20 @@ module Boxcars
|
|
93
93
|
bad_words = %w[commit drop_constraint drop_constraint! drop_extension drop_extension! drop_foreign_key drop_foreign_key! \
|
94
94
|
drop_index drop_index! drop_join_table drop_join_table! drop_materialized_view drop_materialized_view! \
|
95
95
|
drop_partition drop_partition! drop_schema drop_schema! drop_table drop_table! drop_trigger drop_trigger! \
|
96
|
-
drop_view drop_view! eval execute reset revoke rollback truncate
|
96
|
+
drop_view drop_view! eval instance_eval send system execute reset revoke rollback truncate \
|
97
|
+
encrypted_password].freeze
|
97
98
|
without_strings = code.gsub(/('([^'\\]*(\\.[^'\\]*)*)'|"([^"\\]*(\\.[^"\\]*)*"))/, 'XX')
|
98
|
-
|
99
|
+
|
100
|
+
if without_strings.include?("`")
|
101
|
+
Boxcars.info "code included possibly destructive backticks #{code}", :red
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
|
105
|
+
word_list = without_strings.split(/[.,() :\[\]]/)
|
99
106
|
|
100
107
|
bad_words.each do |w|
|
101
108
|
if word_list.include?(w)
|
102
|
-
Boxcars.info "code included destructive instruction: #{w} #{code}", :red
|
109
|
+
Boxcars.info "code included possibly destructive instruction: '#{w}' in #{code}", :red
|
103
110
|
return false
|
104
111
|
end
|
105
112
|
end
|
@@ -107,12 +114,24 @@ module Boxcars
|
|
107
114
|
true
|
108
115
|
end
|
109
116
|
|
117
|
+
# run the code in a safe environment
|
118
|
+
# @param code [String] The code to run
|
119
|
+
# @return [Object] The result of the code
|
120
|
+
def eval_safe_wrapper(code)
|
121
|
+
# if the code used ActiveRecord, we need to add :: in front of it to escape the module
|
122
|
+
new_code = code.gsub(/(\W)ActiveRecord::/, '\1::ActiveRecord::')
|
123
|
+
proc do
|
124
|
+
$SAFE = 4
|
125
|
+
# rubocop:disable Security/Eval
|
126
|
+
eval new_code
|
127
|
+
# rubocop:enable Security/Eval
|
128
|
+
end.call
|
129
|
+
end
|
130
|
+
|
110
131
|
def evaluate_input(code)
|
111
132
|
raise SecurityError, "Found unsafe code while evaluating: #{code}" unless safe_to_run?(code)
|
112
133
|
|
113
|
-
|
114
|
-
eval code
|
115
|
-
# rubocop:enable Security/Eval
|
134
|
+
eval_safe_wrapper code
|
116
135
|
end
|
117
136
|
|
118
137
|
def change_count(changes_code)
|
@@ -176,7 +195,7 @@ module Boxcars
|
|
176
195
|
have_approval = false
|
177
196
|
begin
|
178
197
|
have_approval = approved?(changes_code, code)
|
179
|
-
rescue NameError,
|
198
|
+
rescue NameError, Error => e
|
180
199
|
return Result.new(status: :error, explanation: error_message(e, "ARChanges"), changes_code: changes_code)
|
181
200
|
end
|
182
201
|
|
@@ -185,7 +204,9 @@ module Boxcars
|
|
185
204
|
begin
|
186
205
|
output = clean_up_output(run_active_record_code(code))
|
187
206
|
Result.new(status: :ok, answer: output, explanation: "Answer: #{output.to_json}", code: code)
|
188
|
-
rescue
|
207
|
+
rescue SecurityError => e
|
208
|
+
raise e
|
209
|
+
rescue ::StandardError => e
|
189
210
|
Result.new(status: :error, answer: nil, explanation: error_message(e, "ARCode"), code: code)
|
190
211
|
end
|
191
212
|
end
|
@@ -220,7 +241,8 @@ module Boxcars
|
|
220
241
|
"Only use the following Active Record models: %<model_info>s\n",
|
221
242
|
"Pay attention to use only the attribute names that you can see in the model description.\n",
|
222
243
|
"Do not make up variable or attribute names, and do not share variables between the code in ARChanges and ARCode\n",
|
223
|
-
"Be careful to not query for attributes that do not exist, and to use the format specified above.\n"
|
244
|
+
"Be careful to not query for attributes that do not exist, and to use the format specified above.\n",
|
245
|
+
"Finally, do not use print or puts in your code."
|
224
246
|
),
|
225
247
|
user("Question: %<question>s")
|
226
248
|
].freeze
|
@@ -100,7 +100,7 @@ module Boxcars
|
|
100
100
|
end
|
101
101
|
Boxcars.error answer.to_json, :red
|
102
102
|
{ output_key => "Error: #{answer}" }
|
103
|
-
rescue Boxcars::ConfigurationError => e
|
103
|
+
rescue Boxcars::ConfigurationError, Boxcars::SecurityError => e
|
104
104
|
raise e
|
105
105
|
rescue Boxcars::Error => e
|
106
106
|
Boxcars.error e.message, :red
|
@@ -49,8 +49,8 @@ module Boxcars
|
|
49
49
|
[:answer_box, :snippet_highlighted_words, 0],
|
50
50
|
%i[sports_results game_spotlight],
|
51
51
|
%i[knowledge_graph description],
|
52
|
-
[:organic_results, 0, :
|
53
|
-
[:organic_results, 0, :
|
52
|
+
[:organic_results, 0, :snippet],
|
53
|
+
[:organic_results, 0, :snippet_highlighted_words, 0]
|
54
54
|
].freeze
|
55
55
|
|
56
56
|
def find_answer(res)
|
@@ -50,7 +50,7 @@ module Boxcars
|
|
50
50
|
# the thought should be the frist line here if it doesn't start with "Action:"
|
51
51
|
thought = engine_output.split(/\n+/).reject(&:empty?).first
|
52
52
|
Boxcars.debug("Thought: #{thought}", :yellow)
|
53
|
-
regex = /Action: (?<action
|
53
|
+
regex = /Action(?<extra>[\s\d]*): (?<action>.+?)\n+Action Input:(?<action_input>.+)/m
|
54
54
|
match = regex.match(engine_output)
|
55
55
|
|
56
56
|
# we have an unexpected output from the engine
|
data/lib/boxcars/train.rb
CHANGED
@@ -196,6 +196,8 @@ module Boxcars
|
|
196
196
|
begin
|
197
197
|
observation = boxcar.run(output.boxcar_input)
|
198
198
|
return_direct = boxcar.return_direct
|
199
|
+
rescue Boxcars::ConfigurationError, Boxcars::SecurityError => e
|
200
|
+
raise e
|
199
201
|
rescue StandardError => e
|
200
202
|
Boxcars.error "Error in #{boxcar.name} boxcar#call: #{e}", :red
|
201
203
|
observation = "Error - #{e}, correct and try again."
|
data/lib/boxcars/version.rb
CHANGED
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.2.
|
4
|
+
version: 0.2.5
|
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-03-
|
12
|
+
date: 2023-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debug
|