openai 0.83.0 → 0.84.0

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +1 -1
  4. data/lib/openai/auth/workload_identity_auth.rb +17 -2
  5. data/lib/openai/client.rb +20 -6
  6. data/lib/openai/errors.rb +1 -1
  7. data/lib/openai/helpers/streaming/chat_completion_stream.rb +1 -2
  8. data/lib/openai/helpers/streaming/response_events.rb +2 -2
  9. data/lib/openai/helpers/streaming/response_stream.rb +73 -42
  10. data/lib/openai/helpers/structured_output/chat_completion_parser.rb +6 -0
  11. data/lib/openai/helpers/structured_output/response_parser.rb +1 -1
  12. data/lib/openai/internal/type/base_model.rb +2 -2
  13. data/lib/openai/internal/type/converter.rb +1 -1
  14. data/lib/openai/internal/util.rb +2 -2
  15. data/lib/openai/models/admin/organization/usage_costs_params.rb +10 -1
  16. data/lib/openai/models/batch.rb +4 -4
  17. data/lib/openai/models/beta/beta_response.rb +9 -12
  18. data/lib/openai/models/beta/beta_response_usage.rb +1 -12
  19. data/lib/openai/models/beta/beta_responses_client_event.rb +8 -12
  20. data/lib/openai/models/beta/response_compact_params.rb +7 -7
  21. data/lib/openai/models/beta/response_create_params.rb +8 -12
  22. data/lib/openai/models/chat/completion_create_params.rb +8 -8
  23. data/lib/openai/models/completion_usage.rb +1 -12
  24. data/lib/openai/models/eval_create_response.rb +1 -1
  25. data/lib/openai/models/eval_list_response.rb +1 -1
  26. data/lib/openai/models/eval_retrieve_response.rb +1 -1
  27. data/lib/openai/models/eval_update_response.rb +1 -1
  28. data/lib/openai/models/evals/create_eval_completions_run_data_source.rb +2 -2
  29. data/lib/openai/models/reasoning.rb +0 -2
  30. data/lib/openai/models/responses/response.rb +6 -7
  31. data/lib/openai/models/responses/response_compact_params.rb +7 -7
  32. data/lib/openai/models/responses/response_create_params.rb +5 -7
  33. data/lib/openai/models/responses/response_usage.rb +1 -12
  34. data/lib/openai/models/responses/responses_client_event.rb +5 -7
  35. data/lib/openai/resources/admin/organization/usage.rb +3 -1
  36. data/lib/openai/resources/beta/responses.rb +5 -5
  37. data/lib/openai/resources/chat/completions.rb +2 -2
  38. data/lib/openai/resources/responses.rb +7 -6
  39. data/lib/openai/version.rb +1 -1
  40. data/lib/openai.rb +1 -0
  41. data/rbi/openai/errors.rbi +1 -1
  42. data/rbi/openai/helpers/streaming/events.rbi +2 -2
  43. data/rbi/openai/helpers/streaming/response_stream.rbi +3 -3
  44. data/rbi/openai/models/admin/organization/usage_costs_params.rbi +15 -0
  45. data/rbi/openai/models/batch.rbi +6 -6
  46. data/rbi/openai/models/beta/beta_response.rbi +10 -15
  47. data/rbi/openai/models/beta/beta_response_usage.rbi +3 -13
  48. data/rbi/openai/models/beta/beta_responses_client_event.rbi +9 -15
  49. data/rbi/openai/models/beta/response_compact_params.rbi +9 -9
  50. data/rbi/openai/models/beta/response_create_params.rbi +9 -15
  51. data/rbi/openai/models/chat/completion_create_params.rbi +9 -9
  52. data/rbi/openai/models/completion_usage.rbi +0 -10
  53. data/rbi/openai/models/eval_create_response.rbi +1 -1
  54. data/rbi/openai/models/eval_list_response.rbi +1 -1
  55. data/rbi/openai/models/eval_retrieve_response.rbi +1 -1
  56. data/rbi/openai/models/eval_update_response.rbi +1 -1
  57. data/rbi/openai/models/evals/create_eval_completions_run_data_source.rbi +2 -2
  58. data/rbi/openai/models/reasoning.rbi +0 -2
  59. data/rbi/openai/models/responses/response.rbi +7 -10
  60. data/rbi/openai/models/responses/response_compact_params.rbi +9 -9
  61. data/rbi/openai/models/responses/response_create_params.rbi +6 -10
  62. data/rbi/openai/models/responses/response_usage.rbi +3 -13
  63. data/rbi/openai/models/responses/responses_client_event.rbi +6 -10
  64. data/rbi/openai/resources/admin/organization/usage.rbi +4 -0
  65. data/rbi/openai/resources/beta/responses.rbi +7 -11
  66. data/rbi/openai/resources/chat/completions.rbi +6 -6
  67. data/rbi/openai/resources/responses.rbi +9 -13
  68. data/sig/openai/errors.rbs +1 -1
  69. data/sig/openai/internal/transport/base_client.rbs +4 -19
  70. data/sig/openai/models/admin/organization/usage_costs_params.rbs +7 -0
  71. data/sig/openai/models/beta/beta_response.rbs +2 -1
  72. data/sig/openai/models/beta/beta_response_usage.rbs +3 -8
  73. data/sig/openai/models/completion_usage.rbs +0 -5
  74. data/sig/openai/models/responses/response.rbs +2 -1
  75. data/sig/openai/models/responses/response_usage.rbs +3 -8
  76. data/sig/openai/resources/admin/organization/usage.rbs +1 -0
  77. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06b53caabbe2b1546ef4ec0eb7f677b8de5cb6d193a65e6a7e37b937459e84b1
4
- data.tar.gz: af8a795be6bc7ee391b58b35a48e7a6eb49562c67a98d548437fad6f85871a48
3
+ metadata.gz: 03f5297b1df9de7c2e182a5f76a72a5a15f2e9a34468b554eb92eb7e8022182b
4
+ data.tar.gz: 414ac43f94cee9591533b94c02d1d904cdd6a6ba0beb8694e88745b1b30dfad6
5
5
  SHA512:
6
- metadata.gz: 0a4a975d009308c39829498c31a41cd7ff1e4d6d34302251a2ad1a96858892a6ca52dfb7bb2791dfae729cc000c9c191ced27ae413f96b4a15b1f93f3d729c80
7
- data.tar.gz: af0e5f84ca22955f73cecafe5a06bd9180c414c410cd1d8a88f36ae4566bfcdf43cdbba3300f24648e5cfbea993bd9093f0edc57304b183af6328a0826ad026e
6
+ metadata.gz: a2dda8c58eb47a26113b553b1428d23502d0c6740808954b740c0a5cf5dac361a798f6a14f963cff9d1230256cd800295017dc07489f7c6798f50a59c4066b1c
7
+ data.tar.gz: 72d931bd9cc9fa963b2f9d0d6f6e66078eb778456097d041850013b814ec20c3b5c9661a4b68c9fa2ea56a829aaaccc657e544a8cb68863f6c9ae4dd653aeb01
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.84.0](https://github.com/openai/openai-ruby/compare/v0.83.0...v0.84.0) (2026-09-02)
4
+
5
+
6
+ ### Features
7
+
8
+ * **api:** update usage APIs and documentation ([#566](https://github.com/openai/openai-ruby/issues/566)) ([caac1f0](https://github.com/openai/openai-ruby/commit/caac1f0d0932dff46e25f5879f5a1410425275e9))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * accept custom request hash in RBS ([#554](https://github.com/openai/openai-ruby/issues/554)) ([8d32aa1](https://github.com/openai/openai-ruby/commit/8d32aa1764ecff22d2cb3114fa1a4b6f7faa309b))
14
+ * avoid dispatching nil checks during coercion ([#552](https://github.com/openai/openai-ruby/issues/552)) ([c8d6d32](https://github.com/openai/openai-ruby/commit/c8d6d32e178563e38585d3ad928136e3c6f10ac1))
15
+ * avoid duplicate late-choice stream logprobs ([#564](https://github.com/openai/openai-ruby/issues/564)) ([2359a02](https://github.com/openai/openai-ruby/commit/2359a02dd22fbf599250aaa97e755863562abf53))
16
+ * delimit JSONL request records ([#553](https://github.com/openai/openai-ruby/issues/553)) ([dea0408](https://github.com/openai/openai-ruby/commit/dea04081076da3fde34f4a9b15f445dd92556b0f))
17
+ * enforce workload identity request deadlines ([#547](https://github.com/openai/openai-ruby/issues/547)) ([da421b5](https://github.com/openai/openai-ruby/commit/da421b507c24c35f62ff8679dd5c468ce8a0dab9))
18
+ * handle custom chat completion tool calls ([#556](https://github.com/openai/openai-ruby/issues/556)) ([dd49e31](https://github.com/openai/openai-ruby/commit/dd49e31134cf8dd1442f1b3fb428bab4dbc663d1))
19
+ * load YAML for BaseModel serialization ([#555](https://github.com/openai/openai-ruby/issues/555)) ([5420931](https://github.com/openai/openai-ruby/commit/54209313bca8a7159ad8ee4768678ac61b871d69))
20
+ * make error cause contract read-only ([#557](https://github.com/openai/openai-ruby/issues/557)) ([f3169ca](https://github.com/openai/openai-ruby/commit/f3169caae9a6115c3725c8d4da33e9ffb65085c5))
21
+ * preserve formatter path boundaries ([#559](https://github.com/openai/openai-ruby/issues/559)) ([f8b128c](https://github.com/openai/openai-ruby/commit/f8b128cf0db3198a297caec7e7ca8dfa991f16e8))
22
+ * respect StringIO cursor in multipart uploads ([#558](https://github.com/openai/openai-ruby/issues/558)) ([e8ea7c7](https://github.com/openai/openai-ruby/commit/e8ea7c73e7a233abaf7c919ce10117729918df5e))
23
+ * resume Responses streams from server-side cursors ([#548](https://github.com/openai/openai-ruby/issues/548)) ([3b88261](https://github.com/openai/openai-ruby/commit/3b88261e7cd9a8736028c1867c0c23d21aba54b5))
24
+ * support flattened responses typed function tools ([#550](https://github.com/openai/openai-ruby/issues/550)) ([ab16d33](https://github.com/openai/openai-ruby/commit/ab16d33e18c862608125cf12fac07e72702b8752))
25
+ * support structured chat output with typed function tools ([#546](https://github.com/openai/openai-ruby/issues/546)) ([087d1e2](https://github.com/openai/openai-ruby/commit/087d1e2041099342ed59be211259b81dce14392c))
26
+
27
+
28
+ ### Chores
29
+
30
+ * **deps-dev:** bump the development-dependencies group with 3 updates ([#560](https://github.com/openai/openai-ruby/issues/560)) ([f728e07](https://github.com/openai/openai-ruby/commit/f728e072a0d65e2d66b2e647d36843b76fd92606))
31
+
3
32
  ## [0.83.0](https://github.com/openai/openai-ruby/compare/v0.82.0...v0.83.0) (2026-08-27)
4
33
 
5
34
 
data/README.md CHANGED
@@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application
15
15
  <!-- x-release-please-start-version -->
16
16
 
17
17
  ```ruby
18
- gem "openai", "~> 0.83.0"
18
+ gem "openai", "~> 0.84.0"
19
19
  ```
20
20
 
21
21
  <!-- x-release-please-end -->
@@ -15,6 +15,12 @@ module OpenAI
15
15
  DEFAULT_REFRESH_BUFFER_SECONDS = 1200
16
16
  MAX_REJECTED_TOKEN_REFRESH_ATTEMPTS = 3
17
17
  private_constant :MAX_REJECTED_TOKEN_REFRESH_ATTEMPTS
18
+ # Provider implementations wrap StandardError, but scheduler deadlines
19
+ # must cross that boundary before normalization to Timeout::Error.
20
+ # rubocop:disable Lint/InheritException
21
+ PROVIDER_DEADLINE_ERROR = Class.new(Exception)
22
+ # rubocop:enable Lint/InheritException
23
+ private_constant :PROVIDER_DEADLINE_ERROR
18
24
 
19
25
  def initialize(
20
26
  config,
@@ -274,10 +280,19 @@ module OpenAI
274
280
  private def fetch_token_from_exchange(deadline:)
275
281
  return @token_exchange.fetch(deadline: deadline) unless @token_exchange.nil?
276
282
 
277
- subject_token = @config.provider.get_token
283
+ message = "request timed out during workload identity authentication"
284
+ subject_token, token_type = begin
285
+ timeout = remaining_timeout(deadline)
286
+ Timeout.timeout(timeout, PROVIDER_DEADLINE_ERROR, message) do
287
+ [@config.provider.get_token, @config.provider.token_type]
288
+ end
289
+
290
+ rescue PROVIDER_DEADLINE_ERROR
291
+ raise Timeout::Error, message
292
+ end
293
+
278
294
  check_deadline!(deadline)
279
295
 
280
- token_type = @config.provider.token_type
281
296
  subject_token_type = SUBJECT_TOKEN_TYPES.fetch(token_type) do
282
297
  raise(
283
298
  ArgumentError,
data/lib/openai/client.rb CHANGED
@@ -18,6 +18,8 @@ module OpenAI
18
18
  DEFAULT_MAX_RETRY_DELAY = 8.0
19
19
 
20
20
  WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER = "workload-identity-auth"
21
+ WORKLOAD_IDENTITY_DEADLINE_ERROR = Class.new(Timeout::Error)
22
+ private_constant :WORKLOAD_IDENTITY_DEADLINE_ERROR
21
23
 
22
24
  # @return [String, nil]
23
25
  attr_reader :api_key
@@ -248,9 +250,9 @@ module OpenAI
248
250
 
249
251
  # @api private
250
252
  private def validate_retry_delay!(request, delay:)
251
- deadline = request[:x509_request_context]&.fetch(:deadline)
253
+ deadline = request[:x509_request_context]&.fetch(:deadline) || request[:workload_identity_deadline]
252
254
  if deadline && delay >= deadline - OpenAI::Internal::Util.monotonic_secs
253
- raise Timeout::Error, "request timed out during workload identity authentication"
255
+ raise WORKLOAD_IDENTITY_DEADLINE_ERROR, "request timed out during workload identity authentication"
254
256
  end
255
257
 
256
258
  super
@@ -281,7 +283,8 @@ module OpenAI
281
283
  request[:workload_identity_deadline] ||
282
284
  request[:timeout]&.then { OpenAI::Internal::Util.monotonic_secs + _1 }
283
285
  else
284
- request[:timeout]&.then { OpenAI::Internal::Util.monotonic_secs + _1 }
286
+ request[:workload_identity_deadline] ||
287
+ request[:timeout]&.then { OpenAI::Internal::Util.monotonic_secs + _1 }
285
288
  end
286
289
 
287
290
  request = request.merge(workload_identity_deadline: deadline)
@@ -359,8 +362,16 @@ module OpenAI
359
362
  end
360
363
  end
361
364
 
362
- rescue Timeout::Error
363
- raise unless x509_request
365
+ rescue Timeout::Error => error
366
+ unless x509_request
367
+ raise unless error.is_a?(WORKLOAD_IDENTITY_DEADLINE_ERROR)
368
+
369
+ url = request.fetch(:url).dup
370
+ url.query = nil
371
+ url.fragment = nil
372
+ cause = Timeout::Error.new(error.message)
373
+ raise OpenAI::Errors::APITimeoutError.new(url: url, message: error.message), cause: cause
374
+ end
364
375
 
365
376
  url = request.fetch(:url).dup
366
377
  url.query = nil
@@ -443,7 +454,10 @@ module OpenAI
443
454
  rescue Timeout::Error => e
444
455
  raise if x509_transport?(@requester)
445
456
 
446
- raise OpenAI::Errors::APITimeoutError.new(url: request.fetch(:url), message: e.message)
457
+ url = request.fetch(:url).dup
458
+ url.query = nil
459
+ url.fragment = nil
460
+ raise OpenAI::Errors::APITimeoutError.new(url: url, message: e.message)
447
461
  end
448
462
 
449
463
  # @api private
data/lib/openai/errors.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module OpenAI
4
4
  module Errors
5
5
  class Error < StandardError
6
- # @!attribute cause
6
+ # @!attribute [r] cause
7
7
  #
8
8
  # @return [StandardError, nil]
9
9
  end
@@ -156,6 +156,7 @@ module OpenAI
156
156
  insert_choice_snapshot!(completion_snapshot.choices, choice_snapshot)
157
157
  else
158
158
  update_existing_choice_snapshot(choice, choice_snapshot)
159
+ accumulate_logprobs!(choice.logprobs, choice_snapshot)
159
160
  end
160
161
 
161
162
  tool_calls_by_index = index_choice_snapshot!(choice_snapshot)
@@ -166,8 +167,6 @@ module OpenAI
166
167
  end
167
168
 
168
169
  parse_tool_calls!(choice.delta.tool_calls, tool_calls_by_index)
169
-
170
- accumulate_logprobs!(choice.logprobs, choice_snapshot)
171
170
  end
172
171
 
173
172
  def create_new_choice_snapshot(choice)
@@ -4,7 +4,7 @@ module OpenAI
4
4
  module Helpers
5
5
  module Streaming
6
6
  class ResponseTextDeltaEvent < OpenAI::Models::Responses::ResponseTextDeltaEvent
7
- required :snapshot, String
7
+ optional :snapshot, String, nil?: true
8
8
  end
9
9
 
10
10
  class ResponseTextDoneEvent < OpenAI::Models::Responses::ResponseTextDoneEvent
@@ -12,7 +12,7 @@ module OpenAI
12
12
  end
13
13
 
14
14
  class ResponseFunctionCallArgumentsDeltaEvent < OpenAI::Models::Responses::ResponseFunctionCallArgumentsDeltaEvent
15
- required :snapshot, String
15
+ optional :snapshot, String, nil?: true
16
16
  end
17
17
 
18
18
  class ResponseCompletedEvent < OpenAI::Models::Responses::ResponseCompletedEvent
@@ -13,7 +13,8 @@ module OpenAI
13
13
  @last_response = raw_stream.last_response
14
14
  @iterator = iterator
15
15
  @state = ResponseStreamState.new(
16
- text_format: text_format
16
+ text_format: text_format,
17
+ starting_after: starting_after
17
18
  )
18
19
  end
19
20
 
@@ -26,7 +27,7 @@ module OpenAI
26
27
  OpenAI::Internal::Util.chain_fused(@iterator) do |yielder|
27
28
  @iterator.each do |event|
28
29
  case event
29
- when OpenAI::Streaming::ResponseTextDeltaEvent
30
+ when OpenAI::Models::Responses::ResponseTextDeltaEvent
30
31
  yielder << event.delta
31
32
  end
32
33
  end
@@ -80,10 +81,11 @@ module OpenAI
80
81
  class ResponseStreamState
81
82
  attr_reader :completed_response
82
83
 
83
- def initialize(text_format:)
84
+ def initialize(text_format:, starting_after: nil)
84
85
  @current_snapshot = nil
85
86
  @completed_response = nil
86
87
  @text_format = text_format
88
+ @resumed = !starting_after.nil?
87
89
  end
88
90
 
89
91
  def handle_event(event)
@@ -98,31 +100,43 @@ module OpenAI
98
100
 
99
101
  case event
100
102
  when OpenAI::Models::Responses::ResponseTextDeltaEvent
101
- output = @current_snapshot.output[event.output_index]
102
- assert_type(output, :message)
103
+ if @current_snapshot
104
+ output = @current_snapshot.output[event.output_index]
105
+ assert_type(output, :message)
103
106
 
104
- content = output.content[event.content_index]
105
- assert_type(content, :output_text)
106
-
107
- events_to_yield <<
108
- OpenAI::Streaming::ResponseTextDeltaEvent.new(
109
- content_index: event.content_index,
110
- delta: event.delta,
111
- item_id: event.item_id,
112
- output_index: event.output_index,
113
- sequence_number: event.sequence_number,
114
- type: event.type,
115
- snapshot: content.text
116
- )
107
+ content = output.content[event.content_index]
108
+ assert_type(content, :output_text)
109
+ events_to_yield <<
110
+ OpenAI::Streaming::ResponseTextDeltaEvent.new(
111
+ content_index: event.content_index,
112
+ delta: event.delta,
113
+ item_id: event.item_id,
114
+ output_index: event.output_index,
115
+ sequence_number: event.sequence_number,
116
+ type: event.type,
117
+ snapshot: content.text
118
+ )
119
+ else
120
+ # A server-directed resumed stream may begin after response.created.
121
+ # Without the omitted prefix, a snapshot would be incomplete and
122
+ # materializing every partial prefix would make streaming quadratic.
123
+ events_to_yield <<
124
+ OpenAI::Streaming::ResponseTextDeltaEvent.new(event.to_h.merge(snapshot: nil))
125
+ end
117
126
 
118
127
  when OpenAI::Models::Responses::ResponseTextDoneEvent
119
- output = @current_snapshot.output[event.output_index]
120
- assert_type(output, :message)
128
+ text = if @current_snapshot
129
+ output = @current_snapshot.output[event.output_index]
130
+ assert_type(output, :message)
121
131
 
122
- content = output.content[event.content_index]
123
- assert_type(content, :output_text)
132
+ content = output.content[event.content_index]
133
+ assert_type(content, :output_text)
134
+ content.text
135
+ else
136
+ event.text
137
+ end
124
138
 
125
- parsed = parse_structured_text(content.text)
139
+ parsed = parse_structured_text(text)
126
140
 
127
141
  events_to_yield <<
128
142
  OpenAI::Streaming::ResponseTextDoneEvent.new(
@@ -136,18 +150,26 @@ module OpenAI
136
150
  )
137
151
 
138
152
  when OpenAI::Models::Responses::ResponseFunctionCallArgumentsDeltaEvent
139
- output = @current_snapshot.output[event.output_index]
140
- assert_type(output, :function_call)
141
-
142
- events_to_yield <<
143
- OpenAI::Streaming::ResponseFunctionCallArgumentsDeltaEvent.new(
144
- delta: event.delta,
145
- item_id: event.item_id,
146
- output_index: event.output_index,
147
- sequence_number: event.sequence_number,
148
- type: event.type,
149
- snapshot: output.arguments
150
- )
153
+ if @current_snapshot
154
+ output = @current_snapshot.output[event.output_index]
155
+ assert_type(output, :function_call)
156
+ events_to_yield <<
157
+ OpenAI::Streaming::ResponseFunctionCallArgumentsDeltaEvent.new(
158
+ delta: event.delta,
159
+ item_id: event.item_id,
160
+ output_index: event.output_index,
161
+ sequence_number: event.sequence_number,
162
+ type: event.type,
163
+ snapshot: output.arguments
164
+ )
165
+ else
166
+ # See the text-delta branch above: a partial server resume has no
167
+ # complete argument prefix from which to build a truthful snapshot.
168
+ events_to_yield <<
169
+ OpenAI::Streaming::ResponseFunctionCallArgumentsDeltaEvent.new(
170
+ event.to_h.merge(snapshot: nil)
171
+ )
172
+ end
151
173
 
152
174
  when OpenAI::Models::Responses::ResponseCompletedEvent
153
175
  events_to_yield <<
@@ -167,16 +189,25 @@ module OpenAI
167
189
 
168
190
  def accumulate_event(event:, current_snapshot:)
169
191
  if current_snapshot.nil?
170
- unless event.is_a?(OpenAI::Models::Responses::ResponseCreatedEvent)
192
+ if event.is_a?(OpenAI::Models::Responses::ResponseCreatedEvent)
193
+ # Use the converter to create a new, isolated copy of the response object.
194
+ # This ensures proper type validation and prevents shared object references.
195
+ return OpenAI::Internal::Type::Converter.coerce(
196
+ OpenAI::Models::Responses::Response,
197
+ event.response
198
+ )
199
+ end
200
+
201
+ unless @resumed
171
202
  raise "Expected first event to be response.created"
172
203
  end
173
204
 
174
- # Use the converter to create a new, isolated copy of the response object.
175
- # This ensures proper type validation and prevents shared object references.
176
- return OpenAI::Internal::Type::Converter.coerce(
177
- OpenAI::Models::Responses::Response,
178
- event.response
179
- )
205
+ if event.is_a?(OpenAI::Models::Responses::ResponseCompletedEvent)
206
+ @completed_response = event.response
207
+ return event.response
208
+ end
209
+
210
+ return nil
180
211
  end
181
212
 
182
213
  case event
@@ -28,6 +28,8 @@ module OpenAI
28
28
 
29
29
  raw[:choices]&.each do |choice|
30
30
  choice.dig(:message, :tool_calls)&.each do |tool_call|
31
+ next if tool_call[:type] == "custom"
32
+
31
33
  func = tool_call.fetch(:function)
32
34
  next if (model = tool_models[func.fetch(:name)]).nil?
33
35
 
@@ -81,6 +83,10 @@ module OpenAI
81
83
  }
82
84
  }
83
85
  parsed.dig(:response_format, :json_schema).store(:schema, model.to_json_schema)
86
+ else
87
+ end
88
+
89
+ case parsed
84
90
  in {tools: Array => tools}
85
91
  mapped = tools.map do |tool|
86
92
  case tool
@@ -109,7 +109,7 @@ module OpenAI
109
109
  parameters: tool.to_json_schema
110
110
  }
111
111
  in {type: :function, parameters: OpenAI::StructuredOutput::JsonSchemaConverter => params}
112
- func = tool.fetch(:function)
112
+ func = tool.fetch(:function, tool)
113
113
  name = func[:name] ||= params.name.split("::").last
114
114
  tool_models.store(name, params)
115
115
  func.update(parameters: params.to_json_schema)
@@ -119,7 +119,7 @@ module OpenAI
119
119
  Kernel.then do
120
120
  value = @data.fetch(name_sym) { const == OpenAI::Internal::OMIT ? nil : const }
121
121
  state = OpenAI::Internal::Type::Converter.new_coerce_state(translate_names: false)
122
- if (nilable || !required) && value.nil?
122
+ if (nilable || !required) && nil.equal?(value)
123
123
  nil
124
124
  else
125
125
  OpenAI::Internal::Type::Converter.coerce(
@@ -321,7 +321,7 @@ module OpenAI
321
321
  keys.delete(src_name)
322
322
 
323
323
  state[:error] = nil
324
- converted = if item.nil? && (nilable || !required)
324
+ converted = if nil.equal?(item) && (nilable || !required)
325
325
  exactness[nilable ? :yes : :maybe] += 1
326
326
  nil
327
327
  else
@@ -199,7 +199,7 @@ module OpenAI
199
199
 
200
200
  case target
201
201
  in -> { _1 <= NilClass }
202
- exactness[value.nil? ? :yes : :maybe] += 1
202
+ exactness[nil.equal?(value) ? :yes : :maybe] += 1
203
203
  return nil
204
204
  in -> { _1 <= Integer }
205
205
  case value
@@ -510,7 +510,7 @@ module OpenAI
510
510
  IO.copy_stream(val, y)
511
511
  in StringIO
512
512
  y << format(content_line, content_type || "application/octet-stream")
513
- y << val.string
513
+ y << (val.string.byteslice(val.pos..) || "")
514
514
  in -> { primitive?(_1) }
515
515
  y << format(content_line, content_type || "text/plain")
516
516
  y << val.to_s
@@ -630,7 +630,7 @@ module OpenAI
630
630
  in [OpenAI::Internal::Util::JSON_CONTENT, Hash | Array | -> { primitive?(_1) }]
631
631
  [headers, JSON.generate(body)]
632
632
  in [OpenAI::Internal::Util::JSONL_CONTENT, Enumerable] unless OpenAI::Internal::Type::FileInput === body
633
- [headers, body.lazy.map { JSON.generate(_1) }]
633
+ [headers, body.lazy.map { "#{JSON.generate(_1)}\n" }]
634
634
  in [%r{^multipart/form-data}, Hash | OpenAI::Internal::Type::FileInput]
635
635
  boundary, strio = encode_multipart_streaming(body)
636
636
  headers = {**headers, "content-type" => "#{content_type}; boundary=#{boundary}"}
@@ -51,6 +51,13 @@ module OpenAI
51
51
  # @return [Integer, nil]
52
52
  optional :limit, Integer
53
53
 
54
+ # @!attribute line_items
55
+ # Return only costs for these exact line item names. Each value must match the
56
+ # complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
57
+ #
58
+ # @return [Array<String>, nil]
59
+ optional :line_items, OpenAI::Internal::Type::ArrayOf[String]
60
+
54
61
  # @!attribute page
55
62
  # A cursor for use in pagination. Corresponding to the `next_page` field from the
56
63
  # previous response.
@@ -64,7 +71,7 @@ module OpenAI
64
71
  # @return [Array<String>, nil]
65
72
  optional :project_ids, OpenAI::Internal::Type::ArrayOf[String]
66
73
 
67
- # @!method initialize(start_time:, api_key_ids: nil, bucket_width: nil, end_time: nil, group_by: nil, limit: nil, page: nil, project_ids: nil, request_options: {})
74
+ # @!method initialize(start_time:, api_key_ids: nil, bucket_width: nil, end_time: nil, group_by: nil, limit: nil, line_items: nil, page: nil, project_ids: nil, request_options: {})
68
75
  # Some parameter documentations has been truncated, see
69
76
  # {OpenAI::Models::Admin::Organization::UsageCostsParams} for more details.
70
77
  #
@@ -80,6 +87,8 @@ module OpenAI
80
87
  #
81
88
  # @param limit [Integer] A limit on the number of buckets to be returned. Limit can range between 1 and 1
82
89
  #
90
+ # @param line_items [Array<String>] Return only costs for these exact line item names. Each value must match the com
91
+ #
83
92
  # @param page [String] A cursor for use in pagination. Corresponding to the `next_page` field from the
84
93
  #
85
94
  # @param project_ids [Array<String>] Return only costs for these projects.
@@ -116,9 +116,9 @@ module OpenAI
116
116
  optional :metadata, OpenAI::Internal::Type::HashOf[String], nil?: true
117
117
 
118
118
  # @!attribute model
119
- # Model ID used to process the batch, like `gpt-5-2025-08-07`. OpenAI offers a
120
- # wide range of models with different capabilities, performance characteristics,
121
- # and price points. Refer to the
119
+ # Model ID used to process the batch, like `gpt-5.6-sol`. OpenAI offers a wide
120
+ # range of models with different capabilities, performance characteristics, and
121
+ # price points. Refer to the
122
122
  # [model guide](https://platform.openai.com/docs/models) to browse and compare
123
123
  # available models.
124
124
  #
@@ -183,7 +183,7 @@ module OpenAI
183
183
  #
184
184
  # @param metadata [Hash{Symbol=>String}, nil] Set of 16 key-value pairs that can be attached to an object. This can be
185
185
  #
186
- # @param model [String] Model ID used to process the batch, like `gpt-5-2025-08-07`. OpenAI
186
+ # @param model [String] Model ID used to process the batch, like `gpt-5.6-sol`. OpenAI
187
187
  #
188
188
  # @param output_file_id [String] The ID of the file containing the outputs of successfully executed requests.
189
189
  #
@@ -53,9 +53,9 @@ module OpenAI
53
53
  required :metadata, OpenAI::Internal::Type::HashOf[String], nil?: true
54
54
 
55
55
  # @!attribute model
56
- # Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
57
- # wide range of models with different capabilities, performance characteristics,
58
- # and price points. Refer to the
56
+ # Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
57
+ # range of models with different capabilities, performance characteristics, and
58
+ # price points. Refer to the
59
59
  # [model guide](https://platform.openai.com/docs/models) to browse and compare
60
60
  # available models.
61
61
  #
@@ -242,8 +242,6 @@ module OpenAI
242
242
  )
243
243
 
244
244
  # @!attribute reasoning
245
- # **gpt-5 and o-series models only**
246
- #
247
245
  # Configuration options for
248
246
  # [reasoning models](https://platform.openai.com/docs/guides/reasoning).
249
247
  #
@@ -362,7 +360,7 @@ module OpenAI
362
360
  #
363
361
  # @param metadata [Hash{Symbol=>String}, nil] Set of 16 key-value pairs that can be attached to an object. This can be
364
362
  #
365
- # @param model [Symbol, String, OpenAI::Models::Beta::BetaResponse::Model] Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI
363
+ # @param model [Symbol, String, OpenAI::Models::Beta::BetaResponse::Model] Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI
366
364
  #
367
365
  # @param output [Array<OpenAI::Models::Beta::BetaResponseOutputMessage, OpenAI::Models::Beta::BetaResponseFileSearchToolCall, OpenAI::Models::Beta::BetaResponseFunctionToolCall, OpenAI::Models::Beta::BetaResponseFunctionToolCallOutputItem, OpenAI::Models::Beta::BetaResponseOutputItem::AgentMessage, OpenAI::Models::Beta::BetaResponseOutputItem::MultiAgentCall, OpenAI::Models::Beta::BetaResponseOutputItem::MultiAgentCallOutput, OpenAI::Models::Beta::BetaResponseFunctionWebSearch, OpenAI::Models::Beta::BetaResponseComputerToolCall, OpenAI::Models::Beta::BetaResponseComputerToolCallOutputItem, OpenAI::Models::Beta::BetaResponseReasoningItem, OpenAI::Models::Beta::BetaResponseOutputItem::Program, OpenAI::Models::Beta::BetaResponseOutputItem::ProgramOutput, OpenAI::Models::Beta::BetaResponseToolSearchCall, OpenAI::Models::Beta::BetaResponseToolSearchOutputItem, OpenAI::Models::Beta::BetaResponseOutputItem::AdditionalTools, OpenAI::Models::Beta::BetaResponseCompactionItem, OpenAI::Models::Beta::BetaResponseOutputItem::ImageGenerationCall, OpenAI::Models::Beta::BetaResponseCodeInterpreterToolCall, OpenAI::Models::Beta::BetaResponseOutputItem::LocalShellCall, OpenAI::Models::Beta::BetaResponseOutputItem::LocalShellCallOutput, OpenAI::Models::Beta::BetaResponseFunctionShellToolCall, OpenAI::Models::Beta::BetaResponseFunctionShellToolCallOutput, OpenAI::Models::Beta::BetaResponseApplyPatchToolCall, OpenAI::Models::Beta::BetaResponseApplyPatchToolCallOutput, OpenAI::Models::Beta::BetaResponseOutputItem::McpCall, OpenAI::Models::Beta::BetaResponseOutputItem::McpListTools, OpenAI::Models::Beta::BetaResponseOutputItem::McpApprovalRequest, OpenAI::Models::Beta::BetaResponseOutputItem::McpApprovalResponse, OpenAI::Models::Beta::BetaResponseCustomToolCall, OpenAI::Models::Beta::BetaResponseCustomToolCallOutputItem>] An array of content items generated by the model.
368
366
  #
@@ -398,7 +396,7 @@ module OpenAI
398
396
  #
399
397
  # @param prompt_cache_retention [Symbol, OpenAI::Models::Beta::BetaResponse::PromptCacheRetention, nil] Deprecated. Use `prompt_cache_options.ttl` instead.
400
398
  #
401
- # @param reasoning [OpenAI::Models::Beta::BetaResponse::Reasoning, nil] **gpt-5 and o-series models only**
399
+ # @param reasoning [OpenAI::Models::Beta::BetaResponse::Reasoning, nil] Configuration options for
402
400
  #
403
401
  # @param safety_identifier [String, nil] A stable identifier used to help detect users of your application that may be vi
404
402
  #
@@ -438,6 +436,7 @@ module OpenAI
438
436
  extend OpenAI::Internal::Type::Enum
439
437
 
440
438
  MAX_OUTPUT_TOKENS = :max_output_tokens
439
+ MAX_MESSAGES = :max_messages
441
440
  CONTENT_FILTER = :content_filter
442
441
 
443
442
  # @!method self.values
@@ -472,9 +471,9 @@ module OpenAI
472
471
  ]
473
472
  end
474
473
 
475
- # Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
476
- # wide range of models with different capabilities, performance characteristics,
477
- # and price points. Refer to the
474
+ # Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
475
+ # range of models with different capabilities, performance characteristics, and
476
+ # price points. Refer to the
478
477
  # [model guide](https://platform.openai.com/docs/models) to browse and compare
479
478
  # available models.
480
479
  #
@@ -1290,8 +1289,6 @@ module OpenAI
1290
1289
  # Some parameter documentations has been truncated, see
1291
1290
  # {OpenAI::Models::Beta::BetaResponse::Reasoning} for more details.
1292
1291
  #
1293
- # **gpt-5 and o-series models only**
1294
- #
1295
1292
  # Configuration options for
1296
1293
  # [reasoning models](https://platform.openai.com/docs/guides/reasoning).
1297
1294
  #
@@ -34,16 +34,7 @@ module OpenAI
34
34
  # @return [Integer]
35
35
  required :total_tokens, Integer
36
36
 
37
- # @!attribute compute_units
38
- # Compute units for the request. Currently null when available.
39
- #
40
- # @return [Integer, nil]
41
- optional :compute_units, Integer, nil?: true
42
-
43
- # @!method initialize(input_tokens:, input_tokens_details:, output_tokens:, output_tokens_details:, total_tokens:, compute_units: nil)
44
- # Some parameter documentations has been truncated, see
45
- # {OpenAI::Models::Beta::BetaResponseUsage} for more details.
46
- #
37
+ # @!method initialize(input_tokens:, input_tokens_details:, output_tokens:, output_tokens_details:, total_tokens:)
47
38
  # Represents token usage details including input tokens, output tokens, a
48
39
  # breakdown of output tokens, and the total tokens used.
49
40
  #
@@ -56,8 +47,6 @@ module OpenAI
56
47
  # @param output_tokens_details [OpenAI::Models::Beta::BetaResponseUsage::OutputTokensDetails] A detailed breakdown of the output tokens.
57
48
  #
58
49
  # @param total_tokens [Integer] The total number of tokens used.
59
- #
60
- # @param compute_units [Integer, nil] Compute units for the request. Currently null when available.
61
50
 
62
51
  # @see OpenAI::Models::Beta::BetaResponseUsage#input_tokens_details
63
52
  class InputTokensDetails < OpenAI::Internal::Type::BaseModel