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.
Files changed (239) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +99 -0
  3. data/README.md +241 -8
  4. data/SECURITY.md +6 -22
  5. data/azure.md +110 -0
  6. data/bedrock.md +197 -0
  7. data/examples/mtls_custom_http_client.rb +61 -0
  8. data/lib/openai/auth/workload_identity.rb +13 -2
  9. data/lib/openai/auth/workload_identity_auth.rb +2 -1
  10. data/lib/openai/client.rb +29 -6
  11. data/lib/openai/errors.rb +11 -1
  12. data/lib/openai/helpers/streaming/chat_completion_stream.rb +3 -3
  13. data/lib/openai/helpers/streaming/response_stream.rb +5 -6
  14. data/lib/openai/helpers/structured_output/base_model.rb +20 -19
  15. data/lib/openai/helpers/structured_output/json_schema_converter.rb +12 -5
  16. data/lib/openai/http_client.rb +99 -7
  17. data/lib/openai/internal/conversation_cursor_page.rb +5 -6
  18. data/lib/openai/internal/cursor_page.rb +3 -3
  19. data/lib/openai/internal/logging.rb +419 -0
  20. data/lib/openai/internal/next_cursor_page.rb +5 -6
  21. data/lib/openai/internal/page.rb +2 -2
  22. data/lib/openai/internal/provider.rb +7 -1
  23. data/lib/openai/internal/read_io_adapter.rb +42 -18
  24. data/lib/openai/internal/stream.rb +2 -2
  25. data/lib/openai/internal/transport/base_client.rb +137 -37
  26. data/lib/openai/internal/type/array_of.rb +6 -4
  27. data/lib/openai/internal/type/base_model.rb +39 -1
  28. data/lib/openai/internal/type/base_page.rb +19 -2
  29. data/lib/openai/internal/type/base_stream.rb +39 -7
  30. data/lib/openai/internal/type/hash_of.rb +3 -3
  31. data/lib/openai/internal/util.rb +52 -27
  32. data/lib/openai/models/admin/organization/audit_log_list_params.rb +3 -1
  33. data/lib/openai/models/admin/organization/audit_log_list_response.rb +33 -11
  34. data/lib/openai/models/admin/organization/groups/role_list_response.rb +5 -1
  35. data/lib/openai/models/admin/organization/groups/role_retrieve_response.rb +5 -1
  36. data/lib/openai/models/admin/organization/projects/groups/role_list_response.rb +6 -1
  37. data/lib/openai/models/admin/organization/projects/groups/role_retrieve_response.rb +6 -1
  38. data/lib/openai/models/admin/organization/projects/service_account_create_response.rb +3 -1
  39. data/lib/openai/models/admin/organization/projects/users/role_list_response.rb +6 -1
  40. data/lib/openai/models/admin/organization/projects/users/role_retrieve_response.rb +6 -1
  41. data/lib/openai/models/admin/organization/users/role_list_response.rb +5 -1
  42. data/lib/openai/models/admin/organization/users/role_retrieve_response.rb +5 -1
  43. data/lib/openai/models/all_models.rb +5 -0
  44. data/lib/openai/models/audio/transcription_create_params.rb +7 -1
  45. data/lib/openai/models/audio/translation_create_params.rb +7 -1
  46. data/lib/openai/models/beta/beta_mcp_tool_call_error.rb +84 -0
  47. data/lib/openai/models/beta/beta_response.rb +27 -0
  48. data/lib/openai/models/beta/beta_response_input_item.rb +3 -3
  49. data/lib/openai/models/beta/beta_response_item.rb +3 -3
  50. data/lib/openai/models/beta/beta_response_output_item.rb +3 -3
  51. data/lib/openai/models/beta/beta_response_output_item_added_event.rb +5 -2
  52. data/lib/openai/models/beta/beta_response_output_text_annotation_added_event.rb +201 -4
  53. data/lib/openai/models/beta/beta_response_reasoning_item.rb +5 -0
  54. data/lib/openai/models/beta/beta_response_stream_event.rb +1 -1
  55. data/lib/openai/models/beta/beta_responses_client_event.rb +46 -3
  56. data/lib/openai/models/beta/beta_responses_server_event.rb +1143 -63
  57. data/lib/openai/models/beta/beta_web_search_tool.rb +11 -1
  58. data/lib/openai/models/beta/response_compact_params.rb +18 -0
  59. data/lib/openai/models/beta/response_create_params.rb +27 -0
  60. data/lib/openai/models/chat_model.rb +1 -0
  61. data/lib/openai/models/completion_usage.rb +26 -2
  62. data/lib/openai/models/containers/file_create_params.rb +4 -0
  63. data/lib/openai/models/content_provenance_check_create_params.rb +4 -0
  64. data/lib/openai/models/conversations/conversation_item.rb +3 -3
  65. data/lib/openai/models/file_create_params.rb +4 -0
  66. data/lib/openai/models/image_create_variation_params.rb +4 -0
  67. data/lib/openai/models/image_edit_params.rb +8 -0
  68. data/lib/openai/models/model.rb +9 -1
  69. data/lib/openai/models/responses/mcp_tool_call_error.rb +82 -0
  70. data/lib/openai/models/responses/response.rb +9 -0
  71. data/lib/openai/models/responses/response_compact_params.rb +18 -0
  72. data/lib/openai/models/responses/response_create_params.rb +9 -0
  73. data/lib/openai/models/responses/response_input_item.rb +3 -3
  74. data/lib/openai/models/responses/response_item.rb +3 -3
  75. data/lib/openai/models/responses/response_output_item.rb +3 -3
  76. data/lib/openai/models/responses/response_output_item_added_event.rb +5 -2
  77. data/lib/openai/models/responses/response_output_text_annotation_added_event.rb +201 -4
  78. data/lib/openai/models/responses/response_reasoning_item.rb +5 -0
  79. data/lib/openai/models/responses/response_stream_event.rb +1 -1
  80. data/lib/openai/models/responses/responses_client_event.rb +22 -1
  81. data/lib/openai/models/responses/responses_server_event.rb +1107 -56
  82. data/lib/openai/models/responses/web_search_tool.rb +11 -1
  83. data/lib/openai/models/responses_model.rb +5 -0
  84. data/lib/openai/models/skill_create_params.rb +4 -0
  85. data/lib/openai/models/skills/version_create_params.rb +4 -0
  86. data/lib/openai/models/uploads/part_create_params.rb +4 -0
  87. data/lib/openai/models/vector_store_search_response.rb +3 -1
  88. data/lib/openai/models/video_create_character_params.rb +4 -0
  89. data/lib/openai/models/video_create_params.rb +4 -0
  90. data/lib/openai/models/video_edit_params.rb +4 -0
  91. data/lib/openai/models/video_extend_params.rb +4 -0
  92. data/lib/openai/models.rb +9 -1
  93. data/lib/openai/net_http_client.rb +20 -6
  94. data/lib/openai/providers/azure.rb +216 -0
  95. data/lib/openai/providers/bedrock.rb +130 -17
  96. data/lib/openai/resources/admin/organization/projects/groups/roles.rb +1 -1
  97. data/lib/openai/resources/admin/organization/projects/groups.rb +1 -1
  98. data/lib/openai/resources/admin/organization/projects/users/roles.rb +1 -1
  99. data/lib/openai/resources/admin/organization/projects.rb +4 -2
  100. data/lib/openai/resources/audio/transcriptions.rb +8 -0
  101. data/lib/openai/resources/audio/translations.rb +4 -0
  102. data/lib/openai/resources/beta/responses.rb +1 -1
  103. data/lib/openai/resources/beta/threads/runs/steps.rb +2 -2
  104. data/lib/openai/resources/containers/files.rb +4 -0
  105. data/lib/openai/resources/content_provenance_checks.rb +4 -0
  106. data/lib/openai/resources/conversations/items.rb +1 -1
  107. data/lib/openai/resources/evals/runs/output_items.rb +1 -1
  108. data/lib/openai/resources/files.rb +5 -1
  109. data/lib/openai/resources/images.rb +12 -0
  110. data/lib/openai/resources/responses.rb +3 -2
  111. data/lib/openai/resources/skills/versions.rb +4 -0
  112. data/lib/openai/resources/skills.rb +4 -0
  113. data/lib/openai/resources/uploads/parts.rb +4 -0
  114. data/lib/openai/resources/vector_stores/file_batches.rb +1 -1
  115. data/lib/openai/resources/videos.rb +38 -0
  116. data/lib/openai/resources/webhooks.rb +18 -6
  117. data/lib/openai/version.rb +1 -1
  118. data/lib/openai.rb +111 -105
  119. data/rbi/openai/auth.rbi +4 -4
  120. data/rbi/openai/client.rbi +13 -3
  121. data/rbi/openai/errors.rbi +7 -0
  122. data/rbi/openai/helpers/streaming/events.rbi +12 -0
  123. data/rbi/openai/http_client.rbi +62 -2
  124. data/rbi/openai/internal/logging.rbi +192 -0
  125. data/rbi/openai/internal/provider.rbi +3 -0
  126. data/rbi/openai/internal/read_io_adapter.rbi +6 -1
  127. data/rbi/openai/internal/transport/base_client.rbi +61 -7
  128. data/rbi/openai/internal/type/base_model.rbi +24 -1
  129. data/rbi/openai/internal/type/base_page.rbi +13 -2
  130. data/rbi/openai/internal/type/base_stream.rbi +15 -4
  131. data/rbi/openai/internal/util.rbi +26 -2
  132. data/rbi/openai/models/admin/organization/audit_log_list_params.rbi +5 -0
  133. data/rbi/openai/models/admin/organization/audit_log_list_response.rbi +5 -0
  134. data/rbi/openai/models/all_models.rbi +25 -0
  135. data/rbi/openai/models/audio/transcription_create_params.rbi +14 -2
  136. data/rbi/openai/models/audio/translation_create_params.rbi +14 -2
  137. data/rbi/openai/models/beta/beta_mcp_tool_call_error.rbi +122 -0
  138. data/rbi/openai/models/beta/beta_response.rbi +47 -0
  139. data/rbi/openai/models/beta/beta_response_input_item.rbi +27 -3
  140. data/rbi/openai/models/beta/beta_response_item.rbi +27 -3
  141. data/rbi/openai/models/beta/beta_response_output_item.rbi +27 -3
  142. data/rbi/openai/models/beta/beta_response_output_item_added_event.rbi +8 -2
  143. data/rbi/openai/models/beta/beta_response_output_text_annotation_added_event.rbi +297 -5
  144. data/rbi/openai/models/beta/beta_response_reasoning_item.rbi +10 -0
  145. data/rbi/openai/models/beta/beta_response_stream_event.rbi +1 -1
  146. data/rbi/openai/models/beta/beta_responses_client_event.rbi +69 -1
  147. data/rbi/openai/models/beta/beta_responses_server_event.rbi +3945 -54
  148. data/rbi/openai/models/beta/beta_web_search_tool.rbi +15 -0
  149. data/rbi/openai/models/beta/response_compact_params.rbi +30 -0
  150. data/rbi/openai/models/beta/response_create_params.rbi +47 -0
  151. data/rbi/openai/models/chat_model.rbi +2 -0
  152. data/rbi/openai/models/completion_usage.rbi +39 -6
  153. data/rbi/openai/models/containers/file_create_params.rbi +8 -0
  154. data/rbi/openai/models/content_provenance_check_create_params.rbi +8 -0
  155. data/rbi/openai/models/conversations/conversation_item.rbi +12 -3
  156. data/rbi/openai/models/file_create_params.rbi +8 -0
  157. data/rbi/openai/models/image_create_variation_params.rbi +8 -0
  158. data/rbi/openai/models/image_edit_params.rbi +16 -0
  159. data/rbi/openai/models/model.rbi +14 -1
  160. data/rbi/openai/models/responses/mcp_tool_call_error.rbi +120 -0
  161. data/rbi/openai/models/responses/response.rbi +17 -0
  162. data/rbi/openai/models/responses/response_compact_params.rbi +30 -0
  163. data/rbi/openai/models/responses/response_create_params.rbi +17 -0
  164. data/rbi/openai/models/responses/response_input_item.rbi +27 -3
  165. data/rbi/openai/models/responses/response_item.rbi +27 -3
  166. data/rbi/openai/models/responses/response_output_item.rbi +27 -3
  167. data/rbi/openai/models/responses/response_output_item_added_event.rbi +8 -2
  168. data/rbi/openai/models/responses/response_output_text_annotation_added_event.rbi +297 -5
  169. data/rbi/openai/models/responses/response_reasoning_item.rbi +10 -0
  170. data/rbi/openai/models/responses/response_stream_event.rbi +1 -1
  171. data/rbi/openai/models/responses/responses_client_event.rbi +36 -0
  172. data/rbi/openai/models/responses/responses_server_event.rbi +3385 -53
  173. data/rbi/openai/models/responses/web_search_tool.rbi +15 -0
  174. data/rbi/openai/models/responses_model.rbi +25 -0
  175. data/rbi/openai/models/skill_create_params.rbi +8 -0
  176. data/rbi/openai/models/skills/version_create_params.rbi +8 -0
  177. data/rbi/openai/models/uploads/part_create_params.rbi +8 -0
  178. data/rbi/openai/models/video_create_character_params.rbi +8 -0
  179. data/rbi/openai/models/video_create_params.rbi +8 -0
  180. data/rbi/openai/models/video_edit_params.rbi +8 -0
  181. data/rbi/openai/models/video_extend_params.rbi +8 -0
  182. data/rbi/openai/net_http_client.rbi +2 -2
  183. data/rbi/openai/providers.rbi +11 -0
  184. data/rbi/openai/resources/audio/transcriptions.rbi +14 -2
  185. data/rbi/openai/resources/audio/translations.rbi +7 -1
  186. data/rbi/openai/resources/beta/responses.rbi +8 -0
  187. data/rbi/openai/resources/containers/files.rbi +4 -0
  188. data/rbi/openai/resources/content_provenance_checks.rbi +4 -0
  189. data/rbi/openai/resources/files.rbi +5 -1
  190. data/rbi/openai/resources/images.rbi +20 -0
  191. data/rbi/openai/resources/responses.rbi +8 -0
  192. data/rbi/openai/resources/skills/versions.rbi +4 -0
  193. data/rbi/openai/resources/skills.rbi +4 -0
  194. data/rbi/openai/resources/uploads/parts.rbi +4 -0
  195. data/rbi/openai/resources/videos.rbi +16 -0
  196. data/sig/openai/client.rbs +6 -2
  197. data/sig/openai/errors.rbs +5 -0
  198. data/sig/openai/http_client.rbs +37 -2
  199. data/sig/openai/internal/logging.rbs +88 -0
  200. data/sig/openai/internal/provider.rbs +3 -1
  201. data/sig/openai/internal/read_io_adapter.rbs +3 -1
  202. data/sig/openai/internal/transport/base_client.rbs +33 -5
  203. data/sig/openai/internal/type/base_model.rbs +13 -0
  204. data/sig/openai/internal/type/base_page.rbs +8 -1
  205. data/sig/openai/internal/type/base_stream.rbs +8 -2
  206. data/sig/openai/internal/util.rbs +12 -0
  207. data/sig/openai/models/admin/organization/audit_log_list_params.rbs +2 -0
  208. data/sig/openai/models/admin/organization/audit_log_list_response.rbs +2 -0
  209. data/sig/openai/models/all_models.rbs +10 -0
  210. data/sig/openai/models/beta/beta_mcp_tool_call_error.rbs +75 -0
  211. data/sig/openai/models/beta/beta_response.rbs +14 -1
  212. data/sig/openai/models/beta/beta_response_input_item.rbs +4 -4
  213. data/sig/openai/models/beta/beta_response_item.rbs +4 -4
  214. data/sig/openai/models/beta/beta_response_output_item.rbs +4 -4
  215. data/sig/openai/models/beta/beta_response_output_text_annotation_added_event.rbs +149 -4
  216. data/sig/openai/models/beta/beta_responses_client_event.rbs +21 -1
  217. data/sig/openai/models/beta/beta_responses_server_event.rbs +2005 -53
  218. data/sig/openai/models/beta/beta_web_search_tool.rbs +7 -0
  219. data/sig/openai/models/beta/response_compact_params.rbs +12 -0
  220. data/sig/openai/models/beta/response_create_params.rbs +14 -1
  221. data/sig/openai/models/chat_model.rbs +2 -0
  222. data/sig/openai/models/completion_usage.rbs +27 -6
  223. data/sig/openai/models/conversations/conversation_item.rbs +4 -4
  224. data/sig/openai/models/model.rbs +12 -2
  225. data/sig/openai/models/responses/mcp_tool_call_error.rbs +73 -0
  226. data/sig/openai/models/responses/response.rbs +2 -1
  227. data/sig/openai/models/responses/response_compact_params.rbs +12 -0
  228. data/sig/openai/models/responses/response_create_params.rbs +2 -1
  229. data/sig/openai/models/responses/response_input_item.rbs +4 -4
  230. data/sig/openai/models/responses/response_item.rbs +4 -4
  231. data/sig/openai/models/responses/response_output_item.rbs +4 -4
  232. data/sig/openai/models/responses/response_output_text_annotation_added_event.rbs +149 -4
  233. data/sig/openai/models/responses/responses_client_event.rbs +9 -1
  234. data/sig/openai/models/responses/responses_server_event.rbs +1834 -53
  235. data/sig/openai/models/responses/web_search_tool.rbs +7 -0
  236. data/sig/openai/models/responses_model.rbs +10 -0
  237. data/sig/openai/net_http_client.rbs +2 -2
  238. data/sig/openai/providers.rbs +7 -0
  239. metadata +30 -1
@@ -10,6 +10,14 @@ module OpenAI
10
10
  # @return [Symbol, OpenAI::Models::Responses::WebSearchTool::Type]
11
11
  required :type, enum: -> { OpenAI::Responses::WebSearchTool::Type }
12
12
 
13
+ # @!attribute external_web_access
14
+ # Allow live internet access for web search. Defaults to true when omitted. When
15
+ # false, the web search tool runs in offline/cache-only mode and will not fetch
16
+ # new external content.
17
+ #
18
+ # @return [Boolean, nil]
19
+ optional :external_web_access, OpenAI::Internal::Type::Boolean
20
+
13
21
  # @!attribute filters
14
22
  # Filters for the search.
15
23
  #
@@ -29,7 +37,7 @@ module OpenAI
29
37
  # @return [OpenAI::Models::Responses::WebSearchTool::UserLocation, nil]
30
38
  optional :user_location, -> { OpenAI::Responses::WebSearchTool::UserLocation }, nil?: true
31
39
 
32
- # @!method initialize(type:, filters: nil, search_context_size: nil, user_location: nil)
40
+ # @!method initialize(type:, external_web_access: nil, filters: nil, search_context_size: nil, user_location: nil)
33
41
  # Some parameter documentations has been truncated, see
34
42
  # {OpenAI::Models::Responses::WebSearchTool} for more details.
35
43
  #
@@ -38,6 +46,8 @@ module OpenAI
38
46
  #
39
47
  # @param type [Symbol, OpenAI::Models::Responses::WebSearchTool::Type] The type of the web search tool. One of `web_search` or `web_search_2025_08_26`.
40
48
  #
49
+ # @param external_web_access [Boolean] Allow live internet access for web search. Defaults to true when omitted. When f
50
+ #
41
51
  # @param filters [OpenAI::Models::Responses::WebSearchTool::Filters, nil] Filters for the search.
42
52
  #
43
53
  # @param search_context_size [Symbol, OpenAI::Models::Responses::WebSearchTool::SearchContextSize] High level guidance for the amount of context window space to use for the search
@@ -24,10 +24,15 @@ module OpenAI
24
24
  O4_MINI_DEEP_RESEARCH_2025_06_26 = :"o4-mini-deep-research-2025-06-26"
25
25
  COMPUTER_USE_PREVIEW = :"computer-use-preview"
26
26
  COMPUTER_USE_PREVIEW_2025_03_11 = :"computer-use-preview-2025-03-11"
27
+ GPT_5_5_PRO = :"gpt-5.5-pro"
28
+ GPT_5_5_PRO_2026_04_23 = :"gpt-5.5-pro-2026-04-23"
27
29
  GPT_5_CODEX = :"gpt-5-codex"
28
30
  GPT_5_PRO = :"gpt-5-pro"
29
31
  GPT_5_PRO_2025_10_06 = :"gpt-5-pro-2025-10-06"
30
32
  GPT_5_1_CODEX_MAX = :"gpt-5.1-codex-max"
33
+ GPT_DAYBREAK_BLUE_LATEST = :"gpt-daybreak-blue-latest"
34
+ GPT_DAYBREAK_RED_LATEST = :"gpt-daybreak-red-latest"
35
+ GPT_5_6_CYBER = :"gpt-5.6-cyber"
31
36
 
32
37
  # @!method self.values
33
38
  # @return [Array<Symbol>]
@@ -10,6 +10,10 @@ module OpenAI
10
10
  # @!attribute files
11
11
  # Skill files to upload (directory upload) or a single zip file.
12
12
  #
13
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
14
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
15
+ # content type.
16
+ #
13
17
  # @return [Array<Pathname, StringIO, IO, String, OpenAI::FilePart>, Pathname, StringIO, IO, String, OpenAI::FilePart, nil]
14
18
  optional :files, union: -> { OpenAI::SkillCreateParams::Files }
15
19
 
@@ -22,6 +22,10 @@ module OpenAI
22
22
  # @!attribute files
23
23
  # Skill files to upload (directory upload) or a single zip file.
24
24
  #
25
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
26
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
27
+ # content type.
28
+ #
25
29
  # @return [Array<Pathname, StringIO, IO, String, OpenAI::FilePart>, Pathname, StringIO, IO, String, OpenAI::FilePart, nil]
26
30
  optional :files, union: -> { OpenAI::Skills::VersionCreateParams::Files }
27
31
 
@@ -16,6 +16,10 @@ module OpenAI
16
16
  # @!attribute data
17
17
  # The chunk of bytes for this Part.
18
18
  #
19
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
20
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
21
+ # content type.
22
+ #
19
23
  # @return [Pathname, StringIO, IO, String, OpenAI::FilePart]
20
24
  required :data, OpenAI::Internal::Type::FileInput
21
25
 
@@ -13,7 +13,9 @@ module OpenAI
13
13
  #
14
14
  # @return [Hash{Symbol=>String, Float, Boolean}, nil]
15
15
  required :attributes,
16
- -> { OpenAI::Internal::Type::HashOf[union: OpenAI::Models::VectorStoreSearchResponse::Attribute] },
16
+ -> {
17
+ OpenAI::Internal::Type::HashOf[union: OpenAI::Models::VectorStoreSearchResponse::Attribute]
18
+ },
17
19
  nil?: true
18
20
 
19
21
  # @!attribute content
@@ -16,6 +16,10 @@ module OpenAI
16
16
  # @!attribute video
17
17
  # Video file used to create a character.
18
18
  #
19
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
20
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
21
+ # content type.
22
+ #
19
23
  # @return [Pathname, StringIO, IO, String, OpenAI::FilePart]
20
24
  required :video, OpenAI::Internal::Type::FileInput
21
25
 
@@ -16,6 +16,10 @@ module OpenAI
16
16
  # @!attribute input_reference
17
17
  # Optional reference asset upload or reference object that guides generation.
18
18
  #
19
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
20
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
21
+ # content type.
22
+ #
19
23
  # @return [Pathname, StringIO, IO, String, OpenAI::FilePart, OpenAI::Models::ImageInputReferenceParam, nil]
20
24
  optional :input_reference, union: -> { OpenAI::VideoCreateParams::InputReference }
21
25
 
@@ -16,6 +16,10 @@ module OpenAI
16
16
  # @!attribute video
17
17
  # Reference to the completed video to edit.
18
18
  #
19
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
20
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
21
+ # content type.
22
+ #
19
23
  # @return [Pathname, StringIO, IO, String, OpenAI::FilePart, OpenAI::Models::VideoEditParams::Video::VideoReferenceInputParam]
20
24
  required :video, union: -> { OpenAI::VideoEditParams::Video }
21
25
 
@@ -23,6 +23,10 @@ module OpenAI
23
23
  # @!attribute video
24
24
  # Reference to the completed video to extend.
25
25
  #
26
+ # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload
27
+ # metadata. Use `OpenAI::FilePart` when you need to override the filename or
28
+ # content type.
29
+ #
26
30
  # @return [Pathname, StringIO, IO, String, OpenAI::FilePart, OpenAI::Models::VideoExtendParams::Video::VideoReferenceInputParam]
27
31
  required :video, union: -> { OpenAI::VideoExtendParams::Video }
28
32
 
data/lib/openai/models.rb CHANGED
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenAI
4
- [OpenAI::Internal::Type::BaseModel, *OpenAI::Internal::Type::BaseModel.subclasses].each do |cls|
4
+ # ActiveSupport 6 implements Class#subclasses using ==, which invokes BaseModel's
5
+ # structural equality before all model aliases are loaded.
6
+ base_model = OpenAI::Internal::Type::BaseModel
7
+ subclasses =
8
+ ObjectSpace.each_object(base_model.singleton_class).select do |candidate|
9
+ !candidate.singleton_class? && candidate.superclass.equal?(base_model)
10
+ end
11
+
12
+ [base_model, *subclasses].each do |cls|
5
13
  cls.define_sorbet_constant!(:OrHash) { T.type_alias { T.any(cls, OpenAI::Internal::AnyHash) } }
6
14
  end
7
15
 
@@ -5,6 +5,10 @@ require_relative "internal/read_io_adapter"
5
5
  module OpenAI
6
6
  # The SDK's pooled Net::HTTP implementation.
7
7
  #
8
+ # Network operations from a non-blocking fiber cooperate with its active Ruby
9
+ # Fiber scheduler, allowing concurrent requests and streams without occupying
10
+ # one thread per request.
11
+ #
8
12
  # Pass a block to configure each SDK-created connection before it is pooled
9
13
  # and started.
10
14
  class NetHTTPClient < HTTPClient
@@ -66,15 +70,15 @@ module OpenAI
66
70
  # @api private
67
71
  #
68
72
  # @param conn [Net::HTTP]
69
- # @param deadline [Float]
73
+ # @param deadline [Float, nil]
70
74
  private def calibrate_socket_timeout(conn, deadline)
71
- timeout = remaining_timeout(deadline)
75
+ timeout = deadline&.then { remaining_timeout(_1) }
72
76
  conn.open_timeout = conn.read_timeout = conn.write_timeout = conn.continue_timeout = timeout
73
77
  end
74
78
 
75
79
  # @api private
76
80
  #
77
- # @param deadline [Float]
81
+ # @param deadline [Float, nil]
78
82
  # @return [Float]
79
83
  # @raise [Timeout::Error]
80
84
  private def remaining_timeout(deadline)
@@ -130,7 +134,6 @@ module OpenAI
130
134
  # @yieldparam [Net::HTTP]
131
135
  private def with_pool(url, deadline:, &blk)
132
136
  origin = OpenAI::Internal::Util.uri_origin(url)
133
- timeout = remaining_timeout(deadline)
134
137
  pool =
135
138
  @mutex.synchronize do
136
139
  @pools[origin] ||= ConnectionPool.new(size: @size) do
@@ -138,7 +141,18 @@ module OpenAI
138
141
  end
139
142
  end
140
143
 
141
- pool.with(timeout: timeout, &blk)
144
+ return pool.with(timeout: remaining_timeout(deadline), &blk) if deadline
145
+
146
+ checked_out = false
147
+ begin
148
+ pool.with do |connection|
149
+ checked_out = true
150
+ blk.call(connection)
151
+ end
152
+ rescue ConnectionPool::TimeoutError
153
+ retry unless checked_out
154
+ raise
155
+ end
142
156
  end
143
157
 
144
158
  # @api private
@@ -200,7 +214,7 @@ module OpenAI
200
214
  # @return [OpenAI::HTTPClient::Response]
201
215
  def execute(request)
202
216
  url = request.url
203
- deadline = OpenAI::Internal::Util.monotonic_secs + request.timeout
217
+ deadline = request.timeout&.then { OpenAI::Internal::Util.monotonic_secs + _1 }
204
218
 
205
219
  req = nil
206
220
  finished = false
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenAI
4
+ module Providers
5
+ # @api private
6
+ module Azure
7
+ API_KEY_AUTH_MARKER = :openai_azure_api_key
8
+ BEARER_AUTH_MARKER = :openai_azure_bearer
9
+ AUTH_HEADERS = %w[api-key authorization].freeze
10
+ MISSING_ENDPOINT_MESSAGE =
11
+ "Azure OpenAI requires an endpoint. Pass `endpoint` to `azure(...)`, or set " \
12
+ "`AZURE_OPENAI_ENDPOINT`."
13
+ MISSING_CREDENTIALS_MESSAGE =
14
+ "Could not find credentials for Azure OpenAI. Pass `api_key` or `token_provider` " \
15
+ "to `azure(...)`, or set `AZURE_OPENAI_API_KEY`."
16
+
17
+ class Definition
18
+ attr_reader :name
19
+
20
+ def initialize(base_url:, credential_provider:, authentication:)
21
+ @name = "azure"
22
+ @base_url = base_url.freeze
23
+ @credential_provider = credential_provider
24
+ @authentication = authentication
25
+ end
26
+
27
+ def configure
28
+ auth = Auth.new(
29
+ base_url: @base_url,
30
+ credential_provider: @credential_provider,
31
+ authentication: @authentication
32
+ )
33
+ OpenAI::Internal::Provider::Runtime.new(
34
+ name: name,
35
+ base_url: @base_url,
36
+ prepare_request: auth.method(:prepare_request),
37
+ authentication_headers: AUTH_HEADERS
38
+ )
39
+ end
40
+ end
41
+
42
+ class Auth
43
+ def initialize(base_url:, credential_provider:, authentication:)
44
+ @base_url = URI(base_url)
45
+ @credential_provider = credential_provider
46
+ @authentication = authentication
47
+ end
48
+
49
+ def prepare_request(request)
50
+ Azure.validate_origin!(request.fetch(:url), @base_url)
51
+ marker = @authentication == :api_key ? API_KEY_AUTH_MARKER : BEARER_AUTH_MARKER
52
+ headers = Azure.provider_headers(request, marker: marker)
53
+ credential = resolve_credential
54
+ auth_header =
55
+ if @authentication == :api_key
56
+ {"api-key" => credential}
57
+ else
58
+ {"authorization" => "Bearer #{credential}"}
59
+ end
60
+
61
+ request.merge(headers: headers.merge(auth_header), provider_auth: marker)
62
+ end
63
+
64
+ private def resolve_credential
65
+ credential = @credential_provider.call
66
+ unless credential.is_a?(String) && !credential.strip.empty?
67
+ name = @authentication == :api_key ? "API key" : "token provider"
68
+ raise OpenAI::Errors::Error,
69
+ "The Azure OpenAI #{name} must return a non-empty string."
70
+ end
71
+ credential
72
+ rescue OpenAI::Errors::Error
73
+ raise
74
+ rescue StandardError => e
75
+ message =
76
+ if @authentication == :api_key
77
+ "Failed to resolve an API key for Azure OpenAI."
78
+ else
79
+ "Failed to resolve a bearer token for Azure OpenAI."
80
+ end
81
+ raise OpenAI::Errors::Error.new(message), cause: e
82
+ end
83
+ end
84
+
85
+ class << self
86
+ def normalize_endpoint(endpoint)
87
+ uri = URI(endpoint)
88
+ unless uri.is_a?(URI::HTTP) && uri.host
89
+ raise ArgumentError, "The Azure OpenAI `endpoint` must be an absolute HTTP or HTTPS URL."
90
+ end
91
+ unless uri.query.nil?
92
+ raise ArgumentError, "The Azure OpenAI `endpoint` must not include a query string."
93
+ end
94
+ unless uri.fragment.nil?
95
+ raise ArgumentError, "The Azure OpenAI `endpoint` must not include a fragment."
96
+ end
97
+ unless uri.userinfo.nil?
98
+ raise ArgumentError, "The Azure OpenAI `endpoint` must not include user information."
99
+ end
100
+
101
+ path = uri.path.sub(%r{/+\z}, "")
102
+ uri.path =
103
+ case path
104
+ when %r{/openai/v1\z}
105
+ path
106
+ when %r{/openai\z}
107
+ "#{path}/v1"
108
+ else
109
+ "#{path}/openai/v1"
110
+ end
111
+ uri.to_s
112
+ rescue URI::InvalidURIError => e
113
+ message = "The Azure OpenAI `endpoint` must be an absolute HTTP or HTTPS URL."
114
+ raise ArgumentError.new(message), cause: e
115
+ end
116
+
117
+ def normalize_optional_string(value)
118
+ return nil unless value.is_a?(String)
119
+ normalized = value.strip
120
+ normalized unless normalized.empty?
121
+ end
122
+
123
+ def provider_headers(request, marker:)
124
+ headers = request.fetch(:headers).dup
125
+ if request[:provider_auth] == marker
126
+ AUTH_HEADERS.each { headers.delete(_1) }
127
+ elsif AUTH_HEADERS.any? { headers.key?(_1) }
128
+ raise OpenAI::Errors::Error,
129
+ "Azure OpenAI provider authentication cannot be combined with a custom " \
130
+ "`Authorization` or `api-key` header."
131
+ end
132
+ headers
133
+ end
134
+
135
+ def validate_origin!(url, base_url)
136
+ return if OpenAI::Internal::Util.uri_origin(url) == OpenAI::Internal::Util.uri_origin(base_url)
137
+ raise OpenAI::Errors::Error,
138
+ "Refusing to authenticate a request for an origin other than the configured " \
139
+ "Azure OpenAI endpoint."
140
+ end
141
+ end
142
+ end
143
+
144
+ class << self
145
+ # Configure the standard OpenAI client for the Azure OpenAI v1 API.
146
+ #
147
+ # @param endpoint [String, nil] Azure OpenAI resource endpoint. Defaults to
148
+ # `AZURE_OPENAI_ENDPOINT`. `/openai/v1` is appended when absent.
149
+ #
150
+ # @param api_key [String, nil] Azure OpenAI API key. Defaults to
151
+ # `AZURE_OPENAI_API_KEY` when no token provider is configured. Passing
152
+ # `nil` explicitly skips the environment fallback.
153
+ #
154
+ # @param token_provider [#call, nil] Callable returning a Microsoft Entra
155
+ # bearer token before each request attempt.
156
+ #
157
+ # @return [OpenAI::Provider]
158
+ def azure(
159
+ endpoint: OpenAI::Internal::OMIT,
160
+ api_key: OpenAI::Internal::OMIT,
161
+ token_provider: nil
162
+ )
163
+ configured_endpoint =
164
+ if endpoint.equal?(OpenAI::Internal::OMIT)
165
+ Azure.normalize_optional_string(ENV["AZURE_OPENAI_ENDPOINT"])
166
+ else
167
+ normalized = Azure.normalize_optional_string(endpoint)
168
+ if !endpoint.nil? && normalized.nil?
169
+ raise ArgumentError, "The Azure OpenAI `endpoint` must not be empty."
170
+ end
171
+ normalized
172
+ end
173
+ raise ArgumentError, Azure::MISSING_ENDPOINT_MESSAGE if configured_endpoint.nil?
174
+ base_url = Azure.normalize_endpoint(configured_endpoint)
175
+
176
+ explicit_api_key = !api_key.equal?(OpenAI::Internal::OMIT) && !api_key.nil?
177
+ normalized_api_key = Azure.normalize_optional_string(api_key)
178
+ if explicit_api_key && normalized_api_key.nil?
179
+ raise ArgumentError, "The Azure OpenAI API key must not be empty."
180
+ end
181
+ unless token_provider.nil? || token_provider.respond_to?(:call)
182
+ raise ArgumentError, "The Azure OpenAI `token_provider` must respond to `call`."
183
+ end
184
+ if explicit_api_key && !token_provider.nil?
185
+ raise ArgumentError,
186
+ "The Azure OpenAI `api_key` and `token_provider` options are mutually exclusive."
187
+ end
188
+
189
+ environment_api_key =
190
+ if api_key.equal?(OpenAI::Internal::OMIT) && token_provider.nil?
191
+ Azure.normalize_optional_string(ENV["AZURE_OPENAI_API_KEY"])
192
+ end
193
+ resolved_api_key = normalized_api_key || environment_api_key
194
+ if resolved_api_key.nil? && token_provider.nil?
195
+ raise ArgumentError, Azure::MISSING_CREDENTIALS_MESSAGE
196
+ end
197
+
198
+ if token_provider
199
+ credential_provider = token_provider
200
+ authentication = :bearer
201
+ else
202
+ resolved_api_key.freeze
203
+ credential_provider = -> { resolved_api_key }
204
+ authentication = :api_key
205
+ end
206
+
207
+ definition = Azure::Definition.new(
208
+ base_url: base_url,
209
+ credential_provider: credential_provider,
210
+ authentication: authentication
211
+ )
212
+ OpenAI::Internal::Provider.create(definition)
213
+ end
214
+ end
215
+ end
216
+ end