boxcars 0.1.1 → 0.1.3

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: 6ffc107602f3a2ae47bdb5b5410f6abc8c2ea2c3a13f350630e4d3ece92768f7
4
- data.tar.gz: d4fcc08768de5f512a15bf0ca929d50b2a7296937c27c0f1b390189bf0956782
3
+ metadata.gz: 1d5ae0de1aba4038723912365d6ac1d50da0e34a40aad508130ecc86e71f91de
4
+ data.tar.gz: e0430621516358a59b7eeaadfa44fe78eed0b3ef92d929c264435d0946dbce5e
5
5
  SHA512:
6
- metadata.gz: b89e78ce7fec471c8c4f03d32a37bdffd5d1371ac474d14f7e1c90e653ccce62b30a95a845c5c1c1013f5a028c00bb3c87c35cc1394add0554bb33e232b6de63
7
- data.tar.gz: 9007dcd0b5dfc8d3b886579cd563b085c04c471ffc3bb167f5d80adb74f85ceacdd5d40942f1772bd59ce4da5903deec2262420e28eba430394d0f831d6cd751
6
+ metadata.gz: a054ad6b3b3caef1c6bb39fbd9a667779c4dc638800e905b717285b8f5dde4a578e3325373176b099af9f4f7d07b6f7803f87fe67c4f771bfce8894ef209188e
7
+ data.tar.gz: f3a3020bd0c393c25cd430b8702e26a7a39b87127372787c416042da63c6fb90826773ca81a456ba67a1add908790620757d8b9aab4db32269e3391d576c5ea5
data/CHANGELOG.md CHANGED
@@ -1,7 +1,36 @@
1
- ## [0.1.1] - 2o23-02-16
2
- Changed
3
- - updated to OpenAI gem 3.0
1
+ # Changelog
4
2
 
5
- ## [0.1.0] - 2023-02-15
3
+ ## [Unreleased](https://github.com/BoxcarsAI/boxcars/tree/HEAD)
6
4
 
7
- - Initial release
5
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.2...HEAD)
6
+
7
+ **Closed issues:**
8
+
9
+ - Make sure the yard docs are up to date and have coverage [\#7](https://github.com/BoxcarsAI/boxcars/issues/7)
10
+ - Specs need environment variables to be set to run green [\#4](https://github.com/BoxcarsAI/boxcars/issues/4)
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Get GitHub Actions to green [\#5](https://github.com/BoxcarsAI/boxcars/pull/5) ([petergoldstein](https://github.com/petergoldstein))
15
+ - Fix typo introduced by merge. Pull publish-rubygem into its own job [\#3](https://github.com/BoxcarsAI/boxcars/pull/3) ([petergoldstein](https://github.com/petergoldstein))
16
+
17
+ ## [v0.1.2](https://github.com/BoxcarsAI/boxcars/tree/v0.1.2) (2023-02-17)
18
+
19
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.1...v0.1.2)
20
+
21
+ **Merged pull requests:**
22
+
23
+ - Run GitHub Actions against multiple Rubies [\#2](https://github.com/BoxcarsAI/boxcars/pull/2) ([petergoldstein](https://github.com/petergoldstein))
24
+ - \[infra\] Added deployment step for the RubyGems [\#1](https://github.com/BoxcarsAI/boxcars/pull/1) ([AKovtunov](https://github.com/AKovtunov))
25
+
26
+ ## [v0.1.1](https://github.com/BoxcarsAI/boxcars/tree/v0.1.1) (2023-02-16)
27
+
28
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.0...v0.1.1)
29
+
30
+ ## [v0.1.0](https://github.com/BoxcarsAI/boxcars/tree/v0.1.0) (2023-02-15)
31
+
32
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/e3c50bdc76f71c6d2abb012c38174633a5847028...v0.1.0)
33
+
34
+
35
+
36
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
data/Gemfile CHANGED
@@ -22,3 +22,5 @@ gem "rubocop-rspec", "~> 2.17"
22
22
  gem "sqlite3", "~> 1.6"
23
23
 
24
24
  gem "activerecord", "~> 7.0"
25
+
26
+ gem "github_changelog_generator", "~> 1.16"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.1.1)
4
+ boxcars (0.1.3)
5
5
  google_search_results (~> 2.2)
6
6
  ruby-openai (~> 3.0)
7
7
 
@@ -21,7 +21,28 @@ GEM
21
21
  addressable (2.8.1)
22
22
  public_suffix (>= 2.0.2, < 6.0)
23
23
  ast (2.4.2)
24
+ async (1.30.3)
25
+ console (~> 1.10)
26
+ nio4r (~> 2.3)
27
+ timers (~> 4.1)
28
+ async-http (0.60.1)
29
+ async (>= 1.25)
30
+ async-io (>= 1.28)
31
+ async-pool (>= 0.2)
32
+ protocol-http (~> 0.24.0)
33
+ protocol-http1 (~> 0.15.0)
34
+ protocol-http2 (~> 0.15.0)
35
+ traces (>= 0.8.0)
36
+ async-http-faraday (0.11.0)
37
+ async-http (~> 0.42)
38
+ faraday
39
+ async-io (1.34.3)
40
+ async
41
+ async-pool (0.3.12)
42
+ async (>= 1.25)
24
43
  concurrent-ruby (1.2.0)
44
+ console (1.16.2)
45
+ fiber-local
25
46
  crack (0.4.5)
26
47
  rexml
27
48
  debug (1.7.1)
@@ -29,6 +50,22 @@ GEM
29
50
  reline (>= 0.3.1)
30
51
  diff-lcs (1.5.0)
31
52
  dotenv (2.8.1)
53
+ faraday (2.7.4)
54
+ faraday-net_http (>= 2.0, < 3.1)
55
+ ruby2_keywords (>= 0.0.4)
56
+ faraday-http-cache (2.4.1)
57
+ faraday (>= 0.8)
58
+ faraday-net_http (3.0.2)
59
+ fiber-local (1.0.0)
60
+ github_changelog_generator (1.16.4)
61
+ activesupport
62
+ async (>= 1.25.0)
63
+ async-http-faraday
64
+ faraday-http-cache
65
+ multi_json
66
+ octokit (~> 4.6)
67
+ rainbow (>= 2.2.1)
68
+ rake (>= 10.0)
32
69
  google_search_results (2.2.0)
33
70
  hashdiff (1.0.1)
34
71
  httparty (0.21.0)
@@ -37,15 +74,31 @@ GEM
37
74
  i18n (1.12.0)
38
75
  concurrent-ruby (~> 1.0)
39
76
  io-console (0.6.0)
77
+ io-console (0.6.0-java)
40
78
  irb (1.6.2)
41
79
  reline (>= 0.3.0)
42
80
  json (2.6.3)
81
+ json (2.6.3-java)
43
82
  mini_mime (1.1.2)
83
+ mini_portile2 (2.8.1)
44
84
  minitest (5.17.0)
85
+ multi_json (1.15.0)
45
86
  multi_xml (0.6.0)
87
+ nio4r (2.5.8)
88
+ nio4r (2.5.8-java)
89
+ octokit (4.25.1)
90
+ faraday (>= 1, < 3)
91
+ sawyer (~> 0.9)
46
92
  parallel (1.22.1)
47
93
  parser (3.2.1.0)
48
94
  ast (~> 2.4.1)
95
+ protocol-hpack (1.4.2)
96
+ protocol-http (0.24.1)
97
+ protocol-http1 (0.15.0)
98
+ protocol-http (~> 0.22)
99
+ protocol-http2 (0.15.1)
100
+ protocol-hpack (~> 1.4)
101
+ protocol-http (~> 0.18)
49
102
  public_suffix (5.0.1)
50
103
  rainbow (3.1.1)
51
104
  rake (13.0.6)
@@ -88,7 +141,16 @@ GEM
88
141
  ruby-openai (3.3.0)
89
142
  httparty (>= 0.18.1)
90
143
  ruby-progressbar (1.11.0)
144
+ ruby2_keywords (0.0.5)
145
+ sawyer (0.9.2)
146
+ addressable (>= 2.3.5)
147
+ faraday (>= 0.17.3, < 3)
148
+ sqlite3 (1.6.0)
149
+ mini_portile2 (~> 2.8.0)
91
150
  sqlite3 (1.6.0-x86_64-darwin)
151
+ sqlite3 (1.6.0-x86_64-linux)
152
+ timers (4.3.5)
153
+ traces (0.8.0)
92
154
  tzinfo (2.0.6)
93
155
  concurrent-ruby (~> 1.0)
94
156
  unicode-display_width (2.4.2)
@@ -99,13 +161,17 @@ GEM
99
161
  hashdiff (>= 0.4.0, < 2.0.0)
100
162
 
101
163
  PLATFORMS
164
+ universal-java-11
102
165
  x86_64-darwin-21
166
+ x86_64-darwin-22
167
+ x86_64-linux
103
168
 
104
169
  DEPENDENCIES
105
170
  activerecord (~> 7.0)
106
171
  boxcars!
107
172
  debug (~> 1.1)
108
173
  dotenv (~> 2.8)
174
+ github_changelog_generator (~> 1.16)
109
175
  rake (~> 13.0)
110
176
  rspec (~> 3.2)
111
177
  rubocop (~> 1.21)
@@ -116,4 +182,4 @@ DEPENDENCIES
116
182
  webmock (~> 3.18.1)
117
183
 
118
184
  BUNDLED WITH
119
- 2.2.32
185
+ 2.4.7
data/README.md CHANGED
@@ -1,8 +1,15 @@
1
1
  # Boxcars
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/boxcars`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ This gem lets you compose new systems with composability using systems like OpenAI, Search, and SQL (and many more).
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ The popular python library langchain is a precursor to this work, but we wanted to put a ruby spin on things and make it easier to get started.
6
+
7
+ ## Concepts
8
+ All of these concepts are in a module named Boxcars:
9
+ - Prompt: a prompt is used by an Engine to generate text results.
10
+ - Engine: an entity that generates text from a Prompt.
11
+ - Boxcar: an encapsulation that does one thing (search, math, SQL, etc) and a many use an Engine to get their work accomplished.cc
12
+ - Train: given a list of Boxcars and an Engine, an answer if found by breaking the promlem into peices for an indvidual Boxcar to solve. This is all joined back together to yield a final result. There is currently only one implementation - ZeroShot. You can construct it directly, or just use `Boxcars::default_train`
6
13
 
7
14
  ## Installation
8
15
 
@@ -22,7 +29,74 @@ Or install it yourself as:
22
29
 
23
30
  ## Usage
24
31
 
25
- TODO: Write usage instructions here
32
+ We will document many more examples soon, but here are a couple. The first step is to setup your environment variables for OpenAI and Serp (OPENAI_ACCESS_TOKEN, SERPAPI_API_KEY). If you don't want to set them in your environment, they can be passed in as well to the API.
33
+
34
+ In the examples below, we added one rubygem to load the environment at the first line, but depending on what you want, you might not need this.
35
+ ```ruby
36
+ require "dotenv/load"
37
+ require "boxcars"
38
+ ```
39
+
40
+ ### Direct Boxcar Use
41
+
42
+ ```ruby
43
+ # run the calculator
44
+ engine = Boxcars::Openai.new(max_tokens: 256)
45
+ calc = Boxcars::Calculator.new(engine: engine)
46
+ puts calc.run "what is pi to the forth power divided by 22.1?"
47
+ ```
48
+ This segment above (try it by pasting into an `irb` session)
49
+ ```text
50
+ > Entering Calculator#run
51
+ what is pi to the forth power divided by 22.1?
52
+ RubyREPL: puts(Math::PI**4 / 22.1)
53
+ Answer: 4.407651178009159
54
+
55
+ 4.407651178009159
56
+ < Exiting Calculator#run
57
+ 4.407651178009159
58
+ ```
59
+
60
+ Note that since Openai is currently the most used Engine, if you do not pass in an engine, it will default as expected. So, this is the equialent shorter version of the above script:
61
+ ```ruby
62
+ # run the calculator
63
+ calc = Boxcars::Calculator.new # just use the default Engine
64
+ puts calc.run "what is pi to the forth power divided by 22.1?"
65
+ ```
66
+ ### Boxcars currently implemmented
67
+
68
+ Here is what we have so far, but please put up a PR with your new ideas.
69
+ - Search: uses the SERP API to do seaches
70
+ - Calculator: uses an Engine to generate ruby code to do math
71
+ - SQL: given an ActiveRecord connection, it will generate and run sql statments from a prompt.
72
+
73
+ ### Run a list of Boxcars
74
+ ```ruby
75
+ # run a Train for a calculator, and search using default Engine
76
+ boxcars = [Boxcars::Calculator.new, Boxcars::Serp.new]
77
+ train = Boxcars.default_train.new(boxcars: boxcars)
78
+ puts train.run "What is pi times the square root of the average temperature in Austin TX in January?"
79
+ ```
80
+ This outputs:
81
+ ```text
82
+ > Entering Zero Shot#run
83
+ What is pi times the square root of the average temperature in Austin TX in January?
84
+ Question: Average temperature in Austin TX in January
85
+ Answer: increase from 62°F to 64°F
86
+ #Observation: increase from 62°F to 64°F
87
+ > Entering Calculator#run
88
+ 64°F x pi
89
+ RubyREPL: puts (64 * Math::PI).round(2)
90
+ Answer: 201.06
91
+
92
+ 201.06
93
+ < Exiting Calculator#run
94
+ #Observation: 201.06
95
+ I now know the final answer
96
+ Final Answer: 201.06
97
+ < Exiting Zero Shot#run
98
+ 201.06
99
+ ```
26
100
 
27
101
  ## Development
28
102
 
data/Rakefile CHANGED
@@ -10,3 +10,10 @@ require "rubocop/rake_task"
10
10
  RuboCop::RakeTask.new
11
11
 
12
12
  task default: %i[spec rubocop]
13
+
14
+ require 'github_changelog_generator/task'
15
+
16
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
17
+ config.user = 'BoxcarsAI'
18
+ config.project = 'boxcars'
19
+ end
data/boxcars.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.email = ["hi@boxcars.ai"]
10
10
 
11
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 conductor, and it does the magic."
12
+ spec.description = "You simply give a number of boxcars to a train, and it does the magic."
13
13
  spec.homepage = "https://github.com/BoxcarsAI/boxcars"
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = ">= 2.6.0"
@@ -4,15 +4,15 @@
4
4
  module Boxcars
5
5
  # A Boxcar that interprets a prompt and executes ruby code to do math
6
6
  class Calculator < EngineBoxcar
7
+ # the description of this engine boxcar
7
8
  CALCDESC = "useful for when you need to answer questions about math"
8
9
  attr_accessor :input_key
9
10
 
10
- # @param prompt [Boxcars::Prompt] The prompt to use for this boxcar.
11
- # @param name [String] The name of the boxcar. Defaults to classname.
12
- # @param description [String] A description of the boxcar.
13
- # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a conductor if nil.
11
+ # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
12
+ # @param prompt [Boxcars::Prompt] The prompt to use for this boxcar. Defaults to built-in prompt.
14
13
  # @param input_key [Symbol] The key to use for the input. Defaults to :question.
15
14
  # @param output_key [Symbol] The key to use for the output. Defaults to :answer.
15
+ # @param kwargs [Hash] Any other keyword arguments to pass to the parent class.
16
16
  def initialize(engine: nil, prompt: nil, input_key: :question, output_key: :answer, **kwargs)
17
17
  # def initialize(engine:, prompt: my_prompt, input_key: :question, output_key: :answer, **kwargs)
18
18
  @input_key = input_key
@@ -24,14 +24,19 @@ module Boxcars
24
24
  output_key: output_key)
25
25
  end
26
26
 
27
+ # the prompt input keys
27
28
  def input_keys
28
29
  [input_key]
29
30
  end
30
31
 
32
+ # the output keys
31
33
  def output_keys
32
34
  [output_key]
33
35
  end
34
36
 
37
+ # call the calculator
38
+ # @param inputs [Hash] The inputs to the boxcar.
39
+ # @return [Hash] The outputs from the boxcar.
35
40
  def call(inputs:)
36
41
  t = predict(question: inputs[input_key], stop: ["```output"]).strip
37
42
  answer = get_answer(t)
@@ -58,7 +63,9 @@ module Boxcars
58
63
  end
59
64
  end
60
65
 
61
- TEMPLATE = <<~IPT
66
+ # our template
67
+ # rubocop:disable Style/RedundantHeredocDelimiterQuotes
68
+ TEMPLATE = <<~'IPT'
62
69
  You are GPT-3, and you can't do math.
63
70
  You can do basic math, and your memorization abilities are impressive, but you can't do any complex calculations that a human could not do in their head. You also have an annoying tendency to just make up highly specific, but wrong, answers.
64
71
  So we hooked you up to a Ruby 3 kernel, and now you can execute ruby code. If anyone gives you a hard math problem, just use this format and we’ll take care of the rest:
@@ -93,6 +100,7 @@ module Boxcars
93
100
 
94
101
  Question: %<question>s
95
102
  IPT
103
+ # rubocop:enable Style/RedundantHeredocDelimiterQuotes
96
104
 
97
105
  # The prompt to use for the engine.
98
106
  def my_prompt
@@ -3,55 +3,48 @@
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
  # For Boxcars that use an engine to do their work.
6
- # @abstract
7
- class EngineBoxcar < Boxcars::Boxcar
6
+ class EngineBoxcar < Boxcar
8
7
  attr_accessor :prompt, :engine, :output_key
9
8
 
10
9
  # A Boxcar is a container for a single tool to run.
11
10
  # @param prompt [Boxcars::Prompt] The prompt to use for this boxcar with sane defaults.
12
11
  # @param name [String] The name of the boxcar. Defaults to classname.
13
12
  # @param description [String] A description of the boxcar.
14
- # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a conductor if nil.
15
- def initialize(prompt:, engine:, output_key: "text", name: nil, description: nil)
13
+ # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
14
+ def initialize(prompt:, engine: nil, output_key: "text", name: nil, description: nil)
16
15
  @prompt = prompt
17
- @engine = engine
16
+ @engine = engine || Boxcars.default_engine.new
18
17
  @output_key = output_key
19
18
  super(name: name, description: description)
20
19
  end
21
20
 
21
+ # input keys for the prompt
22
22
  def input_keys
23
23
  prompt.input_variables
24
24
  end
25
25
 
26
+ # output keys
26
27
  def output_keys
27
28
  [output_key]
28
29
  end
29
30
 
30
- # # Check that all inputs are present.
31
- # def validate_inputs(inputs:)
32
- # missing_keys = input_keys - inputs.keys
33
- # raise Boxcars::ArgumentError, "Missing some input keys: #{missing_keys}" if missing_keys.any?
34
-
35
- # inputs
36
- # end
37
-
38
- # def validate_outputs(outputs:)
39
- # return if outputs.sort == output_keys.sort
40
-
41
- # raise Boxcars::ArgumentError, "Did not get out keys that were expected, got: #{outputs}. Expected: #{output_keys}"
42
- # end
43
-
31
+ # generate a response from the engine
32
+ # @param input_list [Array<Hash>] A list of hashes of input values to use for the prompt.
33
+ # @return [Boxcars::EngineResult] The result from the engine.
44
34
  def generate(input_list:)
45
35
  stop = input_list[0][:stop]
46
36
  prompts = []
47
37
  input_list.each do |inputs|
48
38
  new_prompt = prompt.format(**inputs)
49
- puts "Prompt after formatting:\n#{new_prompt.colorize(:cyan)}"
39
+ puts "Prompt after formatting:\n#{new_prompt.colorize(:cyan)}" if Boxcars.configuration.log_prompts
50
40
  prompts.push(new_prompt)
51
41
  end
52
42
  engine.generate(prompts: prompts, stop: stop)
53
43
  end
54
44
 
45
+ # apply a response from the engine
46
+ # @param input_list [Array<Hash>] A list of hashes of input values to use for the prompt.
47
+ # @return [Hash] A hash of the output key and the output value.
55
48
  def apply(input_list:)
56
49
  response = generate(input_list: input_list)
57
50
  response.generations.to_h do |generation|
@@ -59,10 +52,16 @@ module Boxcars
59
52
  end
60
53
  end
61
54
 
55
+ # predict a response from the engine
56
+ # @param kwargs [Hash] A hash of input values to use for the prompt.
57
+ # @return [String] The output value.
62
58
  def predict(**kwargs)
63
59
  apply(input_list: [kwargs])[output_key]
64
60
  end
65
61
 
62
+ # predict a response from the engine and parse it
63
+ # @param kwargs [Hash] A hash of input values to use for the prompt.
64
+ # @return [String] The output value.
66
65
  def predict_and_parse(**kwargs)
67
66
  result = predict(**kwargs)
68
67
  if prompt.output_parser
@@ -72,6 +71,9 @@ module Boxcars
72
71
  end
73
72
  end
74
73
 
74
+ # apply a response from the engine and parse it
75
+ # @param input_list [Array<Hash>] A list of hashes of input values to use for the prompt.
76
+ # @return [Array<String>] The output values.
75
77
  def apply_and_parse(input_list:)
76
78
  result = apply(input_list: input_list)
77
79
  if prompt.output_parser
@@ -81,6 +83,8 @@ module Boxcars
81
83
  end
82
84
  end
83
85
 
86
+ # check that there is exactly one output key
87
+ # @raise [Boxcars::ArgumentError] if there is not exactly one output key.
84
88
  def check_output_keys
85
89
  return unless output_keys.length != 1
86
90
 
@@ -4,14 +4,14 @@ require 'google_search_results'
4
4
  module Boxcars
5
5
  # A Boxcar that uses the Google SerpAPI to get answers to questions.
6
6
  class Serp < Boxcar
7
+ # the description of this boxcar
7
8
  SERPDESC = "useful for when you need to answer questions about current events." \
8
9
  "You should ask targeted questions"
9
10
 
10
11
  # implements a boxcar that uses the Google SerpAPI to get answers to questions.
11
12
  # @param name [String] The name of the boxcar. Defaults to classname.
12
13
  # @param description [String] A description of the boxcar. Defaults to SERPDESC.
13
- # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a Conductor if nil.
14
- #
14
+ # @param serpapi_api_key [String] The API key to use for the SerpAPI. Defaults to Boxcars.configuration.serpapi_api_key.
15
15
  def initialize(name: "Search", description: SERPDESC, serpapi_api_key: "not set")
16
16
  super(name: name, description: description)
17
17
  api_key = Boxcars.configuration.serpapi_api_key(serpapi_api_key: serpapi_api_key)
@@ -4,16 +4,16 @@
4
4
  module Boxcars
5
5
  # A Boxcar that interprets a prompt and executes SQL code to get answers
6
6
  class SQL < EngineBoxcar
7
+ # the description of this engine boxcar
7
8
  SQLDESC = "useful for when you need to query a SQL database"
8
9
  attr_accessor :connection, :input_key
9
10
 
10
11
  # @param connection [ActiveRecord::Connection] The SQL connection to use for this boxcar.
11
- # @param prompt [Boxcars::Prompt] The prompt to use for this boxcar.
12
- # @param name [String] The name of the boxcar. Defaults to classname.
13
- # @param description [String] A description of the boxcar.
14
- # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a conductor if nil.
12
+ # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
15
13
  # @param input_key [Symbol] The key to use for the input. Defaults to :question.
16
14
  # @param output_key [Symbol] The key to use for the output. Defaults to :answer.
15
+ # @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
16
+ # :name, :description and :prompt
17
17
  def initialize(connection:, engine: nil, input_key: :question, output_key: :answer, **kwargs)
18
18
  @connection = connection
19
19
  @input_key = input_key
@@ -25,14 +25,21 @@ module Boxcars
25
25
  output_key: output_key)
26
26
  end
27
27
 
28
+ # the input keys for the prompt
29
+ # @return [Array<Symbol>] The input keys for the prompt.
28
30
  def input_keys
29
31
  [input_key]
30
32
  end
31
33
 
34
+ # the output keys for the prompt
35
+ # @return [Array<Symbol>] The output keys for the prompt.
32
36
  def output_keys
33
37
  [output_key]
34
38
  end
35
39
 
40
+ # call the boxcar
41
+ # @param inputs [Hash] The inputs to the boxcar.
42
+ # @return [Hash] The outputs from the boxcar.
36
43
  def call(inputs:)
37
44
  t = predict(question: inputs[input_key], dialect: dialect, top_k: 5, table_info: schema, stop: ["Answer:"]).strip
38
45
  answer = get_answer(t)
@@ -11,7 +11,7 @@ module Boxcars
11
11
  # @param return_direct [Boolean] If true, return the output of this boxcar directly, without merging it with the inputs.
12
12
  def initialize(description:, name: nil, return_direct: false)
13
13
  @name = name || self.class.name
14
- @description = description
14
+ @description = description || @name
15
15
  @return_direct = return_direct
16
16
  end
17
17
 
@@ -26,6 +26,8 @@ module Boxcars
26
26
  end
27
27
 
28
28
  # Check that all inputs are present.
29
+ # @param inputs [Hash] The inputs.
30
+ # @raise [RuntimeError] If the inputs are not the same.
29
31
  def validate_inputs(inputs:)
30
32
  missing_keys = input_keys - inputs.keys
31
33
  raise "Missing some input keys: #{missing_keys}" if missing_keys.any?
@@ -33,6 +35,9 @@ module Boxcars
33
35
  inputs
34
36
  end
35
37
 
38
+ # check that all outputs are present
39
+ # @param outputs [Array<String>] The output keys.
40
+ # @raise [RuntimeError] If the outputs are not the same.
36
41
  def validate_outputs(outputs:)
37
42
  return if outputs.sort == output_keys.sort
38
43
 
@@ -44,6 +49,28 @@ module Boxcars
44
49
  raise NotImplementedError
45
50
  end
46
51
 
52
+ # Apply the boxcar to a list of inputs.
53
+ # @param input_list [Array<Hash>] The list of inputs.
54
+ # @return [Array<Boxcars::Boxcar>] The list of outputs.
55
+ def apply(input_list:)
56
+ raise NotImplementedError
57
+ end
58
+
59
+ # Get an answer from the boxcar.
60
+ # @param args [Array] The positional arguments to pass to the boxcar.
61
+ # @param kwargs [Hash] The keyword arguments to pass to the boxcar.
62
+ # you can pass one or the other, but not both.
63
+ # @return [String] The answer to the question.
64
+ def run(*args, **kwargs)
65
+ puts "> Entering #{name}#run".colorize(:gray, style: :bold)
66
+ rv = do_run(*args, **kwargs)
67
+ puts "< Exiting #{name}#run".colorize(:gray, style: :bold)
68
+ rv
69
+ end
70
+
71
+ private
72
+
73
+ # Get an answer from the boxcar.
47
74
  def do_call(inputs:, return_only_outputs: false)
48
75
  inputs = our_inputs(inputs)
49
76
  output = nil
@@ -60,22 +87,6 @@ module Boxcars
60
87
  inputs.merge(output)
61
88
  end
62
89
 
63
- def apply(input_list:)
64
- input_list.map { |inputs| new(**inputs) }
65
- end
66
-
67
- # Get an answer from the boxcar.
68
- # @param question [String] The question to ask the boxcar.
69
- # @return [String] The answer to the question.
70
- def run(*args, **kwargs)
71
- puts "> Enterning #{name} boxcar#run".colorize(:gray, style: :bold)
72
- rv = do_run(*args, **kwargs)
73
- puts "< Exiting #{name} boxcar#run".colorize(:gray, style: :bold)
74
- rv
75
- end
76
-
77
- private
78
-
79
90
  def do_run(*args, **kwargs)
80
91
  if kwargs.empty?
81
92
  raise Boxcars::ArgumentError, "run supports only one positional argument." if args.length != 1