Skip to content

Commit a158fd6

Browse files
feat: expose response headers for both streams and errors
1 parent 003ab1d commit a158fd6

File tree

16 files changed

+114
-34
lines changed

16 files changed

+114
-34
lines changed

lib/openai/errors.rb

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class APIError < OpenAI::Errors::Error
4343
# @return [Integer, nil]
4444
attr_accessor :status
4545

46+
# @return [Hash{String=>String}, nil]
47+
attr_accessor :headers
48+
4649
# @return [Object, nil]
4750
attr_accessor :body
4851

@@ -59,13 +62,15 @@ class APIError < OpenAI::Errors::Error
5962
#
6063
# @param url [URI::Generic]
6164
# @param status [Integer, nil]
65+
# @param headers [Hash{String=>String}, nil]
6266
# @param body [Object, nil]
6367
# @param request [nil]
6468
# @param response [nil]
6569
# @param message [String, nil]
66-
def initialize(url:, status: nil, body: nil, request: nil, response: nil, message: nil)
70+
def initialize(url:, status: nil, headers: nil, body: nil, request: nil, response: nil, message: nil)
6771
@url = url
6872
@status = status
73+
@headers = headers
6974
@body = body
7075
@request = request
7176
@response = response
@@ -98,13 +103,15 @@ class APIConnectionError < OpenAI::Errors::APIError
98103
#
99104
# @param url [URI::Generic]
100105
# @param status [nil]
106+
# @param headers [Hash{String=>String}, nil]
101107
# @param body [nil]
102108
# @param request [nil]
103109
# @param response [nil]
104110
# @param message [String, nil]
105111
def initialize(
106112
url:,
107113
status: nil,
114+
headers: nil,
108115
body: nil,
109116
request: nil,
110117
response: nil,
@@ -119,13 +126,15 @@ class APITimeoutError < OpenAI::Errors::APIConnectionError
119126
#
120127
# @param url [URI::Generic]
121128
# @param status [nil]
129+
# @param headers [Hash{String=>String}, nil]
122130
# @param body [nil]
123131
# @param request [nil]
124132
# @param response [nil]
125133
# @param message [String, nil]
126134
def initialize(
127135
url:,
128136
status: nil,
137+
headers: nil,
129138
body: nil,
130139
request: nil,
131140
response: nil,
@@ -140,21 +149,24 @@ class APIStatusError < OpenAI::Errors::APIError
140149
#
141150
# @param url [URI::Generic]
142151
# @param status [Integer]
152+
# @param headers [Hash{String=>String}, nil]
143153
# @param body [Object, nil]
144154
# @param request [nil]
145155
# @param response [nil]
146156
# @param message [String, nil]
147157
#
148158
# @return [self]
149-
def self.for(url:, status:, body:, request:, response:, message: nil)
150-
kwargs = {
151-
url: url,
152-
status: status,
153-
body: body,
154-
request: request,
155-
response: response,
156-
message: message
157-
}
159+
def self.for(url:, status:, headers:, body:, request:, response:, message: nil)
160+
kwargs =
161+
{
162+
url: url,
163+
status: status,
164+
headers: headers,
165+
body: body,
166+
request: request,
167+
response: response,
168+
message: message
169+
}
158170

159171
case status
160172
in 400
@@ -198,18 +210,20 @@ def self.for(url:, status:, body:, request:, response:, message: nil)
198210
#
199211
# @param url [URI::Generic]
200212
# @param status [Integer]
213+
# @param headers [Hash{String=>String}, nil]
201214
# @param body [Object, nil]
202215
# @param request [nil]
203216
# @param response [nil]
204217
# @param message [String, nil]
205-
def initialize(url:, status:, body:, request:, response:, message: nil)
218+
def initialize(url:, status:, headers:, body:, request:, response:, message: nil)
206219
message ||= OpenAI::Internal::Util.dig(body, :message) { {url: url.to_s, status: status, body: body} }
207220
@code = OpenAI::Internal::Type::Converter.coerce(String, OpenAI::Internal::Util.dig(body, :code))
208221
@param = OpenAI::Internal::Type::Converter.coerce(String, OpenAI::Internal::Util.dig(body, :param))
209222
@type = OpenAI::Internal::Type::Converter.coerce(String, OpenAI::Internal::Util.dig(body, :type))
210223
super(
211224
url: url,
212225
status: status,
226+
headers: headers,
213227
body: body,
214228
request: request,
215229
response: response,

lib/openai/internal/conversation_cursor_page.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def auto_paging_each(&blk)
6363
#
6464
# @param client [OpenAI::Internal::Transport::BaseClient]
6565
# @param req [Hash{Symbol=>Object}]
66-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
66+
# @param headers [Hash{String=>String}]
6767
# @param page_data [Hash{Symbol=>Object}]
6868
def initialize(client:, req:, headers:, page_data:)
6969
super

lib/openai/internal/cursor_page.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def auto_paging_each(&blk)
6060
#
6161
# @param client [OpenAI::Internal::Transport::BaseClient]
6262
# @param req [Hash{Symbol=>Object}]
63-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
63+
# @param headers [Hash{String=>String}]
6464
# @param page_data [Hash{Symbol=>Object}]
6565
def initialize(client:, req:, headers:, page_data:)
6666
super

lib/openai/internal/page.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def auto_paging_each(&blk)
5454
#
5555
# @param client [OpenAI::Internal::Transport::BaseClient]
5656
# @param req [Hash{Symbol=>Object}]
57-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
57+
# @param headers [Hash{String=>String}]
5858
# @param page_data [Array<Object>]
5959
def initialize(client:, req:, headers:, page_data:)
6060
super

lib/openai/internal/stream.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Stream
4141
err = OpenAI::Errors::APIStatusError.for(
4242
url: @url,
4343
status: @status,
44+
headers: @headers,
4445
body: data,
4546
request: nil,
4647
response: @response,

lib/openai/internal/transport/base_client.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def validate!(req)
4747
# @api private
4848
#
4949
# @param status [Integer]
50-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
50+
# @param headers [Hash{String=>String}]
5151
#
5252
# @return [Boolean]
5353
def should_retry?(status, headers:)
@@ -85,7 +85,7 @@ def should_retry?(status, headers:)
8585
#
8686
# @param status [Integer]
8787
#
88-
# @param response_headers [Hash{String=>String}, Net::HTTPHeader]
88+
# @param response_headers [Hash{String=>String}]
8989
#
9090
# @return [Hash{Symbol=>Object}]
9191
def follow_redirect(request, status:, response_headers:)
@@ -378,6 +378,7 @@ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
378378
rescue OpenAI::Errors::APIConnectionError => e
379379
status = e
380380
end
381+
headers = OpenAI::Internal::Util.normalized_headers(response&.each_header&.to_h)
381382

382383
case status
383384
in ..299
@@ -390,7 +391,7 @@ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
390391
in 300..399
391392
self.class.reap_connection!(status, stream: stream)
392393

393-
request = self.class.follow_redirect(request, status: status, response_headers: response)
394+
request = self.class.follow_redirect(request, status: status, response_headers: headers)
394395
send_request(
395396
request,
396397
redirect_count: redirect_count + 1,
@@ -399,16 +400,17 @@ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
399400
)
400401
in OpenAI::Errors::APIConnectionError if retry_count >= max_retries
401402
raise status
402-
in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: response)
403+
in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: headers)
403404
decoded = Kernel.then do
404-
OpenAI::Internal::Util.decode_content(response, stream: stream, suppress_error: true)
405+
OpenAI::Internal::Util.decode_content(headers, stream: stream, suppress_error: true)
405406
ensure
406407
self.class.reap_connection!(status, stream: stream)
407408
end
408409

409410
raise OpenAI::Errors::APIStatusError.for(
410411
url: url,
411412
status: status,
413+
headers: headers,
412414
body: decoded,
413415
request: nil,
414416
response: response
@@ -485,19 +487,21 @@ def request(req)
485487
send_retry_header: send_retry_header
486488
)
487489

488-
decoded = OpenAI::Internal::Util.decode_content(response, stream: stream)
490+
headers = OpenAI::Internal::Util.normalized_headers(response.each_header.to_h)
491+
decoded = OpenAI::Internal::Util.decode_content(headers, stream: stream)
489492
case req
490493
in {stream: Class => st}
491494
st.new(
492495
model: model,
493496
url: url,
494497
status: status,
498+
headers: headers,
495499
response: response,
496500
unwrap: unwrap,
497501
stream: decoded
498502
)
499503
in {page: Class => page}
500-
page.new(client: self, req: req, headers: response, page_data: decoded)
504+
page.new(client: self, req: req, headers: headers, page_data: decoded)
501505
else
502506
unwrapped = OpenAI::Internal::Util.dig(decoded, unwrap)
503507
OpenAI::Internal::Type::Converter.coerce(model, unwrapped)

lib/openai/internal/type/base_page.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def to_enum = super(:auto_paging_each)
3939
#
4040
# @param client [OpenAI::Internal::Transport::BaseClient]
4141
# @param req [Hash{Symbol=>Object}]
42-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
42+
# @param headers [Hash{String=>String}]
4343
# @param page_data [Object]
4444
def initialize(client:, req:, headers:, page_data:)
4545
@client = client

lib/openai/internal/type/base_stream.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ class << self
2828
def defer_closing(stream) = ->(_id) { OpenAI::Internal::Util.close_fused!(stream) }
2929
end
3030

31+
# @return [Integer]
32+
attr_reader :status
33+
34+
# @return [Hash{String=>String}]
35+
attr_reader :headers
36+
3137
# @api public
3238
#
3339
# @return [void]
@@ -63,13 +69,15 @@ def to_enum = @iterator
6369
# @param model [Class, OpenAI::Internal::Type::Converter]
6470
# @param url [URI::Generic]
6571
# @param status [Integer]
72+
# @param headers [Hash{String=>String}]
6673
# @param response [Net::HTTPResponse]
6774
# @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc]
6875
# @param stream [Enumerable<Object>]
69-
def initialize(model:, url:, status:, response:, unwrap:, stream:)
76+
def initialize(model:, url:, status:, headers:, response:, unwrap:, stream:)
7077
@model = model
7178
@url = url
7279
@status = status
80+
@headers = headers
7381
@response = response
7482
@unwrap = unwrap
7583
@stream = stream

lib/openai/internal/util.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ def force_charset!(content_type, text:)
647647
#
648648
# Assumes each chunk in stream has `Encoding::BINARY`.
649649
#
650-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
650+
# @param headers [Hash{String=>String}]
651651
# @param stream [Enumerable<String>]
652652
# @param suppress_error [Boolean]
653653
#

rbi/openai/errors.rbi

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ module OpenAI
3333
sig { returns(T.nilable(Integer)) }
3434
attr_accessor :status
3535

36+
sig { returns(T.nilable(T::Hash[String, String])) }
37+
attr_accessor :headers
38+
3639
sig { returns(T.nilable(T.anything)) }
3740
attr_accessor :body
3841

@@ -50,6 +53,7 @@ module OpenAI
5053
params(
5154
url: URI::Generic,
5255
status: T.nilable(Integer),
56+
headers: T.nilable(T::Hash[String, String]),
5357
body: T.nilable(Object),
5458
request: NilClass,
5559
response: NilClass,
@@ -59,6 +63,7 @@ module OpenAI
5963
def self.new(
6064
url:,
6165
status: nil,
66+
headers: nil,
6267
body: nil,
6368
request: nil,
6469
response: nil,
@@ -88,6 +93,7 @@ module OpenAI
8893
params(
8994
url: URI::Generic,
9095
status: NilClass,
96+
headers: T.nilable(T::Hash[String, String]),
9197
body: NilClass,
9298
request: NilClass,
9399
response: NilClass,
@@ -97,6 +103,7 @@ module OpenAI
97103
def self.new(
98104
url:,
99105
status: nil,
106+
headers: nil,
100107
body: nil,
101108
request: nil,
102109
response: nil,
@@ -111,6 +118,7 @@ module OpenAI
111118
params(
112119
url: URI::Generic,
113120
status: NilClass,
121+
headers: T.nilable(T::Hash[String, String]),
114122
body: NilClass,
115123
request: NilClass,
116124
response: NilClass,
@@ -120,6 +128,7 @@ module OpenAI
120128
def self.new(
121129
url:,
122130
status: nil,
131+
headers: nil,
123132
body: nil,
124133
request: nil,
125134
response: nil,
@@ -134,13 +143,22 @@ module OpenAI
134143
params(
135144
url: URI::Generic,
136145
status: Integer,
146+
headers: T.nilable(T::Hash[String, String]),
137147
body: T.nilable(Object),
138148
request: NilClass,
139149
response: NilClass,
140150
message: T.nilable(String)
141151
).returns(T.attached_class)
142152
end
143-
def self.for(url:, status:, body:, request:, response:, message: nil)
153+
def self.for(
154+
url:,
155+
status:,
156+
headers:,
157+
body:,
158+
request:,
159+
response:,
160+
message: nil
161+
)
144162
end
145163

146164
sig { returns(Integer) }
@@ -160,13 +178,22 @@ module OpenAI
160178
params(
161179
url: URI::Generic,
162180
status: Integer,
181+
headers: T.nilable(T::Hash[String, String]),
163182
body: T.nilable(Object),
164183
request: NilClass,
165184
response: NilClass,
166185
message: T.nilable(String)
167186
).returns(T.attached_class)
168187
end
169-
def self.new(url:, status:, body:, request:, response:, message: nil)
188+
def self.new(
189+
url:,
190+
status:,
191+
headers:,
192+
body:,
193+
request:,
194+
response:,
195+
message: nil
196+
)
170197
end
171198
end
172199

0 commit comments

Comments
 (0)