boxcars 0.1.6 → 0.1.7

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: 666f9b91451e8b6db61eb1162cb6508a566a46b676d646f1fb0000175714cede
4
- data.tar.gz: bee7b55be2288bf9361012bd562f88d91f64ce7972b0cf5c495a5fce87eea77c
3
+ metadata.gz: f8cfde09f23575d2b54249bd2b6d2c15697092b7c52db4d5f0546bb0113a3949
4
+ data.tar.gz: d557f681bf8a26251608b17038650868563da9d54753789c568331328cfa456b
5
5
  SHA512:
6
- metadata.gz: '0681f5bd00bbfe2afd67011d76c561640a0575a9656b20913d3c60193208ea33361003ee652d01802b49702b106bd4f3d5bc91c1c7f2023bc2e8224edca14a8d'
7
- data.tar.gz: 794fe20c2f9b8f14402db177bdee3d3e5318ab652e69c02929f9ae79d6daca096ae386a4e85730da4004927ab31aff53cc3a8d960ca661d6a5dd192432ffeabb
6
+ metadata.gz: db5af829a8a6e600cbb402e53aa7aaf1f9374725c5cf1d61ee908cb58ffda305b03ee00a4664e9f1134dc6887be9dab8c7877ad4c6b981e5a5665bfc1cfec2f3
7
+ data.tar.gz: 17930e46fa46b4d1a268367aff6d9b5851fac890f00d5a87e722ed3833552bb851614a5ab606cb9a08afce611a5882c9838094e31c1d34b9d17c05923656fc38
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.1.6)
4
+ boxcars (0.1.7)
5
5
  google_search_results (~> 2.2)
6
6
  ruby-openai (~> 3.0)
7
7
 
data/README.md CHANGED
@@ -17,10 +17,11 @@ This gem was inspired by the popular Python library Langchain. However, we wante
17
17
 
18
18
  ## Concepts
19
19
  All of these concepts are in a module named Boxcars:
20
- - Prompt: used by an Engine to generate text results.
21
- - Engine: an entity that generates text from a Prompt. OpenAI's LLM text generatory is the default Engine if no other is specified.
22
- - Boxcar: an encapsulation of a concept that performs something of interest (such as search, math, or SQL). A can use an Engine to do its work.
23
- - Train: Given a list of Boxcars and an optionally an Engine, a Train breaks down a problem into pieces for individual Boxcars to solve. The individual results are then combined until a final answer is found. ZeroShot is the only current implementation of Train, and you can either construct it directly or use `Boxcars::train` when you want to build a Train.
20
+
21
+ - Boxcar - an encapsulation that performs something of interest (such as search, math, SQL or an Active Record Query). A Boxcar can use an Engine (described below) to do its work.
22
+ - Train - Given a list of Boxcars and optionally an Engine, a Train breaks down a problem into pieces for individual Boxcars to solve. The individual results are then combined until a final answer is found. ZeroShot is the only current implementation of Train (but we are adding more soon), and you can either construct it directly or use `Boxcars::train` when you want to build a Train.
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
+ - Engine - an entity that generates text from a Prompt. OpenAI's LLM text generator is the default Engine if no other is specified.
24
25
 
25
26
  ## Installation
26
27
 
@@ -86,6 +87,7 @@ Here is what we have so far, but please put up a PR with your new ideas.
86
87
  - GoogleSearch: uses the SERP API to do seaches
87
88
  - Calculator: uses an Engine to generate ruby code to do math
88
89
  - SQL: given an ActiveRecord connection, it will generate and run sql statments from a prompt.
90
+ - ActiveRecord: given an ActiveRecord connection, it will generate and run ActiveRecord statements from a prompt.
89
91
 
90
92
  ### Run a list of Boxcars
91
93
  ```ruby
@@ -114,7 +116,18 @@ Final Answer: 201.06
114
116
  < Exiting Zero Shot#run
115
117
  201.06
116
118
  ```
119
+ ### More Examples
120
+ See [this](https://github.com/BoxcarsAI/boxcars/blob/main/notebooks/boxcars_examples.ipynb) Jupyter Notebook for more examples.
121
+
122
+ Note, some folks that we talked to didn't know that you could run Ruby Jupyter notebooks. [You can](https://github.com/SciRuby/iruby).
123
+
124
+ ### Logging
125
+ If you use this in a Rails application, or configure `Boxcars.configuration.logger = your_logger`, logging will go to your log file.
126
+
127
+ Also, if you set this flag: `Boxcars.configuration.lop_prompts = true`
128
+ The actual prompts handed to the connected Engine will be logged. This is off by default because it is very wordy, but handy if you are debugging prompts.
117
129
 
130
+ Otherwise, we print to standard out.
118
131
  ## Development
119
132
 
120
133
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -123,7 +136,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
123
136
 
124
137
  ## Contributing
125
138
 
126
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/boxcars. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/boxcars/blob/main/CODE_OF_CONDUCT.md).
139
+ Bug reports and pull requests are welcome on GitHub at https://github.com/BoxcarsAI/boxcars. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/BoxcarsAI/boxcars/blob/main/CODE_OF_CONDUCT.md).
127
140
 
128
141
  ## License
129
142
 
@@ -131,4 +144,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
131
144
 
132
145
  ## Code of Conduct
133
146
 
134
- Everyone interacting in the Boxcars project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/boxcars/blob/main/CODE_OF_CONDUCT.md).
147
+ Everyone interacting in the Boxcars project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/BoxcarsAI/boxcars/blob/main/CODE_OF_CONDUCT.md).
@@ -50,7 +50,7 @@ module Boxcars
50
50
  def call(inputs:)
51
51
  t = predict(question: inputs[input_key], top_k: 5, model_info: model_info, stop: ["Answer:"]).strip
52
52
  answer = get_answer(t)
53
- puts answer.colorize(:magenta)
53
+ Boxcars.info answer, :magenta
54
54
  { output_key => answer }
55
55
  end
56
56
 
@@ -108,7 +108,7 @@ module Boxcars
108
108
 
109
109
  bad_words.each do |w|
110
110
  if word_list.include?(w)
111
- puts "code included destructive instruction: #{w} #{code}".colorize(:red)
111
+ Boxcars.info "code included destructive instruction: #{w} #{code}", :red
112
112
  return false
113
113
  end
114
114
  end
@@ -128,7 +128,7 @@ module Boxcars
128
128
  return 0 unless changes_code
129
129
 
130
130
  rollback_after_running do
131
- puts "computing change count with: #{changes_code}".colorize(:yellow)
131
+ Boxcars.debug "computing change count with: #{changes_code}", :yellow
132
132
  evaluate_input changes_code
133
133
  end
134
134
  end
@@ -138,7 +138,7 @@ module Boxcars
138
138
  changes = change_count(changes_code)
139
139
  return true unless changes&.positive?
140
140
 
141
- puts "Pending Changes: #{changes}".colorize(:yellow, style: :bold)
141
+ Boxcars.debug "Pending Changes: #{changes}", :yellow, style: :bold
142
142
  change_str = "#{changes} change#{'s' if changes.to_i > 1}"
143
143
  raise SecurityError, "Can not run code that makes #{change_str} in read-only mode" if read_only?
144
144
 
@@ -148,7 +148,7 @@ module Boxcars
148
148
  end
149
149
 
150
150
  def run_active_record_code(code)
151
- puts code.colorize(:yellow)
151
+ Boxcars.debug code, :yellow
152
152
  if read_only?
153
153
  rollback_after_running do
154
154
  evaluate_input code
@@ -40,7 +40,7 @@ module Boxcars
40
40
  def call(inputs:)
41
41
  t = predict(question: inputs[input_key], stop: ["```output"]).strip
42
42
  answer = get_answer(t)
43
- puts answer.colorize(:magenta)
43
+ Boxcars.info answer, :magenta
44
44
  { output_key => answer }
45
45
  end
46
46
 
@@ -36,7 +36,7 @@ module Boxcars
36
36
  prompts = []
37
37
  input_list.each do |inputs|
38
38
  new_prompt = prompt.format(**inputs)
39
- puts "Prompt after formatting:\n#{new_prompt.colorize(:cyan)}" if Boxcars.configuration.log_prompts
39
+ Boxcars.debug("Prompt after formatting:\n#{new_prompt}", :cyan) if Boxcars.configuration.log_prompts
40
40
  prompts.push(new_prompt)
41
41
  end
42
42
  engine.generate(prompts: prompts, stop: stop)
@@ -44,7 +44,7 @@ module Boxcars
44
44
  def call(inputs:)
45
45
  t = predict(question: inputs[input_key], dialect: dialect, top_k: 5, table_info: schema, stop: ["Answer:"]).strip
46
46
  answer = get_answer(t)
47
- puts answer.colorize(:magenta)
47
+ Boxcars.debug answer, :magenta
48
48
  { output_key => answer }
49
49
  end
50
50
 
@@ -72,7 +72,7 @@ module Boxcars
72
72
 
73
73
  def get_embedded_sql_answer(text)
74
74
  code = text[/^SQLQuery: (.*)/, 1]
75
- puts code.colorize(:yellow)
75
+ Boxcars.debug code, :yellow
76
76
  output = connection.exec_query(code).to_a
77
77
  "Answer: #{output}"
78
78
  end
@@ -62,9 +62,9 @@ module Boxcars
62
62
  # you can pass one or the other, but not both.
63
63
  # @return [String] The answer to the question.
64
64
  def run(*args, **kwargs)
65
- puts "> Entering #{name}#run".colorize(:gray, style: :bold)
65
+ Boxcars.info "> Entering #{name}#run", :gray, style: :bold
66
66
  rv = do_run(*args, **kwargs)
67
- puts "< Exiting #{name}#run".colorize(:gray, style: :bold)
67
+ Boxcars.info "< Exiting #{name}#run", :gray, style: :bold
68
68
  rv
69
69
  end
70
70
 
@@ -77,7 +77,7 @@ module Boxcars
77
77
  begin
78
78
  output = call(inputs: inputs)
79
79
  rescue StandardError => e
80
- puts "Error in #{name} boxcar#call: #{e}".colorize(:red)
80
+ error "Error in #{name} boxcar#call: #{e}", :red
81
81
  raise e
82
82
  end
83
83
  validate_outputs(outputs: output.keys)
@@ -101,7 +101,7 @@ module Boxcars
101
101
 
102
102
  def our_inputs(inputs)
103
103
  if inputs.is_a?(String)
104
- puts inputs.colorize(:blue) # the question
104
+ Boxcars.info inputs, :blue # the question
105
105
  if input_keys.length != 1
106
106
  raise Boxcars::ArgumentError, "A single string input was passed in, but this boxcar expects " \
107
107
  "multiple inputs (#{input_keys}). When a boxcar expects " \
@@ -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(:yellow)
9
+ Boxcars.debug "RubyREPL: #{code}", :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(:yellow, style: :bold)
16
+ Boxcars.debug "Answer: #{output}", :yellow, style: :bold
17
17
  output
18
18
  end
19
19
 
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, style: :bold)
115
+ Boxcars.debug output.log, :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
@@ -198,14 +198,14 @@ module Boxcars
198
198
  observation = boxcar.run(output.boxcar_input)
199
199
  return_direct = boxcar.return_direct
200
200
  rescue StandardError => e
201
- puts "Error in #{boxcar.name} boxcar#call: #{e}".colorize(:red)
201
+ error "Error in #{boxcar.name} boxcar#call: #{e}", :red
202
202
  raise e
203
203
  end
204
204
  else
205
205
  observation = "#{output.boxcar} is not a valid boxcar, try another one."
206
206
  return_direct = false
207
207
  end
208
- puts "Observation: #{observation}".colorize(:green)
208
+ Boxcars.debug "Observation: #{observation}", :green
209
209
  intermediate_steps.append([output, observation])
210
210
  if return_direct
211
211
  output = TrainFinish.new({ return_values[0] => observation }, "")
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.1.6"
5
+ VERSION = "0.1.7"
6
6
  end
data/lib/boxcars.rb CHANGED
@@ -19,23 +19,6 @@ module Boxcars
19
19
  # Error class for all Boxcars security errors.
20
20
  class SecurityError < Error; end
21
21
 
22
- # simple string colorization
23
- class ::String
24
- # colorize a string
25
- # @param color [Symbol] The color to use.
26
- # @param options [Hash] The options to use.
27
- def colorize(color, options = {})
28
- background = options[:background] || options[:bg] || false
29
- style = options[:style]
30
- offsets = %i[gray red green yellow blue magenta cyan white]
31
- styles = %i[normal bold dark italic underline xx xx underline xx strikethrough]
32
- start = background ? 40 : 30
33
- color_code = start + (offsets.index(color) || 8)
34
- style_code = styles.index(style) || 0
35
- "\e[#{style_code};#{color_code}m#{self}\e[0m"
36
- end
37
- end
38
-
39
22
  # Configuration contains gem settings
40
23
  class Configuration
41
24
  attr_writer :openai_access_token, :serpapi_api_key
@@ -44,7 +27,6 @@ module Boxcars
44
27
  def initialize
45
28
  @organization_id = nil
46
29
  @logger = Rails.logger if defined?(Rails)
47
- @logger ||= Logger.new($stdout)
48
30
  @log_prompts = false
49
31
  end
50
32
 
@@ -115,6 +97,59 @@ module Boxcars
115
97
  answer.downcase == 'y'
116
98
  end
117
99
  end
100
+
101
+ # Return a logger, possibly if set.
102
+ def self.logger
103
+ Boxcars.configuration.logger
104
+ end
105
+
106
+ # Logging system
107
+ def self.debug(msg, color = nil, **options)
108
+ msg = colorize(msg.to_s, color, **options) if color
109
+ if logger
110
+ logger.debug(msg)
111
+ else
112
+ puts msg
113
+ end
114
+ end
115
+
116
+ def self.info(msg, color = nil, **options)
117
+ msg = colorize(msg.to_s, color, **options) if color
118
+ if logger
119
+ logger.info(msg)
120
+ else
121
+ puts msg
122
+ end
123
+ end
124
+
125
+ def self.warn(msg, color = nil, **options)
126
+ msg = colorize(msg.to_s, color, **options) if color
127
+ if logger
128
+ logger.warn(msg)
129
+ else
130
+ puts msg
131
+ end
132
+ end
133
+
134
+ def self.error(msg, color = nil, **options)
135
+ msg = colorize(msg.to_s, color, **options) if color
136
+ if logger
137
+ logger.error(msg)
138
+ else
139
+ puts msg
140
+ end
141
+ end
142
+
143
+ def self.colorize(str, color, **options)
144
+ background = options[:background] || options[:bg] || false
145
+ style = options[:style]
146
+ offsets = %i[gray red green yellow blue magenta cyan white]
147
+ styles = %i[normal bold dark italic underline xx xx underline xx strikethrough]
148
+ start = background ? 40 : 30
149
+ color_code = start + (offsets.index(color) || 8)
150
+ style_code = styles.index(style) || 0
151
+ "\e[#{style_code};#{color_code}m#{str}\e[0m"
152
+ end
118
153
  end
119
154
 
120
155
  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.6
4
+ version: 0.1.7
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-24 00:00:00.000000000 Z
12
+ date: 2023-02-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: debug