boxcars 0.6.4 → 0.6.5

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: 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.