boxcars 0.2.14 → 0.2.16

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: 1039f86c58712c10143cc26438da14571013081bcff08aed3d9fcac4b1e84060
4
- data.tar.gz: fca9f08855cae8e4e8a4171c043e92884e245582b049b4cc75bfa4d2cd98e51a
3
+ metadata.gz: 92af5ad71c16886afc760bcc1b98a1d050486f8cf7010e0f05ee5015aa767710
4
+ data.tar.gz: a0e5f39d40f79d4e4e5e337c00657764b5f9b329ec6bcc0f5a6e4ea62b0675b5
5
5
  SHA512:
6
- metadata.gz: 5f24a578f4004d99a0d71c05f4a0ed520dda2e562d1327ec68bdf8682a927b741e870ed348a1a273319003f515521ef8a12c8778b70994764a945e31f906f807
7
- data.tar.gz: be067a2ba1ba2e032a58d32f46071f5510a5ce0f15fbc89a06fe84f4fd854ec825a0ea0c786a90ba87c20bb1893ff59512170ac2e338080e25f85b2474b6fb64
6
+ metadata.gz: 06123e9f6ea4cb294b258056b6e50af7654b2c4866b1585fb32e520eee4e64dcdecc7cf8dc4d019a42a3193b01be6a02fba8190ef52add4eb70ad7f1a79d492c
7
+ data.tar.gz: 3320ee8fdb68a4bf9601a6a584d64517242e0e80b14b812b6f04f3e0eaed05b5d520541e5547c72b7b3a8ebea044e7173163cafccc2cc8f9bb5c25b25a231ffe
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
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
+
11
+ ## [v0.2.14](https://github.com/BoxcarsAI/boxcars/tree/v0.2.14) (2023-06-06)
12
+
13
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.13...v0.2.14)
14
+
15
+ **Closed issues:**
16
+
17
+ - VectorAnswer always return error "Query must a string" [\#90](https://github.com/BoxcarsAI/boxcars/issues/90)
18
+ - Readme vector search example 404 [\#86](https://github.com/BoxcarsAI/boxcars/issues/86)
19
+ - Add Boxcar similar to LLMChain [\#85](https://github.com/BoxcarsAI/boxcars/issues/85)
20
+
21
+ **Merged pull requests:**
22
+
23
+ - Chore/refactored vector stores [\#92](https://github.com/BoxcarsAI/boxcars/pull/92) ([jaigouk](https://github.com/jaigouk))
24
+ - Fix the issue of calling the wrong method in vector\_answer.rb. [\#91](https://github.com/BoxcarsAI/boxcars/pull/91) ([xleotranx](https://github.com/xleotranx))
25
+ - issue\_83 Fix readme 404 [\#87](https://github.com/BoxcarsAI/boxcars/pull/87) ([beouk](https://github.com/beouk))
26
+
3
27
  ## [v0.2.13](https://github.com/BoxcarsAI/boxcars/tree/v0.2.13) (2023-05-24)
4
28
 
5
29
  [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.12...v0.2.13)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.2.14)
4
+ boxcars (0.2.16)
5
5
  google_search_results (~> 2.2)
6
6
  gpt4all (~> 0.0.4)
7
7
  hnswlib (~> 0.8)
@@ -88,9 +88,11 @@ GEM
88
88
  i18n (1.13.0)
89
89
  concurrent-ruby (~> 1.0)
90
90
  io-console (0.6.0)
91
+ io-console (0.6.0-java)
91
92
  irb (1.6.4)
92
93
  reline (>= 0.3.0)
93
94
  json (2.6.3)
95
+ json (2.6.3-java)
94
96
  mime-types (3.4.1)
95
97
  mime-types-data (~> 3.2015)
96
98
  mime-types-data (3.2023.0218.1)
@@ -100,6 +102,7 @@ GEM
100
102
  multipart-post (2.3.0)
101
103
  netrc (0.11.0)
102
104
  nio4r (2.5.9)
105
+ nio4r (2.5.9-java)
103
106
  octokit (4.25.1)
104
107
  faraday (>= 1, < 3)
105
108
  sawyer (~> 0.9)
@@ -173,6 +176,9 @@ GEM
173
176
  faraday (>= 0.17.3, < 3)
174
177
  sqlite3 (1.6.3)
175
178
  mini_portile2 (~> 2.8.0)
179
+ sqlite3 (1.6.3-arm64-darwin)
180
+ sqlite3 (1.6.3-x86_64-darwin)
181
+ sqlite3 (1.6.3-x86_64-linux)
176
182
  strings-ansi (0.2.0)
177
183
  timers (4.3.5)
178
184
  traces (0.9.1)
@@ -187,6 +193,7 @@ GEM
187
193
  concurrent-ruby (~> 1.0)
188
194
  unf (0.1.4)
189
195
  unf_ext
196
+ unf (0.1.4-java)
190
197
  unf_ext (0.0.8.2)
191
198
  unicode-display_width (2.4.2)
192
199
  vcr (6.1.0)
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
 
@@ -56,7 +56,7 @@ module Boxcars
56
56
  def apply(input_list:, current_conversation: nil)
57
57
  response = generate(input_list: input_list, current_conversation: current_conversation)
58
58
  response.generations.to_h do |generation|
59
- [output_keys.first, generation[0].text]
59
+ [output_key, generation[0].text]
60
60
  end
61
61
  end
62
62
 
@@ -65,7 +65,7 @@ module Boxcars
65
65
  # @param kwargs [Hash] A hash of input values to use for the prompt.
66
66
  # @return [String] The output value.
67
67
  def predict(current_conversation: nil, **kwargs)
68
- prediction = apply(current_conversation: current_conversation, input_list: [kwargs])[output_keys.first]
68
+ prediction = apply(current_conversation: current_conversation, input_list: [kwargs])[output_key]
69
69
  Boxcars.debug(prediction, :white) if Boxcars.configuration.log_generated
70
70
  prediction
71
71
  end
@@ -95,7 +95,7 @@ module Boxcars
95
95
  conversation.add_user(answer.answer)
96
96
  else
97
97
  Boxcars.debug answer.to_json, :magenta
98
- return { output_keys.first => answer }
98
+ return { output_key => answer }
99
99
  end
100
100
  end
101
101
  Boxcars.error answer.to_json, :red
@@ -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
- class SQL < EngineBoxcar
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 || ::ActiveRecord::Base.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
- @requested_tables = rtables
40
+ requested_tables = rtables
38
41
  all_tables = tables
39
42
  rtables.each do |t|
40
- raise ArgumentError, "table #{t} needs to be an Active Record model" unless all_tables.include?(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
- @requested_tables = tables
48
+ requested_tables = tables.to_a
46
49
  end
47
- @except_models = LOCKED_OUT_TABLES + exceptions.to_a
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
- ["CREATE TABLE #{table} (",
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
- wanted_tables = @requested_tables - @except_models
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
- connection.class.name.split("::").last.sub("Adapter", "")
69
+ raise NotImplementedError
67
70
  end
68
71
 
69
- def clean_up_output(output)
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(connection.exec_query(code))
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
@@ -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.sort == output_keys.sort
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/sql"
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"
@@ -5,22 +5,25 @@ module Boxcars
5
5
  # A Train using the zero-shot react method.
6
6
  class ZeroShot < Train
7
7
  attr_reader :boxcars, :observation_prefix, :engine_prefix
8
+ attr_accessor :wants_next_actions
8
9
 
9
10
  # @param boxcars [Array<Boxcars::Boxcar>] The boxcars to run.
10
11
  # @param engine [Boxcars::Engine] The engine to use for this train.
11
12
  # @param name [String] The name of the train. Defaults to 'Zero Shot'.
12
13
  # @param description [String] The description of the train. Defaults to 'Zero Shot Train'.
13
14
  # @param prompt [Boxcars::Prompt] The prompt to use. Defaults to the built-in prompt.
14
- def initialize(boxcars:, engine: nil, name: 'Zero Shot', description: 'Zero Shot Train', prompt: nil)
15
+ # @param kwargs [Hash] Additional arguments to pass to the train. wants_next_actions: true
16
+ def initialize(boxcars:, engine: nil, name: 'Zero Shot', description: 'Zero Shot Train', prompt: nil, **kwargs)
15
17
  @observation_prefix = 'Observation: '
16
18
  @engine_prefix = 'Thought:'
19
+ @wants_next_actions = kwargs.fetch(:wants_next_actions, false)
17
20
  prompt ||= my_prompt
18
21
  super(engine: engine, boxcars: boxcars, prompt: prompt, name: name, description: description)
19
22
  end
20
23
 
21
24
  # @return Hash The additional variables for this boxcar.
22
25
  def prediction_additional(_inputs)
23
- { boxcar_names: boxcar_names, boxcar_descriptions: boxcar_descriptions }.merge super
26
+ { boxcar_names: boxcar_names, boxcar_descriptions: boxcar_descriptions, next_actions: next_actions }.merge super
24
27
  end
25
28
 
26
29
  # Extract the boxcar and input from the engine output.
@@ -60,7 +63,9 @@ module Boxcars
60
63
  end
61
64
 
62
65
  action = match[:action].strip
66
+ Boxcars.debug("Action: #{action}", :yellow)
63
67
  action_input = match[:action_input].strip.delete_prefix('"').delete_suffix('"')
68
+ Boxcars.debug("Action Input: #{action_input}", :yellow)
64
69
  [action, action_input]
65
70
  end
66
71
  end
@@ -72,13 +77,14 @@ module Boxcars
72
77
  "Question: the input question you must answer\n",
73
78
  "Thought: you should always think about what to do\n",
74
79
  "Action: the action to take, should be one from this list: %<boxcar_names>s\n",
75
- "Action Input: the input to the action\n",
80
+ "Action Input: an input question to the action\n",
76
81
  "Observation: the result of the action\n",
77
82
  "... (this Thought/Action/Action Input/Observation sequence can repeat N times)\n",
78
83
  "Thought: I know the final answer\n",
79
84
  "Final Answer: the final answer to the original input question\n",
80
- "Next Actions: Up to 3 logical suggested next questions for the user to ask after getting this answer.\n",
85
+ "%<next_actions>s\n",
81
86
  "Remember to start a line with \"Final Answer:\" to give me the final answer.\n",
87
+ "Also make sure to specify a question for the Action Input.\n",
82
88
  "Begin!"),
83
89
  user("Question: %<input>s"),
84
90
  assi("Thought: %<agent_scratchpad>s")
@@ -92,13 +98,21 @@ module Boxcars
92
98
  @boxcar_descriptions ||= boxcars.map { |boxcar| "#{boxcar.name}: #{boxcar.description}" }.join("\n")
93
99
  end
94
100
 
101
+ def next_actions
102
+ if wants_next_actions
103
+ "Next Actions: Up to 3 logical suggested next questions for the user to ask after getting this answer.\n"
104
+ else
105
+ ""
106
+ end
107
+ end
108
+
95
109
  # The prompt to use for the train.
96
110
  def my_prompt
97
111
  @conversation ||= Conversation.new(lines: CTEMPLATE)
98
112
  @my_prompt ||= ConversationPrompt.new(
99
113
  conversation: @conversation,
100
114
  input_variables: [:input],
101
- other_inputs: [:boxcar_names, :boxcar_descriptions, :agent_scratchpad],
115
+ other_inputs: [:boxcar_names, :boxcar_descriptions, :next_actions, :agent_scratchpad],
102
116
  output_variables: [:answer])
103
117
  end
104
118
  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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.2.14"
5
+ VERSION = "0.2.16"
6
6
  end
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.14
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-06 00:00:00.000000000 Z
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
@@ -92,6 +92,7 @@ files:
92
92
  - ".env_sample"
93
93
  - ".rspec"
94
94
  - ".rubocop.yml"
95
+ - ".ruby-version"
95
96
  - CHANGELOG.md
96
97
  - CODE_OF_CONDUCT.md
97
98
  - Gemfile
@@ -108,7 +109,9 @@ files:
108
109
  - lib/boxcars/boxcar/calculator.rb
109
110
  - lib/boxcars/boxcar/engine_boxcar.rb
110
111
  - lib/boxcars/boxcar/google_search.rb
111
- - lib/boxcars/boxcar/sql.rb
112
+ - lib/boxcars/boxcar/sql_active_record.rb
113
+ - lib/boxcars/boxcar/sql_base.rb
114
+ - lib/boxcars/boxcar/sql_sequel.rb
112
115
  - lib/boxcars/boxcar/swagger.rb
113
116
  - lib/boxcars/boxcar/vector_answer.rb
114
117
  - lib/boxcars/boxcar/wikipedia_search.rb
@@ -167,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
170
  - !ruby/object:Gem::Version
168
171
  version: '0'
169
172
  requirements: []
170
- rubygems_version: 3.2.32
173
+ rubygems_version: 3.4.10
171
174
  signing_key:
172
175
  specification_version: 4
173
176
  summary: Boxcars is a gem that enables you to create new systems with AI composability.