boxcars 0.2.15 → 0.2.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/lib/boxcars/boxcar/sql_active_record.rb +33 -0
- data/lib/boxcars/boxcar/{sql.rb → sql_base.rb} +25 -16
- data/lib/boxcars/boxcar/sql_sequel.rb +32 -0
- data/lib/boxcars/boxcar.rb +4 -2
- data/lib/boxcars/train/zero_shot.rb +2 -0
- data/lib/boxcars/train.rb +5 -5
- data/lib/boxcars/version.rb +1 -1
- data/lib/boxcars.rb +17 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92af5ad71c16886afc760bcc1b98a1d050486f8cf7010e0f05ee5015aa767710
|
4
|
+
data.tar.gz: a0e5f39d40f79d4e4e5e337c00657764b5f9b329ec6bcc0f5a6e4ea62b0675b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06123e9f6ea4cb294b258056b6e50af7654b2c4866b1585fb32e520eee4e64dcdecc7cf8dc4d019a42a3193b01be6a02fba8190ef52add4eb70ad7f1a79d492c
|
7
|
+
data.tar.gz: 3320ee8fdb68a4bf9601a6a584d64517242e0e80b14b812b6f04f3e0eaed05b5d520541e5547c72b7b3a8ebea044e7173163cafccc2cc8f9bb5c25b25a231ffe
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.2.15](https://github.com/BoxcarsAI/boxcars/tree/v0.2.15) (2023-06-09)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.14...v0.2.15)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- make suggested next actions optional [\#94](https://github.com/BoxcarsAI/boxcars/pull/94) ([francis](https://github.com/francis))
|
10
|
+
|
3
11
|
## [v0.2.14](https://github.com/BoxcarsAI/boxcars/tree/v0.2.14) (2023-06-06)
|
4
12
|
|
5
13
|
[Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.13...v0.2.14)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
<a href="https://github.com/BoxcarsAI/boxcars/blob/main/LICENSE.txt"><img src="https://img.shields.io/badge/license-MIT-informational" alt="License"></a>
|
11
11
|
</p>
|
12
12
|
|
13
|
-
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, Vector Search and more. This can even be extended with your concepts as well (including your concepts).
|
13
|
+
Boxcars is a gem that enables you to create new systems with AI composability, using various concepts such as OpenAI, Search, SQL (with both Sequel an Active Record support), Rails Active Record, Vector Search and more. This can even be extended with your concepts as well (including your concepts).
|
14
14
|
|
15
15
|
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.
|
16
16
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Boxcars is a framework for running a series of tools to get an answer to a question.
|
4
|
+
module Boxcars
|
5
|
+
# A Boxcar that interprets a prompt and executes SQL code using Active Record to get answers
|
6
|
+
class SQLActiveRecord < SQLBase
|
7
|
+
# @param connection [ActiveRecord::Connection] The SQL connection to use for this boxcar.
|
8
|
+
# @param tables [Array<String>] The tables to use for this boxcar. Will use all if nil.
|
9
|
+
# @param except_tables [Array<String>] The tables to exclude from this boxcar. Will exclude none if nil.
|
10
|
+
# @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
|
11
|
+
# :name, :description, :prompt, :top_k, :stop, and :engine
|
12
|
+
def initialize(connection: nil, tables: nil, except_tables: nil, **kwargs)
|
13
|
+
connection ||= ::ActiveRecord::Base.connection
|
14
|
+
super(connection: connection, tables: tables, except_tables: except_tables, **kwargs)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def table_schema(table)
|
20
|
+
["CREATE TABLE #{table} (",
|
21
|
+
connection&.columns(table)&.map { |c| " #{c.name} #{c.sql_type} #{c.null ? "NULL" : "NOT NULL"}" }&.join(",\n"),
|
22
|
+
");"].join("\n")
|
23
|
+
end
|
24
|
+
|
25
|
+
def dialect
|
26
|
+
connection.class.name.split("::").last.sub("Adapter", "")
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_output(code)
|
30
|
+
connection&.exec_query(code)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -3,19 +3,21 @@
|
|
3
3
|
# Boxcars is a framework for running a series of tools to get an answer to a question.
|
4
4
|
module Boxcars
|
5
5
|
# A Boxcar that interprets a prompt and executes SQL code to get answers
|
6
|
-
|
6
|
+
# Use one of the subclasses for ActiveRecord or Sequel
|
7
|
+
# @abstract
|
8
|
+
class SQLBase < EngineBoxcar
|
7
9
|
# the description of this engine boxcar
|
8
10
|
SQLDESC = "useful for when you need to query a database for %<name>s."
|
9
11
|
LOCKED_OUT_TABLES = %w[schema_migrations ar_internal_metadata].freeze
|
10
|
-
attr_accessor :connection
|
12
|
+
attr_accessor :connection, :the_tables
|
11
13
|
|
12
|
-
# @param connection [ActiveRecord::Connection] The SQL connection to use for this boxcar.
|
14
|
+
# @param connection [ActiveRecord::Connection] or [Sequel Object] The SQL connection to use for this boxcar.
|
13
15
|
# @param tables [Array<String>] The tables to use for this boxcar. Will use all if nil.
|
14
16
|
# @param except_tables [Array<String>] The tables to exclude from this boxcar. Will exclude none if nil.
|
15
17
|
# @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
|
16
18
|
# :name, :description, :prompt, :top_k, :stop, and :engine
|
17
19
|
def initialize(connection: nil, tables: nil, except_tables: nil, **kwargs)
|
18
|
-
@connection = connection
|
20
|
+
@connection = connection
|
19
21
|
check_tables(tables, except_tables)
|
20
22
|
kwargs[:name] ||= "Database"
|
21
23
|
kwargs[:description] ||= format(SQLDESC, name: name)
|
@@ -33,40 +35,47 @@ module Boxcars
|
|
33
35
|
private
|
34
36
|
|
35
37
|
def check_tables(rtables, exceptions)
|
38
|
+
requested_tables = nil
|
36
39
|
if rtables.is_a?(Array) && tables.length.positive?
|
37
|
-
|
40
|
+
requested_tables = rtables
|
38
41
|
all_tables = tables
|
39
42
|
rtables.each do |t|
|
40
|
-
raise ArgumentError, "table #{t}
|
43
|
+
raise ArgumentError, "table #{t} not found in database" unless all_tables.include?(t)
|
41
44
|
end
|
42
45
|
elsif rtables
|
43
46
|
raise ArgumentError, "tables needs to be an array of Strings"
|
44
47
|
else
|
45
|
-
|
48
|
+
requested_tables = tables.to_a
|
46
49
|
end
|
47
|
-
|
50
|
+
except_tables = LOCKED_OUT_TABLES + exceptions.to_a
|
51
|
+
@the_tables = requested_tables - except_tables
|
48
52
|
end
|
49
53
|
|
50
54
|
def tables
|
51
55
|
connection&.tables
|
52
56
|
end
|
53
57
|
|
58
|
+
# abstract method to get the prompt for this boxcar
|
54
59
|
def table_schema(table)
|
55
|
-
|
56
|
-
connection&.columns(table)&.map { |c| " #{c.name} #{c.sql_type} #{c.null ? "NULL" : "NOT NULL"}" }&.join(",\n"),
|
57
|
-
");"].join("\n")
|
60
|
+
raise NotImplementedError
|
58
61
|
end
|
59
62
|
|
60
63
|
def schema
|
61
|
-
|
62
|
-
wanted_tables.map(&method(:table_schema)).join("\n")
|
64
|
+
the_tables.map(&method(:table_schema)).join("\n")
|
63
65
|
end
|
64
66
|
|
67
|
+
# abstract method to get the prompt for this boxcar
|
65
68
|
def dialect
|
66
|
-
|
69
|
+
raise NotImplementedError
|
67
70
|
end
|
68
71
|
|
69
|
-
|
72
|
+
# abstract method to get the output for the last query
|
73
|
+
def get_output(code)
|
74
|
+
raise NotImplementedError
|
75
|
+
end
|
76
|
+
|
77
|
+
def clean_up_output(code)
|
78
|
+
output = get_output(code)
|
70
79
|
output = output.as_json if output.is_a?(::ActiveRecord::Result)
|
71
80
|
output = 0 if output.is_a?(Array) && output.empty?
|
72
81
|
output = output.first if output.is_a?(Array) && output.length == 1
|
@@ -79,7 +88,7 @@ module Boxcars
|
|
79
88
|
code = text[/^SQLQuery: (.*)/, 1]
|
80
89
|
code = extract_code text.split('SQLQuery:').last.strip
|
81
90
|
Boxcars.debug code, :yellow
|
82
|
-
output = clean_up_output(
|
91
|
+
output = clean_up_output(code)
|
83
92
|
Result.new(status: :ok, answer: output, explanation: "Answer: #{output.to_json}", code: code)
|
84
93
|
rescue StandardError => e
|
85
94
|
Result.new(status: :error, answer: nil, explanation: "Error: #{e.message}", code: code)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Boxcars is a framework for running a series of tools to get an answer to a question.
|
4
|
+
module Boxcars
|
5
|
+
# A Boxcar that interprets a prompt and executes Sequel SQL code to get answers
|
6
|
+
class SQLSequel < SQLBase
|
7
|
+
# @param connection [SEQUEL Database object] The Sequel connection to use for this boxcar.
|
8
|
+
# @param tables [Array<String>] The tables to use for this boxcar. Will use all if nil.
|
9
|
+
# @param except_tables [Array<String>] The tables to exclude from this boxcar. Will exclude none if nil.
|
10
|
+
# @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
|
11
|
+
# :name, :description, :prompt, :top_k, :stop, and :engine
|
12
|
+
def initialize(connection: nil, tables: nil, except_tables: nil, **kwargs)
|
13
|
+
super(connection: connection, tables: tables, except_tables: except_tables, **kwargs)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def table_schema(table)
|
19
|
+
["CREATE TABLE #{table} (",
|
20
|
+
connection&.schema(table)&.map { |c| " #{c[0]} #{c[1][:type]} #{c[1][:allow_null] ? "NULL" : "NOT NULL"}" }&.join(",\n"),
|
21
|
+
");"].join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def dialect
|
25
|
+
connection.database_type
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_output(code)
|
29
|
+
connection[code].all
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/boxcars/boxcar.rb
CHANGED
@@ -39,7 +39,7 @@ module Boxcars
|
|
39
39
|
# @param outputs [Array<String>] The output keys.
|
40
40
|
# @raise [RuntimeError] If the outputs are not the same.
|
41
41
|
def validate_outputs(outputs:)
|
42
|
-
return if outputs
|
42
|
+
return if (outputs - output_keys - ['log']).empty?
|
43
43
|
|
44
44
|
raise "Did not get output keys that were expected, got: #{outputs}. Expected: #{output_keys}"
|
45
45
|
end
|
@@ -153,7 +153,9 @@ require "boxcars/boxcar/engine_boxcar"
|
|
153
153
|
require "boxcars/boxcar/calculator"
|
154
154
|
require "boxcars/boxcar/google_search"
|
155
155
|
require "boxcars/boxcar/wikipedia_search"
|
156
|
-
require "boxcars/boxcar/
|
156
|
+
require "boxcars/boxcar/sql_base"
|
157
|
+
require "boxcars/boxcar/sql_active_record"
|
158
|
+
require "boxcars/boxcar/sql_sequel"
|
157
159
|
require "boxcars/boxcar/swagger"
|
158
160
|
require "boxcars/boxcar/active_record"
|
159
161
|
require "boxcars/vector_store"
|
@@ -63,7 +63,9 @@ module Boxcars
|
|
63
63
|
end
|
64
64
|
|
65
65
|
action = match[:action].strip
|
66
|
+
Boxcars.debug("Action: #{action}", :yellow)
|
66
67
|
action_input = match[:action_input].strip.delete_prefix('"').delete_suffix('"')
|
68
|
+
Boxcars.debug("Action Input: #{action_input}", :yellow)
|
67
69
|
[action, action_input]
|
68
70
|
end
|
69
71
|
end
|
data/lib/boxcars/train.rb
CHANGED
@@ -154,7 +154,7 @@ module Boxcars
|
|
154
154
|
def return_stopped_response(early_stopping_method, intermediate_steps, **kwargs)
|
155
155
|
case early_stopping_method
|
156
156
|
when "force"
|
157
|
-
TrainFinish({ output: "Agent stopped due to max iterations." }, "")
|
157
|
+
TrainFinish.new({ output: "Agent stopped due to max iterations." }, "")
|
158
158
|
when "generate"
|
159
159
|
thoughts = ""
|
160
160
|
intermediate_steps.each do |action, observation|
|
@@ -167,13 +167,13 @@ module Boxcars
|
|
167
167
|
full_output = predict(**full_inputs)
|
168
168
|
parsed_output = extract_boxcar_and_input(full_output)
|
169
169
|
if parsed_output.nil?
|
170
|
-
TrainFinish({ output: full_output }, full_output)
|
170
|
+
TrainFinish.new({ output: full_output }, full_output)
|
171
171
|
else
|
172
172
|
boxcar, boxcar_input = parsed_output
|
173
173
|
if boxcar == finish_boxcar_name
|
174
|
-
TrainFinish({ output: boxcar_input }, full_output)
|
174
|
+
TrainFinish.new({ output: boxcar_input }, full_output)
|
175
175
|
else
|
176
|
-
TrainFinish({ output: full_output }, full_output)
|
176
|
+
TrainFinish.new({ output: full_output }, full_output)
|
177
177
|
end
|
178
178
|
end
|
179
179
|
else
|
@@ -199,7 +199,7 @@ module Boxcars
|
|
199
199
|
rescue Boxcars::ConfigurationError, Boxcars::SecurityError => e
|
200
200
|
raise e
|
201
201
|
rescue StandardError => e
|
202
|
-
Boxcars.error "Error in #{boxcar.name} boxcar#call: #{e}", :red
|
202
|
+
Boxcars.error "Error in #{boxcar.name} boxcar#call: #{e}\nbt:#{caller[0..5].join("\n ")}", :red
|
203
203
|
observation = "Error - #{e}, correct and try again."
|
204
204
|
end
|
205
205
|
elsif output.boxcar == :error
|
data/lib/boxcars/version.rb
CHANGED
data/lib/boxcars.rb
CHANGED
@@ -107,10 +107,24 @@ module Boxcars
|
|
107
107
|
Boxcars.configuration.logger
|
108
108
|
end
|
109
109
|
|
110
|
+
# Keep a running log of log messages
|
111
|
+
def self.log
|
112
|
+
@log ||= []
|
113
|
+
@log
|
114
|
+
end
|
115
|
+
|
116
|
+
# Resets the log and return the old log
|
117
|
+
def self.take_log
|
118
|
+
logs = @log
|
119
|
+
@log = []
|
120
|
+
logs
|
121
|
+
end
|
122
|
+
|
110
123
|
# Logging system
|
111
124
|
# debug log
|
112
125
|
def self.debug(msg, color = nil, **options)
|
113
126
|
msg = colorize(msg.to_s, color, **options) if color
|
127
|
+
log << msg
|
114
128
|
if logger
|
115
129
|
logger.debug(msg)
|
116
130
|
else
|
@@ -121,6 +135,7 @@ module Boxcars
|
|
121
135
|
# info log
|
122
136
|
def self.info(msg, color = nil, **options)
|
123
137
|
msg = colorize(msg.to_s, color, **options) if color
|
138
|
+
log << msg
|
124
139
|
if logger
|
125
140
|
logger.info(msg)
|
126
141
|
else
|
@@ -131,6 +146,7 @@ module Boxcars
|
|
131
146
|
# warn log
|
132
147
|
def self.warn(msg, color = nil, **options)
|
133
148
|
msg = colorize(msg.to_s, color, **options) if color
|
149
|
+
log << msg
|
134
150
|
if logger
|
135
151
|
logger.warn(msg)
|
136
152
|
else
|
@@ -141,6 +157,7 @@ module Boxcars
|
|
141
157
|
# error log
|
142
158
|
def self.error(msg, color = nil, **options)
|
143
159
|
msg = colorize(msg.to_s, color, **options) if color
|
160
|
+
log << msg
|
144
161
|
if logger
|
145
162
|
logger.error(msg)
|
146
163
|
else
|
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.16
|
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-06-
|
12
|
+
date: 2023-06-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: google_search_results
|
@@ -109,7 +109,9 @@ files:
|
|
109
109
|
- lib/boxcars/boxcar/calculator.rb
|
110
110
|
- lib/boxcars/boxcar/engine_boxcar.rb
|
111
111
|
- lib/boxcars/boxcar/google_search.rb
|
112
|
-
- lib/boxcars/boxcar/
|
112
|
+
- lib/boxcars/boxcar/sql_active_record.rb
|
113
|
+
- lib/boxcars/boxcar/sql_base.rb
|
114
|
+
- lib/boxcars/boxcar/sql_sequel.rb
|
113
115
|
- lib/boxcars/boxcar/swagger.rb
|
114
116
|
- lib/boxcars/boxcar/vector_answer.rb
|
115
117
|
- lib/boxcars/boxcar/wikipedia_search.rb
|