boxcars 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7288e4aaea4065096105c84b78e9425330847eba37b55b6479466c55ae840a8
4
- data.tar.gz: 6ff9ec0d0c69113dc70360750fa8fe5460236401c75a3b4e681009efad733301
3
+ metadata.gz: 164d55b95e134a691b4525f778a83f6d06cdaa1948028b87371cb6cb8a16fd43
4
+ data.tar.gz: 2707ac063bbc1127d75398be8770812ae258447ba021c0b9527cd3fbf113ff02
5
5
  SHA512:
6
- metadata.gz: e48e4debf178dca7647a08357eaabbdd8ceef6edc3ae40d49a25a31b8ee21fbc41ded409fd55c2b840762868546f1edb0ada72729178c15953e6f48cc0d832fe
7
- data.tar.gz: 3717955095a68207dd7c0ee914132573d84126c01ea4e53cec0d5a46985f512a59da3fb1b78ededac29c0d72d16e5f70301629ab326aa20ec2feb4b6664ae343
6
+ metadata.gz: 8b0d7909ef7c049d7c393b646abd072c01dd4b295a7af909bbe2f85fe48b8425c946f956440548fa3902182c636d3e57e81279332d331a9d7c4df4181cd4e4d5
7
+ data.tar.gz: ed1c903fb5575ae563ccab8adb0e80cfed9c00e529a5850fc7b89acaf165eecaf42e69cdd548cdec3622855645b5bf88591b3d6a6cb1af47bfbe97d5d6d16ad9
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.3
1
+ 3.3.5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.6.4](https://github.com/BoxcarsAI/boxcars/tree/v0.6.4) (2024-07-27)
4
+
5
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.6.3...v0.6.4)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Add Ollama Engine [\#200](https://github.com/BoxcarsAI/boxcars/pull/200) ([francis](https://github.com/francis))
10
+
3
11
  ## [v0.6.3](https://github.com/BoxcarsAI/boxcars/tree/v0.6.3) (2024-07-26)
4
12
 
5
13
  [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.6.2...v0.6.3)
data/Gemfile CHANGED
@@ -15,13 +15,13 @@ gem "sqlite3", "~> 1.7"
15
15
 
16
16
  gem "async", "~>1.32.1"
17
17
 
18
- gem "activerecord", "~> 7.0"
18
+ gem "activerecord", "~> 7.1"
19
19
 
20
20
  gem "github_changelog_generator", "~> 1.16"
21
21
 
22
22
  gem "faraday-retry", "~> 2.0"
23
23
 
24
- gem "activesupport", "~> 7.0"
24
+ gem "activesupport", "~> 7.1"
25
25
 
26
26
  gem "rest-client", "~> 2.1"
27
27
 
@@ -32,9 +32,9 @@ gem "pgvector", "~> 0.2.2"
32
32
 
33
33
  group :development, :test do
34
34
  gem "rspec", "~> 3.13"
35
- gem "rubocop", "~> 1.64"
36
- gem "vcr", "~> 6.2.0"
35
+ gem "rubocop", "~> 1.66"
36
+ gem "vcr", "~> 6.3.1"
37
37
  gem "webmock", "~> 3.23.1"
38
38
  gem "rubocop-rake", "~> 0.6.0"
39
- gem "rubocop-rspec", "~> 3.0"
39
+ gem "rubocop-rspec", "~> 3.1"
40
40
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.6.4)
4
+ boxcars (0.6.5)
5
5
  anthropic (~> 0.1)
6
6
  google_search_results (~> 2.2)
7
7
  gpt4all (~> 0.0.4)
@@ -13,13 +13,13 @@ PATH
13
13
  GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
- activemodel (7.1.3.4)
17
- activesupport (= 7.1.3.4)
18
- activerecord (7.1.3.4)
19
- activemodel (= 7.1.3.4)
20
- activesupport (= 7.1.3.4)
16
+ activemodel (7.1.4)
17
+ activesupport (= 7.1.4)
18
+ activerecord (7.1.4)
19
+ activemodel (= 7.1.4)
20
+ activesupport (= 7.1.4)
21
21
  timeout (>= 0.4.0)
22
- activesupport (7.1.3.4)
22
+ activesupport (7.1.4)
23
23
  base64
24
24
  bigdecimal
25
25
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -57,7 +57,7 @@ GEM
57
57
  async (>= 1.25)
58
58
  base64 (0.2.0)
59
59
  bigdecimal (3.1.8)
60
- concurrent-ruby (1.3.3)
60
+ concurrent-ruby (1.3.4)
61
61
  connection_pool (2.4.1)
62
62
  console (1.27.0)
63
63
  fiber-annotation
@@ -71,7 +71,7 @@ GEM
71
71
  reline (>= 0.3.8)
72
72
  diff-lcs (1.5.1)
73
73
  domain_name (0.6.20240107)
74
- dotenv (3.1.2)
74
+ dotenv (3.1.4)
75
75
  drb (2.2.1)
76
76
  event_stream_parser (1.0.0)
77
77
  faraday (2.10.0)
@@ -120,7 +120,7 @@ GEM
120
120
  mime-types (3.5.2)
121
121
  mime-types-data (~> 3.2015)
122
122
  mime-types-data (3.2024.0702)
123
- minitest (5.24.1)
123
+ minitest (5.25.1)
124
124
  multi_json (1.15.0)
125
125
  multipart-post (2.4.1)
126
126
  mutex_m (0.2.0)
@@ -128,19 +128,19 @@ GEM
128
128
  uri
129
129
  netrc (0.11.0)
130
130
  nio4r (2.7.3)
131
- nokogiri (1.16.6-arm64-darwin)
131
+ nokogiri (1.16.7-arm64-darwin)
132
132
  racc (~> 1.4)
133
- nokogiri (1.16.6-x86_64-linux)
133
+ nokogiri (1.16.7-x86_64-linux)
134
134
  racc (~> 1.4)
135
135
  octokit (4.25.1)
136
136
  faraday (>= 1, < 3)
137
137
  sawyer (~> 0.9)
138
138
  os (1.1.4)
139
- parallel (1.25.1)
140
- parser (3.3.4.0)
139
+ parallel (1.26.3)
140
+ parser (3.3.5.0)
141
141
  ast (~> 2.4.1)
142
142
  racc
143
- pg (1.5.6)
143
+ pg (1.5.8)
144
144
  pgvector (0.2.2)
145
145
  protocol-hpack (1.4.3)
146
146
  protocol-http (0.26.8)
@@ -152,7 +152,7 @@ GEM
152
152
  psych (5.1.2)
153
153
  stringio
154
154
  public_suffix (6.0.0)
155
- racc (1.8.0)
155
+ racc (1.8.1)
156
156
  rainbow (3.1.1)
157
157
  rake (13.2.1)
158
158
  rdoc (6.7.0)
@@ -165,8 +165,7 @@ GEM
165
165
  http-cookie (>= 1.0.2, < 2.0)
166
166
  mime-types (>= 1.16, < 4.0)
167
167
  netrc (~> 0.8)
168
- rexml (3.3.2)
169
- strscan
168
+ rexml (3.3.8)
170
169
  rspec (3.13.0)
171
170
  rspec-core (~> 3.13.0)
172
171
  rspec-expectations (~> 3.13.0)
@@ -180,22 +179,21 @@ GEM
180
179
  diff-lcs (>= 1.2.0, < 2.0)
181
180
  rspec-support (~> 3.13.0)
182
181
  rspec-support (3.13.1)
183
- rubocop (1.65.0)
182
+ rubocop (1.66.1)
184
183
  json (~> 2.3)
185
184
  language_server-protocol (>= 3.17.0)
186
185
  parallel (~> 1.10)
187
186
  parser (>= 3.3.0.2)
188
187
  rainbow (>= 2.2.2, < 4.0)
189
188
  regexp_parser (>= 2.4, < 3.0)
190
- rexml (>= 3.2.5, < 4.0)
191
- rubocop-ast (>= 1.31.1, < 2.0)
189
+ rubocop-ast (>= 1.32.2, < 2.0)
192
190
  ruby-progressbar (~> 1.7)
193
191
  unicode-display_width (>= 2.4.0, < 3.0)
194
- rubocop-ast (1.31.3)
192
+ rubocop-ast (1.32.3)
195
193
  parser (>= 3.3.1.0)
196
194
  rubocop-rake (0.6.0)
197
195
  rubocop (~> 1.0)
198
- rubocop-rspec (3.0.3)
196
+ rubocop-rspec (3.1.0)
199
197
  rubocop (~> 1.61)
200
198
  ruby-openai (7.1.0)
201
199
  event_stream_parser (>= 0.3.0, < 2.0.0)
@@ -209,7 +207,6 @@ GEM
209
207
  sqlite3 (1.7.3-x86_64-linux)
210
208
  stringio (3.1.1)
211
209
  strings-ansi (0.2.0)
212
- strscan (3.1.0)
213
210
  timeout (0.4.1)
214
211
  timers (4.3.5)
215
212
  traces (0.11.1)
@@ -222,9 +219,10 @@ GEM
222
219
  tty-screen (0.8.2)
223
220
  tzinfo (2.0.6)
224
221
  concurrent-ruby (~> 1.0)
225
- unicode-display_width (2.5.0)
222
+ unicode-display_width (2.6.0)
226
223
  uri (0.13.0)
227
- vcr (6.2.0)
224
+ vcr (6.3.1)
225
+ base64
228
226
  webmock (3.23.1)
229
227
  addressable (>= 2.8.0)
230
228
  crack (>= 0.3.2)
@@ -233,11 +231,12 @@ GEM
233
231
  PLATFORMS
234
232
  arm64-darwin-22
235
233
  arm64-darwin-23
234
+ arm64-darwin-24
236
235
  x86_64-linux
237
236
 
238
237
  DEPENDENCIES
239
- activerecord (~> 7.0)
240
- activesupport (~> 7.0)
238
+ activerecord (~> 7.1)
239
+ activesupport (~> 7.1)
241
240
  async (~> 1.32.1)
242
241
  boxcars!
243
242
  debug (~> 1.9)
@@ -250,11 +249,11 @@ DEPENDENCIES
250
249
  rake (~> 13.2)
251
250
  rest-client (~> 2.1)
252
251
  rspec (~> 3.13)
253
- rubocop (~> 1.64)
252
+ rubocop (~> 1.66)
254
253
  rubocop-rake (~> 0.6.0)
255
- rubocop-rspec (~> 3.0)
254
+ rubocop-rspec (~> 3.1)
256
255
  sqlite3 (~> 1.7)
257
- vcr (~> 6.2.0)
256
+ vcr (~> 6.3.1)
258
257
  webmock (~> 3.23.1)
259
258
 
260
259
  BUNDLED WITH
@@ -50,12 +50,14 @@ module Boxcars
50
50
  # @param engine_output [String] The output from the engine.
51
51
  # @return [Result] The result.
52
52
  def get_answer(engine_output)
53
- # sometimes the LLM adds text in front of the JSON output, so let's strip it here
54
- json_start = engine_output.index("{")
55
- json_end = engine_output.rindex("}")
56
- extract_answer(JSON.parse(engine_output[json_start..json_end], symbolize_names: symbolize))
53
+ json_string = extract_json(engine_output)
54
+ reply = JSON.parse(json_string, symbolize_names: symbolize)
55
+ Result.new(status: :ok, answer: reply, explanation: reply)
56
+ rescue JSON::ParserError => e
57
+ Boxcars.debug "JSON: #{engine_output}", :red
58
+ Result.from_error("JSON parsing error: #{e.message}")
57
59
  rescue StandardError => e
58
- Result.from_error("Error: #{e.message}:\n#{engine_output}")
60
+ Result.from_error("Unexpected error: #{e.message}")
59
61
  end
60
62
 
61
63
  # get answer from parsed JSON
@@ -63,14 +65,29 @@ module Boxcars
63
65
  # @return [Result] The result.
64
66
  def extract_answer(data)
65
67
  reply = data
68
+ Result.new(status: :ok, answer: reply, explanation: reply)
69
+ end
70
+
71
+ private
72
+
73
+ def extract_json(text)
74
+ # Escape control characters (U+0000 to U+001F)
75
+ text = text.gsub(/[\u0000-\u001F]/, '')
76
+ # first strip hidden characters
77
+ # text = text.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
78
+
79
+ # sometimes the LLM adds text in front of the JSON output, so let's strip it here
80
+ json_start = text.index("{")
81
+ json_end = text.rindex("}")
82
+ text[json_start..json_end]
83
+ end
84
+
85
+ def extract_json2(text)
86
+ # Match the outermost JSON object
87
+ match = text.match(/\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}/)
88
+ raise StandardError, "No valid JSON object found in the output" unless match
66
89
 
67
- if reply.present?
68
- Result.new(status: :ok, answer: reply, explanation: reply)
69
- else
70
- # we have an unexpected output from the engine
71
- Result.new(status: :error, answer: nil,
72
- explanation: "You gave me an improperly formatted answer. I was expecting a valid reply.")
73
- end
90
+ match[0]
74
91
  end
75
92
  end
76
93
  end
@@ -166,7 +166,7 @@ module Boxcars
166
166
  output = call(inputs: inputs)
167
167
  rescue StandardError => e
168
168
  Boxcars.error "Error in #{name} boxcar#call: #{e}\nbt:#{e.backtrace[0..5].join("\n ")}", :red
169
- Boxcars.error("Response Body: #{e.response[:body]}", :red) if e.respond_to?(:response)
169
+ Boxcars.error("Response Body: #{e.response[:body]}", :red) if e.respond_to?(:response) && e.response.present?
170
170
  raise e
171
171
  end
172
172
  validate_outputs(outputs: output.keys)
@@ -28,6 +28,11 @@ module Boxcars
28
28
  # @param batch_size [Integer] The number of prompts to send to the engine at once. Defaults to 20.
29
29
  def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs)
30
30
  @open_ai_params = DEFAULT_PARAMS.merge(kwargs)
31
+ if @open_ai_params[:model] =~ /^o/ && @open_ai_params[:max_tokens].present?
32
+ @open_ai_params[:max_completion_tokens] = @open_ai_params.delete(:max_tokens)
33
+ @open_ai_params.delete(:temperature)
34
+ end
35
+
31
36
  @prompts = prompts
32
37
  @batch_size = batch_size
33
38
  super(description: description, name: name)
@@ -44,7 +49,7 @@ module Boxcars
44
49
  end
45
50
 
46
51
  def conversation_model?(model)
47
- !!(model =~ /(^gpt-4)|(-turbo\b)/)
52
+ !!(model =~ /(^gpt-4)|(-turbo\b)|(^o\d)/)
48
53
  end
49
54
 
50
55
  # Get an answer from the engine.
@@ -57,6 +62,10 @@ module Boxcars
57
62
  params = open_ai_params.merge(kwargs)
58
63
  if conversation_model?(params[:model])
59
64
  prompt = prompt.first if prompt.is_a?(Array)
65
+ if params[:model] =~ /^o/
66
+ params.delete(:response_format)
67
+ params.delete(:stop)
68
+ end
60
69
  params = prompt.as_messages(inputs).merge(params)
61
70
  if Boxcars.configuration.log_prompts
62
71
  Boxcars.debug(params[:messages].last(2).map { |p| ">>>>>> Role: #{p[:role]} <<<<<<\n#{p[:content]}" }.join("\n"), :cyan)
@@ -40,6 +40,7 @@ module Boxcars
40
40
  faraday.request :json
41
41
  faraday.response :json
42
42
  faraday.response :raise_error
43
+ # faraday.options.timeout = 180 # 3 minutes
43
44
  end
44
45
 
45
46
  response = conn.post do |req|
@@ -122,4 +123,51 @@ module Boxcars
122
123
  def max_tokens_for_prompt(_prompt_text)
123
124
  8096
124
125
  end
126
+
127
+ # Get generation informaton
128
+ # @param sub_choices [Array<Hash>] The choices to get generation info for.
129
+ # @return [Array<Generation>] The generation information.
130
+ def generation_info(sub_choices)
131
+ sub_choices.map do |choice|
132
+ Generation.new(
133
+ text: choice.dig("message", "content") || choice["text"],
134
+ generation_info: {
135
+ finish_reason: choice.fetch("finish_reason", nil),
136
+ logprobs: choice.fetch("logprobs", nil)
137
+ }
138
+ )
139
+ end
140
+ end
141
+
142
+ # Call out to endpoint with k unique prompts.
143
+ # @param prompts [Array<String>] The prompts to pass into the model.
144
+ # @param inputs [Array<String>] The inputs to subsitite into the prompt.
145
+ # @param stop [Array<String>] Optional list of stop words to use when generating.
146
+ # @return [EngineResult] The full engine output.
147
+ def generate(prompts:, stop: nil)
148
+ params = {}
149
+ params[:stop] = stop if stop
150
+ choices = []
151
+ token_usage = {}
152
+ # Get the token usage from the response.
153
+ # Includes prompt, completion, and total tokens used.
154
+ inkeys = %w[completion_tokens prompt_tokens total_tokens].freeze
155
+ prompts.each_slice(batch_size) do |sub_prompts|
156
+ sub_prompts.each do |sprompts, inputs|
157
+ response = client(prompt: sprompts, inputs: inputs, **params)
158
+ check_response(response)
159
+ choices.concat(response["choices"])
160
+ usage_keys = inkeys & response["usage"].keys
161
+ usage_keys.each { |key| token_usage[key] = token_usage[key].to_i + response["usage"][key] }
162
+ end
163
+ end
164
+
165
+ n = params.fetch(:n, 1)
166
+ generations = []
167
+ prompts.each_with_index do |_prompt, i|
168
+ sub_choices = choices[i * n, (i + 1) * n]
169
+ generations.push(generation_info(sub_choices))
170
+ end
171
+ EngineResult.new(generations: generations, engine_output: { token_usage: token_usage })
172
+ end
125
173
  end
@@ -68,7 +68,6 @@ module Boxcars
68
68
  end
69
69
  EngineResult.new(generations: generations, engine_output: { token_usage: token_usage })
70
70
  end
71
- # rubocop:enable Metrics/AbcSize
72
71
  end
73
72
  end
74
73
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.6.4"
5
+ VERSION = "0.6.5"
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.6.4
4
+ version: 0.6.5
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: 2024-07-27 00:00:00.000000000 Z
12
+ date: 2024-10-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: anthropic
@@ -219,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
219
  - !ruby/object:Gem::Version
220
220
  version: '0'
221
221
  requirements: []
222
- rubygems_version: 3.5.11
222
+ rubygems_version: 3.5.20
223
223
  signing_key:
224
224
  specification_version: 4
225
225
  summary: Boxcars is a gem that enables you to create new systems with AI composability.