openai 0.78.0 → 0.80.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +99 -0
- data/README.md +241 -8
- data/SECURITY.md +6 -22
- data/azure.md +110 -0
- data/bedrock.md +197 -0
- data/examples/mtls_custom_http_client.rb +61 -0
- data/lib/openai/auth/workload_identity.rb +13 -2
- data/lib/openai/auth/workload_identity_auth.rb +2 -1
- data/lib/openai/client.rb +29 -6
- data/lib/openai/errors.rb +11 -1
- data/lib/openai/helpers/streaming/chat_completion_stream.rb +3 -3
- data/lib/openai/helpers/streaming/response_stream.rb +5 -6
- data/lib/openai/helpers/structured_output/base_model.rb +20 -19
- data/lib/openai/helpers/structured_output/json_schema_converter.rb +12 -5
- data/lib/openai/http_client.rb +99 -7
- data/lib/openai/internal/conversation_cursor_page.rb +5 -6
- data/lib/openai/internal/cursor_page.rb +3 -3
- data/lib/openai/internal/logging.rb +419 -0
- data/lib/openai/internal/next_cursor_page.rb +5 -6
- data/lib/openai/internal/page.rb +2 -2
- data/lib/openai/internal/provider.rb +7 -1
- data/lib/openai/internal/read_io_adapter.rb +42 -18
- data/lib/openai/internal/stream.rb +2 -2
- data/lib/openai/internal/transport/base_client.rb +137 -37
- data/lib/openai/internal/type/array_of.rb +6 -4
- data/lib/openai/internal/type/base_model.rb +39 -1
- data/lib/openai/internal/type/base_page.rb +19 -2
- data/lib/openai/internal/type/base_stream.rb +39 -7
- data/lib/openai/internal/type/hash_of.rb +3 -3
- data/lib/openai/internal/util.rb +52 -27
- data/lib/openai/models/admin/organization/audit_log_list_params.rb +3 -1
- data/lib/openai/models/admin/organization/audit_log_list_response.rb +33 -11
- data/lib/openai/models/admin/organization/groups/role_list_response.rb +5 -1
- data/lib/openai/models/admin/organization/groups/role_retrieve_response.rb +5 -1
- data/lib/openai/models/admin/organization/projects/groups/role_list_response.rb +6 -1
- data/lib/openai/models/admin/organization/projects/groups/role_retrieve_response.rb +6 -1
- data/lib/openai/models/admin/organization/projects/service_account_create_response.rb +3 -1
- data/lib/openai/models/admin/organization/projects/users/role_list_response.rb +6 -1
- data/lib/openai/models/admin/organization/projects/users/role_retrieve_response.rb +6 -1
- data/lib/openai/models/admin/organization/users/role_list_response.rb +5 -1
- data/lib/openai/models/admin/organization/users/role_retrieve_response.rb +5 -1
- data/lib/openai/models/all_models.rb +5 -0
- data/lib/openai/models/audio/transcription_create_params.rb +7 -1
- data/lib/openai/models/audio/translation_create_params.rb +7 -1
- data/lib/openai/models/beta/beta_mcp_tool_call_error.rb +84 -0
- data/lib/openai/models/beta/beta_response.rb +27 -0
- data/lib/openai/models/beta/beta_response_input_item.rb +3 -3
- data/lib/openai/models/beta/beta_response_item.rb +3 -3
- data/lib/openai/models/beta/beta_response_output_item.rb +3 -3
- data/lib/openai/models/beta/beta_response_output_item_added_event.rb +5 -2
- data/lib/openai/models/beta/beta_response_output_text_annotation_added_event.rb +201 -4
- data/lib/openai/models/beta/beta_response_reasoning_item.rb +5 -0
- data/lib/openai/models/beta/beta_response_stream_event.rb +1 -1
- data/lib/openai/models/beta/beta_responses_client_event.rb +46 -3
- data/lib/openai/models/beta/beta_responses_server_event.rb +1143 -63
- data/lib/openai/models/beta/beta_web_search_tool.rb +11 -1
- data/lib/openai/models/beta/response_compact_params.rb +18 -0
- data/lib/openai/models/beta/response_create_params.rb +27 -0
- data/lib/openai/models/chat_model.rb +1 -0
- data/lib/openai/models/completion_usage.rb +26 -2
- data/lib/openai/models/containers/file_create_params.rb +4 -0
- data/lib/openai/models/content_provenance_check_create_params.rb +4 -0
- data/lib/openai/models/conversations/conversation_item.rb +3 -3
- data/lib/openai/models/file_create_params.rb +4 -0
- data/lib/openai/models/image_create_variation_params.rb +4 -0
- data/lib/openai/models/image_edit_params.rb +8 -0
- data/lib/openai/models/model.rb +9 -1
- data/lib/openai/models/responses/mcp_tool_call_error.rb +82 -0
- data/lib/openai/models/responses/response.rb +9 -0
- data/lib/openai/models/responses/response_compact_params.rb +18 -0
- data/lib/openai/models/responses/response_create_params.rb +9 -0
- data/lib/openai/models/responses/response_input_item.rb +3 -3
- data/lib/openai/models/responses/response_item.rb +3 -3
- data/lib/openai/models/responses/response_output_item.rb +3 -3
- data/lib/openai/models/responses/response_output_item_added_event.rb +5 -2
- data/lib/openai/models/responses/response_output_text_annotation_added_event.rb +201 -4
- data/lib/openai/models/responses/response_reasoning_item.rb +5 -0
- data/lib/openai/models/responses/response_stream_event.rb +1 -1
- data/lib/openai/models/responses/responses_client_event.rb +22 -1
- data/lib/openai/models/responses/responses_server_event.rb +1107 -56
- data/lib/openai/models/responses/web_search_tool.rb +11 -1
- data/lib/openai/models/responses_model.rb +5 -0
- data/lib/openai/models/skill_create_params.rb +4 -0
- data/lib/openai/models/skills/version_create_params.rb +4 -0
- data/lib/openai/models/uploads/part_create_params.rb +4 -0
- data/lib/openai/models/vector_store_search_response.rb +3 -1
- data/lib/openai/models/video_create_character_params.rb +4 -0
- data/lib/openai/models/video_create_params.rb +4 -0
- data/lib/openai/models/video_edit_params.rb +4 -0
- data/lib/openai/models/video_extend_params.rb +4 -0
- data/lib/openai/models.rb +9 -1
- data/lib/openai/net_http_client.rb +20 -6
- data/lib/openai/providers/azure.rb +216 -0
- data/lib/openai/providers/bedrock.rb +130 -17
- data/lib/openai/resources/admin/organization/projects/groups/roles.rb +1 -1
- data/lib/openai/resources/admin/organization/projects/groups.rb +1 -1
- data/lib/openai/resources/admin/organization/projects/users/roles.rb +1 -1
- data/lib/openai/resources/admin/organization/projects.rb +4 -2
- data/lib/openai/resources/audio/transcriptions.rb +8 -0
- data/lib/openai/resources/audio/translations.rb +4 -0
- data/lib/openai/resources/beta/responses.rb +1 -1
- data/lib/openai/resources/beta/threads/runs/steps.rb +2 -2
- data/lib/openai/resources/containers/files.rb +4 -0
- data/lib/openai/resources/content_provenance_checks.rb +4 -0
- data/lib/openai/resources/conversations/items.rb +1 -1
- data/lib/openai/resources/evals/runs/output_items.rb +1 -1
- data/lib/openai/resources/files.rb +5 -1
- data/lib/openai/resources/images.rb +12 -0
- data/lib/openai/resources/responses.rb +3 -2
- data/lib/openai/resources/skills/versions.rb +4 -0
- data/lib/openai/resources/skills.rb +4 -0
- data/lib/openai/resources/uploads/parts.rb +4 -0
- data/lib/openai/resources/vector_stores/file_batches.rb +1 -1
- data/lib/openai/resources/videos.rb +38 -0
- data/lib/openai/resources/webhooks.rb +18 -6
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +111 -105
- data/rbi/openai/auth.rbi +4 -4
- data/rbi/openai/client.rbi +13 -3
- data/rbi/openai/errors.rbi +7 -0
- data/rbi/openai/helpers/streaming/events.rbi +12 -0
- data/rbi/openai/http_client.rbi +62 -2
- data/rbi/openai/internal/logging.rbi +192 -0
- data/rbi/openai/internal/provider.rbi +3 -0
- data/rbi/openai/internal/read_io_adapter.rbi +6 -1
- data/rbi/openai/internal/transport/base_client.rbi +61 -7
- data/rbi/openai/internal/type/base_model.rbi +24 -1
- data/rbi/openai/internal/type/base_page.rbi +13 -2
- data/rbi/openai/internal/type/base_stream.rbi +15 -4
- data/rbi/openai/internal/util.rbi +26 -2
- data/rbi/openai/models/admin/organization/audit_log_list_params.rbi +5 -0
- data/rbi/openai/models/admin/organization/audit_log_list_response.rbi +5 -0
- data/rbi/openai/models/all_models.rbi +25 -0
- data/rbi/openai/models/audio/transcription_create_params.rbi +14 -2
- data/rbi/openai/models/audio/translation_create_params.rbi +14 -2
- data/rbi/openai/models/beta/beta_mcp_tool_call_error.rbi +122 -0
- data/rbi/openai/models/beta/beta_response.rbi +47 -0
- data/rbi/openai/models/beta/beta_response_input_item.rbi +27 -3
- data/rbi/openai/models/beta/beta_response_item.rbi +27 -3
- data/rbi/openai/models/beta/beta_response_output_item.rbi +27 -3
- data/rbi/openai/models/beta/beta_response_output_item_added_event.rbi +8 -2
- data/rbi/openai/models/beta/beta_response_output_text_annotation_added_event.rbi +297 -5
- data/rbi/openai/models/beta/beta_response_reasoning_item.rbi +10 -0
- data/rbi/openai/models/beta/beta_response_stream_event.rbi +1 -1
- data/rbi/openai/models/beta/beta_responses_client_event.rbi +69 -1
- data/rbi/openai/models/beta/beta_responses_server_event.rbi +3945 -54
- data/rbi/openai/models/beta/beta_web_search_tool.rbi +15 -0
- data/rbi/openai/models/beta/response_compact_params.rbi +30 -0
- data/rbi/openai/models/beta/response_create_params.rbi +47 -0
- data/rbi/openai/models/chat_model.rbi +2 -0
- data/rbi/openai/models/completion_usage.rbi +39 -6
- data/rbi/openai/models/containers/file_create_params.rbi +8 -0
- data/rbi/openai/models/content_provenance_check_create_params.rbi +8 -0
- data/rbi/openai/models/conversations/conversation_item.rbi +12 -3
- data/rbi/openai/models/file_create_params.rbi +8 -0
- data/rbi/openai/models/image_create_variation_params.rbi +8 -0
- data/rbi/openai/models/image_edit_params.rbi +16 -0
- data/rbi/openai/models/model.rbi +14 -1
- data/rbi/openai/models/responses/mcp_tool_call_error.rbi +120 -0
- data/rbi/openai/models/responses/response.rbi +17 -0
- data/rbi/openai/models/responses/response_compact_params.rbi +30 -0
- data/rbi/openai/models/responses/response_create_params.rbi +17 -0
- data/rbi/openai/models/responses/response_input_item.rbi +27 -3
- data/rbi/openai/models/responses/response_item.rbi +27 -3
- data/rbi/openai/models/responses/response_output_item.rbi +27 -3
- data/rbi/openai/models/responses/response_output_item_added_event.rbi +8 -2
- data/rbi/openai/models/responses/response_output_text_annotation_added_event.rbi +297 -5
- data/rbi/openai/models/responses/response_reasoning_item.rbi +10 -0
- data/rbi/openai/models/responses/response_stream_event.rbi +1 -1
- data/rbi/openai/models/responses/responses_client_event.rbi +36 -0
- data/rbi/openai/models/responses/responses_server_event.rbi +3385 -53
- data/rbi/openai/models/responses/web_search_tool.rbi +15 -0
- data/rbi/openai/models/responses_model.rbi +25 -0
- data/rbi/openai/models/skill_create_params.rbi +8 -0
- data/rbi/openai/models/skills/version_create_params.rbi +8 -0
- data/rbi/openai/models/uploads/part_create_params.rbi +8 -0
- data/rbi/openai/models/video_create_character_params.rbi +8 -0
- data/rbi/openai/models/video_create_params.rbi +8 -0
- data/rbi/openai/models/video_edit_params.rbi +8 -0
- data/rbi/openai/models/video_extend_params.rbi +8 -0
- data/rbi/openai/net_http_client.rbi +2 -2
- data/rbi/openai/providers.rbi +11 -0
- data/rbi/openai/resources/audio/transcriptions.rbi +14 -2
- data/rbi/openai/resources/audio/translations.rbi +7 -1
- data/rbi/openai/resources/beta/responses.rbi +8 -0
- data/rbi/openai/resources/containers/files.rbi +4 -0
- data/rbi/openai/resources/content_provenance_checks.rbi +4 -0
- data/rbi/openai/resources/files.rbi +5 -1
- data/rbi/openai/resources/images.rbi +20 -0
- data/rbi/openai/resources/responses.rbi +8 -0
- data/rbi/openai/resources/skills/versions.rbi +4 -0
- data/rbi/openai/resources/skills.rbi +4 -0
- data/rbi/openai/resources/uploads/parts.rbi +4 -0
- data/rbi/openai/resources/videos.rbi +16 -0
- data/sig/openai/client.rbs +6 -2
- data/sig/openai/errors.rbs +5 -0
- data/sig/openai/http_client.rbs +37 -2
- data/sig/openai/internal/logging.rbs +88 -0
- data/sig/openai/internal/provider.rbs +3 -1
- data/sig/openai/internal/read_io_adapter.rbs +3 -1
- data/sig/openai/internal/transport/base_client.rbs +33 -5
- data/sig/openai/internal/type/base_model.rbs +13 -0
- data/sig/openai/internal/type/base_page.rbs +8 -1
- data/sig/openai/internal/type/base_stream.rbs +8 -2
- data/sig/openai/internal/util.rbs +12 -0
- data/sig/openai/models/admin/organization/audit_log_list_params.rbs +2 -0
- data/sig/openai/models/admin/organization/audit_log_list_response.rbs +2 -0
- data/sig/openai/models/all_models.rbs +10 -0
- data/sig/openai/models/beta/beta_mcp_tool_call_error.rbs +75 -0
- data/sig/openai/models/beta/beta_response.rbs +14 -1
- data/sig/openai/models/beta/beta_response_input_item.rbs +4 -4
- data/sig/openai/models/beta/beta_response_item.rbs +4 -4
- data/sig/openai/models/beta/beta_response_output_item.rbs +4 -4
- data/sig/openai/models/beta/beta_response_output_text_annotation_added_event.rbs +149 -4
- data/sig/openai/models/beta/beta_responses_client_event.rbs +21 -1
- data/sig/openai/models/beta/beta_responses_server_event.rbs +2005 -53
- data/sig/openai/models/beta/beta_web_search_tool.rbs +7 -0
- data/sig/openai/models/beta/response_compact_params.rbs +12 -0
- data/sig/openai/models/beta/response_create_params.rbs +14 -1
- data/sig/openai/models/chat_model.rbs +2 -0
- data/sig/openai/models/completion_usage.rbs +27 -6
- data/sig/openai/models/conversations/conversation_item.rbs +4 -4
- data/sig/openai/models/model.rbs +12 -2
- data/sig/openai/models/responses/mcp_tool_call_error.rbs +73 -0
- data/sig/openai/models/responses/response.rbs +2 -1
- data/sig/openai/models/responses/response_compact_params.rbs +12 -0
- data/sig/openai/models/responses/response_create_params.rbs +2 -1
- data/sig/openai/models/responses/response_input_item.rbs +4 -4
- data/sig/openai/models/responses/response_item.rbs +4 -4
- data/sig/openai/models/responses/response_output_item.rbs +4 -4
- data/sig/openai/models/responses/response_output_text_annotation_added_event.rbs +149 -4
- data/sig/openai/models/responses/responses_client_event.rbs +9 -1
- data/sig/openai/models/responses/responses_server_event.rbs +1834 -53
- data/sig/openai/models/responses/web_search_tool.rbs +7 -0
- data/sig/openai/models/responses_model.rbs +10 -0
- data/sig/openai/net_http_client.rbs +2 -2
- data/sig/openai/providers.rbs +7 -0
- metadata +30 -1
|
@@ -36,7 +36,6 @@ module OpenAI
|
|
|
36
36
|
def to_a
|
|
37
37
|
values = []
|
|
38
38
|
loop { values << self.next }
|
|
39
|
-
rescue StopIteration
|
|
40
39
|
values
|
|
41
40
|
end
|
|
42
41
|
|
|
@@ -70,18 +69,36 @@ module OpenAI
|
|
|
70
69
|
#
|
|
71
70
|
# @param max_len [Integer, nil]
|
|
72
71
|
#
|
|
73
|
-
# @return [String]
|
|
72
|
+
# @return [String, nil]
|
|
74
73
|
private def read_enum(max_len)
|
|
75
74
|
case max_len
|
|
76
75
|
in nil
|
|
77
|
-
|
|
76
|
+
# `loop` rescues StopIteration, but this method handles it below.
|
|
77
|
+
# rubocop:disable Style/InfiniteLoop
|
|
78
|
+
@buf << @stream.next.b while true
|
|
79
|
+
# rubocop:enable Style/InfiniteLoop
|
|
78
80
|
in Integer
|
|
79
|
-
@buf << @stream.next while @buf.
|
|
80
|
-
|
|
81
|
+
@buf << @stream.next.b while @buf.bytesize < max_len
|
|
82
|
+
read_buffer(max_len)
|
|
81
83
|
end
|
|
82
84
|
rescue StopIteration
|
|
85
|
+
return @buf.slice!(0..) if max_len.nil?
|
|
86
|
+
|
|
83
87
|
@stream = nil
|
|
84
|
-
@buf.
|
|
88
|
+
return nil if @buf.bytesize.zero?
|
|
89
|
+
|
|
90
|
+
read_buffer(max_len)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @api private
|
|
94
|
+
#
|
|
95
|
+
# @param max_len [Integer]
|
|
96
|
+
#
|
|
97
|
+
# @return [String]
|
|
98
|
+
private def read_buffer(max_len)
|
|
99
|
+
read = @buf.byteslice(0, max_len)
|
|
100
|
+
@buf = @buf.byteslice(max_len..) || String.new.b
|
|
101
|
+
read
|
|
85
102
|
end
|
|
86
103
|
|
|
87
104
|
# @api private
|
|
@@ -91,19 +108,26 @@ module OpenAI
|
|
|
91
108
|
#
|
|
92
109
|
# @return [String, nil]
|
|
93
110
|
def read(max_len = nil, out_string = nil)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
read = read_enum(max_len)
|
|
101
|
-
case out_string
|
|
102
|
-
in String
|
|
103
|
-
out_string.replace(read)
|
|
111
|
+
if max_len.is_a?(Integer) && max_len.negative?
|
|
112
|
+
raise ArgumentError, "negative length #{max_len} given"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
read =
|
|
116
|
+
case @stream
|
|
104
117
|
in nil
|
|
105
|
-
|
|
118
|
+
max_len.nil? || max_len.zero? ? +"" : nil
|
|
119
|
+
in IO | StringIO
|
|
120
|
+
return @stream.read(max_len, out_string).tap(&@blk)
|
|
121
|
+
in Enumerator | InterruptibleEnumerator
|
|
122
|
+
read_enum(max_len)
|
|
106
123
|
end
|
|
124
|
+
|
|
125
|
+
case out_string
|
|
126
|
+
in String
|
|
127
|
+
out_string.replace(read || +"")
|
|
128
|
+
read.nil? ? nil : out_string
|
|
129
|
+
in nil
|
|
130
|
+
read
|
|
107
131
|
end
|
|
108
132
|
.tap(&@blk)
|
|
109
133
|
end
|
|
@@ -128,7 +152,7 @@ module OpenAI
|
|
|
128
152
|
else
|
|
129
153
|
src
|
|
130
154
|
end
|
|
131
|
-
@buf = String.new
|
|
155
|
+
@buf = String.new.b
|
|
132
156
|
@blk = blk
|
|
133
157
|
end
|
|
134
158
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../logging"
|
|
4
|
+
|
|
3
5
|
module OpenAI
|
|
4
6
|
module Internal
|
|
5
7
|
module Transport
|
|
@@ -165,8 +167,10 @@ module OpenAI
|
|
|
165
167
|
|
|
166
168
|
# from undici
|
|
167
169
|
if OpenAI::Internal::Util.uri_origin(url) != OpenAI::Internal::Util.uri_origin(location)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
headers = request.fetch(:headers).reject do |name, _|
|
|
171
|
+
name == "host" || OpenAI::Internal::Logging.credential_header?(name)
|
|
172
|
+
end
|
|
173
|
+
request = {**request, headers: headers}
|
|
170
174
|
end
|
|
171
175
|
|
|
172
176
|
unless request_body_replayable?(request[:body])
|
|
@@ -199,7 +203,7 @@ module OpenAI
|
|
|
199
203
|
# @return [URI::Generic]
|
|
200
204
|
attr_reader :base_url
|
|
201
205
|
|
|
202
|
-
# @return [Float]
|
|
206
|
+
# @return [Float, nil]
|
|
203
207
|
attr_reader :timeout
|
|
204
208
|
|
|
205
209
|
# @return [Integer]
|
|
@@ -217,6 +221,15 @@ module OpenAI
|
|
|
217
221
|
# @return [String, nil]
|
|
218
222
|
attr_reader :idempotency_header
|
|
219
223
|
|
|
224
|
+
# @return [#debug, #info, #warn, #error, nil]
|
|
225
|
+
attr_reader :logger
|
|
226
|
+
|
|
227
|
+
# @return [Symbol]
|
|
228
|
+
attr_reader :log_level
|
|
229
|
+
|
|
230
|
+
# @return [Proc, nil]
|
|
231
|
+
attr_reader :on_retry
|
|
232
|
+
|
|
220
233
|
# @api private
|
|
221
234
|
# @return [#execute]
|
|
222
235
|
attr_reader :requester
|
|
@@ -224,13 +237,16 @@ module OpenAI
|
|
|
224
237
|
# @api private
|
|
225
238
|
#
|
|
226
239
|
# @param base_url [String]
|
|
227
|
-
# @param timeout [Float]
|
|
240
|
+
# @param timeout [Float, nil]
|
|
228
241
|
# @param max_retries [Integer]
|
|
229
242
|
# @param initial_retry_delay [Float]
|
|
230
243
|
# @param max_retry_delay [Float]
|
|
231
244
|
# @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}]
|
|
232
245
|
# @param idempotency_header [String, nil]
|
|
233
246
|
# @param http_client [#execute, nil]
|
|
247
|
+
# @param logger [#debug, #info, #warn, #error, nil]
|
|
248
|
+
# @param log_level [Symbol, String, nil]
|
|
249
|
+
# @param on_retry [Proc, nil]
|
|
234
250
|
def initialize(
|
|
235
251
|
base_url:,
|
|
236
252
|
timeout: 0.0,
|
|
@@ -239,12 +255,26 @@ module OpenAI
|
|
|
239
255
|
max_retry_delay: 0.0,
|
|
240
256
|
headers: {},
|
|
241
257
|
idempotency_header: nil,
|
|
242
|
-
http_client: nil
|
|
258
|
+
http_client: nil,
|
|
259
|
+
logger: nil,
|
|
260
|
+
log_level: nil,
|
|
261
|
+
on_retry: nil
|
|
243
262
|
)
|
|
244
263
|
unless http_client.nil? || http_client.respond_to?(:execute)
|
|
245
264
|
raise ArgumentError, "`http_client` must respond to `execute`"
|
|
246
265
|
end
|
|
266
|
+
unless on_retry.nil? || on_retry.respond_to?(:call)
|
|
267
|
+
raise ArgumentError, "`on_retry` must respond to `call`"
|
|
268
|
+
end
|
|
247
269
|
|
|
270
|
+
if log_level.nil?
|
|
271
|
+
log_level = ENV.fetch("OPENAI_LOG", logger.nil? ? :off : :info)
|
|
272
|
+
end
|
|
273
|
+
@log_level = OpenAI::Internal::Logging.normalize_level(log_level)
|
|
274
|
+
OpenAI::Internal::Logging.validate_logger!(logger)
|
|
275
|
+
@logger = logger
|
|
276
|
+
@logger ||= OpenAI::Internal::Logging.default_logger unless @log_level == :off
|
|
277
|
+
@on_retry = on_retry
|
|
248
278
|
@requester = http_client || OpenAI::NetHTTPClient.new
|
|
249
279
|
@headers = OpenAI::Internal::Util.normalized_headers(
|
|
250
280
|
self.class::PLATFORM_HEADERS,
|
|
@@ -355,8 +385,9 @@ module OpenAI
|
|
|
355
385
|
headers["x-stainless-retry-count"] = "0"
|
|
356
386
|
end
|
|
357
387
|
|
|
358
|
-
timeout = opts.fetch(:timeout, @timeout)
|
|
359
|
-
|
|
388
|
+
timeout = opts.fetch(:timeout, @timeout)
|
|
389
|
+
timeout = timeout.to_f.clamp(0..) unless timeout.nil?
|
|
390
|
+
unless headers.key?("x-stainless-timeout") || timeout.nil? || timeout.zero?
|
|
360
391
|
headers["x-stainless-timeout"] = timeout.to_s
|
|
361
392
|
end
|
|
362
393
|
|
|
@@ -403,23 +434,22 @@ module OpenAI
|
|
|
403
434
|
#
|
|
404
435
|
# @return [Float]
|
|
405
436
|
private def retry_delay(headers, retry_count:)
|
|
406
|
-
# Non-standard extension
|
|
407
|
-
span = Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 }
|
|
408
|
-
return span if span
|
|
409
|
-
|
|
410
437
|
retry_header = headers["retry-after"]
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
438
|
+
delays = [
|
|
439
|
+
Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 },
|
|
440
|
+
Float(retry_header, exception: false),
|
|
441
|
+
retry_header&.then do
|
|
442
|
+
Time.httpdate(_1) - Time.now
|
|
443
|
+
rescue ArgumentError
|
|
444
|
+
nil
|
|
445
|
+
end
|
|
446
|
+
]
|
|
447
|
+
server_delay = delays.find { _1&.finite? && !_1.negative? }
|
|
448
|
+
return [server_delay, @max_retry_delay].min if server_delay
|
|
419
449
|
|
|
420
|
-
|
|
450
|
+
delay = (@initial_retry_delay * (2**retry_count)).clamp(0, @max_retry_delay)
|
|
421
451
|
jitter = 1 - (0.25 * rand)
|
|
422
|
-
|
|
452
|
+
delay * jitter
|
|
423
453
|
end
|
|
424
454
|
|
|
425
455
|
# @api private
|
|
@@ -454,7 +484,7 @@ module OpenAI
|
|
|
454
484
|
#
|
|
455
485
|
# @option request [Integer] :max_retries
|
|
456
486
|
#
|
|
457
|
-
# @option request [Float] :timeout
|
|
487
|
+
# @option request [Float, nil] :timeout
|
|
458
488
|
#
|
|
459
489
|
# @param redirect_count [Integer]
|
|
460
490
|
#
|
|
@@ -462,9 +492,15 @@ module OpenAI
|
|
|
462
492
|
#
|
|
463
493
|
# @param send_retry_header [Boolean]
|
|
464
494
|
#
|
|
495
|
+
# @yieldreturn [OpenAI::Internal::Logging::Context]
|
|
496
|
+
#
|
|
465
497
|
# @raise [OpenAI::Errors::APIError]
|
|
466
498
|
# @return [OpenAI::HTTPClient::Response]
|
|
467
|
-
def send_request(request, redirect_count:, retry_count:, send_retry_header
|
|
499
|
+
def send_request(request, redirect_count:, retry_count:, send_retry_header:, &context_provider)
|
|
500
|
+
# Generated clients override this hook. A block keeps observability
|
|
501
|
+
# state out of their stable keyword signature and is forwarded by
|
|
502
|
+
# their existing `super` calls.
|
|
503
|
+
log_context = context_provider.call
|
|
468
504
|
if send_retry_header
|
|
469
505
|
request = request.merge(
|
|
470
506
|
headers: request.fetch(:headers).merge("x-stainless-retry-count" => retry_count.to_s)
|
|
@@ -489,6 +525,7 @@ module OpenAI
|
|
|
489
525
|
body: prepared_request[:body],
|
|
490
526
|
timeout: timeout
|
|
491
527
|
)
|
|
528
|
+
log_context.request_started(input, redirect_count: redirect_count)
|
|
492
529
|
|
|
493
530
|
begin
|
|
494
531
|
http_response = @requester.execute(input)
|
|
@@ -497,12 +534,14 @@ module OpenAI
|
|
|
497
534
|
end
|
|
498
535
|
|
|
499
536
|
status = http_response.status
|
|
500
|
-
stream = http_response.body
|
|
501
537
|
headers = http_response.headers
|
|
538
|
+
http_response = log_context.response_received(http_response)
|
|
539
|
+
stream = http_response.body
|
|
502
540
|
rescue OpenAI::Errors::APIConnectionError => e
|
|
503
541
|
status = e
|
|
504
542
|
stream = nil
|
|
505
543
|
headers = {}
|
|
544
|
+
log_context.attempt_failed(e)
|
|
506
545
|
end
|
|
507
546
|
|
|
508
547
|
terminal_status =
|
|
@@ -545,7 +584,8 @@ module OpenAI
|
|
|
545
584
|
redirected_request,
|
|
546
585
|
redirect_count: redirect_count + 1,
|
|
547
586
|
retry_count: retry_count,
|
|
548
|
-
send_retry_header: send_retry_header
|
|
587
|
+
send_retry_header: send_retry_header,
|
|
588
|
+
&context_provider
|
|
549
589
|
)
|
|
550
590
|
in OpenAI::Errors::APIConnectionError if retry_count >= max_retries
|
|
551
591
|
raise status
|
|
@@ -553,13 +593,21 @@ module OpenAI
|
|
|
553
593
|
self.class.reap_connection!(status, stream: stream)
|
|
554
594
|
|
|
555
595
|
delay = retry_delay(headers, retry_count: retry_count)
|
|
596
|
+
log_context.retry_scheduled(
|
|
597
|
+
status,
|
|
598
|
+
delay: delay,
|
|
599
|
+
response: http_response&.metadata,
|
|
600
|
+
retry_count: retry_count,
|
|
601
|
+
max_retries: max_retries
|
|
602
|
+
)
|
|
556
603
|
sleep(delay)
|
|
557
604
|
|
|
558
605
|
send_request(
|
|
559
606
|
request,
|
|
560
607
|
redirect_count: redirect_count,
|
|
561
608
|
retry_count: retry_count + 1,
|
|
562
|
-
send_retry_header: send_retry_header
|
|
609
|
+
send_retry_header: send_retry_header,
|
|
610
|
+
&context_provider
|
|
563
611
|
)
|
|
564
612
|
end
|
|
565
613
|
end
|
|
@@ -606,13 +654,29 @@ module OpenAI
|
|
|
606
654
|
# @raise [OpenAI::Errors::APIError]
|
|
607
655
|
# @return [Object]
|
|
608
656
|
def request(req)
|
|
657
|
+
url, response, log_context = perform_request(req)
|
|
658
|
+
finish_request(log_context, response) do
|
|
659
|
+
parse_response(req, url: url, response: response)
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# @api private
|
|
664
|
+
#
|
|
665
|
+
# @param req [Hash{Symbol=>Object}]
|
|
666
|
+
# @return [Array(URI::Generic, OpenAI::HTTPClient::Response, OpenAI::Internal::Logging::Context)]
|
|
667
|
+
private def perform_request(req)
|
|
609
668
|
self.class.validate!(req)
|
|
610
|
-
model = req.fetch(:model) { OpenAI::Internal::Type::Unknown }
|
|
611
669
|
opts = req[:options].to_h
|
|
612
|
-
unwrap = req[:unwrap]
|
|
613
670
|
OpenAI::RequestOptions.validate!(opts)
|
|
614
671
|
request = build_request(req.except(:options), opts)
|
|
615
672
|
url = request.fetch(:url)
|
|
673
|
+
log_context = OpenAI::Internal::Logging::Context.new(
|
|
674
|
+
logger: @logger,
|
|
675
|
+
log_level: @log_level,
|
|
676
|
+
on_retry: @on_retry,
|
|
677
|
+
method: request.fetch(:method),
|
|
678
|
+
url: url
|
|
679
|
+
)
|
|
616
680
|
|
|
617
681
|
# Don't send the current retry count in the headers if the caller modified the header defaults.
|
|
618
682
|
send_retry_header = request.fetch(:headers)["x-stainless-retry-count"] == "0"
|
|
@@ -621,7 +685,41 @@ module OpenAI
|
|
|
621
685
|
redirect_count: 0,
|
|
622
686
|
retry_count: 0,
|
|
623
687
|
send_retry_header: send_retry_header
|
|
624
|
-
)
|
|
688
|
+
) { log_context }
|
|
689
|
+
[url, response, log_context]
|
|
690
|
+
rescue StandardError => e
|
|
691
|
+
log_context&.request_failed(e)
|
|
692
|
+
raise
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# @api private
|
|
696
|
+
#
|
|
697
|
+
# @param log_context [OpenAI::Internal::Logging::Context]
|
|
698
|
+
# @param response [OpenAI::HTTPClient::Response]
|
|
699
|
+
# @return [Object]
|
|
700
|
+
private def finish_request(log_context, response)
|
|
701
|
+
result = yield
|
|
702
|
+
if result.is_a?(OpenAI::Internal::Type::BaseStream)
|
|
703
|
+
return log_context.observe_stream(result, response: response)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
log_context.completed(response)
|
|
707
|
+
result
|
|
708
|
+
rescue StandardError => e
|
|
709
|
+
log_context.request_failed(e)
|
|
710
|
+
raise
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
# @api private
|
|
714
|
+
#
|
|
715
|
+
# @param req [Hash{Symbol=>Object}]
|
|
716
|
+
# @param url [URI::Generic]
|
|
717
|
+
# @param response [OpenAI::HTTPClient::Response]
|
|
718
|
+
# @return [Object]
|
|
719
|
+
private def parse_response(req, url:, response:)
|
|
720
|
+
model = req.fetch(:model) { OpenAI::Internal::Type::Unknown }
|
|
721
|
+
unwrap = req[:unwrap]
|
|
722
|
+
response_metadata = response.metadata
|
|
625
723
|
|
|
626
724
|
decoded = OpenAI::Internal::Util.decode_content(response.headers, stream: response.body)
|
|
627
725
|
case req
|
|
@@ -629,17 +727,20 @@ module OpenAI
|
|
|
629
727
|
st.new(
|
|
630
728
|
model: model,
|
|
631
729
|
url: url,
|
|
632
|
-
|
|
633
|
-
headers: response.headers,
|
|
730
|
+
response_metadata: response_metadata,
|
|
634
731
|
response: response,
|
|
635
732
|
unwrap: unwrap,
|
|
636
733
|
stream: decoded
|
|
637
734
|
)
|
|
638
735
|
in {page: Class => page}
|
|
639
|
-
page.new(client: self, req: req,
|
|
736
|
+
page.new(client: self, req: req, response_metadata: response_metadata, page_data: decoded)
|
|
640
737
|
else
|
|
641
738
|
unwrapped = OpenAI::Internal::Util.dig(decoded, unwrap)
|
|
642
|
-
OpenAI::Internal::Type::Converter.coerce(model, unwrapped)
|
|
739
|
+
OpenAI::Internal::Type::Converter.coerce(model, unwrapped).tap do |result|
|
|
740
|
+
if result.is_a?(OpenAI::Internal::Type::BaseModel)
|
|
741
|
+
result._set_last_response(response_metadata)
|
|
742
|
+
end
|
|
743
|
+
end
|
|
643
744
|
end
|
|
644
745
|
end
|
|
645
746
|
|
|
@@ -647,9 +748,8 @@ module OpenAI
|
|
|
647
748
|
#
|
|
648
749
|
# @return [String]
|
|
649
750
|
def inspect
|
|
650
|
-
#
|
|
651
|
-
|
|
652
|
-
# rubocop:enable Layout/LineLength
|
|
751
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{@base_url} " \
|
|
752
|
+
"max_retries=#{@max_retries} timeout=#{@timeout}>"
|
|
653
753
|
end
|
|
654
754
|
|
|
655
755
|
define_sorbet_constant!(:RequestComponents) do
|
|
@@ -693,7 +793,7 @@ module OpenAI
|
|
|
693
793
|
headers: T::Hash[String, String],
|
|
694
794
|
body: T.anything,
|
|
695
795
|
max_retries: Integer,
|
|
696
|
-
timeout: Float
|
|
796
|
+
timeout: T.nilable(Float)
|
|
697
797
|
}
|
|
698
798
|
end
|
|
699
799
|
end
|
|
@@ -46,9 +46,9 @@ module OpenAI
|
|
|
46
46
|
#
|
|
47
47
|
# @return [Boolean]
|
|
48
48
|
def ==(other)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
other.is_a?(OpenAI::Internal::Type::ArrayOf) &&
|
|
50
|
+
other.nilable? == nilable? &&
|
|
51
|
+
other.item_type == item_type
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# @api public
|
|
@@ -120,7 +120,9 @@ module OpenAI
|
|
|
120
120
|
#
|
|
121
121
|
# @return [Object]
|
|
122
122
|
def to_sorbet_type
|
|
123
|
-
|
|
123
|
+
type = OpenAI::Internal::Util::SorbetRuntimeSupport.to_sorbet_type(item_type)
|
|
124
|
+
type = T.nilable(type) if nilable?
|
|
125
|
+
T::Array[type]
|
|
124
126
|
end
|
|
125
127
|
|
|
126
128
|
# @api private
|
|
@@ -226,6 +226,32 @@ module OpenAI
|
|
|
226
226
|
# @return [Integer]
|
|
227
227
|
def hash = [self.class, @data].hash
|
|
228
228
|
|
|
229
|
+
# The ID of the API request, returned via the `x-request-id` response
|
|
230
|
+
# header. This is only populated on top-level response objects returned
|
|
231
|
+
# by the client.
|
|
232
|
+
#
|
|
233
|
+
# @api public
|
|
234
|
+
#
|
|
235
|
+
# @return [String, nil]
|
|
236
|
+
def _request_id = @last_response&.request_id
|
|
237
|
+
|
|
238
|
+
# Metadata from the HTTP response that produced this model. This is
|
|
239
|
+
# only populated on top-level response objects returned by the client.
|
|
240
|
+
#
|
|
241
|
+
# @api public
|
|
242
|
+
#
|
|
243
|
+
# @return [OpenAI::ResponseMetadata, nil]
|
|
244
|
+
attr_reader :last_response
|
|
245
|
+
|
|
246
|
+
# @api private
|
|
247
|
+
#
|
|
248
|
+
# @param response [OpenAI::ResponseMetadata]
|
|
249
|
+
# @return [self]
|
|
250
|
+
def _set_last_response(response)
|
|
251
|
+
@last_response = response
|
|
252
|
+
self
|
|
253
|
+
end
|
|
254
|
+
|
|
229
255
|
class << self
|
|
230
256
|
# @api private
|
|
231
257
|
#
|
|
@@ -247,7 +273,7 @@ module OpenAI
|
|
|
247
273
|
def coerce(value, state:)
|
|
248
274
|
exactness = state.fetch(:exactness)
|
|
249
275
|
|
|
250
|
-
if value.is_a?(self
|
|
276
|
+
if value.is_a?(self)
|
|
251
277
|
exactness[:yes] += 1
|
|
252
278
|
return value
|
|
253
279
|
end
|
|
@@ -470,12 +496,24 @@ module OpenAI
|
|
|
470
496
|
# @return [String]
|
|
471
497
|
def to_yaml(*a) = OpenAI::Internal::Type::Converter.dump(self.class, self).to_yaml(*a)
|
|
472
498
|
|
|
499
|
+
# Keep transport metadata outside Psych's object serialization path.
|
|
500
|
+
#
|
|
501
|
+
# @api private
|
|
502
|
+
#
|
|
503
|
+
# @param coder [Psych::Coder]
|
|
504
|
+
# @return [void]
|
|
505
|
+
def encode_with(coder)
|
|
506
|
+
coder["data"] = @data
|
|
507
|
+
coder["coerced"] = @coerced
|
|
508
|
+
end
|
|
509
|
+
|
|
473
510
|
# Create a new instance of a model.
|
|
474
511
|
#
|
|
475
512
|
# @param data [Hash{Symbol=>Object}, self]
|
|
476
513
|
def initialize(data = {})
|
|
477
514
|
@data = {}
|
|
478
515
|
@coerced = {}
|
|
516
|
+
@last_response = nil
|
|
479
517
|
OpenAI::Internal::Util.coerce_hash!(data).each do
|
|
480
518
|
if self.class.known_fields.key?(_1)
|
|
481
519
|
public_send(:"#{_1}=", _2)
|
|
@@ -11,6 +11,22 @@ module OpenAI
|
|
|
11
11
|
module BasePage
|
|
12
12
|
# rubocop:disable Lint/UnusedMethodArgument
|
|
13
13
|
|
|
14
|
+
# The ID of the API request, returned via the `x-request-id` response
|
|
15
|
+
# header. This is only populated on top-level response objects returned
|
|
16
|
+
# by the client.
|
|
17
|
+
#
|
|
18
|
+
# @api public
|
|
19
|
+
#
|
|
20
|
+
# @return [String, nil]
|
|
21
|
+
def _request_id = last_response.request_id
|
|
22
|
+
|
|
23
|
+
# Metadata from the HTTP response that produced this page.
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
#
|
|
27
|
+
# @return [OpenAI::ResponseMetadata]
|
|
28
|
+
attr_reader :last_response
|
|
29
|
+
|
|
14
30
|
# @api public
|
|
15
31
|
#
|
|
16
32
|
# @return [Boolean]
|
|
@@ -39,12 +55,13 @@ module OpenAI
|
|
|
39
55
|
#
|
|
40
56
|
# @param client [OpenAI::Internal::Transport::BaseClient]
|
|
41
57
|
# @param req [Hash{Symbol=>Object}]
|
|
42
|
-
# @param
|
|
58
|
+
# @param response_metadata [OpenAI::ResponseMetadata]
|
|
43
59
|
# @param page_data [Object]
|
|
44
|
-
def initialize(client:, req:,
|
|
60
|
+
def initialize(client:, req:, response_metadata:, page_data:)
|
|
45
61
|
@client = client
|
|
46
62
|
@req = req
|
|
47
63
|
@model = req.fetch(:model)
|
|
64
|
+
@last_response = response_metadata
|
|
48
65
|
super()
|
|
49
66
|
end
|
|
50
67
|
|
|
@@ -14,16 +14,50 @@ module OpenAI
|
|
|
14
14
|
include Enumerable
|
|
15
15
|
|
|
16
16
|
# @return [Integer]
|
|
17
|
-
|
|
17
|
+
def status = last_response.status
|
|
18
18
|
|
|
19
19
|
# @return [Hash{String=>String}]
|
|
20
|
-
|
|
20
|
+
def headers = last_response.headers
|
|
21
|
+
|
|
22
|
+
# Metadata from the HTTP response that opened this stream.
|
|
23
|
+
#
|
|
24
|
+
# @api public
|
|
25
|
+
#
|
|
26
|
+
# @return [OpenAI::ResponseMetadata]
|
|
27
|
+
attr_reader :last_response
|
|
21
28
|
|
|
22
29
|
# @api public
|
|
23
30
|
#
|
|
24
31
|
# @return [void]
|
|
25
32
|
def close = OpenAI::Internal::Util.close_fused!(@iterator)
|
|
26
33
|
|
|
34
|
+
# Attach request lifecycle logging without changing the stream's identity.
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
#
|
|
38
|
+
# @param context [OpenAI::Internal::Logging::Context]
|
|
39
|
+
# @param response [OpenAI::HTTPClient::Response]
|
|
40
|
+
# @return [self]
|
|
41
|
+
def observe(context:, response:)
|
|
42
|
+
source = @iterator
|
|
43
|
+
@iterator = OpenAI::Internal::Util.chain_fused(source) do |yielder|
|
|
44
|
+
loop do
|
|
45
|
+
event =
|
|
46
|
+
begin
|
|
47
|
+
source.next
|
|
48
|
+
rescue StopIteration
|
|
49
|
+
context.completed(response)
|
|
50
|
+
break
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
context.request_failed(e)
|
|
53
|
+
raise
|
|
54
|
+
end
|
|
55
|
+
yielder << event
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
27
61
|
# @api private
|
|
28
62
|
#
|
|
29
63
|
# @return [Enumerable<generic<Elem>>]
|
|
@@ -53,16 +87,14 @@ module OpenAI
|
|
|
53
87
|
#
|
|
54
88
|
# @param model [Class, OpenAI::Internal::Type::Converter]
|
|
55
89
|
# @param url [URI::Generic]
|
|
56
|
-
# @param
|
|
57
|
-
# @param headers [Hash{String=>String}]
|
|
90
|
+
# @param response_metadata [OpenAI::ResponseMetadata]
|
|
58
91
|
# @param response [OpenAI::HTTPClient::Response]
|
|
59
92
|
# @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc]
|
|
60
93
|
# @param stream [Enumerable<Object>]
|
|
61
|
-
def initialize(model:, url:,
|
|
94
|
+
def initialize(model:, url:, response_metadata:, response:, unwrap:, stream:)
|
|
62
95
|
@model = model
|
|
63
96
|
@url = url
|
|
64
|
-
@
|
|
65
|
-
@headers = headers
|
|
97
|
+
@last_response = response_metadata
|
|
66
98
|
@response = response
|
|
67
99
|
@unwrap = unwrap
|
|
68
100
|
@stream = stream
|
|
@@ -61,9 +61,9 @@ module OpenAI
|
|
|
61
61
|
#
|
|
62
62
|
# @return [Boolean]
|
|
63
63
|
def ==(other)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
other.is_a?(OpenAI::Internal::Type::HashOf) &&
|
|
65
|
+
other.nilable? == nilable? &&
|
|
66
|
+
other.item_type == item_type
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# @api public
|