Skip to content

Commit 5db14bc

Browse files
committed
Expose response header metadata on SDK responses
1 parent c86923e commit 5db14bc

15 files changed

Lines changed: 261 additions & 3 deletions

File tree

lib/openai/helpers/streaming/chat_completion_stream.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ class ChatCompletionStream
88

99
def initialize(raw_stream:, response_format: nil, input_tools: nil)
1010
@raw_stream = raw_stream
11+
@headers = raw_stream.openai_response_headers
12+
@openai_response_headers = @headers
13+
@status = raw_stream.status
1114
@state = ChatCompletionStreamState.new(
1215
response_format: response_format,
1316
input_tools: input_tools
@@ -17,7 +20,7 @@ def initialize(raw_stream:, response_format: nil, input_tools: nil)
1720

1821
def get_final_completion
1922
until_done
20-
@state.get_final_completion
23+
@state.get_final_completion.__set_openai_response_headers(openai_response_headers)
2124
end
2225

2326
def get_output_text

lib/openai/helpers/streaming/response_stream.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ def initialize(raw_stream:, text_format: nil, starting_after: nil)
1010
@text_format = text_format
1111
@starting_after = starting_after
1212
@raw_stream = raw_stream
13+
@headers = raw_stream.openai_response_headers
14+
@openai_response_headers = @headers
15+
@status = raw_stream.status
1316
@iterator = iterator
1417
@state = ResponseStreamState.new(
1518
text_format: text_format
@@ -36,7 +39,7 @@ def get_final_response
3639
until_done
3740
response = @state.completed_response
3841
raise RuntimeError.new("Didn't receive a 'response.completed' event") unless response
39-
response
42+
response.__set_openai_response_headers(openai_response_headers)
4043
end
4144

4245
def get_output_text

lib/openai/internal/transport/base_client.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,22 @@ def request(req)
510510
page.new(client: self, req: req, headers: headers, page_data: decoded)
511511
else
512512
unwrapped = OpenAI::Internal::Util.dig(decoded, unwrap)
513-
OpenAI::Internal::Type::Converter.coerce(model, unwrapped)
513+
attach_response_metadata(OpenAI::Internal::Type::Converter.coerce(model, unwrapped), headers)
514514
end
515515
end
516516

517+
# @api private
518+
#
519+
# @param result [Object]
520+
# @param headers [Hash{String=>String}]
521+
#
522+
# @return [Object]
523+
private def attach_response_metadata(result, headers)
524+
return result unless result.respond_to?(:__set_openai_response_headers)
525+
526+
result.__set_openai_response_headers(headers)
527+
end
528+
517529
# @api private
518530
#
519531
# @return [String]

lib/openai/internal/type/base_model.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ class BaseModel
88
extend OpenAI::Internal::Type::Converter
99
extend OpenAI::Internal::Util::SorbetRuntimeSupport
1010

11+
# @api public
12+
#
13+
# @return [Hash{String=>String}, nil]
14+
attr_reader :openai_response_headers
15+
1116
class << self
1217
# @api private
1318
#
@@ -409,6 +414,16 @@ def [](key)
409414
@data[key]
410415
end
411416

417+
# @api public
418+
#
419+
# @return [Float, nil]
420+
def openai_processing_ms = @openai_response_headers&.[]("openai-processing-ms")&.to_f
421+
422+
# @api public
423+
#
424+
# @return [String, nil]
425+
def openai_request_id = @openai_response_headers&.[]("x-request-id")
426+
412427
# @api public
413428
#
414429
# Returns a Hash of the data underlying this object. O(1)
@@ -486,6 +501,15 @@ def initialize(data = {})
486501
end
487502
end
488503

504+
# @api private
505+
#
506+
# @param headers [Hash{String=>String}]
507+
# @return [self]
508+
def __set_openai_response_headers(headers)
509+
@openai_response_headers = headers
510+
self
511+
end
512+
489513
class << self
490514
# @api private
491515
#

lib/openai/internal/type/base_page.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ module Type
99
#
1010
# This module provides a base implementation for paginated responses in the SDK.
1111
module BasePage
12+
# @api public
13+
#
14+
# @return [Hash{String=>String}, nil]
15+
attr_reader :openai_response_headers
16+
1217
# rubocop:disable Lint/UnusedMethodArgument
1318

1419
# @api public
@@ -35,6 +40,16 @@ def to_enum = super(:auto_paging_each)
3540

3641
alias_method :enum_for, :to_enum
3742

43+
# @api public
44+
#
45+
# @return [Float, nil]
46+
def openai_processing_ms = @openai_response_headers&.[]("openai-processing-ms")&.to_f
47+
48+
# @api public
49+
#
50+
# @return [String, nil]
51+
def openai_request_id = @openai_response_headers&.[]("x-request-id")
52+
3853
# @api private
3954
#
4055
# @param client [OpenAI::Internal::Transport::BaseClient]
@@ -45,9 +60,19 @@ def initialize(client:, req:, headers:, page_data:)
4560
@client = client
4661
@req = req
4762
@model = req.fetch(:model)
63+
@openai_response_headers = headers
4864
super()
4965
end
5066

67+
# @api private
68+
#
69+
# @param headers [Hash{String=>String}]
70+
# @return [self]
71+
def __set_openai_response_headers(headers)
72+
@openai_response_headers = headers
73+
self
74+
end
75+
5176
# rubocop:enable Lint/UnusedMethodArgument
5277
end
5378
end

lib/openai/internal/type/base_stream.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ module BaseStream
1919
# @return [Hash{String=>String}]
2020
attr_reader :headers
2121

22+
# @api public
23+
#
24+
# @return [Hash{String=>String}, nil]
25+
def openai_response_headers = @openai_response_headers || @headers
26+
27+
# @api public
28+
#
29+
# @return [Float, nil]
30+
def openai_processing_ms = openai_response_headers&.[]("openai-processing-ms")&.to_f
31+
32+
# @api public
33+
#
34+
# @return [String, nil]
35+
def openai_request_id = openai_response_headers&.[]("x-request-id")
36+
2237
# @api public
2338
#
2439
# @return [void]
@@ -62,13 +77,23 @@ def initialize(model:, url:, status:, headers:, response:, unwrap:, stream:)
6277
@model = model
6378
@url = url
6479
@status = status
80+
@openai_response_headers = headers
6581
@headers = headers
6682
@response = response
6783
@unwrap = unwrap
6884
@stream = stream
6985
@iterator = iterator
7086
end
7187

88+
# @api private
89+
#
90+
# @param headers [Hash{String=>String}]
91+
# @return [self]
92+
def __set_openai_response_headers(headers)
93+
@openai_response_headers = headers
94+
self
95+
end
96+
7297
# @api private
7398
#
7499
# @return [String]

rbi/openai/internal/type/base_model.rbi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ module OpenAI
219219
def [](key)
220220
end
221221

222+
sig { returns(T.nilable(T::Hash[String, String])) }
223+
attr_reader :openai_response_headers
224+
225+
sig { returns(T.nilable(Float)) }
226+
def openai_processing_ms
227+
end
228+
229+
sig { returns(T.nilable(String)) }
230+
def openai_request_id
231+
end
232+
222233
# Returns a Hash of the data underlying this object. O(1)
223234
#
224235
# Keys are Symbols and values are the raw values from the response. The return
@@ -278,6 +289,10 @@ module OpenAI
278289
def self.new(data = {})
279290
end
280291

292+
sig { params(headers: T::Hash[String, String]).returns(T.self_type) }
293+
def __set_openai_response_headers(headers)
294+
end
295+
281296
class << self
282297
# @api private
283298
sig { params(depth: Integer).returns(String) }

rbi/openai/internal/type/base_page.rbi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ module OpenAI
2525
def to_enum
2626
end
2727

28+
sig { returns(T.nilable(T::Hash[String, String])) }
29+
attr_reader :openai_response_headers
30+
31+
sig { returns(T.nilable(Float)) }
32+
def openai_processing_ms
33+
end
34+
35+
sig { returns(T.nilable(String)) }
36+
def openai_request_id
37+
end
38+
2839
# @api private
2940
sig do
3041
params(
@@ -36,6 +47,10 @@ module OpenAI
3647
end
3748
def initialize(client:, req:, headers:, page_data:)
3849
end
50+
51+
sig { params(headers: T::Hash[String, String]).returns(T.self_type) }
52+
def __set_openai_response_headers(headers)
53+
end
3954
end
4055
end
4156
end

rbi/openai/internal/type/base_stream.rbi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ module OpenAI
1818
sig { returns(T::Hash[String, String]) }
1919
attr_reader :headers
2020

21+
sig { returns(T.nilable(T::Hash[String, String])) }
22+
def openai_response_headers
23+
end
24+
25+
sig { returns(T.nilable(Float)) }
26+
def openai_processing_ms
27+
end
28+
29+
sig { returns(T.nilable(String)) }
30+
def openai_request_id
31+
end
32+
2133
sig { void }
2234
def close
2335
end
@@ -65,6 +77,10 @@ module OpenAI
6577
)
6678
end
6779

80+
sig { params(headers: T::Hash[String, String]).returns(T.self_type) }
81+
def __set_openai_response_headers(headers)
82+
end
83+
6884
# @api private
6985
sig { returns(String) }
7086
def inspect

sig/openai/internal/type/base_model.rbs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ module OpenAI
7777

7878
def []: (Symbol key) -> top?
7979

80+
def openai_response_headers: -> ::Hash[String, String]?
81+
82+
def openai_processing_ms: -> Float?
83+
84+
def openai_request_id: -> String?
85+
8086
def to_h: -> ::Hash[Symbol, top]
8187

8288
alias to_hash to_h
@@ -91,6 +97,8 @@ module OpenAI
9197

9298
def initialize: (?::Hash[Symbol, top] | instance data) -> void
9399

100+
def __set_openai_response_headers: (::Hash[String, String] headers) -> self
101+
94102
def self.inspect: (?depth: Integer) -> String
95103

96104
def to_s: -> String

0 commit comments

Comments
 (0)