boxcars 0.10.9 → 0.10.11

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: 64b7e543514bf7d455bbc551248f34adff8c302422f59ebae31f43b314bf64d7
4
- data.tar.gz: 577e41b5b0361fc07173f2eec4a452f4021d5c826de6d89514c16d50d88bf24e
3
+ metadata.gz: 759acf707674768e5d7ced63f2cfea2543077c0680b5d2e6de7f157c43a5e63e
4
+ data.tar.gz: eb7b9ee285c072634a8ce1bd1a8be4d8ca1e14e14adcac7a4f181add50878163
5
5
  SHA512:
6
- metadata.gz: e54505c0d6a801c364d671032469795f17ac094e77855becbe8770d909c9771fc52efa7434fcb1ad225282bad519e16d68181c22851a5555c48abb648c10f0b5
7
- data.tar.gz: 84f62b772954edfd3555da6de18ba0e300e7fbdc76b6862dfae15f1b4e86de80161fc9990fc509c3d9508173b46b36adebebf43562e95c8f1fcab9ce8c5c0039
6
+ metadata.gz: 93efe7f98f822323a740d4bc4f98b5a92098a7cb1a14022e5a63166fab5f28d8e6f6ca7597ab229d72be6c35a9c11460152d24c6aeaf2b5e52777d6328f8436e
7
+ data.tar.gz: 423b48707ab590debe87ac73872613084bc7c6a1cf9b4258fc48379320585a7efc7cc8717e8ea32097ef13dd76cd95f924c44105623f5b3a2187cccd46caf0a2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.10.11] - 2026-06-26
6
+
7
+ ### Fixed
8
+
9
+ - Anthropic Opus 4.7 requests now omit unsupported sampling parameters (`temperature`, `top_p`, and `top_k`) while preserving existing defaults for older Claude models.
10
+
11
+ ### Maintenance
12
+
13
+ - Refreshed bundled dependencies and applied RuboCop autocorrections for the release.
14
+
15
+ ## [0.10.10] - 2026-05-07
16
+
17
+ ### Fixed
18
+
19
+ - Anthropic responses now extract and join all text-bearing content blocks, including Claude responses where `thinking` blocks precede the final text block.
20
+
5
21
  ## [0.10.9] - 2026-04-30
6
22
 
7
23
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.10.9)
4
+ boxcars (0.10.11)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -30,13 +30,12 @@ GEM
30
30
  ast (2.4.3)
31
31
  base64 (0.3.0)
32
32
  bigdecimal (4.1.2)
33
- cgi (0.5.1)
34
- concurrent-ruby (1.3.6)
33
+ cgi (0.5.2)
34
+ concurrent-ruby (1.3.7)
35
35
  connection_pool (3.0.2)
36
36
  crack (1.0.1)
37
37
  bigdecimal
38
38
  rexml
39
- date (3.5.1)
40
39
  debug (1.11.1)
41
40
  irb (~> 1.10)
42
41
  reline (>= 0.3.8)
@@ -45,18 +44,18 @@ GEM
45
44
  drb (2.2.3)
46
45
  erb (6.0.4)
47
46
  event_stream_parser (1.0.0)
48
- faraday (2.14.1)
47
+ faraday (2.14.3)
49
48
  faraday-net_http (>= 2.0, < 3.5)
50
49
  json
51
50
  logger
52
51
  faraday-multipart (1.2.0)
53
52
  multipart-post (~> 2.0)
54
- faraday-net_http (3.4.2)
53
+ faraday-net_http (3.4.4)
55
54
  net-http (~> 0.5)
56
55
  google_search_results (2.2.0)
57
56
  hashdiff (1.2.1)
58
57
  hnswlib (0.9.3)
59
- i18n (1.14.8)
58
+ i18n (1.15.2)
60
59
  concurrent-ruby (~> 1.0)
61
60
  io-console (0.8.2)
62
61
  irb (1.18.0)
@@ -64,33 +63,33 @@ GEM
64
63
  prism (>= 1.3.0)
65
64
  rdoc (>= 4.0.0)
66
65
  reline (>= 0.4.2)
67
- json (2.19.4)
66
+ json (2.20.0)
68
67
  language_server-protocol (3.17.0.5)
69
68
  lint_roller (1.1.0)
70
69
  logger (1.7.0)
71
- minitest (6.0.3)
70
+ minitest (6.0.6)
72
71
  drb (~> 2.0)
73
72
  prism (~> 1.5)
74
73
  multipart-post (2.4.1)
75
74
  net-http (0.9.1)
76
75
  uri (>= 0.11.1)
77
- nokogiri (1.19.3-aarch64-linux-gnu)
76
+ nokogiri (1.19.4-aarch64-linux-gnu)
78
77
  racc (~> 1.4)
79
- nokogiri (1.19.3-aarch64-linux-musl)
78
+ nokogiri (1.19.4-aarch64-linux-musl)
80
79
  racc (~> 1.4)
81
- nokogiri (1.19.3-arm-linux-gnu)
80
+ nokogiri (1.19.4-arm-linux-gnu)
82
81
  racc (~> 1.4)
83
- nokogiri (1.19.3-arm-linux-musl)
82
+ nokogiri (1.19.4-arm-linux-musl)
84
83
  racc (~> 1.4)
85
- nokogiri (1.19.3-arm64-darwin)
84
+ nokogiri (1.19.4-arm64-darwin)
86
85
  racc (~> 1.4)
87
- nokogiri (1.19.3-x86_64-darwin)
86
+ nokogiri (1.19.4-x86_64-darwin)
88
87
  racc (~> 1.4)
89
- nokogiri (1.19.3-x86_64-linux-gnu)
88
+ nokogiri (1.19.4-x86_64-linux-gnu)
90
89
  racc (~> 1.4)
91
- nokogiri (1.19.3-x86_64-linux-musl)
90
+ nokogiri (1.19.4-x86_64-linux-musl)
92
91
  racc (~> 1.4)
93
- openai (0.60.0)
92
+ openai (0.68.0)
94
93
  base64
95
94
  cgi
96
95
  connection_pool
@@ -106,22 +105,24 @@ GEM
106
105
  pg (1.6.3-x86_64-linux)
107
106
  pg (1.6.3-x86_64-linux-musl)
108
107
  pgvector (0.3.3)
109
- posthog-ruby (3.6.5)
108
+ posthog-ruby (3.14.3)
110
109
  concurrent-ruby (~> 1)
111
- pp (0.6.3)
110
+ pp (0.6.4)
112
111
  prettyprint
113
112
  prettyprint (0.2.0)
114
113
  prism (1.9.0)
115
- psych (5.3.1)
116
- date
117
- stringio
118
114
  public_suffix (7.0.5)
119
115
  racc (1.8.1)
120
116
  rainbow (3.1.1)
121
117
  rake (13.4.2)
122
- rdoc (7.2.0)
118
+ rbs (4.0.3)
119
+ logger
120
+ prism (>= 1.6.0)
121
+ tsort
122
+ rdoc (8.0.0)
123
123
  erb
124
- psych (>= 4.0.0)
124
+ prism (>= 1.6.0)
125
+ rbs (>= 4.0.0)
125
126
  tsort
126
127
  regexp_parser (2.12.0)
127
128
  reline (0.6.3)
@@ -140,7 +141,7 @@ GEM
140
141
  diff-lcs (>= 1.2.0, < 2.0)
141
142
  rspec-support (~> 3.13.0)
142
143
  rspec-support (3.13.7)
143
- rubocop (1.86.1)
144
+ rubocop (1.88.0)
144
145
  json (~> 2.3)
145
146
  language_server-protocol (~> 3.17.0.2)
146
147
  lint_roller (~> 1.1.0)
@@ -157,26 +158,26 @@ GEM
157
158
  rubocop-rake (0.7.1)
158
159
  lint_roller (~> 1.1)
159
160
  rubocop (>= 1.72.1)
160
- rubocop-rspec (3.9.0)
161
+ rubocop-rspec (3.10.2)
161
162
  lint_roller (~> 1.1)
162
- rubocop (~> 1.81)
163
+ regexp_parser (>= 2.0)
164
+ rubocop (~> 1.86, >= 1.86.2)
163
165
  ruby-anthropic (0.4.2)
164
166
  event_stream_parser (>= 0.3.0, < 2.0.0)
165
167
  faraday (>= 1)
166
168
  faraday-multipart (>= 1)
167
169
  ruby-progressbar (1.13.0)
168
170
  securerandom (0.4.1)
169
- sequel (5.103.0)
171
+ sequel (5.105.0)
170
172
  bigdecimal
171
- sqlite3 (2.9.3-aarch64-linux-gnu)
172
- sqlite3 (2.9.3-aarch64-linux-musl)
173
- sqlite3 (2.9.3-arm-linux-gnu)
174
- sqlite3 (2.9.3-arm-linux-musl)
175
- sqlite3 (2.9.3-arm64-darwin)
176
- sqlite3 (2.9.3-x86_64-darwin)
177
- sqlite3 (2.9.3-x86_64-linux-gnu)
178
- sqlite3 (2.9.3-x86_64-linux-musl)
179
- stringio (3.2.0)
173
+ sqlite3 (2.9.5-aarch64-linux-gnu)
174
+ sqlite3 (2.9.5-aarch64-linux-musl)
175
+ sqlite3 (2.9.5-arm-linux-gnu)
176
+ sqlite3 (2.9.5-arm-linux-musl)
177
+ sqlite3 (2.9.5-arm64-darwin)
178
+ sqlite3 (2.9.5-x86_64-darwin)
179
+ sqlite3 (2.9.5-x86_64-linux-gnu)
180
+ sqlite3 (2.9.5-x86_64-linux-musl)
180
181
  timeout (0.6.1)
181
182
  tsort (0.2.0)
182
183
  tzinfo (2.0.6)
@@ -179,6 +179,27 @@ engine.run("Write a one-line summary")
179
179
 
180
180
  Groq, Gemini, Ollama, Google, Cerebras, and Together all use the same official OpenAI client path with provider-specific base URLs.
181
181
 
182
+ For enterprise deployments, the same OpenAI-compatible path can point at an
183
+ internal model gateway or AI control plane. For example, Tuning Engines users can
184
+ set an inference key and base URL while keeping regular OpenAI behavior as the
185
+ default fallback:
186
+
187
+ ```ruby
188
+ Boxcars.configure do |config|
189
+ config.openai_official_client_builder = lambda do |access_token:, uri_base:, organization_id:, log_errors:|
190
+ OpenAI::Client.new(
191
+ api_key: ENV.fetch("TUNING_ENGINES_INFERENCE_KEY", access_token),
192
+ base_url: ENV.fetch("OPENAI_BASE_URL", uri_base),
193
+ organization: organization_id
194
+ )
195
+ end
196
+ end
197
+ ```
198
+
199
+ This is useful when model calls need centralized routing, audit trails,
200
+ guardrails, approval workflows, or cost controls before reaching the final model
201
+ provider.
202
+
182
203
  #### Custom Client Builder
183
204
 
184
205
  If you want explicit control over the official SDK client shape:
@@ -15,6 +15,8 @@ module Boxcars
15
15
  max_tokens: 4096,
16
16
  temperature: 0.1
17
17
  }.freeze
18
+ OPUS_4_7_MODEL_PREFIX = "claude-opus-4-7"
19
+ OPUS_4_7_UNSUPPORTED_SAMPLING_PARAMS = %i[temperature top_p top_k].freeze
18
20
 
19
21
  # The default name of the engine.
20
22
  DEFAULT_NAME = "Anthropic engine"
@@ -27,7 +29,7 @@ module Boxcars
27
29
  raise ArgumentError, "unknown keyword: :prompts" if kwargs.key?(:prompts)
28
30
 
29
31
  user_id = kwargs.delete(:user_id)
30
- @llm_params = DEFAULT_PARAMS.merge(kwargs)
32
+ @llm_params = remove_opus_4_7_unsupported_sampling_params(DEFAULT_PARAMS.merge(kwargs))
31
33
  super(description:, name:, batch_size: 20, user_id:)
32
34
  end
33
35
 
@@ -83,6 +85,7 @@ module Boxcars
83
85
  params[:stop_sequences] = params.delete(:stop) if params.key?(:stop)
84
86
  params[:system] = params[:messages].shift[:content] if params.dig(:messages, 0, :role) == :system
85
87
  params[:messages].pop if params[:messages].last[:content].nil? || params[:messages].last[:content].strip.empty?
88
+ remove_opus_4_7_unsupported_sampling_params(params)
86
89
  combine_assistant(params)
87
90
  end
88
91
 
@@ -111,6 +114,20 @@ module Boxcars
111
114
 
112
115
  private
113
116
 
117
+ def remove_opus_4_7_unsupported_sampling_params(params)
118
+ return params unless opus_4_7_model?(params[:model] || params["model"])
119
+
120
+ OPUS_4_7_UNSUPPORTED_SAMPLING_PARAMS.each do |key|
121
+ params.delete(key)
122
+ params.delete(key.to_s)
123
+ end
124
+ params
125
+ end
126
+
127
+ def opus_4_7_model?(model)
128
+ model.to_s.start_with?(OPUS_4_7_MODEL_PREFIX)
129
+ end
130
+
114
131
  # Process the raw response from Anthropic API
115
132
  def process_anthropic_response(raw_response, response_data)
116
133
  normalized_response = normalize_anthropic_payload(raw_response)
@@ -118,18 +135,21 @@ module Boxcars
118
135
  response_data[:parsed_json] = normalized_response
119
136
 
120
137
  if normalized_response && !normalized_response["error"]
121
- response_data[:success] = true
122
138
  response_data[:status_code] = 200 # Inferred
139
+ normalize_anthropic_usage!(normalized_response)
140
+
123
141
  # Transform response to match expected format
124
- normalized_response["completion"] = normalized_response.dig("content", 0, "text")
142
+ normalized_response["completion"] = extract_anthropic_completion(normalized_response)
143
+
144
+ if normalized_response["completion"].empty?
145
+ response_data[:success] = false
146
+ response_data[:error] ||= Error.new("Anthropic response contained no text content")
147
+ return
148
+ end
149
+
150
+ response_data[:success] = true
125
151
  normalized_response["choices"] ||= [{ "text" => normalized_response["completion"],
126
152
  "finish_reason" => normalized_response["stop_reason"] }]
127
- if normalized_response["usage"].is_a?(Hash)
128
- normalized_response["usage"]["prompt_tokens"] ||= normalized_response["usage"]["input_tokens"]
129
- normalized_response["usage"]["completion_tokens"] ||= normalized_response["usage"]["output_tokens"]
130
- normalized_response["usage"]["total_tokens"] ||= normalized_response["usage"]["prompt_tokens"].to_i +
131
- normalized_response["usage"]["completion_tokens"].to_i
132
- end
133
153
  normalized_response.delete("content")
134
154
  else
135
155
  response_data[:success] = false
@@ -139,6 +159,33 @@ module Boxcars
139
159
  end
140
160
  end
141
161
 
162
+ def extract_anthropic_completion(normalized_response)
163
+ content = normalized_response["content"]
164
+ blocks = content.is_a?(Array) ? content : [content]
165
+
166
+ blocks.filter_map { |block| extract_anthropic_text_block(block) }.join("\n").strip
167
+ end
168
+
169
+ def extract_anthropic_text_block(block)
170
+ case block
171
+ when String
172
+ block
173
+ when Hash
174
+ block["text"] || block[:text]
175
+ else
176
+ block.text if block.respond_to?(:text)
177
+ end
178
+ end
179
+
180
+ def normalize_anthropic_usage!(normalized_response)
181
+ return unless normalized_response["usage"].is_a?(Hash)
182
+
183
+ normalized_response["usage"]["prompt_tokens"] ||= normalized_response["usage"]["input_tokens"]
184
+ normalized_response["usage"]["completion_tokens"] ||= normalized_response["usage"]["output_tokens"]
185
+ normalized_response["usage"]["total_tokens"] ||= normalized_response["usage"]["prompt_tokens"].to_i +
186
+ normalized_response["usage"]["completion_tokens"].to_i
187
+ end
188
+
142
189
  # Handle errors from Anthropic API calls
143
190
  def handle_anthropic_error(error, response_data)
144
191
  response_data[:error] = error
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.10.9"
5
+ VERSION = "0.10.11"
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.10.9
4
+ version: 0.10.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francis Sullivan