boxcars 0.2.0 → 0.2.1

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: 34001190ca60f2b9fe68d603267ffccaaef398cae2f8054e34d6d0926fd8bf9d
4
- data.tar.gz: 7918d0bad50e6758490a9baa58393e38071e524b0d284c43dd479cf1d091deab
3
+ metadata.gz: 50a47cf449c063e7ca02cb36ad315008fa6a5813063a24d92ed4c0f94743c12e
4
+ data.tar.gz: fad47ad6fefc7d580251edf1675ae575a9f4eebf28fb903a1c82ba2868310bc4
5
5
  SHA512:
6
- metadata.gz: dacd721802478a783ac9c50c4ebd959764b7601254c3a4b94546f093177cd94bdb4636d9f635154e6419a7d8505152ac7092c491238baab8ac0e47277323a7af
7
- data.tar.gz: 73c638b8486681e3268ff94e390550f565c011891e9c025556f41df4e0e741ae705fa9fdeb6c3d1996919344abbfff84a8f1454e22a5d108dcae6d65e4718403
6
+ metadata.gz: '073919902e84d95158573400ebe593d1e5aa72cc1ab6efb0c4cfccaf9806a91a0c55913d9d9ec47dd8b56f7fb81660fdfd81753092e2a3bd590e4ef782c51ba8'
7
+ data.tar.gz: 87f1ec995889c7ca107ccf187f1b01874f136787861e5116abc13da5f204f4f3e7e8cf101692fed7285ac875f405e1f8e08701f3785e0c5e83f25d0e5a0b9b15
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.2.0](https://github.com/BoxcarsAI/boxcars/tree/v0.2.0) (2023-03-07)
4
+
5
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.8...v0.2.0)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Default to chatgpt [\#35](https://github.com/BoxcarsAI/boxcars/pull/35) ([francis](https://github.com/francis))
10
+
11
+ ## [v0.1.8](https://github.com/BoxcarsAI/boxcars/tree/v0.1.8) (2023-03-02)
12
+
13
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.7...v0.1.8)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - return JSON from the Active Record boxcar [\#34](https://github.com/BoxcarsAI/boxcars/pull/34) ([francis](https://github.com/francis))
18
+ - validate return values from Open AI API [\#33](https://github.com/BoxcarsAI/boxcars/pull/33) ([francis](https://github.com/francis))
19
+ - simplify prompting and parameters used. refs \#29 [\#30](https://github.com/BoxcarsAI/boxcars/pull/30) ([francis](https://github.com/francis))
20
+ - \[infra\] Added sample .env file and updated the lookup to save the key [\#27](https://github.com/BoxcarsAI/boxcars/pull/27) ([AKovtunov](https://github.com/AKovtunov))
21
+
3
22
  ## [v0.1.7](https://github.com/BoxcarsAI/boxcars/tree/v0.1.7) (2023-02-27)
4
23
 
5
24
  [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.1.6...v0.1.7)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.2.0)
4
+ boxcars (0.2.1)
5
5
  google_search_results (~> 2.2)
6
6
  ruby-openai (~> 3.0)
7
7
 
@@ -7,28 +7,24 @@ 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, :requested_models, :read_only, :approval_callback
10
+ attr_accessor :connection, :requested_models, :read_only, :approval_callback, :code_only
11
11
  attr_reader :except_models
12
12
 
13
- # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
14
13
  # @param models [Array<ActiveRecord::Model>] The models to use for this boxcar. Will use all if nil.
14
+ # @param except_models [Array<ActiveRecord::Model>] The models to exclude from this boxcar. Will exclude none if nil.
15
15
  # @param read_only [Boolean] Whether to use read only models. Defaults to true unless you pass an approval function.
16
16
  # @param approval_callback [Proc] A function to call to approve changes. Defaults to nil.
17
17
  # @param kwargs [Hash] Any other keyword arguments. These can include:
18
- # :name, :description, :prompt, :except_models, :top_k, and :stop
19
- def initialize(engine: nil, models: nil, read_only: nil, approval_callback: nil, **kwargs)
20
- check_models(models)
21
- @except_models = LOCKED_OUT_MODELS + kwargs[:except_models].to_a
18
+ # :name, :description, :prompt, :except_models, :top_k, :stop, :code_only and :engine
19
+ def initialize(models: nil, except_models: nil, read_only: nil, approval_callback: nil, **kwargs)
20
+ check_models(models, except_models)
22
21
  @approval_callback = approval_callback
23
22
  @read_only = read_only.nil? ? !approval_callback : read_only
24
- the_prompt = kwargs[prompt] || my_prompt
25
- name = kwargs[:name] || "Data"
26
- kwargs[:stop] ||= ["Answer:"]
27
- super(name: name,
28
- description: kwargs[:description] || format(ARDESC, name: name),
29
- engine: engine,
30
- prompt: the_prompt,
31
- **kwargs)
23
+ @code_only = kwargs.delete(:code_only) || false
24
+ kwargs[:name] ||= "Data"
25
+ kwargs[:description] ||= format(ARDESC, name: name)
26
+ kwargs[:prompt] ||= my_prompt
27
+ super(**kwargs)
32
28
  end
33
29
 
34
30
  # @return Hash The additional variables for this boxcar.
@@ -42,7 +38,11 @@ module Boxcars
42
38
  read_only
43
39
  end
44
40
 
45
- def check_models(models)
41
+ def code_only?
42
+ code_only
43
+ end
44
+
45
+ def check_models(models, exceptions)
46
46
  if models.is_a?(Array) && models.length.positive?
47
47
  @requested_models = models
48
48
  models.each do |m|
@@ -51,6 +51,7 @@ module Boxcars
51
51
  elsif models
52
52
  raise ArgumentError, "models needs to be an array of Active Record models"
53
53
  end
54
+ @except_models = LOCKED_OUT_MODELS + exceptions.to_a
54
55
  end
55
56
 
56
57
  def wanted_models
@@ -120,7 +121,7 @@ module Boxcars
120
121
  changes = change_count(changes_code)
121
122
  return true unless changes&.positive?
122
123
 
123
- Boxcars.debug "Pending Changes: #{changes}", :yellow, style: :bold
124
+ Boxcars.debug "#{name}(Pending Changes): #{changes}", :yellow
124
125
  change_str = "#{changes} change#{'s' if changes.to_i > 1}"
125
126
  raise SecurityError, "Can not run code that makes #{change_str} in read-only mode" if read_only?
126
127
 
@@ -140,18 +141,26 @@ module Boxcars
140
141
  end
141
142
  end
142
143
 
144
+ def clean_up_output(output)
145
+ output = output.as_json if output.is_a?(::ActiveRecord::Result)
146
+ output = 0 if output.is_a?(Array) && output.empty?
147
+ output = output.first if output.is_a?(Array) && output.length == 1
148
+ output = output[output.keys.first] if output.is_a?(Hash) && output.length == 1
149
+ output = output.as_json if output.is_a?(::ActiveRecord::Relation)
150
+ output
151
+ end
152
+
143
153
  def get_active_record_answer(text)
144
154
  code = text[/^ARCode: (.*)/, 1]
145
155
  changes_code = text[/^ARChanges: (.*)/, 1]
156
+ return Result.new(status: :ok, explanation: "code to run", code: code, changes_code: changes_code) if code_only?
157
+
146
158
  raise SecurityError, "Permission to run code that makes changes denied" unless approved?(changes_code, code)
147
159
 
148
- output = run_active_record_code(code)
149
- output = 0 if output.is_a?(Array) && output.empty?
150
- output = output.first if output.is_a?(Array) && output.length == 1
151
- output = output[output.keys.first] if output.is_a?(Hash) && output.length == 1
152
- "Answer: #{output.to_json}"
160
+ output = clean_up_output(run_active_record_code(code))
161
+ Result.new(status: :ok, answer: output, explanation: "Answer: #{output.to_json}", code: code)
153
162
  rescue StandardError => e
154
- "Error: #{e.message}"
163
+ Result.new(status: :error, answer: nil, explanation: "Error: #{e.message}", code: code)
155
164
  end
156
165
 
157
166
  def get_answer(text)
@@ -159,9 +168,9 @@ module Boxcars
159
168
  when /^ARCode:/
160
169
  get_active_record_answer(text)
161
170
  when /^Answer:/
162
- text
171
+ Result.from_text(text)
163
172
  else
164
- raise Boxcars::Error "Unknown format from engine: #{text}"
173
+ Result.from_error("Unknown format from engine: #{text}")
165
174
  end
166
175
  end
167
176
 
@@ -13,11 +13,9 @@ module Boxcars
13
13
  def initialize(engine: nil, prompt: nil, **kwargs)
14
14
  the_prompt = prompt || my_prompt
15
15
  kwargs[:stop] ||= ["```output"]
16
- super(name: kwargs[:name] || "Calculator",
17
- description: kwargs[:description] || CALCDESC,
18
- engine: engine,
19
- prompt: the_prompt,
20
- **kwargs)
16
+ kwargs[:name] ||= "Calculator"
17
+ kwargs[:description] ||= CALCDESC
18
+ super(engine: engine, prompt: the_prompt, **kwargs)
21
19
  end
22
20
 
23
21
  private
@@ -25,7 +23,7 @@ module Boxcars
25
23
  def get_embedded_ruby_answer(text)
26
24
  code = text[8..-4].split("```").first.strip
27
25
  ruby_executor = Boxcars::RubyREPL.new
28
- ruby_executor.call(code: code).strip
26
+ ruby_executor.call(code: code)
29
27
  end
30
28
 
31
29
  def get_answer(text)
@@ -33,9 +31,9 @@ module Boxcars
33
31
  when /^```ruby/
34
32
  get_embedded_ruby_answer(text)
35
33
  when /^Answer:/
36
- text
34
+ Result.from_text(text)
37
35
  else
38
- raise Boxcars::Error "Unknown format from engine: #{text}"
36
+ Result.new(status: :error, explanation: "Unknown format from engine: #{text}")
39
37
  end
40
38
  end
41
39
 
@@ -16,7 +16,7 @@ module Boxcars
16
16
  @engine = engine || Boxcars.engine.new
17
17
  @top_k = kwargs[:top_k] || 5
18
18
  @stop = kwargs[:stop] || ["Answer:"]
19
- super(name: name, description: description)
19
+ super(name: name, description: description, return_direct: kwargs[:return_direct])
20
20
  end
21
21
 
22
22
  # input keys for the prompt
@@ -95,7 +95,7 @@ module Boxcars
95
95
  def check_output_keys
96
96
  return unless output_keys.length != 1
97
97
 
98
- raise Boxcars::ArgumentError, "run not supported when there is not exactly one output key. Got #{output_keys}."
98
+ raise Boxcars::ArgumentError, "not supported when there is not exactly one output key. Got #{output_keys}."
99
99
  end
100
100
 
101
101
  # call the boxcar
@@ -104,7 +104,7 @@ module Boxcars
104
104
  def call(inputs:)
105
105
  t = predict(**prediction_variables(inputs)).strip
106
106
  answer = get_answer(t)
107
- Boxcars.debug answer, :magenta
107
+ Boxcars.debug answer.to_json, :magenta
108
108
  { output_keys.first => answer }
109
109
  end
110
110
 
@@ -6,21 +6,21 @@ module Boxcars
6
6
  class SQL < EngineBoxcar
7
7
  # the description of this engine boxcar
8
8
  SQLDESC = "useful for when you need to query a database for %<name>s."
9
+ LOCKED_OUT_TABLES = %w[schema_migrations ar_internal_metadata].freeze
9
10
  attr_accessor :connection
10
11
 
11
12
  # @param connection [ActiveRecord::Connection] The SQL connection to use for this boxcar.
12
- # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
13
+ # @param tables [Array<String>] The tables to use for this boxcar. Will use all if nil.
14
+ # @param except_tables [Array<String>] The tables to exclude from this boxcar. Will exclude none if nil.
13
15
  # @param kwargs [Hash] Any other keyword arguments to pass to the parent class. This can include
14
- # :name, :description, :prompt and :top_k
15
- def initialize(connection: nil, engine: nil, **kwargs)
16
+ # :name, :description, :prompt, :top_k, :stop, and :engine
17
+ def initialize(connection: nil, tables: nil, except_tables: nil, **kwargs)
16
18
  @connection = connection || ::ActiveRecord::Base.connection
17
- the_prompt = kwargs[prompt] || my_prompt
18
- kwargs[:stop] ||= ["Answer:"]
19
- name = kwargs[:name] || "database"
20
- super(name: name,
21
- description: kwargs[:description] || format(SQLDESC, name: name),
22
- engine: engine,
23
- prompt: the_prompt)
19
+ check_tables(tables, except_tables)
20
+ kwargs[:name] ||= "Database"
21
+ kwargs[:description] ||= format(SQLDESC, name: name)
22
+ kwargs[:prompt] ||= my_prompt
23
+ super(**kwargs)
24
24
  end
25
25
 
26
26
  # @return Hash The additional variables for this boxcar.
@@ -30,6 +30,19 @@ module Boxcars
30
30
 
31
31
  private
32
32
 
33
+ def check_tables(rtables, exceptions)
34
+ if rtables.is_a?(Array) && tables.length.positive?
35
+ @requested_tables = rtables
36
+ all_tables = tables
37
+ rtables.each do |t|
38
+ raise ArgumentError, "table #{t} needs to be an Active Record model" unless all_tables.include?(t)
39
+ end
40
+ elsif rtables
41
+ raise ArgumentError, "tables needs to be an array of Strings"
42
+ end
43
+ @except_models = LOCKED_OUT_TABLES + exceptions.to_a
44
+ end
45
+
33
46
  def tables
34
47
  connection&.tables
35
48
  end
@@ -49,14 +62,22 @@ module Boxcars
49
62
  connection.class.name.split("::").last.sub("Adapter", "")
50
63
  end
51
64
 
52
- def get_embedded_sql_answer(text)
53
- code = text[/^SQLQuery: (.*)/, 1]
54
- Boxcars.debug code, :yellow
55
- output = connection.exec_query(code)
65
+ def clean_up_output(output)
66
+ output = output.as_json if output.is_a?(::ActiveRecord::Result)
56
67
  output = 0 if output.is_a?(Array) && output.empty?
57
68
  output = output.first if output.is_a?(Array) && output.length == 1
58
69
  output = output[output.keys.first] if output.is_a?(Hash) && output.length == 1
59
- "Answer: #{output.to_json}"
70
+ output = output.as_json if output.is_a?(::ActiveRecord::Relation)
71
+ output
72
+ end
73
+
74
+ def get_embedded_sql_answer(text)
75
+ code = text[/^SQLQuery: (.*)/, 1]
76
+ Boxcars.debug code, :yellow
77
+ output = clean_up_output(connection.exec_query(code))
78
+ Result.new(status: :ok, answer: output, explanation: "Answer: #{output.to_json}", code: code)
79
+ rescue StandardError => e
80
+ Result.new(status: :error, answer: nil, explanation: "Error: #{e.message}", code: code)
60
81
  end
61
82
 
62
83
  def get_answer(text)
@@ -64,14 +85,14 @@ module Boxcars
64
85
  when /^SQLQuery:/
65
86
  get_embedded_sql_answer(text)
66
87
  when /^Answer:/
67
- text
88
+ Result.from_text(text)
68
89
  else
69
- raise Boxcars::Error "Unknown format from engine: #{text}"
90
+ Result.from_error("Unknown format from engine: #{text}")
70
91
  end
71
92
  end
72
93
 
73
94
  TEMPLATE = <<~IPT
74
- Given an input question, first create a syntactically correct %<dialect>s query to run,
95
+ Given an input question, first create a syntactically correct %<dialect>s SQL query to run,
75
96
  then look at the results of the query and return the answer. Unless the user specifies
76
97
  in his question a specific number of examples he wishes to obtain, always limit your query
77
98
  to at most %<top_k>s results using a LIMIT clause. You can order the results by a relevant column
@@ -62,8 +62,18 @@ 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
+ rv = conduct(*args, **kwargs)
66
+ rv.is_a?(Result) ? rv.to_answer : rv
67
+ end
68
+
69
+ # Get an extended answer from the boxcar.
70
+ # @param args [Array] The positional arguments to pass to the boxcar.
71
+ # @param kwargs [Hash] The keyword arguments to pass to the boxcar.
72
+ # you can pass one or the other, but not both.
73
+ # @return [Boxcars::Result] The answer to the question.
74
+ def conduct(*args, **kwargs)
65
75
  Boxcars.info "> Entering #{name}#run", :gray, style: :bold
66
- rv = do_run(*args, **kwargs)
76
+ rv = depart(*args, **kwargs)
67
77
  Boxcars.info "< Exiting #{name}#run", :gray, style: :bold
68
78
  rv
69
79
  end
@@ -71,7 +81,7 @@ module Boxcars
71
81
  private
72
82
 
73
83
  # Get an answer from the boxcar.
74
- def do_call(inputs:, return_only_outputs: false)
84
+ def run_boxcar(inputs:, return_only_outputs: false)
75
85
  inputs = our_inputs(inputs)
76
86
  output = nil
77
87
  begin
@@ -81,19 +91,19 @@ module Boxcars
81
91
  raise e
82
92
  end
83
93
  validate_outputs(outputs: output.keys)
84
- # memory&.save_convext(inputs: inputs, outputs: outputs)
85
94
  return output if return_only_outputs
86
95
 
87
96
  inputs.merge(output)
88
97
  end
89
98
 
90
- def do_run(*args, **kwargs)
99
+ # line up parameters and run boxcar
100
+ def depart(*args, **kwargs)
91
101
  if kwargs.empty?
92
102
  raise Boxcars::ArgumentError, "run supports only one positional argument." if args.length != 1
93
103
 
94
- return do_call(inputs: args[0])[output_keys.first]
104
+ return run_boxcar(inputs: args[0])[output_keys.first]
95
105
  end
96
- return do_call(**kwargs)[output_keys].first if args.empty?
106
+ return run_boxcar(**kwargs)[output_keys].first if args.empty?
97
107
 
98
108
  raise Boxcars::ArgumentError, "run supported with either positional or keyword arguments but not both. Got args" \
99
109
  ": #{args} and kwargs: #{kwargs}."
@@ -114,11 +124,12 @@ module Boxcars
114
124
 
115
125
  # the default answer is the text passed in
116
126
  def get_answer(text)
117
- text
127
+ Result.from_text(text)
118
128
  end
119
129
  end
120
130
  end
121
131
 
132
+ require "boxcars/result"
122
133
  require "boxcars/boxcar/engine_boxcar"
123
134
  require "boxcars/boxcar/calculator"
124
135
  require "boxcars/boxcar/google_search"
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Boxcars
4
+ # used by Boxcars to return structured result and additional context
5
+ class Result
6
+ attr_reader :status, :answer, :explanation, :suggestions, :added_context
7
+
8
+ # @param status [Symbol] :ok or :error
9
+ # @param answer [String] The answer to the question
10
+ # @param explanation [String] The explanation of the answer
11
+ # @param suggestions [Array<String>] The next suggestions for the user
12
+ # @param added_context [Hash] Any additional context to add to the result
13
+ def initialize(status:, answer: nil, explanation: nil, suggestions: nil, **added_context)
14
+ @status = status
15
+ @answer = answer || explanation
16
+ @explanation = explanation
17
+ @suggestions = suggestions
18
+ @added_context = added_context
19
+ end
20
+
21
+ # @return [Hash] The result as a hash
22
+ def to_h
23
+ {
24
+ status: status,
25
+ answer: answer,
26
+ explanation: explanation,
27
+ suggestions: suggestions
28
+ }.merge(added_context).compact
29
+ end
30
+
31
+ # @return [String] The result as a json string
32
+ def to_json(*args)
33
+ JSON.generate(to_h, *args)
34
+ end
35
+
36
+ # @return [String] An explanation of the result
37
+ def to_s
38
+ explanation
39
+ end
40
+
41
+ # @return [String] The answer data to the question
42
+ def to_answer
43
+ answer
44
+ end
45
+
46
+ # create a new Result from a text string
47
+ # @param text [String] The text to use for the result
48
+ # @param kwargs [Hash] Any additional kwargs to pass to the result
49
+ # @return [Boxcars::Result] The result
50
+ def self.from_text(text, **kwargs)
51
+ answer = text.delete_prefix('"').delete_suffix('"').strip
52
+ answer = Regexp.last_match(:answer) if answer =~ /^Answer:\s*(?<answer>.*)$/
53
+ explanation = "Answer: #{answer}"
54
+ new(status: :ok, answer: answer, explanation: explanation, **kwargs)
55
+ end
56
+
57
+ # create a new Result from an error string
58
+ # @param error [String] The error to use for the result
59
+ # @param kwargs [Hash] Any additional kwargs to pass to the result
60
+ # @return [Boxcars::Result] The error result
61
+ def self.from_error(error, **kwargs)
62
+ answer = error
63
+ answer = Regexp.last_match(:answer) if answer =~ /^Error:\s*(?<answer>.*)$/
64
+ explanation = "Error: #{answer}"
65
+ new(status: :error, answer: answer, explanation: explanation, **kwargs)
66
+ end
67
+ end
68
+ end
@@ -9,19 +9,20 @@ module Boxcars
9
9
  Boxcars.debug "RubyREPL: #{code}", :yellow
10
10
 
11
11
  # wrap the code in an excption block so we can catch errors
12
- code = "begin\n#{code}\nrescue Exception => e\n puts 'Error: ' + e.message\nend"
12
+ wrapped = "begin\n#{code}\nrescue Exception => e\n puts 'Error: ' + e.message\nend"
13
13
  output = ""
14
14
  IO.popen("ruby", "r+") do |io|
15
- io.puts code
15
+ io.puts wrapped
16
16
  io.close_write
17
17
  output = io.read
18
18
  end
19
19
  if output =~ /^Error: /
20
- Boxcars.error output
21
- output
20
+ Boxcars.debug output, :red
21
+ Result.from_error(output, code: code)
22
+ else
23
+ Boxcars.debug "Answer: #{output}", :yellow, style: :bold
24
+ Result.from_text(output, code: code)
22
25
  end
23
- Boxcars.debug "Answer: #{output}", :yellow, style: :bold
24
- output
25
26
  end
26
27
 
27
28
  # Execute ruby code
@@ -5,10 +5,24 @@ module Boxcars
5
5
  class TrainAction
6
6
  attr_accessor :boxcar, :boxcar_input, :log
7
7
 
8
- def initialize(boxcar: nil, boxcar_input: nil, log: nil)
9
- @boxcar = boxcar
8
+ # record for a train action
9
+ # @param boxcar [String] The boxcar to run.
10
+ # @param log [String] The log of the action.
11
+ # @param boxcar_input [String] The input to the boxcar.
12
+ # @return [Boxcars::TrainAction] The train action.
13
+ def initialize(boxcar:, log:, boxcar_input: nil)
10
14
  @boxcar_input = boxcar_input
15
+ @boxcar = boxcar
11
16
  @log = log
12
17
  end
18
+
19
+ # build a train action from a result
20
+ # @param result [Boxcars::Result] The result to build from.
21
+ # @param boxcar [String] The boxcar to run.
22
+ # @param log [String] The log of the action.
23
+ # @return [Boxcars::TrainAction] The train action.
24
+ def self.from_result(result:, boxcar:, log:)
25
+ new(boxcar: boxcar, boxcar_input: result.to_answer, log: log)
26
+ end
13
27
  end
14
28
  end
@@ -69,7 +69,8 @@ module Boxcars
69
69
  # with "Action Input:" should be separated by a newline.
70
70
  if engine_output.include?(FINAL_ANSWER_ACTION)
71
71
  answer = engine_output.split(FINAL_ANSWER_ACTION).last.strip
72
- ['Final Answer', answer]
72
+ Result.new(status: :ok, answer: answer, explanation: engine_output)
73
+ # ['Final Answer', answer]
73
74
  else
74
75
  # the thought should be the frist line here if it doesn't start with "Action:"
75
76
  thought = engine_output.split(/\n+/).reject(&:empty?).first
data/lib/boxcars/train.rb CHANGED
@@ -51,13 +51,16 @@ module Boxcars
51
51
  full_output = predict(**full_inputs)
52
52
  parsed_output = extract_boxcar_and_input(full_output)
53
53
  while parsed_output.nil?
54
- full_output = _fix_text(full_output)
55
54
  full_inputs[:agent_scratchpad] += full_output
56
55
  output = predict(**full_inputs)
57
56
  full_output += output
58
57
  parsed_output = extract_boxcar_and_input(full_output)
59
58
  end
60
- TrainAction.new(boxcar: parsed_output[0], boxcar_input: parsed_output[1], log: full_output)
59
+ if parsed_output.is_a?(Result)
60
+ TrainAction.from_result(boxcar: "Final Answer", result: parsed_output, log: full_output)
61
+ else
62
+ TrainAction.new(boxcar: parsed_output[0], boxcar_input: parsed_output[1], log: full_output)
63
+ end
61
64
  end
62
65
 
63
66
  # Given input, decided what to do.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.2.0"
5
+ VERSION = "0.2.1"
6
6
  end
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.0
4
+ version: 0.2.1
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-03-07 00:00:00.000000000 Z
12
+ date: 2023-03-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: debug
@@ -114,6 +114,7 @@ files:
114
114
  - lib/boxcars/engine/openai.rb
115
115
  - lib/boxcars/generation.rb
116
116
  - lib/boxcars/prompt.rb
117
+ - lib/boxcars/result.rb
117
118
  - lib/boxcars/ruby_repl.rb
118
119
  - lib/boxcars/train.rb
119
120
  - lib/boxcars/train/train_action.rb