boxcars 0.1.4 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +4 -1
- data/README.md +5 -4
- data/boxcars.gemspec +2 -2
- data/lib/boxcars/boxcar/active_record.rb +110 -25
- data/lib/boxcars/boxcar/google_search.rb +1 -1
- data/lib/boxcars/engine/openai.rb +1 -1
- data/lib/boxcars/ruby_repl.rb +2 -2
- data/lib/boxcars/train/zero_shot.rb +4 -3
- data/lib/boxcars/train.rb +1 -1
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars.rb +13 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 666f9b91451e8b6db61eb1162cb6508a566a46b676d646f1fb0000175714cede
|
4
|
+
data.tar.gz: bee7b55be2288bf9361012bd562f88d91f64ce7972b0cf5c495a5fce87eea77c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0681f5bd00bbfe2afd67011d76c561640a0575a9656b20913d3c60193208ea33361003ee652d01802b49702b106bd4f3d5bc91c1c7f2023bc2e8224edca14a8d'
|
7
|
+
data.tar.gz: 794fe20c2f9b8f14402db177bdee3d3e5318ab652e69c02929f9ae79d6daca096ae386a4e85730da4004927ab31aff53cc3a8d960ca661d6a5dd192432ffeabb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,45 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [Unreleased](https://github.com/BoxcarsAI/boxcars/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.5...HEAD)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Add a callback function for Boxcars::ActiveRecord to approve changes [\#24](https://github.com/BoxcarsAI/boxcars/issues/24)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Add approval callback function for Boxcars::ActiveRecord for changes to the data [\#25](https://github.com/BoxcarsAI/boxcars/pull/25) ([francis](https://github.com/francis))
|
14
|
+
- \[fix\] Fixed specs which required a key [\#23](https://github.com/BoxcarsAI/boxcars/pull/23) ([AKovtunov](https://github.com/AKovtunov))
|
15
|
+
|
16
|
+
## [v0.1.5](https://github.com/BoxcarsAI/boxcars/tree/v0.1.5) (2023-02-22)
|
17
|
+
|
18
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.4...v0.1.5)
|
19
|
+
|
20
|
+
**Implemented enhancements:**
|
21
|
+
|
22
|
+
- Make Boxcars::ActiveRecord read\_only by default [\#20](https://github.com/BoxcarsAI/boxcars/issues/20)
|
23
|
+
|
24
|
+
**Merged pull requests:**
|
25
|
+
|
26
|
+
- Active Record readonly [\#21](https://github.com/BoxcarsAI/boxcars/pull/21) ([francis](https://github.com/francis))
|
27
|
+
|
28
|
+
## [v0.1.4](https://github.com/BoxcarsAI/boxcars/tree/v0.1.4) (2023-02-22)
|
29
|
+
|
30
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.3...v0.1.4)
|
31
|
+
|
32
|
+
**Implemented enhancements:**
|
33
|
+
|
34
|
+
- Extend Sql concept to produce and run ActiveRecord code instead of SQL [\#9](https://github.com/BoxcarsAI/boxcars/issues/9)
|
35
|
+
|
36
|
+
**Merged pull requests:**
|
37
|
+
|
38
|
+
- first pass at an ActiveRecord boxcar [\#18](https://github.com/BoxcarsAI/boxcars/pull/18) ([francis](https://github.com/francis))
|
39
|
+
- change Boxcars::default\_train to Boxcars::train to improve code reada… [\#17](https://github.com/BoxcarsAI/boxcars/pull/17) ([francis](https://github.com/francis))
|
40
|
+
- rename class Serp to GoogleSearch [\#16](https://github.com/BoxcarsAI/boxcars/pull/16) ([francis](https://github.com/francis))
|
41
|
+
- Update README.md [\#15](https://github.com/BoxcarsAI/boxcars/pull/15) ([tabrez-syed](https://github.com/tabrez-syed))
|
42
|
+
|
3
43
|
## [v0.1.3](https://github.com/BoxcarsAI/boxcars/tree/v0.1.3) (2023-02-17)
|
4
44
|
|
5
45
|
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.2...v0.1.3)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
boxcars (0.1.
|
4
|
+
boxcars (0.1.6)
|
5
5
|
google_search_results (~> 2.2)
|
6
6
|
ruby-openai (~> 3.0)
|
7
7
|
|
@@ -56,6 +56,8 @@ 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.0.0)
|
60
|
+
faraday (~> 2.0)
|
59
61
|
fiber-local (1.0.0)
|
60
62
|
github_changelog_generator (1.16.4)
|
61
63
|
activesupport
|
@@ -171,6 +173,7 @@ DEPENDENCIES
|
|
171
173
|
boxcars!
|
172
174
|
debug (~> 1.1)
|
173
175
|
dotenv (~> 2.8)
|
176
|
+
faraday-retry (~> 2.0)
|
174
177
|
github_changelog_generator (~> 1.16)
|
175
178
|
rake (~> 13.0)
|
176
179
|
rspec (~> 3.2)
|
data/README.md
CHANGED
@@ -4,16 +4,16 @@
|
|
4
4
|
<a href="https://www.boxcars.ai">Website</a> |
|
5
5
|
<a href="https://www.boxcars.ai/roadmap">Roadmap</a> |
|
6
6
|
<a href="https://www.boxcars.ai/blog">Blog</a> |
|
7
|
-
<a href="https://
|
7
|
+
<a href="https://github.com/BoxcarsAI/boxcars/wiki">Documentation</a>
|
8
8
|
</h4>
|
9
9
|
|
10
10
|
<p align="center">
|
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 (including your concepts).
|
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
|
-
This
|
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
|
|
18
18
|
## Concepts
|
19
19
|
All of these concepts are in a module named Boxcars:
|
@@ -79,10 +79,11 @@ Note that since Openai is currently the most used Engine, if you do not pass in
|
|
79
79
|
calc = Boxcars::Calculator.new # just use the default Engine
|
80
80
|
puts calc.run "what is pi to the forth power divided by 22.1?"
|
81
81
|
```
|
82
|
+
You can change the default_engine with `Boxcars::configuration.default_engine = NewDefaultEngine`
|
82
83
|
### Boxcars currently implemmented
|
83
84
|
|
84
85
|
Here is what we have so far, but please put up a PR with your new ideas.
|
85
|
-
-
|
86
|
+
- GoogleSearch: uses the SERP API to do seaches
|
86
87
|
- Calculator: uses an Engine to generate ruby code to do math
|
87
88
|
- SQL: given an ActiveRecord connection, it will generate and run sql statments from a prompt.
|
88
89
|
|
data/boxcars.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["Francis Sullivan", "Tabrez Syed"]
|
9
9
|
spec.email = ["hi@boxcars.ai"]
|
10
10
|
|
11
|
-
spec.summary = "Boxcars
|
12
|
-
spec.description = "You simply give a number of
|
11
|
+
spec.summary = "Boxcars is a gem that enables you to create new systems with AI composability. Inspired by python langchain."
|
12
|
+
spec.description = "You simply set an OpenAI key, give a number of Boxcars to a Train, and magic ensues when you run it."
|
13
13
|
spec.homepage = "https://github.com/BoxcarsAI/boxcars"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = ">= 2.6.0"
|
@@ -7,26 +7,22 @@ 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, :input_key, :requested_models
|
10
|
+
attr_accessor :connection, :input_key, :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.
|
14
14
|
# @param models [Array<ActiveRecord::Model>] The models to use for this boxcar. Will use all if nil.
|
15
|
-
# @param
|
16
|
-
# @param
|
17
|
-
# @param kwargs [Hash] Any other keyword arguments
|
18
|
-
# :name, :description and :
|
19
|
-
def initialize(engine: nil, models: nil,
|
20
|
-
|
21
|
-
@requested_models = models
|
22
|
-
models.each do |m|
|
23
|
-
raise ArgumentError, "model #{m} needs to be an Active Record model" unless m.ancestors.include?(::ActiveRecord::Base)
|
24
|
-
end
|
25
|
-
elsif models
|
26
|
-
raise ArgumentError, "models needs to be an array of Active Record models"
|
27
|
-
end
|
15
|
+
# @param read_only [Boolean] Whether to use read only models. Defaults to true unless you pass an approval function.
|
16
|
+
# @param approval_callback [Proc] A function to call to approve changes. Defaults to nil.
|
17
|
+
# @param kwargs [Hash] Any other keyword arguments. These can include:
|
18
|
+
# :name, :description, :prompt, :input_key, :output_key and :except_models
|
19
|
+
def initialize(engine: nil, models: nil, read_only: nil, approval_callback: nil, **kwargs)
|
20
|
+
check_models(models)
|
28
21
|
@except_models = LOCKED_OUT_MODELS + kwargs[:except_models].to_a
|
29
|
-
@
|
22
|
+
@approval_callback = approval_callback
|
23
|
+
@read_only = read_only.nil? ? !approval_callback : read_only
|
24
|
+
@input_key = kwargs[:input_key] || :question
|
25
|
+
@output_key = kwargs[:output_key] || :answer
|
30
26
|
the_prompt = kwargs[prompt] || my_prompt
|
31
27
|
name = kwargs[:name] || "Data"
|
32
28
|
super(name: name,
|
@@ -60,6 +56,21 @@ module Boxcars
|
|
60
56
|
|
61
57
|
private
|
62
58
|
|
59
|
+
def read_only?
|
60
|
+
read_only
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_models(models)
|
64
|
+
if models.is_a?(Array) && models.length.positive?
|
65
|
+
@requested_models = models
|
66
|
+
models.each do |m|
|
67
|
+
raise ArgumentError, "model #{m} needs to be an Active Record model" unless m.ancestors.include?(::ActiveRecord::Base)
|
68
|
+
end
|
69
|
+
elsif models
|
70
|
+
raise ArgumentError, "models needs to be an array of Active Record models"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
63
74
|
def wanted_models
|
64
75
|
the_models = requested_models || ::ActiveRecord::Base.descendants
|
65
76
|
the_models.reject { |m| except_models.include?(m.name) }
|
@@ -75,21 +86,94 @@ module Boxcars
|
|
75
86
|
models.pretty_inspect
|
76
87
|
end
|
77
88
|
|
78
|
-
|
79
|
-
|
89
|
+
# to be safe, we wrap the code in a transaction and rollback
|
90
|
+
def rollback_after_running
|
91
|
+
rv = nil
|
92
|
+
::ActiveRecord::Base.transaction do
|
93
|
+
rv = yield
|
94
|
+
ensure
|
95
|
+
raise ::ActiveRecord::Rollback
|
96
|
+
end
|
97
|
+
rv
|
98
|
+
end
|
99
|
+
|
100
|
+
# check for dangerous code that is outside of ActiveRecord
|
101
|
+
def safe_to_run?(code)
|
102
|
+
bad_words = %w[commit drop_constraint drop_constraint! drop_extension drop_extension! drop_foreign_key drop_foreign_key! \
|
103
|
+
drop_index drop_index! drop_join_table drop_join_table! drop_materialized_view drop_materialized_view! \
|
104
|
+
drop_partition drop_partition! drop_schema drop_schema! drop_table drop_table! drop_trigger drop_trigger! \
|
105
|
+
drop_view drop_view! eval execute reset revoke rollback truncate].freeze
|
106
|
+
without_strings = code.gsub(/('([^'\\]*(\\.[^'\\]*)*)'|"([^"\\]*(\\.[^"\\]*)*"))/, 'XX')
|
107
|
+
word_list = without_strings.split(/[.,()]/)
|
108
|
+
|
109
|
+
bad_words.each do |w|
|
110
|
+
if word_list.include?(w)
|
111
|
+
puts "code included destructive instruction: #{w} #{code}".colorize(:red)
|
112
|
+
return false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def evaluate_input(code)
|
120
|
+
raise SecurityError, "Found unsafe code while evaluating: #{code}" unless safe_to_run?(code)
|
121
|
+
|
122
|
+
# rubocop:disable Security/Eval
|
123
|
+
eval code
|
124
|
+
# rubocop:enable Security/Eval
|
125
|
+
end
|
126
|
+
|
127
|
+
def change_count(changes_code)
|
128
|
+
return 0 unless changes_code
|
129
|
+
|
130
|
+
rollback_after_running do
|
131
|
+
puts "computing change count with: #{changes_code}".colorize(:yellow)
|
132
|
+
evaluate_input changes_code
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def approved?(changes_code, code)
|
137
|
+
# find out how many changes there are
|
138
|
+
changes = change_count(changes_code)
|
139
|
+
return true unless changes&.positive?
|
140
|
+
|
141
|
+
puts "Pending Changes: #{changes}".colorize(:yellow, style: :bold)
|
142
|
+
change_str = "#{changes} change#{'s' if changes.to_i > 1}"
|
143
|
+
raise SecurityError, "Can not run code that makes #{change_str} in read-only mode" if read_only?
|
144
|
+
|
145
|
+
return approval_callback.call(changes, code) if approval_callback.is_a?(Proc)
|
146
|
+
|
147
|
+
true
|
148
|
+
end
|
149
|
+
|
150
|
+
def run_active_record_code(code)
|
80
151
|
puts code.colorize(:yellow)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
rescue StandardError => e
|
88
|
-
"Error: #{e.message}"
|
152
|
+
if read_only?
|
153
|
+
rollback_after_running do
|
154
|
+
evaluate_input code
|
155
|
+
end
|
156
|
+
else
|
157
|
+
evaluate_input code
|
89
158
|
end
|
90
159
|
end
|
91
160
|
|
161
|
+
def get_active_record_answer(text)
|
162
|
+
code = text[/^ARCode: (.*)/, 1]
|
163
|
+
changes_code = text[/^ARChanges: (.*)/, 1]
|
164
|
+
raise SecurityError, "Permission to run code that makes changes denied" unless approved?(changes_code, code)
|
165
|
+
|
166
|
+
output = run_active_record_code(code)
|
167
|
+
output = 0 if output.is_a?(Array) && output.empty?
|
168
|
+
output = output.first if output.is_a?(Array) && output.length == 1
|
169
|
+
output = output[output.keys.first] if output.is_a?(Hash) && output.length == 1
|
170
|
+
"Answer: #{output.inspect}"
|
171
|
+
rescue StandardError => e
|
172
|
+
"Error: #{e.message}"
|
173
|
+
end
|
174
|
+
|
92
175
|
def get_answer(text)
|
176
|
+
# debugger
|
93
177
|
case text
|
94
178
|
when /^ARCode:/
|
95
179
|
get_active_record_answer(text)
|
@@ -114,6 +198,7 @@ module Boxcars
|
|
114
198
|
Use the following format:
|
115
199
|
Question: "Question here"
|
116
200
|
ARCode: "Active Record code to run"
|
201
|
+
ARChanges: "Active Record code to compute the number of records going to change" - Only add this line if the ARCode on the line before will make data changes
|
117
202
|
Answer: "Final answer here"
|
118
203
|
|
119
204
|
Only use the following Active Record models:
|
@@ -13,7 +13,7 @@ module Boxcars
|
|
13
13
|
# @param name [String] The name of the boxcar. Defaults to classname.
|
14
14
|
# @param description [String] A description of the boxcar. Defaults to SERPDESC.
|
15
15
|
# @param serpapi_api_key [String] The API key to use for the SerpAPI. Defaults to Boxcars.configuration.serpapi_api_key.
|
16
|
-
def initialize(name: "Search", description: SERPDESC, serpapi_api_key:
|
16
|
+
def initialize(name: "Search", description: SERPDESC, serpapi_api_key: nil)
|
17
17
|
super(name: name, description: description)
|
18
18
|
api_key = Boxcars.configuration.serpapi_api_key(serpapi_api_key: serpapi_api_key)
|
19
19
|
::GoogleSearch.api_key = api_key
|
@@ -38,7 +38,7 @@ module Boxcars
|
|
38
38
|
# @param openai_access_token [String] The access token to use when asking the engine.
|
39
39
|
# Defaults to Boxcars.configuration.openai_access_token.
|
40
40
|
# @param kwargs [Hash] Additional parameters to pass to the engine if wanted.
|
41
|
-
def client(prompt:, openai_access_token:
|
41
|
+
def client(prompt:, openai_access_token: nil, **kwargs)
|
42
42
|
access_token = Boxcars.configuration.openai_access_token(openai_access_token: openai_access_token)
|
43
43
|
organization_id = Boxcars.configuration.organization_id
|
44
44
|
clnt = ::OpenAI::Client.new(access_token: access_token, organization_id: organization_id)
|
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
|
-
puts "RubyREPL: #{code}".colorize(:
|
9
|
+
puts "RubyREPL: #{code}".colorize(: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
|
-
puts "Answer: #{output}".colorize(:
|
16
|
+
puts "Answer: #{output}".colorize(:yellow, style: :bold)
|
17
17
|
output
|
18
18
|
end
|
19
19
|
|
@@ -33,14 +33,15 @@ module Boxcars
|
|
33
33
|
# @param engine [Boxcars::Engine] The engine to use for this train.
|
34
34
|
# @param name [String] The name of the train. Defaults to 'Zero Shot'.
|
35
35
|
# @param description [String] The description of the train. Defaults to 'Zero Shot Train'.
|
36
|
-
|
36
|
+
# @param prompt [Boxcars::Prompt] The prompt to use. Defaults to the built-in prompt.
|
37
|
+
def initialize(boxcars:, engine: nil, name: 'Zero Shot', description: 'Zero Shot Train', prompt: nil)
|
37
38
|
@observation_prefix = 'Observation: '
|
38
39
|
@engine_prefix = 'Thought:'
|
39
|
-
prompt
|
40
|
+
prompt ||= self.class.create_prompt(boxcars: boxcars)
|
40
41
|
super(engine: engine, boxcars: boxcars, prompt: prompt, name: name, description: description)
|
41
42
|
end
|
42
43
|
|
43
|
-
# Create prompt in the style of the zero shot agent.
|
44
|
+
# Create prompt in the style of the zero shot agent. Without arguments, returns the default prompt.
|
44
45
|
# @param boxcars [Array<Boxcars::Boxcar>] List of boxcars the agent will have access to, used to format the prompt.
|
45
46
|
# @param prefix [String] String to put before the main prompt.
|
46
47
|
# @param suffix [String] String to put after the main prompt.
|
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
|
-
puts output.log.colorize(:yellow)
|
115
|
+
puts output.log.colorize(: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
|
data/lib/boxcars/version.rb
CHANGED
data/lib/boxcars.rb
CHANGED
@@ -16,6 +16,9 @@ module Boxcars
|
|
16
16
|
# Error class for all Boxcars value errors.
|
17
17
|
class ValueError < Error; end
|
18
18
|
|
19
|
+
# Error class for all Boxcars security errors.
|
20
|
+
class SecurityError < Error; end
|
21
|
+
|
19
22
|
# simple string colorization
|
20
23
|
class ::String
|
21
24
|
# colorize a string
|
@@ -65,7 +68,7 @@ module Boxcars
|
|
65
68
|
end
|
66
69
|
|
67
70
|
def key_lookup(key, kwargs)
|
68
|
-
rv = if kwargs.key?(key) && kwargs[key]
|
71
|
+
rv = if kwargs.key?(key) && !kwargs[key].nil?
|
69
72
|
# override with kwargs if present
|
70
73
|
kwargs[key]
|
71
74
|
elsif (set_val = instance_variable_get("@#{key}"))
|
@@ -74,7 +77,6 @@ module Boxcars
|
|
74
77
|
else
|
75
78
|
# otherwise, dig out of the environment
|
76
79
|
new_key = ENV.fetch(key.to_s.upcase, nil)
|
77
|
-
send("#{key}=", new_key) if new_key
|
78
80
|
new_key
|
79
81
|
end
|
80
82
|
check_key(key, rv)
|
@@ -104,6 +106,15 @@ module Boxcars
|
|
104
106
|
def self.engine
|
105
107
|
configuration.default_engine || Boxcars::Openai
|
106
108
|
end
|
109
|
+
|
110
|
+
# return a proc that will ask the user for input
|
111
|
+
def self.ask_user
|
112
|
+
proc do |changes, _code|
|
113
|
+
puts "This request will make #{changes} changes. Are you sure you want to run it? (y/[n])"
|
114
|
+
answer = gets.chomp
|
115
|
+
answer.downcase == 'y'
|
116
|
+
end
|
117
|
+
end
|
107
118
|
end
|
108
119
|
|
109
120
|
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.6
|
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-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debug
|
@@ -81,7 +81,8 @@ dependencies:
|
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '3.0'
|
84
|
-
description: You simply give a number of
|
84
|
+
description: You simply set an OpenAI key, give a number of Boxcars to a Train, and
|
85
|
+
magic ensues when you run it.
|
85
86
|
email:
|
86
87
|
- hi@boxcars.ai
|
87
88
|
executables: []
|
@@ -144,6 +145,6 @@ requirements: []
|
|
144
145
|
rubygems_version: 3.2.32
|
145
146
|
signing_key:
|
146
147
|
specification_version: 4
|
147
|
-
summary: Boxcars
|
148
|
+
summary: Boxcars is a gem that enables you to create new systems with AI composability.
|
148
149
|
Inspired by python langchain.
|
149
150
|
test_files: []
|