boxcars 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fc42578d07a962cd91fbab547ba657842c131729070e57bcf7789aa1648aaee
4
- data.tar.gz: 3e3009663c45d58ee616d2d705c74f939b12922b24bceff74d231f5aeaff573d
3
+ metadata.gz: 8eb0adb30464688c5a68391ffb19a5d08188f87283d7cf3de28b12a30b927971
4
+ data.tar.gz: 94605b14f7974159d21eb2cf219b8d4695533761084fe784419e47a04843c6c2
5
5
  SHA512:
6
- metadata.gz: d5a2664fb2b4973b57e41f682a9b194394bc196ada00b9c8cf9106a7876b92065cd9f40ff270650219c282143f812241e16fc450a24f61567acaaf413f44150f
7
- data.tar.gz: 1d73a0c1519a3834852ffe7108e43e03460cc6e1b99dbcc9c16bf73c7824016d71628878b32394a33b548a0c84de7f9a41feefa1dc922628957eb0251269cd7a
6
+ metadata.gz: 4b92e4e8c98ada38bb4acf5ed5f0bd563bcfe2ab29d9781002a585799aba35cfa82992086484886563585ad0b7a9795691f9804ef4807b58aaa29c8bea88a761
7
+ data.tar.gz: e46f9a26fad0b0eb339868daef5bd4c05d750af08277f83b4edb15839a712553584223f056dc2aa403fccb8b5292a6ea57aadf12c04595f909d561f1fbbe2995
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.1.4](https://github.com/BoxcarsAI/boxcars/tree/v0.1.4) (2023-02-22)
4
+
5
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.3...v0.1.4)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Extend Sql concept to produce and run ActiveRecord code instead of SQL [\#9](https://github.com/BoxcarsAI/boxcars/issues/9)
10
+
11
+ **Merged pull requests:**
12
+
13
+ - first pass at an ActiveRecord boxcar [\#18](https://github.com/BoxcarsAI/boxcars/pull/18) ([francis](https://github.com/francis))
14
+ - change Boxcars::default\_train to Boxcars::train to improve code reada… [\#17](https://github.com/BoxcarsAI/boxcars/pull/17) ([francis](https://github.com/francis))
15
+ - rename class Serp to GoogleSearch [\#16](https://github.com/BoxcarsAI/boxcars/pull/16) ([francis](https://github.com/francis))
16
+ - Update README.md [\#15](https://github.com/BoxcarsAI/boxcars/pull/15) ([tabrez-syed](https://github.com/tabrez-syed))
17
+
3
18
  ## [v0.1.3](https://github.com/BoxcarsAI/boxcars/tree/v0.1.3) (2023-02-17)
4
19
 
5
20
  [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.2...v0.1.3)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.1.4)
4
+ boxcars (0.1.5)
5
5
  google_search_results (~> 2.2)
6
6
  ruby-openai (~> 3.0)
7
7
 
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://www.boxcars.ai/en/introduction/">Documentation</a>
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 work 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.
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
- - Search: uses the SERP API to do seaches
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 provide an API to connect together Boxcars and then conduct them. Inspired by python langchain."
12
- spec.description = "You simply give a number of boxcars to a train, and it does the magic."
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,20 @@ 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
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 input_key [Symbol] The key to use for the input. Defaults to :question.
16
- # @param output_key [Symbol] The key to use for the output. Defaults to :answer.
17
- # @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
18
- # :name, :description and :prompt
19
- def initialize(engine: nil, models: nil, input_key: :question, output_key: :answer, **kwargs)
20
- if models.is_a?(Array) && models.length.positive?
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.
16
+ # @param kwargs [Hash] Any other keyword arguments. These can include:
17
+ # :name, :description, :prompt, :input_key, :output_key and :except_models
18
+ def initialize(engine: nil, models: nil, read_only: true, **kwargs)
19
+ check_models(models)
28
20
  @except_models = LOCKED_OUT_MODELS + kwargs[:except_models].to_a
29
- @input_key = input_key
21
+ @read_only = read_only
22
+ @input_key = kwargs[:input_key] || :question
23
+ @output_key = kwargs[:output_key] || :answer
30
24
  the_prompt = kwargs[prompt] || my_prompt
31
25
  name = kwargs[:name] || "Data"
32
26
  super(name: name,
@@ -60,6 +54,21 @@ module Boxcars
60
54
 
61
55
  private
62
56
 
57
+ def read_only?
58
+ read_only
59
+ end
60
+
61
+ def check_models(models)
62
+ if models.is_a?(Array) && models.length.positive?
63
+ @requested_models = models
64
+ models.each do |m|
65
+ raise ArgumentError, "model #{m} needs to be an Active Record model" unless m.ancestors.include?(::ActiveRecord::Base)
66
+ end
67
+ elsif models
68
+ raise ArgumentError, "models needs to be an array of Active Record models"
69
+ end
70
+ end
71
+
63
72
  def wanted_models
64
73
  the_models = requested_models || ::ActiveRecord::Base.descendants
65
74
  the_models.reject { |m| except_models.include?(m.name) }
@@ -75,18 +84,49 @@ module Boxcars
75
84
  models.pretty_inspect
76
85
  end
77
86
 
87
+ # to be safe, we wrap the code in a transaction and rollback
88
+ # rubocop:disable Lint/SuppressedException
89
+ def wrap_in_transaction
90
+ ::ActiveRecord::Base.transaction do
91
+ yield
92
+ ensure
93
+ raise ::ActiveRecord::Rollback
94
+ end
95
+ rescue ::ActiveRecord::Rollback
96
+ end
97
+ # rubocop:enable Lint/SuppressedException
98
+
99
+ def safe_to_run?(code)
100
+ return true unless read_only?
101
+
102
+ bad_words = %w[delete delete_all destroy destroy_all update update_all upsert upsert_all create save insert drop alter
103
+ truncate revoke commit rollback reset execute].freeze
104
+ without_strings = code.gsub(/('([^'\\]*(\\.[^'\\]*)*)'|"([^"\\]*(\\.[^"\\]*)*"))/, 'XX')
105
+ word_list = without_strings.split(/[.,()]/)
106
+
107
+ bad_words.each do |w|
108
+ if word_list.include?(w)
109
+ puts "code included destructive instruction: #{w} #{code}"
110
+ return false
111
+ end
112
+ end
113
+
114
+ true
115
+ end
116
+
78
117
  def get_active_record_answer(text)
79
118
  code = text[/^ARCode: (.*)/, 1]
80
119
  puts code.colorize(:yellow)
81
- begin
82
- # rubocop:disable Security/Eval
83
- output = eval code
84
- # rubocop:enable Security/Eval
85
- output = output.first if output.is_a?(Array) && output.length == 1
86
- "Answer: #{output.inspect}"
87
- rescue StandardError => e
88
- "Error: #{e.message}"
89
- end
120
+ raise SecurityError, "Can not run code that makes changes in read-only mode" unless safe_to_run?(code)
121
+
122
+ # rubocop:disable Security/Eval
123
+ output = eval code
124
+ # rubocop:enable Security/Eval
125
+ output = 0 if output.is_a?(Array) && output.empty?
126
+ output = output.first if output.is_a?(Array) && output.length == 1
127
+ "Answer: #{output.inspect}"
128
+ rescue StandardError => e
129
+ "Error: #{e.message}"
90
130
  end
91
131
 
92
132
  def get_answer(text)
@@ -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
- def initialize(boxcars:, engine: nil, name: 'Zero Shot', description: 'Zero Shot Train')
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 = self.class.create_prompt(boxcars: boxcars)
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.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.1.4"
5
+ VERSION = "0.1.5"
6
6
  end
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
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
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francis Sullivan
@@ -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 boxcars to a train, and it does the magic.
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 provide an API to connect together Boxcars and then conduct them.
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: []