openai 0.102.0 → 0.104.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 (426) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -0
  3. data/README.md +23 -1
  4. data/lib/openai/client.rb +3 -386
  5. data/lib/openai/helpers/admin/organization/projects/api_keys.rb +42 -0
  6. data/lib/openai/helpers/agents/session_stream_resource.rb +60 -0
  7. data/lib/openai/helpers/beta/agents/artifacts.rb +18 -0
  8. data/lib/openai/helpers/beta/agents/files.rb +41 -0
  9. data/lib/openai/helpers/client_runtime.rb +391 -0
  10. data/lib/openai/helpers/files.rb +47 -0
  11. data/lib/openai/helpers/live/websocket.rb +8 -57
  12. data/lib/openai/helpers/models/chat/parsed.rb +32 -0
  13. data/lib/openai/helpers/models/responses/parsed.rb +30 -0
  14. data/lib/openai/helpers/models/responses/response.rb +32 -0
  15. data/lib/openai/helpers/models/webhooks/webhook_endpoint_with_secret.rb +20 -0
  16. data/lib/openai/helpers/realtime/client_extension.rb +1 -2
  17. data/lib/openai/helpers/realtime/connection_manager.rb +1 -1
  18. data/lib/openai/helpers/realtime/translation.rb +7 -50
  19. data/lib/openai/helpers/responses_websocket/client_extension.rb +1 -2
  20. data/lib/openai/helpers/streaming/chat_completion_resource.rb +36 -0
  21. data/lib/openai/helpers/streaming/response_stream.rb +1 -16
  22. data/lib/openai/helpers/streaming/response_stream_resource.rb +78 -0
  23. data/lib/openai/helpers/vector_stores/file_batches.rb +188 -0
  24. data/lib/openai/helpers/vector_stores/files.rb +198 -0
  25. data/lib/openai/helpers/websocket/client_request.rb +31 -6
  26. data/lib/openai/helpers/websocket/connection.rb +58 -0
  27. data/lib/openai/helpers/websocket/connection_manager.rb +4 -2
  28. data/lib/openai/helpers/websocket/protocol.rb +2 -1
  29. data/lib/openai/helpers/websocket/resource.rb +54 -0
  30. data/lib/openai/helpers/websocket/typed_connection.rb +95 -0
  31. data/lib/openai/helpers/websocket.rb +4 -0
  32. data/lib/openai/models/admin/organization/audit_log_list_response.rb +5 -3
  33. data/lib/openai/models/admin/organization/external_storage_configuration.rb +5 -3
  34. data/lib/openai/models/admin/organization/external_storage_create_params.rb +33 -3
  35. data/lib/openai/models/admin/organization/oci_external_storage_provider.rb +37 -0
  36. data/lib/openai/models/admin/organization/project_residency.rb +3 -0
  37. data/lib/openai/models/admin/organization/projects/api_key_update_params.rb +64 -0
  38. data/lib/openai/models/admin/organization/projects/api_key_update_scopes_params.rb +34 -0
  39. data/lib/openai/models/admin/organization/usage_decisions_params.rb +177 -0
  40. data/lib/openai/models/admin/organization/usage_decisions_response.rb +351 -0
  41. data/lib/openai/models/audio/speech_create_params.rb +6 -3
  42. data/lib/openai/models/audio/translation_create_response.rb +0 -1
  43. data/lib/openai/models/audio/voice.rb +17 -2
  44. data/lib/openai/models/audio/voice_create_params.rb +100 -3
  45. data/lib/openai/models/beta/agent_create_params.rb +4 -2
  46. data/lib/openai/models/beta/agent_list_params.rb +4 -4
  47. data/lib/openai/models/beta/agent_session.rb +37 -1
  48. data/lib/openai/models/beta/agent_session_environment_expired_event.rb +57 -0
  49. data/lib/openai/models/beta/agent_session_environment_state.rb +6 -0
  50. data/lib/openai/models/beta/agent_session_environment_suspended_event.rb +57 -0
  51. data/lib/openai/models/beta/agent_session_event.rb +7 -1
  52. data/lib/openai/models/beta/agent_update_params.rb +4 -2
  53. data/lib/openai/models/beta/agents/environment_info.rb +3 -0
  54. data/lib/openai/models/beta/agents/environment_list_params.rb +7 -7
  55. data/lib/openai/models/beta/agents/session_create_params.rb +39 -3
  56. data/lib/openai/models/beta/agents/session_list_params.rb +7 -10
  57. data/lib/openai/models/beta/agents/session_update_params.rb +33 -1
  58. data/lib/openai/models/beta/agents/sessions/artifact_list_params.rb +11 -11
  59. data/lib/openai/models/beta/agents/vault_list_params.rb +23 -13
  60. data/lib/openai/models/beta/agents/vault_status_filter.rb +1 -3
  61. data/lib/openai/models/beta/agents/vault_update_params.rb +47 -0
  62. data/lib/openai/models/beta/agents/vaults/credential_list_params.rb +23 -13
  63. data/lib/openai/models/beta/beta_response.rb +23 -2
  64. data/lib/openai/models/beta/beta_response_error.rb +19 -1
  65. data/lib/openai/models/beta/beta_response_input_item.rb +107 -1
  66. data/lib/openai/models/beta/beta_response_item.rb +107 -1
  67. data/lib/openai/models/beta/beta_response_output_item.rb +107 -1
  68. data/lib/openai/models/beta/beta_responses_client_event.rb +23 -2
  69. data/lib/openai/models/beta/response_compact_params.rb +21 -0
  70. data/lib/openai/models/beta/response_create_params.rb +23 -2
  71. data/lib/openai/models/beta/session_turn_error.rb +3 -0
  72. data/lib/openai/models/chat/chat_completion_audio_param.rb +3 -3
  73. data/lib/openai/models/chat/chat_completion_message.rb +0 -8
  74. data/lib/openai/models/chat/chat_completion_message_function_tool_call.rb +0 -8
  75. data/lib/openai/models/chat/completion_create_params.rb +5 -12
  76. data/lib/openai/models/chat_model.rb +21 -0
  77. data/lib/openai/models/conversations/conversation_item.rb +107 -1
  78. data/lib/openai/models/decision.rb +17 -2
  79. data/lib/openai/models/decision_create_params.rb +29 -12
  80. data/lib/openai/models/decision_input_image.rb +4 -3
  81. data/lib/openai/models/decision_input_message.rb +4 -4
  82. data/lib/openai/models/decision_input_part.rb +3 -2
  83. data/lib/openai/models/image_edit_completed_event.rb +1 -2
  84. data/lib/openai/models/image_edit_partial_image_event.rb +1 -2
  85. data/lib/openai/models/image_gen_completed_event.rb +1 -2
  86. data/lib/openai/models/image_gen_partial_image_event.rb +1 -2
  87. data/lib/openai/models/images_response.rb +1 -2
  88. data/lib/openai/models/live/built_in_voice.rb +1 -0
  89. data/lib/openai/models/live/client_event.rb +14 -2
  90. data/lib/openai/models/live/decision_create_event.rb +26 -0
  91. data/lib/openai/models/live/decision_event.rb +117 -0
  92. data/lib/openai/models/live/decision_item_create_event.rb +194 -0
  93. data/lib/openai/models/live/decision_item_created_event.rb +202 -0
  94. data/lib/openai/models/live/decision_item_delete_event.rb +38 -0
  95. data/lib/openai/models/live/decision_item_deleted_event.rb +46 -0
  96. data/lib/openai/models/live/decision_trigger.rb +244 -0
  97. data/lib/openai/models/live/decisions_delegation_config.rb +201 -0
  98. data/lib/openai/models/live/decisions_delegation_update_config.rb +208 -0
  99. data/lib/openai/models/live/delegation_created_event.rb +10 -10
  100. data/lib/openai/models/live/fork_client_event.rb +14 -2
  101. data/lib/openai/models/live/fork_server_event.rb +17 -2
  102. data/lib/openai/models/live/media_session_config.rb +47 -11
  103. data/lib/openai/models/live/output_audio_fixture_play_event.rb +48 -0
  104. data/lib/openai/models/live/output_audio_fixture_played_event.rb +42 -0
  105. data/lib/openai/models/live/output_audio_fixture_playing_event.rb +41 -0
  106. data/lib/openai/models/live/server_event.rb +17 -2
  107. data/lib/openai/models/live/session_accept_params.rb +47 -11
  108. data/lib/openai/models/live/session_config.rb +47 -11
  109. data/lib/openai/models/live/session_resource.rb +47 -11
  110. data/lib/openai/models/live/session_update_config.rb +38 -6
  111. data/lib/openai/models/live/session_update_event.rb +8 -8
  112. data/lib/openai/models/live/sideband_client_event.rb +57 -0
  113. data/lib/openai/models/live/sideband_server_event.rb +78 -0
  114. data/lib/openai/models/realtime/realtime_audio_config_output.rb +6 -3
  115. data/lib/openai/models/realtime/realtime_response_create_audio_output.rb +6 -3
  116. data/lib/openai/models/realtime/realtime_response_status.rb +10 -1
  117. data/lib/openai/models/response_format_json_schema.rb +1 -1
  118. data/lib/openai/models/responses/function_tool.rb +8 -6
  119. data/lib/openai/models/responses/response.rb +2 -23
  120. data/lib/openai/models/responses/response_compact_params.rb +21 -0
  121. data/lib/openai/models/responses/response_create_params.rb +3 -6
  122. data/lib/openai/models/responses/response_error.rb +19 -1
  123. data/lib/openai/models/responses/response_format_text_config.rb +0 -4
  124. data/lib/openai/models/responses/response_function_tool_call.rb +0 -8
  125. data/lib/openai/models/responses/response_input_item.rb +107 -1
  126. data/lib/openai/models/responses/response_item.rb +107 -1
  127. data/lib/openai/models/responses/response_output_item.rb +107 -1
  128. data/lib/openai/models/responses/response_output_text.rb +0 -8
  129. data/lib/openai/models/responses/responses_client_event.rb +2 -2
  130. data/lib/openai/models/safety/safety_alert.rb +12 -1
  131. data/lib/openai/models/video_create_error.rb +19 -1
  132. data/lib/openai/models/webhooks/agent_environment_expired_webhook_event.rb +72 -0
  133. data/lib/openai/models/webhooks/agent_environment_suspended_webhook_event.rb +72 -0
  134. data/lib/openai/models/webhooks/unwrap_webhook_event.rb +8 -3
  135. data/lib/openai/models/webhooks/webhook_create_params.rb +2 -0
  136. data/lib/openai/models/webhooks/webhook_endpoint_with_secret.rb +0 -9
  137. data/lib/openai/models/webhooks/webhook_test_params.rb +2 -0
  138. data/lib/openai/models/webhooks/webhook_update_params.rb +2 -0
  139. data/lib/openai/resources/admin/organization/external_storage.rb +1 -1
  140. data/lib/openai/resources/admin/organization/projects/api_keys.rb +61 -0
  141. data/lib/openai/resources/admin/organization/usage.rb +67 -0
  142. data/lib/openai/resources/audio/speech.rb +2 -1
  143. data/lib/openai/resources/audio/voices.rb +10 -10
  144. data/lib/openai/resources/beta/agents/environments/files.rb +3 -31
  145. data/lib/openai/resources/beta/agents/environments.rb +3 -3
  146. data/lib/openai/resources/beta/agents/sessions/artifacts.rb +5 -11
  147. data/lib/openai/resources/beta/agents/sessions/events.rb +1 -1
  148. data/lib/openai/resources/beta/agents/sessions.rb +20 -56
  149. data/lib/openai/resources/beta/agents/vaults/credentials.rb +10 -6
  150. data/lib/openai/resources/beta/agents/vaults.rb +45 -6
  151. data/lib/openai/resources/beta/agents.rb +6 -4
  152. data/lib/openai/resources/beta/responses.rb +2 -2
  153. data/lib/openai/resources/chat/completions.rb +11 -37
  154. data/lib/openai/resources/decisions.rb +9 -15
  155. data/lib/openai/resources/files.rb +0 -39
  156. data/lib/openai/resources/live/forks.rb +19 -0
  157. data/lib/openai/resources/live/sideband.rb +19 -0
  158. data/lib/openai/resources/live.rb +19 -0
  159. data/lib/openai/resources/realtime/translations.rb +19 -0
  160. data/lib/openai/resources/realtime.rb +19 -0
  161. data/lib/openai/resources/responses.rb +44 -95
  162. data/lib/openai/resources/vector_stores/file_batches.rb +0 -178
  163. data/lib/openai/resources/vector_stores/files.rb +0 -188
  164. data/lib/openai/resources/webhooks.rb +13 -29
  165. data/lib/openai/version.rb +1 -1
  166. data/lib/openai.rb +35 -0
  167. data/rbi/openai/client.rbi +0 -103
  168. data/rbi/openai/helpers/admin/organization/projects/api_keys.rbi +25 -0
  169. data/rbi/openai/helpers/beta/agents/artifacts.rbi +25 -0
  170. data/rbi/openai/helpers/beta/agents/files.rbi +48 -0
  171. data/rbi/openai/helpers/client_runtime.rbi +108 -0
  172. data/rbi/openai/helpers/files.rbi +25 -0
  173. data/rbi/openai/helpers/models/chat/parsed.rbi +21 -0
  174. data/rbi/openai/helpers/models/responses/parsed.rbi +19 -0
  175. data/rbi/openai/helpers/models/responses/response.rbi +15 -0
  176. data/rbi/openai/helpers/streaming/response_stream.rbi +301 -0
  177. data/rbi/openai/helpers/vector_stores/file_batches.rbi +102 -0
  178. data/rbi/openai/helpers/vector_stores/files.rbi +122 -0
  179. data/rbi/openai/helpers/websocket.rbi +82 -0
  180. data/rbi/openai/models/admin/organization/audit_log_list_response.rbi +6 -3
  181. data/rbi/openai/models/admin/organization/external_storage_configuration.rbi +4 -2
  182. data/rbi/openai/models/admin/organization/external_storage_create_params.rbi +63 -4
  183. data/rbi/openai/models/admin/organization/oci_external_storage_provider.rbi +71 -0
  184. data/rbi/openai/models/admin/organization/project_residency.rbi +12 -0
  185. data/rbi/openai/models/admin/organization/projects/api_key_update_params.rbi +110 -0
  186. data/rbi/openai/models/admin/organization/projects/api_key_update_scopes_params.rbi +72 -0
  187. data/rbi/openai/models/admin/organization/usage_decisions_params.rbi +229 -0
  188. data/rbi/openai/models/admin/organization/usage_decisions_response.rbi +479 -0
  189. data/rbi/openai/models/audio/speech_create_params.rbi +6 -3
  190. data/rbi/openai/models/audio/voice.rbi +21 -2
  191. data/rbi/openai/models/audio/voice_create_params.rbi +149 -4
  192. data/rbi/openai/models/beta/agent_create_params.rbi +4 -2
  193. data/rbi/openai/models/beta/agent_list_params.rbi +8 -5
  194. data/rbi/openai/models/beta/agent_session.rbi +64 -1
  195. data/rbi/openai/models/beta/agent_session_environment_expired_event.rbi +95 -0
  196. data/rbi/openai/models/beta/agent_session_environment_state.rbi +6 -0
  197. data/rbi/openai/models/beta/agent_session_environment_suspended_event.rbi +95 -0
  198. data/rbi/openai/models/beta/agent_session_event.rbi +2 -0
  199. data/rbi/openai/models/beta/agent_update_params.rbi +4 -2
  200. data/rbi/openai/models/beta/agents/environment_info.rbi +3 -0
  201. data/rbi/openai/models/beta/agents/environment_list_params.rbi +7 -7
  202. data/rbi/openai/models/beta/agents/session_create_params.rbi +62 -2
  203. data/rbi/openai/models/beta/agents/session_list_params.rbi +11 -11
  204. data/rbi/openai/models/beta/agents/session_update_params.rbi +56 -0
  205. data/rbi/openai/models/beta/agents/sessions/artifact_list_params.rbi +19 -13
  206. data/rbi/openai/models/beta/agents/vault_list_params.rbi +30 -13
  207. data/rbi/openai/models/beta/agents/vault_status_filter.rbi +1 -3
  208. data/rbi/openai/models/beta/agents/vault_update_params.rbi +87 -0
  209. data/rbi/openai/models/beta/agents/vaults/credential_list_params.rbi +30 -13
  210. data/rbi/openai/models/beta/beta_response.rbi +2 -2
  211. data/rbi/openai/models/beta/beta_response_error.rbi +24 -3
  212. data/rbi/openai/models/beta/beta_response_input_item.rbi +191 -0
  213. data/rbi/openai/models/beta/beta_response_item.rbi +189 -0
  214. data/rbi/openai/models/beta/beta_response_output_item.rbi +191 -0
  215. data/rbi/openai/models/beta/beta_responses_client_event.rbi +2 -2
  216. data/rbi/openai/models/beta/response_create_params.rbi +2 -2
  217. data/rbi/openai/models/beta/session_turn_error.rbi +3 -0
  218. data/rbi/openai/models/chat/chat_completion_audio_param.rbi +3 -3
  219. data/rbi/openai/models/chat/chat_completion_message.rbi +0 -4
  220. data/rbi/openai/models/chat/chat_completion_message_function_tool_call.rbi +0 -4
  221. data/rbi/openai/models/chat/completion_create_params.rbi +63 -78
  222. data/rbi/openai/models/conversations/conversation_item.rbi +191 -0
  223. data/rbi/openai/models/decision.rbi +14 -1
  224. data/rbi/openai/models/decision_create_params.rbi +23 -12
  225. data/rbi/openai/models/decision_input_image.rbi +4 -3
  226. data/rbi/openai/models/decision_input_message.rbi +4 -4
  227. data/rbi/openai/models/decision_input_part.rbi +2 -1
  228. data/rbi/openai/models/image_edit_completed_event.rbi +1 -0
  229. data/rbi/openai/models/image_edit_partial_image_event.rbi +1 -0
  230. data/rbi/openai/models/image_gen_completed_event.rbi +1 -0
  231. data/rbi/openai/models/image_gen_partial_image_event.rbi +1 -0
  232. data/rbi/openai/models/images_response.rbi +1 -0
  233. data/rbi/openai/models/live/built_in_voice.rbi +1 -0
  234. data/rbi/openai/models/live/client_event.rbi +4 -0
  235. data/rbi/openai/models/live/decision_create_event.rbi +55 -0
  236. data/rbi/openai/models/live/decision_event.rbi +185 -0
  237. data/rbi/openai/models/live/decision_item_create_event.rbi +338 -0
  238. data/rbi/openai/models/live/decision_item_created_event.rbi +354 -0
  239. data/rbi/openai/models/live/decision_item_delete_event.rbi +66 -0
  240. data/rbi/openai/models/live/decision_item_deleted_event.rbi +77 -0
  241. data/rbi/openai/models/live/decision_trigger.rbi +465 -0
  242. data/rbi/openai/models/live/decisions_delegation_config.rbi +393 -0
  243. data/rbi/openai/models/live/decisions_delegation_update_config.rbi +412 -0
  244. data/rbi/openai/models/live/delegation_created_event.rbi +8 -10
  245. data/rbi/openai/models/live/fork_client_event.rbi +4 -0
  246. data/rbi/openai/models/live/fork_server_event.rbi +5 -0
  247. data/rbi/openai/models/live/media_session_config.rbi +95 -14
  248. data/rbi/openai/models/live/output_audio_fixture_play_event.rbi +76 -0
  249. data/rbi/openai/models/live/output_audio_fixture_played_event.rbi +73 -0
  250. data/rbi/openai/models/live/output_audio_fixture_playing_event.rbi +72 -0
  251. data/rbi/openai/models/live/server_event.rbi +5 -0
  252. data/rbi/openai/models/live/session_accept_params.rbi +88 -12
  253. data/rbi/openai/models/live/session_config.rbi +100 -14
  254. data/rbi/openai/models/live/session_resource.rbi +81 -10
  255. data/rbi/openai/models/live/session_update_config.rbi +86 -9
  256. data/rbi/openai/models/live/session_update_event.rbi +8 -8
  257. data/rbi/openai/models/live/sideband_client_event.rbi +40 -0
  258. data/rbi/openai/models/live/sideband_server_event.rbi +47 -0
  259. data/rbi/openai/models/realtime/realtime_audio_config_output.rbi +6 -3
  260. data/rbi/openai/models/realtime/realtime_response_create_audio_output.rbi +6 -3
  261. data/rbi/openai/models/realtime/realtime_response_status.rbi +13 -1
  262. data/rbi/openai/models/realtime/realtime_translation_input_audio_buffer_append_event.rbi +1 -1
  263. data/rbi/openai/models/realtime/realtime_translation_output_audio_delta_event.rbi +3 -3
  264. data/rbi/openai/models/realtime/realtime_translation_session_close_event.rbi +1 -1
  265. data/rbi/openai/models/realtime/realtime_translation_session_update_event.rbi +5 -1
  266. data/rbi/openai/models/responses/response.rbi +2 -7
  267. data/rbi/openai/models/responses/response_create_params.rbi +5 -9
  268. data/rbi/openai/models/responses/response_error.rbi +24 -3
  269. data/rbi/openai/models/responses/response_function_tool_call.rbi +0 -4
  270. data/rbi/openai/models/responses/response_function_web_search.rbi +21 -17
  271. data/rbi/openai/models/responses/response_input_item.rbi +191 -0
  272. data/rbi/openai/models/responses/response_item.rbi +189 -0
  273. data/rbi/openai/models/responses/response_output_item.rbi +191 -0
  274. data/rbi/openai/models/responses/response_output_text.rbi +0 -4
  275. data/rbi/openai/models/responses/responses_client_event.rbi +2 -2
  276. data/rbi/openai/models/safety/safety_alert.rbi +13 -1
  277. data/rbi/openai/models/video_create_error.rbi +24 -3
  278. data/rbi/openai/models/webhooks/agent_environment_expired_webhook_event.rbi +133 -0
  279. data/rbi/openai/models/webhooks/agent_environment_suspended_webhook_event.rbi +133 -0
  280. data/rbi/openai/models/webhooks/unwrap_webhook_event.rbi +3 -2
  281. data/rbi/openai/models/webhooks/webhook_create_params.rbi +8 -0
  282. data/rbi/openai/models/webhooks/webhook_test_params.rbi +8 -0
  283. data/rbi/openai/models/webhooks/webhook_update_params.rbi +8 -0
  284. data/rbi/openai/resources/admin/organization/external_storage.rbi +2 -1
  285. data/rbi/openai/resources/admin/organization/projects/api_keys.rbi +48 -0
  286. data/rbi/openai/resources/admin/organization/usage.rbi +56 -0
  287. data/rbi/openai/resources/audio/speech.rbi +2 -1
  288. data/rbi/openai/resources/audio/voices.rbi +13 -10
  289. data/rbi/openai/resources/beta/agents/environments/files.rbi +3 -33
  290. data/rbi/openai/resources/beta/agents/environments.rbi +3 -3
  291. data/rbi/openai/resources/beta/agents/sessions/artifacts.rbi +5 -13
  292. data/rbi/openai/resources/beta/agents/sessions.rbi +15 -4
  293. data/rbi/openai/resources/beta/agents/vaults/credentials.rbi +9 -5
  294. data/rbi/openai/resources/beta/agents/vaults.rbi +34 -5
  295. data/rbi/openai/resources/beta/agents.rbi +6 -4
  296. data/rbi/openai/resources/beta/responses.rbi +2 -2
  297. data/rbi/openai/resources/chat/completions.rbi +26 -29
  298. data/rbi/openai/resources/decisions.rbi +9 -15
  299. data/rbi/openai/resources/files.rbi +0 -18
  300. data/rbi/openai/resources/live/forks.rbi +17 -0
  301. data/rbi/openai/resources/live/sideband.rbi +17 -0
  302. data/rbi/openai/resources/live.rbi +17 -0
  303. data/rbi/openai/resources/realtime/translations.rbi +17 -0
  304. data/rbi/openai/resources/realtime.rbi +17 -0
  305. data/rbi/openai/resources/responses.rbi +41 -333
  306. data/rbi/openai/resources/vector_stores/file_batches.rbi +0 -93
  307. data/rbi/openai/resources/vector_stores/files.rbi +0 -113
  308. data/rbi/openai/resources/webhooks.rbi +2 -0
  309. data/sig/openai/client.rbs +0 -50
  310. data/sig/openai/helpers/admin/organization/projects/api_keys.rbs +18 -0
  311. data/sig/openai/helpers/beta/agents/artifacts.rbs +17 -0
  312. data/sig/openai/helpers/beta/agents/files.rbs +30 -0
  313. data/sig/openai/helpers/client_runtime.rbs +53 -0
  314. data/sig/openai/helpers/files.rbs +12 -0
  315. data/sig/openai/helpers/models/responses/response.rbs +9 -0
  316. data/sig/openai/helpers/streaming/response_stream.rbs +37 -0
  317. data/sig/openai/helpers/vector_stores/file_batches.rbs +38 -0
  318. data/sig/openai/helpers/vector_stores/files.rbs +43 -0
  319. data/sig/openai/helpers/websocket.rbs +40 -0
  320. data/sig/openai/models/admin/organization/audit_log_list_response.rbs +1 -0
  321. data/sig/openai/models/admin/organization/external_storage_configuration.rbs +1 -0
  322. data/sig/openai/models/admin/organization/external_storage_create_params.rbs +33 -0
  323. data/sig/openai/models/admin/organization/oci_external_storage_provider.rbs +34 -0
  324. data/sig/openai/models/admin/organization/project_residency.rbs +6 -0
  325. data/sig/openai/models/admin/organization/projects/api_key_update_params.rbs +77 -0
  326. data/sig/openai/models/admin/organization/projects/api_key_update_scopes_params.rbs +38 -0
  327. data/sig/openai/models/admin/organization/usage_decisions_params.rbs +170 -0
  328. data/sig/openai/models/admin/organization/usage_decisions_response.rbs +319 -0
  329. data/sig/openai/models/audio/speech_create_params.rbs +4 -2
  330. data/sig/openai/models/audio/voice.rbs +21 -3
  331. data/sig/openai/models/audio/voice_create_params.rbs +90 -1
  332. data/sig/openai/models/beta/agent_create_params.rbs +4 -2
  333. data/sig/openai/models/beta/agent_list_params.rbs +9 -6
  334. data/sig/openai/models/beta/agent_session.rbs +36 -2
  335. data/sig/openai/models/beta/agent_session_environment_expired_event.rbs +57 -0
  336. data/sig/openai/models/beta/agent_session_environment_state.rbs +14 -1
  337. data/sig/openai/models/beta/agent_session_environment_suspended_event.rbs +57 -0
  338. data/sig/openai/models/beta/agent_session_event.rbs +2 -0
  339. data/sig/openai/models/beta/agent_update_params.rbs +4 -2
  340. data/sig/openai/models/beta/agents/environment_info.rbs +11 -1
  341. data/sig/openai/models/beta/agents/environment_list_params.rbs +9 -9
  342. data/sig/openai/models/beta/agents/session_create_params.rbs +29 -2
  343. data/sig/openai/models/beta/agents/session_list_params.rbs +12 -12
  344. data/sig/openai/models/beta/agents/session_update_params.rbs +23 -0
  345. data/sig/openai/models/beta/agents/sessions/artifact_list_params.rbs +21 -15
  346. data/sig/openai/models/beta/agents/vault_list_params.rbs +32 -14
  347. data/sig/openai/models/beta/agents/vault_update_params.rbs +54 -0
  348. data/sig/openai/models/beta/agents/vaults/credential_list_params.rbs +32 -14
  349. data/sig/openai/models/beta/beta_response.rbs +2 -2
  350. data/sig/openai/models/beta/beta_response_error.rbs +19 -3
  351. data/sig/openai/models/beta/beta_response_input_item.rbs +110 -0
  352. data/sig/openai/models/beta/beta_response_item.rbs +110 -0
  353. data/sig/openai/models/beta/beta_response_output_item.rbs +110 -0
  354. data/sig/openai/models/beta/beta_responses_client_event.rbs +2 -2
  355. data/sig/openai/models/beta/response_create_params.rbs +2 -2
  356. data/sig/openai/models/beta/session_turn_error.rbs +4 -0
  357. data/sig/openai/models/chat/chat_completion_audio_param.rbs +2 -2
  358. data/sig/openai/models/chat/completion_create_params.rbs +2 -2
  359. data/sig/openai/models/conversations/conversation_item.rbs +110 -0
  360. data/sig/openai/models/decision.rbs +13 -0
  361. data/sig/openai/models/decision_create_params.rbs +19 -8
  362. data/sig/openai/models/decision_input_image.rbs +2 -2
  363. data/sig/openai/models/decision_input_message.rbs +2 -2
  364. data/sig/openai/models/live/built_in_voice.rbs +2 -0
  365. data/sig/openai/models/live/client_event.rbs +4 -0
  366. data/sig/openai/models/live/decision_create_event.rbs +18 -0
  367. data/sig/openai/models/live/decision_event.rbs +102 -0
  368. data/sig/openai/models/live/decision_item_create_event.rbs +183 -0
  369. data/sig/openai/models/live/decision_item_created_event.rbs +195 -0
  370. data/sig/openai/models/live/decision_item_delete_event.rbs +33 -0
  371. data/sig/openai/models/live/decision_item_deleted_event.rbs +49 -0
  372. data/sig/openai/models/live/decision_trigger.rbs +234 -0
  373. data/sig/openai/models/live/decisions_delegation_config.rbs +195 -0
  374. data/sig/openai/models/live/decisions_delegation_update_config.rbs +202 -0
  375. data/sig/openai/models/live/delegation_created_event.rbs +6 -7
  376. data/sig/openai/models/live/fork_client_event.rbs +4 -0
  377. data/sig/openai/models/live/fork_server_event.rbs +5 -0
  378. data/sig/openai/models/live/media_session_config.rbs +58 -11
  379. data/sig/openai/models/live/output_audio_fixture_play_event.rbs +42 -0
  380. data/sig/openai/models/live/output_audio_fixture_played_event.rbs +44 -0
  381. data/sig/openai/models/live/output_audio_fixture_playing_event.rbs +44 -0
  382. data/sig/openai/models/live/server_event.rbs +5 -0
  383. data/sig/openai/models/live/session_accept_params.rbs +57 -10
  384. data/sig/openai/models/live/session_config.rbs +58 -11
  385. data/sig/openai/models/live/session_resource.rbs +57 -10
  386. data/sig/openai/models/live/session_update_config.rbs +54 -3
  387. data/sig/openai/models/live/session_update_event.rbs +6 -6
  388. data/sig/openai/models/live/sideband_client_event.rbs +26 -0
  389. data/sig/openai/models/live/sideband_server_event.rbs +33 -0
  390. data/sig/openai/models/realtime/realtime_audio_config_output.rbs +6 -3
  391. data/sig/openai/models/realtime/realtime_response_create_audio_output.rbs +6 -3
  392. data/sig/openai/models/realtime/realtime_response_status.rbs +15 -3
  393. data/sig/openai/models/responses/response.rbs +2 -4
  394. data/sig/openai/models/responses/response_create_params.rbs +2 -2
  395. data/sig/openai/models/responses/response_error.rbs +19 -3
  396. data/sig/openai/models/responses/response_input_item.rbs +110 -0
  397. data/sig/openai/models/responses/response_item.rbs +110 -0
  398. data/sig/openai/models/responses/response_output_item.rbs +110 -0
  399. data/sig/openai/models/responses/responses_client_event.rbs +2 -2
  400. data/sig/openai/models/safety/safety_alert.rbs +12 -2
  401. data/sig/openai/models/video_create_error.rbs +19 -3
  402. data/sig/openai/models/webhooks/agent_environment_expired_webhook_event.rbs +70 -0
  403. data/sig/openai/models/webhooks/agent_environment_suspended_webhook_event.rbs +70 -0
  404. data/sig/openai/models/webhooks/unwrap_webhook_event.rbs +3 -1
  405. data/sig/openai/models/webhooks/webhook_create_params.rbs +4 -0
  406. data/sig/openai/models/webhooks/webhook_test_params.rbs +4 -0
  407. data/sig/openai/models/webhooks/webhook_update_params.rbs +4 -0
  408. data/sig/openai/resources/admin/organization/projects/api_keys.rbs +8 -0
  409. data/sig/openai/resources/admin/organization/usage.rbs +15 -0
  410. data/sig/openai/resources/beta/agents/environments/files.rbs +0 -17
  411. data/sig/openai/resources/beta/agents/sessions/artifacts.rbs +2 -6
  412. data/sig/openai/resources/beta/agents/sessions.rbs +4 -1
  413. data/sig/openai/resources/beta/agents/vaults/credentials.rbs +2 -1
  414. data/sig/openai/resources/beta/agents/vaults.rbs +9 -1
  415. data/sig/openai/resources/beta/agents.rbs +1 -1
  416. data/sig/openai/resources/files.rbs +0 -7
  417. data/sig/openai/resources/live/forks.rbs +8 -0
  418. data/sig/openai/resources/live/sideband.rbs +8 -0
  419. data/sig/openai/resources/live.rbs +7 -0
  420. data/sig/openai/resources/realtime/translations.rbs +7 -0
  421. data/sig/openai/resources/realtime.rbs +7 -0
  422. data/sig/openai/resources/responses.rbs +7 -31
  423. data/sig/openai/resources/vector_stores/file_batches.rbs +0 -31
  424. data/sig/openai/resources/vector_stores/files.rbs +0 -36
  425. data/sig/openai/resources/webhooks.rbs +3 -1
  426. metadata +101 -1
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenAI
4
+ module Resources
5
+ class Beta
6
+ class Agents
7
+ class Sessions
8
+ # Submit input to an idle session and stream one turn through idle or failure.
9
+ # The caller must be the session's only input writer during iteration.
10
+ # Handlers run sequentially; failures submit a generic error without exception text.
11
+ # Input and each tool result use distinct retry-safe idempotency keys.
12
+ # A block closes the connection on every exit path without cancelling the turn.
13
+ #
14
+ # @param session_id [String]
15
+ # @param input [String, Array<OpenAI::Models::Beta::AgentSessionInputMessageParam>]
16
+ # @param tool_handlers [Hash{String=>Proc}]
17
+ # @param on_tool_error [Proc, nil]
18
+ # Beta: use with tool_handlers to log or monitor argument parsing, handler
19
+ # execution, and output conversion failures locally. Receives ToolError;
20
+ # API/transport errors still propagate normally. Ordinary observer errors
21
+ # are ignored, and the submitted tool failure stays generic.
22
+ # @param idempotency_key [String, nil] Applies only to the input submission.
23
+ # @param output_type [Class<OpenAI::BaseModel>, nil] Beta: parse output without changing session configuration.
24
+ # @param request_options [OpenAI::RequestOptions, Hash{Symbol=>Object}, nil]
25
+ # @yieldparam stream [OpenAI::Helpers::Agents::SessionStream]
26
+ # @return [OpenAI::Helpers::Agents::SessionStream]
27
+ def stream(
28
+ session_id,
29
+ input:,
30
+ tool_handlers: {},
31
+ on_tool_error: nil,
32
+ idempotency_key: nil,
33
+ output_type: nil,
34
+ request_options: {}
35
+ )
36
+ stream = OpenAI::Helpers::Agents::SessionStream.new(
37
+ sessions: self,
38
+ session_id: session_id,
39
+ input: input,
40
+ tool_handlers: tool_handlers,
41
+ on_tool_error: on_tool_error,
42
+ idempotency_key: idempotency_key,
43
+ output_type: output_type,
44
+ request_options: request_options
45
+ )
46
+ return stream unless block_given?
47
+
48
+ begin
49
+ yield stream
50
+ ensure
51
+ stream.close
52
+ end
53
+
54
+ stream
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -70,3 +70,21 @@ module OpenAI
70
70
  end
71
71
  end
72
72
  end
73
+
74
+ module OpenAI
75
+ module Resources
76
+ class Beta
77
+ class Agents
78
+ class Sessions
79
+ class Artifacts
80
+ # Access artifacts from exactly this completed turn (beta).
81
+ # @return [OpenAI::Helpers::Beta::Agents::ResultArtifacts]
82
+ def for_result(result)
83
+ OpenAI::Helpers::Beta::Agents::ResultArtifacts.new(artifacts: self, client: @client, result: result)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -234,3 +234,44 @@ module OpenAI
234
234
  end
235
235
  end
236
236
  end
237
+
238
+ module OpenAI
239
+ module Resources
240
+ class Beta
241
+ class Agents
242
+ class Environments
243
+ class Files
244
+ # Upload selected local files for a new hosted environment (beta).
245
+ # @return [OpenAI::Helpers::Beta::Agents::PreparedFiles]
246
+ def prepare(files, request_options: {})
247
+ OpenAI::Helpers::Beta::Agents::FilePreparation
248
+ .new(client: @client, environment_files: self)
249
+ .prepare(files, request_options: request_options)
250
+ end
251
+
252
+ # Upload an explicitly selected directory snapshot (beta).
253
+ # @return [OpenAI::Helpers::Beta::Agents::PreparedFiles]
254
+ def prepare_directory(directory, destination:, include:, request_options: {})
255
+ OpenAI::Helpers::Beta::Agents::FilePreparation
256
+ .new(client: @client, environment_files: self)
257
+ .prepare_directory(
258
+ directory,
259
+ destination: destination,
260
+ include: include,
261
+ request_options: request_options
262
+ )
263
+ end
264
+
265
+ # Upload and stage a local file in an existing environment (beta).
266
+ # @return [OpenAI::Helpers::Beta::Agents::PreparedFiles]
267
+ def upload(environment_id, file:, path:, request_options: {})
268
+ OpenAI::Helpers::Beta::Agents::FilePreparation
269
+ .new(client: @client, environment_files: self)
270
+ .upload(environment_id, file: file, path: path, request_options: request_options)
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,391 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenAI
4
+ class Client < OpenAI::Internal::Transport::BaseClient
5
+ # @api private
6
+ private def x509_identity?(identity)
7
+ OpenAI::Auth::X509Transport.exact_instance?(identity, OpenAI::Auth::X509WorkloadIdentity)
8
+ end
9
+
10
+ # @api private
11
+ private def x509_transport?(transport)
12
+ OpenAI::Auth::X509Transport.exact_instance?(transport, OpenAI::Auth::X509Transport)
13
+ end
14
+
15
+ # Creates and returns a new client for interacting with the API.
16
+ #
17
+ # @param api_key [String, nil] Defaults to `ENV["OPENAI_API_KEY"]`.
18
+ # Mutually exclusive with `workload_identity`.
19
+ #
20
+
21
+ # @api private
22
+ private def build_request(request, options)
23
+ if @provider_runtime
24
+ built = super
25
+ body = built[:body]
26
+ # Capture only the final model; provider hooks run after body encoding.
27
+ model = body.reverse_each.find { |key, _| key == :model || key == "model" }&.last if body.is_a?(Hash)
28
+ model = model.to_s if model.is_a?(Symbol)
29
+ return built.merge(provider_request_model: model)
30
+ end
31
+
32
+ unless @workload_identity_auth && x509_transport?(@requester)
33
+ return super
34
+ end
35
+
36
+ selected = {}.merge(request.fetch(:security, {bearer_auth: true, admin_api_key_auth: true})).freeze
37
+ if selected.any? { |scheme, enabled| enabled && scheme != :bearer_auth && scheme != :admin_api_key_auth }
38
+ raise ArgumentError, "Unsupported authentication security scheme for X.509 workload identity"
39
+ end
40
+
41
+ security = {
42
+ bearer_auth: selected[:bearer_auth] == true,
43
+ admin_api_key_auth: selected[:admin_api_key_auth] == true
44
+ }.freeze
45
+ expected = auth_headers(security: security)["authorization"]
46
+ built = super(request.merge(security: security), options)
47
+ canonical_headers = @requester.validate_api_request!(
48
+ url: built.fetch(:url),
49
+ headers: built.fetch(:headers)
50
+ )
51
+ unless expected == canonical_headers["authorization"]
52
+ raise OpenAI::Errors::Error, "X.509 requests cannot override the selected authorization credential"
53
+ end
54
+
55
+ auth_max_retries = options.fetch(:max_retries, @max_retries)
56
+ built.merge(headers: canonical_headers, x509_auth_max_retries: auth_max_retries)
57
+ end
58
+
59
+ # @api private
60
+ private def prepare_request(request, redirect_count:, retry_count:)
61
+ if workload_identity_request?(request)
62
+ request = prepare_workload_identity_request(request, retry_count: retry_count)
63
+ end
64
+
65
+ preparer = @provider_runtime&.prepare_request
66
+ return super(request, redirect_count: redirect_count, retry_count: retry_count) unless preparer
67
+ preparer.call(request)
68
+ end
69
+
70
+ # @api private
71
+ private def validate_prepared_request(request, original_request:)
72
+ prepared_request = super
73
+ return prepared_request unless @workload_identity_auth && x509_transport?(@requester)
74
+
75
+ canonical_headers = @requester.validate_api_request!(
76
+ url: prepared_request.fetch(:url),
77
+ headers: prepared_request.fetch(:headers)
78
+ )
79
+ expected = if (context = original_request[:x509_request_context])
80
+ "Bearer #{context.fetch(:token)}"
81
+ else
82
+ original_request.fetch(:headers)["authorization"]
83
+ end
84
+
85
+ unless canonical_headers["authorization"] == expected
86
+ raise OpenAI::Errors::Error, "X.509 requests cannot override the selected authorization credential"
87
+ end
88
+
89
+ prepared_request.merge(headers: canonical_headers)
90
+ end
91
+
92
+ # @api private
93
+ private def validate_retry_delay!(request, delay:)
94
+ deadline = request[:x509_request_context]&.fetch(:deadline) || request[:workload_identity_deadline]
95
+ if deadline && delay >= deadline - OpenAI::Internal::Util.monotonic_secs
96
+ raise WORKLOAD_IDENTITY_DEADLINE_ERROR, "request timed out during workload identity authentication"
97
+ end
98
+
99
+ super
100
+ end
101
+
102
+ # @api private
103
+ private def send_request(request, redirect_count:, retry_count:, send_retry_header:)
104
+ if @workload_identity_auth && x509_transport?(@requester)
105
+ canonical_headers = @requester.validate_api_request!(
106
+ url: request.fetch(:url),
107
+ headers: request.fetch(:headers)
108
+ )
109
+ request = request.merge(headers: canonical_headers)
110
+ authorization = canonical_headers["authorization"]
111
+ expected = "Bearer #{WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}"
112
+ admin_authorization = @admin_api_key && "Bearer #{@admin_api_key}"
113
+ unless authorization.nil? || authorization == expected || authorization == admin_authorization
114
+ raise OpenAI::Errors::Error, "X.509 requests cannot override the selected authorization credential"
115
+ end
116
+ end
117
+
118
+ return super unless workload_identity_request?(request)
119
+
120
+ x509_request = x509_transport?(@requester)
121
+ previous_context = request[:x509_request_context]
122
+ deadline = if x509_request
123
+ previous_context&.fetch(:deadline) ||
124
+ request[:workload_identity_deadline] ||
125
+ request[:timeout]&.then { OpenAI::Internal::Util.monotonic_secs + _1 }
126
+ else
127
+ request[:workload_identity_deadline] ||
128
+ request[:timeout]&.then { OpenAI::Internal::Util.monotonic_secs + _1 }
129
+ end
130
+
131
+ request = request.merge(workload_identity_deadline: deadline)
132
+ if x509_request
133
+ replay_state = previous_context&.fetch(:replay_state) || []
134
+ issuer_retries = previous_context&.fetch(:issuer_retries, 0) || 0
135
+ auth_max_retries = previous_context&.fetch(:auth_max_retries) || request.fetch(:x509_auth_max_retries)
136
+ api_max_retries = previous_context&.fetch(:api_max_retries) || request.fetch(:max_retries)
137
+ log_context = previous_context&.fetch(:log_context) || yield
138
+ context = {
139
+ deadline: deadline,
140
+ replay_state: replay_state,
141
+ issuer_retries: issuer_retries,
142
+ auth_max_retries: auth_max_retries,
143
+ api_max_retries: api_max_retries,
144
+ log_context: log_context,
145
+ token: nil
146
+ }
147
+ request = request.merge(x509_request_context: context, max_retries: 0)
148
+ end
149
+
150
+ begin
151
+ super(
152
+ request,
153
+ redirect_count: redirect_count,
154
+ retry_count: retry_count,
155
+ send_retry_header: send_retry_header
156
+ )
157
+ rescue OpenAI::Errors::AuthenticationError
158
+ if x509_request
159
+ rejected_token = request.fetch(:x509_request_context).fetch(:token)
160
+ raise if rejected_token.nil?
161
+
162
+ @workload_identity_auth.invalidate_token(rejected_token)
163
+ end
164
+
165
+ replay_allowed = request_replayable?(request)
166
+ replay_allowed &&= x509_request ? replay_state.empty? : retry_count.zero?
167
+ raise unless replay_allowed
168
+
169
+ if x509_request
170
+ replay_state << true
171
+ replay_state.freeze
172
+ issuer_retries = context.fetch(:issuer_retries)
173
+ auth_max_retries = context.fetch(:auth_max_retries)
174
+ api_max_retries = context.fetch(:api_max_retries)
175
+ log_context = context.fetch(:log_context)
176
+ context = {
177
+ deadline: deadline,
178
+ replay_state: replay_state,
179
+ issuer_retries: issuer_retries,
180
+ auth_max_retries: auth_max_retries,
181
+ api_max_retries: api_max_retries,
182
+ log_context: log_context,
183
+ token: nil
184
+ }
185
+ request = request.merge(x509_request_context: context)
186
+ else
187
+ @workload_identity_auth.invalidate_token
188
+ end
189
+
190
+ begin
191
+ super(
192
+ request,
193
+ redirect_count: redirect_count,
194
+ retry_count: retry_count + 1,
195
+ send_retry_header: send_retry_header
196
+ )
197
+ rescue OpenAI::Errors::AuthenticationError
198
+ if x509_request && (rejected_token = request.fetch(:x509_request_context).fetch(:token))
199
+ @workload_identity_auth.invalidate_token(rejected_token)
200
+ end
201
+
202
+ raise
203
+ end
204
+ end
205
+
206
+ rescue Timeout::Error => error
207
+ unless x509_request
208
+ raise unless error.is_a?(WORKLOAD_IDENTITY_DEADLINE_ERROR)
209
+
210
+ url = request.fetch(:url).dup
211
+ url.query = nil
212
+ url.fragment = nil
213
+ cause = Timeout::Error.new(error.message)
214
+ raise OpenAI::Errors::APITimeoutError.new(url: url, message: error.message), cause: cause
215
+ end
216
+
217
+ url = request.fetch(:url).dup
218
+ url.query = nil
219
+ url.fragment = nil
220
+ message = "request timed out during workload identity authentication"
221
+ raise OpenAI::Errors::APITimeoutError.new(url: url, message: message), cause: nil
222
+ end
223
+
224
+ private def workload_identity_request?(request)
225
+ return false unless @workload_identity_auth
226
+
227
+ expected = "Bearer #{WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}"
228
+ request[:headers]["authorization"] == expected
229
+ end
230
+
231
+ private def prepare_workload_identity_request(request, retry_count:)
232
+ deadline = request[:workload_identity_deadline]
233
+ previous_issuer_retries = request[:x509_request_context]&.fetch(:issuer_retries, 0) || 0
234
+ attempts = 0
235
+ begin
236
+ token = @workload_identity_auth.get_token(deadline: deadline)
237
+ rescue OpenAI::Errors::APIError => error
238
+ status = error.status
239
+ connection_failure = error.is_a?(OpenAI::Errors::APIConnectionError)
240
+ retryable_connection_failure = connection_failure &&
241
+ false.equal?(error.request_may_have_been_sent?)
242
+ retryable_status = retryable_connection_failure ||
243
+ [408, 409, 429].include?(status) ||
244
+ (status.is_a?(Integer) && (500..599).cover?(status))
245
+ consumed_retries = attempts + previous_issuer_retries + retry_count
246
+ unless x509_transport?(@requester) &&
247
+ retryable_status &&
248
+ consumed_retries < request.fetch(:x509_request_context).fetch(:auth_max_retries) &&
249
+ (retryable_connection_failure || self.class.should_retry?(status, headers: error.headers || {}))
250
+ raise
251
+ end
252
+
253
+ delay = retry_delay(error.headers || {}, retry_count: attempts)
254
+ if deadline && delay >= deadline - OpenAI::Internal::Util.monotonic_secs
255
+ raise Timeout::Error, "request timed out during workload identity authentication"
256
+ end
257
+
258
+ context = request.fetch(:x509_request_context)
259
+ response = if connection_failure
260
+ nil
261
+ else
262
+ OpenAI::ResponseMetadata.new(status: status, headers: error.headers || {})
263
+ end
264
+
265
+ context.fetch(:log_context).retry_scheduled(
266
+ connection_failure ? error : status,
267
+ delay: delay,
268
+ response: response,
269
+ retry_count: consumed_retries,
270
+ max_retries: context.fetch(:auth_max_retries)
271
+ )
272
+ sleep(delay)
273
+ attempts += 1
274
+ retry
275
+ end
276
+
277
+ if (context = request[:x509_request_context])
278
+ context[:issuer_retries] = previous_issuer_retries + attempts
279
+ context[:token] = token.dup.freeze
280
+ context.freeze
281
+ end
282
+
283
+ updated_headers = request[:headers].merge("authorization" => "Bearer #{token}")
284
+ updated_request = request
285
+ .except(:workload_identity_deadline, :x509_request_context, :x509_auth_max_retries)
286
+ .merge(headers: updated_headers)
287
+ if context
288
+ updated_request = updated_request.merge(
289
+ max_retries: [context.fetch(:api_max_retries) - context.fetch(:issuer_retries), 0].max
290
+ )
291
+ end
292
+
293
+ request_with_remaining_timeout(
294
+ updated_request,
295
+ deadline
296
+ )
297
+ rescue Timeout::Error => e
298
+ raise if x509_transport?(@requester)
299
+
300
+ url = request.fetch(:url).dup
301
+ url.query = nil
302
+ url.fragment = nil
303
+ raise OpenAI::Errors::APITimeoutError.new(url: url, message: e.message)
304
+ end
305
+
306
+ # @api private
307
+ private def request_with_remaining_timeout(request, deadline)
308
+ return request if deadline.nil?
309
+
310
+ remaining = deadline - OpenAI::Internal::Util.monotonic_secs
311
+ unless remaining.positive?
312
+ raise Timeout::Error, "request timed out during workload identity authentication"
313
+ end
314
+
315
+ request.merge(timeout: remaining)
316
+ end
317
+
318
+ # Returns a new client of the same class with the supplied constructor options overridden.
319
+ # Other settings and the HTTP transport are inherited. The original client
320
+ # is unchanged. Header overrides are merged; nil removes an individual
321
+ # header, and default_headers: nil clears all custom default headers.
322
+ # Subclasses with incompatible constructors or a private .new must override
323
+ # this method if they need to support copying.
324
+ #
325
+ # @example Select a regional endpoint for a call
326
+ # client.with_options(base_url: "https://eu.api.openai.com/v1").responses.create(...)
327
+ # @param overrides [Hash{Symbol=>Object}] Options accepted by {#initialize}.
328
+ # @return [self]
329
+ def with_options(**overrides)
330
+ if overrides.key?(:workload_identity) && !overrides.key?(:http_client)
331
+ selected_identity = overrides.fetch(:workload_identity)
332
+ if x509_identity?(selected_identity) && (configured_transport = selected_identity.transport)
333
+ overrides = overrides.merge(http_client: configured_transport)
334
+ end
335
+ end
336
+
337
+ previous_transport = @copy_options.fetch(:http_client)
338
+ transport = overrides.fetch(:http_client, previous_transport)
339
+ if overrides[:data_residency] && x509_transport?(transport)
340
+ residency = overrides.fetch(:data_residency)
341
+ options = OpenAI::Internal::ClientOptions.copy(@copy_options, overrides.except(:data_residency))
342
+ options.delete(:base_url) unless overrides.key?(:base_url)
343
+ return copy_with_workload_identity_auth(options.merge(data_residency: residency))
344
+ end
345
+
346
+ options = OpenAI::Internal::ClientOptions.copy(@copy_options, overrides)
347
+ adopted_identity = x509_identity?(options.fetch(:workload_identity)) &&
348
+ !x509_identity?(@copy_options.fetch(:workload_identity))
349
+ previous_origin = previous_transport.api_origin if x509_transport?(previous_transport)
350
+ selected_origin = transport.api_origin if x509_transport?(transport)
351
+ if adopted_identity && selected_origin
352
+ inherited_origin = OpenAI::Internal::Util.uri_origin(URI(options.fetch(:base_url).to_s))
353
+ adopted_identity = !inherited_origin.casecmp?(selected_origin)
354
+ end
355
+
356
+ if (adopted_identity || previous_origin != selected_origin) &&
357
+ !overrides.key?(:base_url) &&
358
+ !overrides[:data_residency]
359
+ options.delete(:base_url)
360
+ end
361
+
362
+ copy_with_workload_identity_auth(options)
363
+ end
364
+
365
+ # @api private
366
+ private def copy_with_workload_identity_auth(options)
367
+ copied = self.class.new(**options)
368
+ identity = @copy_options.fetch(:workload_identity)
369
+ if x509_identity?(identity) &&
370
+ identity.equal?(options.fetch(:workload_identity)) &&
371
+ copied.requester.equal?(@requester)
372
+ copied.adopt_workload_identity_auth!(@workload_identity_auth)
373
+ end
374
+
375
+ copied
376
+ end
377
+
378
+ # @api private
379
+ def adopt_workload_identity_auth!(authenticator)
380
+ identity = @copy_options.fetch(:workload_identity)
381
+ unless x509_identity?(identity) &&
382
+ x509_transport?(@requester) &&
383
+ OpenAI::Auth::X509Transport.exact_instance?(authenticator, OpenAI::Auth::WorkloadIdentityAuth) &&
384
+ authenticator.bound_to?(identity, transport: @requester)
385
+ raise ArgumentError, "X.509 authenticator must match its workload identity and attested transport"
386
+ end
387
+
388
+ @workload_identity_auth = authenticator
389
+ end
390
+ end
391
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resources/files"
4
+ require_relative "resource_polling"
5
+
6
+ module OpenAI
7
+ module Resources
8
+ class Files
9
+ # Wait for an uploaded file to finish processing.
10
+ #
11
+ # The returned file may have an `error` status; callers should inspect the
12
+ # status before using it. Polling intervals and the overall timeout are in
13
+ # seconds. Finite timeouts include authentication and request replay time and
14
+ # disable transport retries so the deadline remains strict. Set `timeout` to
15
+ # `nil` to wait indefinitely and retain configured transport retries.
16
+ #
17
+ # @overload wait_for_processing(file_id, poll_interval: nil, timeout: 1800.0, request_options: {})
18
+ #
19
+ # @param file_id [String] The ID of the file to wait for.
20
+ #
21
+ # @param poll_interval [Integer, Float, nil] How often to retrieve the file. When omitted, the SDK honors the server's
22
+ # polling hint and otherwise waits 5 seconds.
23
+ #
24
+ # @param timeout [Integer, Float, nil] Maximum total time to poll. Defaults to 30 minutes. Set to `nil` to wait
25
+ # indefinitely.
26
+ #
27
+ # @param request_options [OpenAI::RequestOptions, Hash{Symbol=>Object}, nil]
28
+ #
29
+ # @raise [ArgumentError, OpenAI::Errors::PollingError]
30
+ # @return [OpenAI::Models::FileObject]
31
+ def wait_for_processing(
32
+ file_id,
33
+ poll_interval: nil,
34
+ timeout: OpenAI::Internal::Poller::DEFAULT_TIMEOUT,
35
+ request_options: {}
36
+ )
37
+ OpenAI::Helpers::ResourcePolling.wait_for_file(
38
+ self,
39
+ file_id,
40
+ poll_interval: poll_interval,
41
+ timeout: timeout,
42
+ request_options: request_options
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
@@ -63,6 +63,7 @@ module OpenAI
63
63
  # send session.start and observe session.started before further commands.
64
64
  class Connection < OpenAI::WebSocket::Connection
65
65
  include OpenAI::WebSocket::Protocol
66
+ include OpenAI::WebSocket::GuardedConnection
66
67
 
67
68
  # @api private
68
69
  def initialize(socket:, url:)
@@ -77,58 +78,6 @@ module OpenAI
77
78
  send_raw(encode_client_event(event))
78
79
  end
79
80
 
80
- def closed? = @poisoned || @closed || super
81
-
82
- def close(code: 1000, reason: "")
83
- @poisoned || @closed ? abort : super
84
- @closed = true
85
- nil
86
- rescue StandardError
87
- @poisoned = true
88
- raise OpenAI::Errors::LiveConnectionError.new(url: @url), cause: nil
89
- end
90
-
91
- # @api private
92
- def abort
93
- super
94
- @closed = true
95
- nil
96
- rescue StandardError
97
- @poisoned = true
98
- raise OpenAI::Errors::LiveConnectionError.new(url: @url), cause: nil
99
- end
100
-
101
- private def write_text(text)
102
- complete = false
103
- super
104
- complete = true
105
- nil
106
- rescue StandardError
107
- raise OpenAI::Errors::LiveConnectionError.new(url: @url), cause: nil
108
- ensure
109
- # Also retire a possibly buffered write when cancellation bypasses
110
- # StandardError, before another sender or normal cleanup can flush it.
111
- @poisoned = true unless complete
112
- end
113
-
114
- private def read_raw_message
115
- raise connection_error("Cannot read from a failed Live WebSocket.") if @poisoned
116
- return nil if @closed
117
- text = super
118
- @closed = text.nil?
119
- text
120
- rescue StandardError
121
- @poisoned = true
122
- raise OpenAI::Errors::LiveConnectionError.new(url: @url), cause: nil
123
- end
124
-
125
- private def socket_closed?
126
- super
127
- rescue StandardError
128
- @poisoned = true
129
- raise OpenAI::Errors::LiveConnectionError.new(url: @url), cause: nil
130
- end
131
-
132
81
  private def encode_client_event(event)
133
82
  validate_event_tree!(event)
134
83
  payload = OpenAI::Internal::Type::Converter.dump(client_event_type, event)
@@ -244,6 +193,9 @@ module OpenAI
244
193
  end
245
194
  end
246
195
 
196
+ private def transport_error = OpenAI::Errors::LiveConnectionError.new(url: @url)
197
+ private def failed_read_message = "Cannot read from a failed Live WebSocket."
198
+
247
199
  private def connection_error(message)
248
200
  OpenAI::Errors::LiveConnectionError.new(url: @url, message: message)
249
201
  end
@@ -305,8 +257,7 @@ module OpenAI
305
257
  websocket_base_url = websocket_base_url&.to_s&.dup&.freeze
306
258
  build = lambda do |deadline|
307
259
  build_shared_websocket_connection_request(
308
- path: path,
309
- query: query,
260
+ request: live.__websocket_request(query: query).merge(path: path),
310
261
  websocket_base_url: websocket_base_url,
311
262
  options: options,
312
263
  deadline: deadline,
@@ -344,7 +295,7 @@ module OpenAI
344
295
  # not in the URL. This opens only the transport, never starts a session.
345
296
  def connect(websocket_base_url: nil, request_options: nil, transport: nil, transport_options: {}, &block)
346
297
  open_live_websocket(
347
- path: "live/sessions",
298
+ path: __websocket_request.fetch(:path),
348
299
  connection_class: OpenAI::Live::Connection,
349
300
  websocket_base_url: websocket_base_url,
350
301
  request_options: request_options,
@@ -429,7 +380,7 @@ class OpenAI::Resources::Live::Sideband
429
380
  end
430
381
 
431
382
  open_live_websocket(
432
- path: ["live/sessions/%1$s/attach", session_id],
383
+ path: __websocket_request(session_id).fetch(:path),
433
384
  query: graceful_close.nil? ? {} : {"graceful_close" => graceful_close.to_s},
434
385
  connection_class: OpenAI::Live::SidebandConnection,
435
386
  websocket_base_url: websocket_base_url,
@@ -448,7 +399,7 @@ class OpenAI::Resources::Live::Forks
448
399
  # session: {} to inherit the recording, then wait for session.started.
449
400
  def connect(session_id, websocket_base_url: nil, request_options: nil, transport: nil, transport_options: {}, &block)
450
401
  open_live_websocket(
451
- path: ["live/sessions/%1$s/fork", session_id],
402
+ path: __websocket_request(session_id).fetch(:path),
452
403
  connection_class: OpenAI::Live::ForkConnection,
453
404
  websocket_base_url: websocket_base_url,
454
405
  request_options: request_options,