openai 0.9.0 → 0.11.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 +40 -0
- data/README.md +79 -1
- data/lib/openai/client.rb +11 -0
- data/lib/openai/errors.rb +25 -0
- data/lib/openai/internal/type/array_of.rb +6 -1
- data/lib/openai/internal/type/base_model.rb +76 -24
- data/lib/openai/internal/type/boolean.rb +7 -1
- data/lib/openai/internal/type/converter.rb +42 -34
- data/lib/openai/internal/type/enum.rb +10 -2
- data/lib/openai/internal/type/file_input.rb +6 -1
- data/lib/openai/internal/type/hash_of.rb +6 -1
- data/lib/openai/internal/type/union.rb +12 -7
- data/lib/openai/internal/type/unknown.rb +7 -1
- data/lib/openai/models/all_models.rb +4 -0
- data/lib/openai/models/audio/speech_create_params.rb +23 -2
- data/lib/openai/models/audio/transcription.rb +118 -1
- data/lib/openai/models/audio/transcription_text_done_event.rb +80 -1
- data/lib/openai/models/audio/transcription_verbose.rb +31 -1
- data/lib/openai/models/chat/chat_completion.rb +32 -31
- data/lib/openai/models/chat/chat_completion_chunk.rb +30 -29
- data/lib/openai/models/chat/completion_create_params.rb +34 -31
- data/lib/openai/models/fine_tuning/checkpoints/permission_retrieve_response.rb +60 -25
- data/lib/openai/models/images_response.rb +92 -1
- data/lib/openai/models/responses/response.rb +59 -35
- data/lib/openai/models/responses/response_code_interpreter_call_code_delta_event.rb +17 -8
- data/lib/openai/models/responses/response_code_interpreter_call_code_done_event.rb +14 -10
- data/lib/openai/models/responses/response_code_interpreter_call_completed_event.rb +11 -10
- data/lib/openai/models/responses/response_code_interpreter_call_in_progress_event.rb +11 -10
- data/lib/openai/models/responses/response_code_interpreter_call_interpreting_event.rb +11 -10
- data/lib/openai/models/responses/response_code_interpreter_tool_call.rb +49 -78
- data/lib/openai/models/responses/response_create_params.rb +92 -67
- data/lib/openai/models/responses/response_function_web_search.rb +115 -1
- data/lib/openai/models/responses/response_includable.rb +8 -6
- data/lib/openai/models/responses/response_output_text.rb +18 -2
- data/lib/openai/models/responses/response_stream_event.rb +2 -2
- data/lib/openai/models/responses/tool_choice_mcp.rb +40 -0
- data/lib/openai/models/responses/tool_choice_types.rb +0 -3
- data/lib/openai/models/responses_model.rb +4 -0
- data/lib/openai/models/webhooks/batch_cancelled_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/batch_completed_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/batch_expired_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/batch_failed_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/eval_run_canceled_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/eval_run_failed_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/eval_run_succeeded_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/fine_tuning_job_cancelled_webhook_event.rb +85 -0
- data/lib/openai/models/webhooks/fine_tuning_job_failed_webhook_event.rb +85 -0
- data/lib/openai/models/webhooks/fine_tuning_job_succeeded_webhook_event.rb +85 -0
- data/lib/openai/models/webhooks/response_cancelled_webhook_event.rb +85 -0
- data/lib/openai/models/webhooks/response_completed_webhook_event.rb +85 -0
- data/lib/openai/models/webhooks/response_failed_webhook_event.rb +84 -0
- data/lib/openai/models/webhooks/response_incomplete_webhook_event.rb +85 -0
- data/lib/openai/models/webhooks/unwrap_webhook_event.rb +59 -0
- data/lib/openai/models/webhooks/webhook_unwrap_params.rb +16 -0
- data/lib/openai/models.rb +2 -0
- data/lib/openai/resources/audio/speech.rb +3 -1
- data/lib/openai/resources/chat/completions.rb +10 -2
- data/lib/openai/resources/fine_tuning/checkpoints/permissions.rb +1 -2
- data/lib/openai/resources/responses.rb +24 -16
- data/lib/openai/resources/webhooks.rb +124 -0
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +18 -0
- data/rbi/openai/client.rbi +3 -0
- data/rbi/openai/errors.rbi +16 -0
- data/rbi/openai/internal/type/boolean.rbi +2 -0
- data/rbi/openai/internal/type/converter.rbi +15 -15
- data/rbi/openai/internal/type/union.rbi +5 -0
- data/rbi/openai/internal/type/unknown.rbi +2 -0
- data/rbi/openai/models/all_models.rbi +20 -0
- data/rbi/openai/models/audio/speech_create_params.rbi +59 -2
- data/rbi/openai/models/audio/transcription.rbi +213 -3
- data/rbi/openai/models/audio/transcription_text_done_event.rbi +146 -1
- data/rbi/openai/models/audio/transcription_verbose.rbi +47 -0
- data/rbi/openai/models/chat/chat_completion.rbi +47 -42
- data/rbi/openai/models/chat/chat_completion_chunk.rbi +47 -42
- data/rbi/openai/models/chat/completion_create_params.rbi +51 -42
- data/rbi/openai/models/fine_tuning/checkpoints/permission_retrieve_response.rbi +95 -26
- data/rbi/openai/models/images_response.rbi +146 -0
- data/rbi/openai/models/responses/response.rbi +75 -44
- data/rbi/openai/models/responses/response_code_interpreter_call_code_delta_event.rbi +17 -7
- data/rbi/openai/models/responses/response_code_interpreter_call_code_done_event.rbi +13 -5
- data/rbi/openai/models/responses/response_code_interpreter_call_completed_event.rbi +13 -21
- data/rbi/openai/models/responses/response_code_interpreter_call_in_progress_event.rbi +13 -21
- data/rbi/openai/models/responses/response_code_interpreter_call_interpreting_event.rbi +13 -21
- data/rbi/openai/models/responses/response_code_interpreter_tool_call.rbi +83 -125
- data/rbi/openai/models/responses/response_create_params.rbi +174 -115
- data/rbi/openai/models/responses/response_function_web_search.rbi +163 -0
- data/rbi/openai/models/responses/response_includable.rbi +17 -11
- data/rbi/openai/models/responses/response_output_text.rbi +26 -4
- data/rbi/openai/models/responses/tool_choice_mcp.rbi +53 -0
- data/rbi/openai/models/responses/tool_choice_types.rbi +0 -5
- data/rbi/openai/models/responses_model.rbi +20 -0
- data/rbi/openai/models/webhooks/batch_cancelled_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/batch_completed_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/batch_expired_webhook_event.rbi +150 -0
- data/rbi/openai/models/webhooks/batch_failed_webhook_event.rbi +149 -0
- data/rbi/openai/models/webhooks/eval_run_canceled_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/eval_run_failed_webhook_event.rbi +151 -0
- data/rbi/openai/models/webhooks/eval_run_succeeded_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/fine_tuning_job_cancelled_webhook_event.rbi +158 -0
- data/rbi/openai/models/webhooks/fine_tuning_job_failed_webhook_event.rbi +156 -0
- data/rbi/openai/models/webhooks/fine_tuning_job_succeeded_webhook_event.rbi +158 -0
- data/rbi/openai/models/webhooks/response_cancelled_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/response_completed_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/response_failed_webhook_event.rbi +154 -0
- data/rbi/openai/models/webhooks/response_incomplete_webhook_event.rbi +155 -0
- data/rbi/openai/models/webhooks/unwrap_webhook_event.rbi +40 -0
- data/rbi/openai/models/webhooks/webhook_unwrap_params.rbi +32 -0
- data/rbi/openai/models.rbi +2 -0
- data/rbi/openai/resources/audio/speech.rbi +6 -1
- data/rbi/openai/resources/chat/completions.rbi +34 -30
- data/rbi/openai/resources/fine_tuning/checkpoints/permissions.rbi +1 -3
- data/rbi/openai/resources/responses.rbi +108 -84
- data/rbi/openai/resources/webhooks.rbi +68 -0
- data/sig/openai/client.rbs +2 -0
- data/sig/openai/errors.rbs +9 -0
- data/sig/openai/internal/type/converter.rbs +7 -1
- data/sig/openai/models/all_models.rbs +8 -0
- data/sig/openai/models/audio/speech_create_params.rbs +21 -1
- data/sig/openai/models/audio/transcription.rbs +95 -3
- data/sig/openai/models/audio/transcription_text_done_event.rbs +72 -2
- data/sig/openai/models/audio/transcription_verbose.rbs +21 -0
- data/sig/openai/models/chat/chat_completion.rbs +2 -1
- data/sig/openai/models/chat/chat_completion_chunk.rbs +2 -1
- data/sig/openai/models/chat/completion_create_params.rbs +2 -1
- data/sig/openai/models/fine_tuning/checkpoints/permission_retrieve_response.rbs +53 -16
- data/sig/openai/models/images_response.rbs +83 -0
- data/sig/openai/models/responses/response.rbs +13 -1
- data/sig/openai/models/responses/response_code_interpreter_call_code_delta_event.rbs +5 -0
- data/sig/openai/models/responses/response_code_interpreter_call_code_done_event.rbs +5 -0
- data/sig/openai/models/responses/response_code_interpreter_call_completed_event.rbs +4 -4
- data/sig/openai/models/responses/response_code_interpreter_call_in_progress_event.rbs +4 -4
- data/sig/openai/models/responses/response_code_interpreter_call_interpreting_event.rbs +4 -4
- data/sig/openai/models/responses/response_code_interpreter_tool_call.rbs +31 -52
- data/sig/openai/models/responses/response_create_params.rbs +31 -11
- data/sig/openai/models/responses/response_function_web_search.rbs +54 -0
- data/sig/openai/models/responses/response_includable.rbs +7 -5
- data/sig/openai/models/responses/response_output_text.rbs +15 -1
- data/sig/openai/models/responses/tool_choice_mcp.rbs +23 -0
- data/sig/openai/models/responses/tool_choice_types.rbs +0 -2
- data/sig/openai/models/responses_model.rbs +8 -0
- data/sig/openai/models/webhooks/batch_cancelled_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/batch_completed_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/batch_expired_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/batch_failed_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/eval_run_canceled_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/eval_run_failed_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/eval_run_succeeded_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/fine_tuning_job_cancelled_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/fine_tuning_job_failed_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/fine_tuning_job_succeeded_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/response_cancelled_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/response_completed_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/response_failed_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/response_incomplete_webhook_event.rbs +66 -0
- data/sig/openai/models/webhooks/unwrap_webhook_event.rbs +27 -0
- data/sig/openai/models/webhooks/webhook_unwrap_params.rbs +17 -0
- data/sig/openai/models.rbs +2 -0
- data/sig/openai/resources/audio/speech.rbs +1 -0
- data/sig/openai/resources/fine_tuning/checkpoints/permissions.rbs +1 -1
- data/sig/openai/resources/responses.rbs +8 -4
- data/sig/openai/resources/webhooks.rbs +33 -0
- metadata +56 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 252a9ce9833b0a9f66be94b76081f34716e348ebca732e63c3e16c105ed42ea1
|
4
|
+
data.tar.gz: d4d8b36822ee74af77508ec8e48b472d72d619333e468055695158e242c49760
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f27c40e40df727c8da570a4f6b1e20f72a87baa710efd3957bb0fbbcc23eb1dd78d03b1bcc3c3cf5684d4db4b062c392c1c3d970093c8e0fac89ef85db053db3
|
7
|
+
data.tar.gz: 21fe44d1605c6196d7321dcb7efa7d951a2523ba71859eab1bf242ff3314afe0cce25201c21b141f9f3902b684cb577ba51c520d12602d1279eccb770769e3a4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,45 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.11.0 (2025-06-26)
|
4
|
+
|
5
|
+
Full Changelog: [v0.10.0...v0.11.0](https://github.com/openai/openai-ruby/compare/v0.10.0...v0.11.0)
|
6
|
+
|
7
|
+
### Features
|
8
|
+
|
9
|
+
* **api:** webhook and deep research support ([6228400](https://github.com/openai/openai-ruby/commit/6228400e19aadefc5f87e24b3c104fc0b44d3cee))
|
10
|
+
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
* **ci:** release-doctor — report correct token name ([c12c991](https://github.com/openai/openai-ruby/commit/c12c9911beaeb8b1c72d7c5cc5f14dcb9cd5452e))
|
15
|
+
|
16
|
+
|
17
|
+
### Chores
|
18
|
+
|
19
|
+
* **api:** remove unsupported property ([1073c3a](https://github.com/openai/openai-ruby/commit/1073c3a6059f2d1e1ef92937326699e0240503e5))
|
20
|
+
* **client:** throw specific errors ([0cf937e](https://github.com/openai/openai-ruby/commit/0cf937ea8abebc05e52a419e19e275a45b5da646))
|
21
|
+
* **docs:** update README to include links to docs on Webhooks ([2d8f23e](https://github.com/openai/openai-ruby/commit/2d8f23ecb245c88f3f082f93eb906af857d64c7d))
|
22
|
+
|
23
|
+
## 0.10.0 (2025-06-23)
|
24
|
+
|
25
|
+
Full Changelog: [v0.9.0...v0.10.0](https://github.com/openai/openai-ruby/compare/v0.9.0...v0.10.0)
|
26
|
+
|
27
|
+
### Features
|
28
|
+
|
29
|
+
* **api:** make model and inputs not required to create response ([2087fb5](https://github.com/openai/openai-ruby/commit/2087fb53d775f6481dd34737f6d554c5c35f65e7))
|
30
|
+
* **api:** update api shapes for usage and code interpreter ([733ebfb](https://github.com/openai/openai-ruby/commit/733ebfbafe14d9733149b174c99d41d471a42865))
|
31
|
+
|
32
|
+
|
33
|
+
### Bug Fixes
|
34
|
+
|
35
|
+
* **internal:** fix: should publish to ruby gems when a release is created ([aebd8eb](https://github.com/openai/openai-ruby/commit/aebd8eb2855d6a8f4fe685bdb5a458346d509e50))
|
36
|
+
* issue where we cannot mutate arrays on base model derivatives ([266d072](https://github.com/openai/openai-ruby/commit/266d072946c75f93abeff45eec9787ce4e7fea56))
|
37
|
+
|
38
|
+
|
39
|
+
### Chores
|
40
|
+
|
41
|
+
* allow more free formatted json response input ([#726](https://github.com/openai/openai-ruby/issues/726)) ([69fb0af](https://github.com/openai/openai-ruby/commit/69fb0afabf86ecc3d1ca469d9700c42447569f3b))
|
42
|
+
|
3
43
|
## 0.9.0 (2025-06-17)
|
4
44
|
|
5
45
|
Full Changelog: [v0.8.0...v0.9.0](https://github.com/openai/openai-ruby/compare/v0.8.0...v0.9.0)
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application
|
|
15
15
|
<!-- x-release-please-start-version -->
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
gem "openai", "~> 0.
|
18
|
+
gem "openai", "~> 0.11.0"
|
19
19
|
```
|
20
20
|
|
21
21
|
<!-- x-release-please-end -->
|
@@ -112,6 +112,84 @@ puts(edited.data.first)
|
|
112
112
|
|
113
113
|
Note that you can also pass a raw `IO` descriptor, but this disables retries, as the library can't be sure if the descriptor is a file or pipe (which cannot be rewound).
|
114
114
|
|
115
|
+
## Webhook Verification
|
116
|
+
|
117
|
+
Verifying webhook signatures is _optional but encouraged_.
|
118
|
+
|
119
|
+
For more information about webhooks, see [the API docs](https://platform.openai.com/docs/guides/webhooks).
|
120
|
+
|
121
|
+
### Parsing webhook payloads
|
122
|
+
|
123
|
+
For most use cases, you will likely want to verify the webhook and parse the payload at the same time. To achieve this, we provide the method `client.webhooks.unwrap`, which parses a webhook request and verifies that it was sent by OpenAI. This method will raise an error if the signature is invalid.
|
124
|
+
|
125
|
+
Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). The `unwrap` method will parse this JSON for you into an event object after verifying the webhook was sent from OpenAI.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
require 'sinatra'
|
129
|
+
require 'openai'
|
130
|
+
|
131
|
+
# Set up the client with webhook secret from environment variable
|
132
|
+
client = OpenAI::Client.new(webhook_secret: ENV['OPENAI_WEBHOOK_SECRET'])
|
133
|
+
|
134
|
+
post '/webhook' do
|
135
|
+
request_body = request.body.read
|
136
|
+
|
137
|
+
begin
|
138
|
+
event = client.webhooks.unwrap(request_body, request.env)
|
139
|
+
|
140
|
+
case event.type
|
141
|
+
when 'response.completed'
|
142
|
+
puts "Response completed: #{event.data}"
|
143
|
+
when 'response.failed'
|
144
|
+
puts "Response failed: #{event.data}"
|
145
|
+
else
|
146
|
+
puts "Unhandled event type: #{event.type}"
|
147
|
+
end
|
148
|
+
|
149
|
+
status 200
|
150
|
+
'ok'
|
151
|
+
rescue StandardError => e
|
152
|
+
puts "Invalid signature: #{e}"
|
153
|
+
status 400
|
154
|
+
'Invalid signature'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
### Verifying webhook payloads directly
|
160
|
+
|
161
|
+
In some cases, you may want to verify the webhook separately from parsing the payload. If you prefer to handle these steps separately, we provide the method `client.webhooks.verify_signature` to _only verify_ the signature of a webhook request. Like `unwrap`, this method will raise an error if the signature is invalid.
|
162
|
+
|
163
|
+
Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). You will then need to parse the body after verifying the signature.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
require 'sinatra'
|
167
|
+
require 'json'
|
168
|
+
require 'openai'
|
169
|
+
|
170
|
+
# Set up the client with webhook secret from environment variable
|
171
|
+
client = OpenAI::Client.new(webhook_secret: ENV['OPENAI_WEBHOOK_SECRET'])
|
172
|
+
|
173
|
+
post '/webhook' do
|
174
|
+
request_body = request.body.read
|
175
|
+
|
176
|
+
begin
|
177
|
+
client.webhooks.verify_signature(request_body, request.env)
|
178
|
+
|
179
|
+
# Parse the body after verification
|
180
|
+
event = JSON.parse(request_body)
|
181
|
+
puts "Verified event: #{event}"
|
182
|
+
|
183
|
+
status 200
|
184
|
+
'ok'
|
185
|
+
rescue StandardError => e
|
186
|
+
puts "Invalid signature: #{e}"
|
187
|
+
status 400
|
188
|
+
'Invalid signature'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
115
193
|
### [Structured outputs](https://platform.openai.com/docs/guides/structured-outputs) and function calling
|
116
194
|
|
117
195
|
This SDK ships with helpers in `OpenAI::BaseModel`, `OpenAI::ArrayOf`, `OpenAI::EnumOf`, and `OpenAI::UnionOf` to help you define the supported JSON schemas used in making structured outputs and function calling requests.
|
data/lib/openai/client.rb
CHANGED
@@ -24,6 +24,9 @@ module OpenAI
|
|
24
24
|
# @return [String, nil]
|
25
25
|
attr_reader :project
|
26
26
|
|
27
|
+
# @return [String, nil]
|
28
|
+
attr_reader :webhook_secret
|
29
|
+
|
27
30
|
# @return [OpenAI::Resources::Completions]
|
28
31
|
attr_reader :completions
|
29
32
|
|
@@ -57,6 +60,9 @@ module OpenAI
|
|
57
60
|
# @return [OpenAI::Resources::VectorStores]
|
58
61
|
attr_reader :vector_stores
|
59
62
|
|
63
|
+
# @return [OpenAI::Resources::Webhooks]
|
64
|
+
attr_reader :webhooks
|
65
|
+
|
60
66
|
# @return [OpenAI::Resources::Beta]
|
61
67
|
attr_reader :beta
|
62
68
|
|
@@ -92,6 +98,8 @@ module OpenAI
|
|
92
98
|
#
|
93
99
|
# @param project [String, nil] Defaults to `ENV["OPENAI_PROJECT_ID"]`
|
94
100
|
#
|
101
|
+
# @param webhook_secret [String, nil] Defaults to `ENV["OPENAI_WEBHOOK_SECRET"]`
|
102
|
+
#
|
95
103
|
# @param base_url [String, nil] Override the default base URL for the API, e.g.,
|
96
104
|
# `"https://api.example.com/v2/"`. Defaults to `ENV["OPENAI_BASE_URL"]`
|
97
105
|
#
|
@@ -106,6 +114,7 @@ module OpenAI
|
|
106
114
|
api_key: ENV["OPENAI_API_KEY"],
|
107
115
|
organization: ENV["OPENAI_ORG_ID"],
|
108
116
|
project: ENV["OPENAI_PROJECT_ID"],
|
117
|
+
webhook_secret: ENV["OPENAI_WEBHOOK_SECRET"],
|
109
118
|
base_url: ENV["OPENAI_BASE_URL"],
|
110
119
|
max_retries: self.class::DEFAULT_MAX_RETRIES,
|
111
120
|
timeout: self.class::DEFAULT_TIMEOUT_IN_SECONDS,
|
@@ -124,6 +133,7 @@ module OpenAI
|
|
124
133
|
}
|
125
134
|
|
126
135
|
@api_key = api_key.to_s
|
136
|
+
@webhook_secret = webhook_secret&.to_s
|
127
137
|
|
128
138
|
super(
|
129
139
|
base_url: base_url,
|
@@ -145,6 +155,7 @@ module OpenAI
|
|
145
155
|
@fine_tuning = OpenAI::Resources::FineTuning.new(client: self)
|
146
156
|
@graders = OpenAI::Resources::Graders.new(client: self)
|
147
157
|
@vector_stores = OpenAI::Resources::VectorStores.new(client: self)
|
158
|
+
@webhooks = OpenAI::Resources::Webhooks.new(client: self)
|
148
159
|
@beta = OpenAI::Resources::Beta.new(client: self)
|
149
160
|
@batches = OpenAI::Resources::Batches.new(client: self)
|
150
161
|
@uploads = OpenAI::Resources::Uploads.new(client: self)
|
data/lib/openai/errors.rb
CHANGED
@@ -8,7 +8,32 @@ module OpenAI
|
|
8
8
|
# @return [StandardError, nil]
|
9
9
|
end
|
10
10
|
|
11
|
+
class InvalidWebhookSignatureError < OpenAI::Errors::Error
|
12
|
+
end
|
13
|
+
|
11
14
|
class ConversionError < OpenAI::Errors::Error
|
15
|
+
# @return [StandardError, nil]
|
16
|
+
def cause = @cause.nil? ? super : @cause
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
# @param on [Class<StandardError>]
|
21
|
+
# @param method [Symbol]
|
22
|
+
# @param target [Object]
|
23
|
+
# @param value [Object]
|
24
|
+
# @param cause [StandardError, nil]
|
25
|
+
def initialize(on:, method:, target:, value:, cause: nil)
|
26
|
+
cls = on.name.split("::").last
|
27
|
+
|
28
|
+
message = [
|
29
|
+
"Failed to parse #{cls}.#{method} from #{value.class} to #{target.inspect}.",
|
30
|
+
"To get the unparsed API response, use #{cls}[#{method.inspect}].",
|
31
|
+
cause && "Cause: #{cause.message}"
|
32
|
+
].filter(&:itself).join(" ")
|
33
|
+
|
34
|
+
@cause = cause
|
35
|
+
super(message)
|
36
|
+
end
|
12
37
|
end
|
13
38
|
|
14
39
|
class APIError < OpenAI::Errors::Error
|
@@ -62,10 +62,14 @@ module OpenAI
|
|
62
62
|
#
|
63
63
|
# @param state [Hash{Symbol=>Object}] .
|
64
64
|
#
|
65
|
-
# @option state [Boolean
|
65
|
+
# @option state [Boolean] :translate_names
|
66
|
+
#
|
67
|
+
# @option state [Boolean] :strictness
|
66
68
|
#
|
67
69
|
# @option state [Hash{Symbol=>Object}] :exactness
|
68
70
|
#
|
71
|
+
# @option state [Class<StandardError>] :error
|
72
|
+
#
|
69
73
|
# @option state [Integer] :branched
|
70
74
|
#
|
71
75
|
# @return [Array<Object>, Object]
|
@@ -74,6 +78,7 @@ module OpenAI
|
|
74
78
|
|
75
79
|
unless value.is_a?(Array)
|
76
80
|
exactness[:no] += 1
|
81
|
+
state[:error] = TypeError.new("#{value.class} can't be coerced into #{Array}")
|
77
82
|
return value
|
78
83
|
end
|
79
84
|
|
@@ -60,7 +60,7 @@ module OpenAI
|
|
60
60
|
[OpenAI::Internal::Type::Converter.type_info(type_info), type_info]
|
61
61
|
end
|
62
62
|
|
63
|
-
setter = "#{name_sym}="
|
63
|
+
setter = :"#{name_sym}="
|
64
64
|
api_name = info.fetch(:api_name, name_sym)
|
65
65
|
nilable = info.fetch(:nil?, false)
|
66
66
|
const = required && !nilable ? info.fetch(:const, OpenAI::Internal::OMIT) : OpenAI::Internal::OMIT
|
@@ -77,30 +77,61 @@ module OpenAI
|
|
77
77
|
type_fn: type_fn
|
78
78
|
}
|
79
79
|
|
80
|
-
define_method(setter)
|
80
|
+
define_method(setter) do |value|
|
81
|
+
target = type_fn.call
|
82
|
+
state = OpenAI::Internal::Type::Converter.new_coerce_state(translate_names: false)
|
83
|
+
coerced = OpenAI::Internal::Type::Converter.coerce(target, value, state: state)
|
84
|
+
error = @coerced.store(name_sym, state.fetch(:error) || true)
|
85
|
+
stored =
|
86
|
+
case [target, error]
|
87
|
+
in [OpenAI::Internal::Type::Converter | Symbol, nil]
|
88
|
+
coerced
|
89
|
+
else
|
90
|
+
value
|
91
|
+
end
|
92
|
+
@data.store(name_sym, stored)
|
93
|
+
end
|
81
94
|
|
95
|
+
# rubocop:disable Style/CaseEquality
|
96
|
+
# rubocop:disable Metrics/BlockLength
|
82
97
|
define_method(name_sym) do
|
83
98
|
target = type_fn.call
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
OpenAI::
|
90
|
-
|
91
|
-
|
92
|
-
|
99
|
+
|
100
|
+
case @coerced[name_sym]
|
101
|
+
in true | false if OpenAI::Internal::Type::Converter === target
|
102
|
+
@data.fetch(name_sym)
|
103
|
+
in ::StandardError => e
|
104
|
+
raise OpenAI::Errors::ConversionError.new(
|
105
|
+
on: self.class,
|
106
|
+
method: __method__,
|
107
|
+
target: target,
|
108
|
+
value: @data.fetch(name_sym),
|
109
|
+
cause: e
|
93
110
|
)
|
111
|
+
else
|
112
|
+
Kernel.then do
|
113
|
+
value = @data.fetch(name_sym) { const == OpenAI::Internal::OMIT ? nil : const }
|
114
|
+
state = OpenAI::Internal::Type::Converter.new_coerce_state(translate_names: false)
|
115
|
+
if (nilable || !required) && value.nil?
|
116
|
+
nil
|
117
|
+
else
|
118
|
+
OpenAI::Internal::Type::Converter.coerce(
|
119
|
+
target, value, state: state
|
120
|
+
)
|
121
|
+
end
|
122
|
+
rescue StandardError => e
|
123
|
+
raise OpenAI::Errors::ConversionError.new(
|
124
|
+
on: self.class,
|
125
|
+
method: __method__,
|
126
|
+
target: target,
|
127
|
+
value: value,
|
128
|
+
cause: e
|
129
|
+
)
|
130
|
+
end
|
94
131
|
end
|
95
|
-
rescue StandardError => e
|
96
|
-
cls = self.class.name.split("::").last
|
97
|
-
message = [
|
98
|
-
"Failed to parse #{cls}.#{__method__} from #{value.class} to #{target.inspect}.",
|
99
|
-
"To get the unparsed API response, use #{cls}[#{__method__.inspect}].",
|
100
|
-
"Cause: #{e.message}"
|
101
|
-
].join(" ")
|
102
|
-
raise OpenAI::Errors::ConversionError.new(message)
|
103
132
|
end
|
133
|
+
# rubocop:enable Metrics/BlockLength
|
134
|
+
# rubocop:enable Style/CaseEquality
|
104
135
|
end
|
105
136
|
|
106
137
|
# @api private
|
@@ -200,10 +231,14 @@ module OpenAI
|
|
200
231
|
#
|
201
232
|
# @param state [Hash{Symbol=>Object}] .
|
202
233
|
#
|
203
|
-
# @option state [Boolean
|
234
|
+
# @option state [Boolean] :translate_names
|
235
|
+
#
|
236
|
+
# @option state [Boolean] :strictness
|
204
237
|
#
|
205
238
|
# @option state [Hash{Symbol=>Object}] :exactness
|
206
239
|
#
|
240
|
+
# @option state [Class<StandardError>] :error
|
241
|
+
#
|
207
242
|
# @option state [Integer] :branched
|
208
243
|
#
|
209
244
|
# @return [self, Object]
|
@@ -217,6 +252,7 @@ module OpenAI
|
|
217
252
|
|
218
253
|
unless (val = OpenAI::Internal::Util.coerce_hash(value)).is_a?(Hash)
|
219
254
|
exactness[:no] += 1
|
255
|
+
state[:error] = TypeError.new("#{value.class} can't be coerced into #{Hash}")
|
220
256
|
return value
|
221
257
|
end
|
222
258
|
exactness[:yes] += 1
|
@@ -224,13 +260,15 @@ module OpenAI
|
|
224
260
|
keys = val.keys.to_set
|
225
261
|
instance = new
|
226
262
|
data = instance.to_h
|
263
|
+
viability = instance.instance_variable_get(:@coerced)
|
227
264
|
|
228
265
|
# rubocop:disable Metrics/BlockLength
|
229
266
|
fields.each do |name, field|
|
230
267
|
mode, required, target = field.fetch_values(:mode, :required, :type)
|
231
268
|
api_name, nilable, const = field.fetch_values(:api_name, :nilable, :const)
|
269
|
+
src_name = state.fetch(:translate_names) ? api_name : name
|
232
270
|
|
233
|
-
unless val.key?(
|
271
|
+
unless val.key?(src_name)
|
234
272
|
if required && mode != :dump && const == OpenAI::Internal::OMIT
|
235
273
|
exactness[nilable ? :maybe : :no] += 1
|
236
274
|
else
|
@@ -239,9 +277,10 @@ module OpenAI
|
|
239
277
|
next
|
240
278
|
end
|
241
279
|
|
242
|
-
item = val.fetch(
|
243
|
-
keys.delete(
|
280
|
+
item = val.fetch(src_name)
|
281
|
+
keys.delete(src_name)
|
244
282
|
|
283
|
+
state[:error] = nil
|
245
284
|
converted =
|
246
285
|
if item.nil? && (nilable || !required)
|
247
286
|
exactness[nilable ? :yes : :maybe] += 1
|
@@ -255,6 +294,8 @@ module OpenAI
|
|
255
294
|
item
|
256
295
|
end
|
257
296
|
end
|
297
|
+
|
298
|
+
viability.store(name, state.fetch(:error) || true)
|
258
299
|
data.store(name, converted)
|
259
300
|
end
|
260
301
|
# rubocop:enable Metrics/BlockLength
|
@@ -430,7 +471,18 @@ module OpenAI
|
|
430
471
|
# Create a new instance of a model.
|
431
472
|
#
|
432
473
|
# @param data [Hash{Symbol=>Object}, self]
|
433
|
-
def initialize(data = {})
|
474
|
+
def initialize(data = {})
|
475
|
+
@data = {}
|
476
|
+
@coerced = {}
|
477
|
+
OpenAI::Internal::Util.coerce_hash!(data).each do
|
478
|
+
if self.class.known_fields.key?(_1)
|
479
|
+
public_send(:"#{_1}=", _2)
|
480
|
+
else
|
481
|
+
@data.store(_1, _2)
|
482
|
+
@coerced.store(_1, false)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
434
486
|
|
435
487
|
class << self
|
436
488
|
# @api private
|
@@ -31,14 +31,20 @@ module OpenAI
|
|
31
31
|
class << self
|
32
32
|
# @api private
|
33
33
|
#
|
34
|
+
# Coerce value to Boolean if possible, otherwise return the original value.
|
35
|
+
#
|
34
36
|
# @param value [Boolean, Object]
|
35
37
|
#
|
36
38
|
# @param state [Hash{Symbol=>Object}] .
|
37
39
|
#
|
38
|
-
# @option state [Boolean
|
40
|
+
# @option state [Boolean] :translate_names
|
41
|
+
#
|
42
|
+
# @option state [Boolean] :strictness
|
39
43
|
#
|
40
44
|
# @option state [Hash{Symbol=>Object}] :exactness
|
41
45
|
#
|
46
|
+
# @option state [Class<StandardError>] :error
|
47
|
+
#
|
42
48
|
# @option state [Integer] :branched
|
43
49
|
#
|
44
50
|
# @return [Boolean, Object]
|
@@ -15,10 +15,14 @@ module OpenAI
|
|
15
15
|
#
|
16
16
|
# @param state [Hash{Symbol=>Object}] .
|
17
17
|
#
|
18
|
-
# @option state [Boolean
|
18
|
+
# @option state [Boolean] :translate_names
|
19
|
+
#
|
20
|
+
# @option state [Boolean] :strictness
|
19
21
|
#
|
20
22
|
# @option state [Hash{Symbol=>Object}] :exactness
|
21
23
|
#
|
24
|
+
# @option state [Class<StandardError>] :error
|
25
|
+
#
|
22
26
|
# @option state [Integer] :branched
|
23
27
|
#
|
24
28
|
# @return [Object]
|
@@ -94,6 +98,21 @@ module OpenAI
|
|
94
98
|
end
|
95
99
|
end
|
96
100
|
|
101
|
+
# @api private
|
102
|
+
#
|
103
|
+
# @param translate_names [Boolean]
|
104
|
+
#
|
105
|
+
# @return [Hash{Symbol=>Object}]
|
106
|
+
def new_coerce_state(translate_names: true)
|
107
|
+
{
|
108
|
+
translate_names: translate_names,
|
109
|
+
strictness: true,
|
110
|
+
exactness: {yes: 0, no: 0, maybe: 0},
|
111
|
+
error: nil,
|
112
|
+
branched: 0
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
97
116
|
# @api private
|
98
117
|
#
|
99
118
|
# Based on `target`, transform `value` into `target`, to the extent possible:
|
@@ -110,14 +129,11 @@ module OpenAI
|
|
110
129
|
#
|
111
130
|
# @param value [Object]
|
112
131
|
#
|
113
|
-
# @param state [Hash{Symbol=>Object}] The `strictness` is one of `true`, `false
|
114
|
-
#
|
115
|
-
# targets:
|
132
|
+
# @param state [Hash{Symbol=>Object}] The `strictness` is one of `true`, `false`. This informs the coercion strategy
|
133
|
+
# when we have to decide between multiple possible conversion targets:
|
116
134
|
#
|
117
135
|
# - `true`: the conversion must be exact, with minimum coercion.
|
118
136
|
# - `false`: the conversion can be approximate, with some coercion.
|
119
|
-
# - `:strong`: the conversion must be exact, with no coercion, and raise an error
|
120
|
-
# if not possible.
|
121
137
|
#
|
122
138
|
# The `exactness` is `Hash` with keys being one of `yes`, `no`, or `maybe`. For
|
123
139
|
# any given conversion attempt, the exactness will be updated based on how closely
|
@@ -130,21 +146,20 @@ module OpenAI
|
|
130
146
|
#
|
131
147
|
# See implementation below for more details.
|
132
148
|
#
|
133
|
-
# @option state [Boolean
|
149
|
+
# @option state [Boolean] :translate_names
|
150
|
+
#
|
151
|
+
# @option state [Boolean] :strictness
|
134
152
|
#
|
135
153
|
# @option state [Hash{Symbol=>Object}] :exactness
|
136
154
|
#
|
155
|
+
# @option state [Class<StandardError>] :error
|
156
|
+
#
|
137
157
|
# @option state [Integer] :branched
|
138
158
|
#
|
139
159
|
# @return [Object]
|
140
|
-
def coerce(
|
141
|
-
target,
|
142
|
-
value,
|
143
|
-
state: {strictness: true, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0}
|
144
|
-
)
|
145
|
-
# rubocop:disable Lint/SuppressedException
|
160
|
+
def coerce(target, value, state: OpenAI::Internal::Type::Converter.new_coerce_state)
|
146
161
|
# rubocop:disable Metrics/BlockNesting
|
147
|
-
|
162
|
+
exactness = state.fetch(:exactness)
|
148
163
|
|
149
164
|
case target
|
150
165
|
in OpenAI::Internal::Type::Converter
|
@@ -160,29 +175,26 @@ module OpenAI
|
|
160
175
|
exactness[value.nil? ? :yes : :maybe] += 1
|
161
176
|
return nil
|
162
177
|
in -> { _1 <= Integer }
|
163
|
-
|
178
|
+
case value
|
179
|
+
in Integer
|
164
180
|
exactness[:yes] += 1
|
165
181
|
return value
|
166
|
-
elsif strictness == :strong && Integer(value, exception: false) != value
|
167
|
-
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
168
|
-
raise value.is_a?(Numeric) ? ArgumentError.new(message) : TypeError.new(message)
|
169
182
|
else
|
170
183
|
Kernel.then do
|
171
184
|
return Integer(value).tap { exactness[:maybe] += 1 }
|
172
|
-
rescue ArgumentError, TypeError
|
185
|
+
rescue ArgumentError, TypeError => e
|
186
|
+
state[:error] = e
|
173
187
|
end
|
174
188
|
end
|
175
189
|
in -> { _1 <= Float }
|
176
190
|
if value.is_a?(Numeric)
|
177
191
|
exactness[:yes] += 1
|
178
192
|
return Float(value)
|
179
|
-
elsif strictness == :strong
|
180
|
-
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
181
|
-
raise TypeError.new(message)
|
182
193
|
else
|
183
194
|
Kernel.then do
|
184
195
|
return Float(value).tap { exactness[:maybe] += 1 }
|
185
|
-
rescue ArgumentError, TypeError
|
196
|
+
rescue ArgumentError, TypeError => e
|
197
|
+
state[:error] = e
|
186
198
|
end
|
187
199
|
end
|
188
200
|
in -> { _1 <= String }
|
@@ -194,16 +206,13 @@ module OpenAI
|
|
194
206
|
exactness[:yes] += 1
|
195
207
|
return value.string
|
196
208
|
else
|
197
|
-
|
198
|
-
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
199
|
-
raise TypeError.new(message)
|
200
|
-
end
|
209
|
+
state[:error] = TypeError.new("#{value.class} can't be coerced into #{String}")
|
201
210
|
end
|
202
211
|
in -> { _1 <= Date || _1 <= Time }
|
203
212
|
Kernel.then do
|
204
213
|
return target.parse(value).tap { exactness[:yes] += 1 }
|
205
214
|
rescue ArgumentError, TypeError => e
|
206
|
-
|
215
|
+
state[:error] = e
|
207
216
|
end
|
208
217
|
in -> { _1 <= StringIO } if value.is_a?(String)
|
209
218
|
exactness[:yes] += 1
|
@@ -221,10 +230,8 @@ module OpenAI
|
|
221
230
|
return value
|
222
231
|
end
|
223
232
|
else
|
224
|
-
|
225
|
-
|
226
|
-
raise ArgumentError.new(message)
|
227
|
-
end
|
233
|
+
message = "cannot convert non-matching #{value.class} into #{target.inspect}"
|
234
|
+
state[:error] = ArgumentError.new(message)
|
228
235
|
end
|
229
236
|
else
|
230
237
|
end
|
@@ -232,7 +239,6 @@ module OpenAI
|
|
232
239
|
exactness[:no] += 1
|
233
240
|
value
|
234
241
|
# rubocop:enable Metrics/BlockNesting
|
235
|
-
# rubocop:enable Lint/SuppressedException
|
236
242
|
end
|
237
243
|
|
238
244
|
# @api private
|
@@ -277,8 +283,10 @@ module OpenAI
|
|
277
283
|
define_sorbet_constant!(:CoerceState) do
|
278
284
|
T.type_alias do
|
279
285
|
{
|
280
|
-
|
286
|
+
translate_names: T::Boolean,
|
287
|
+
strictness: T::Boolean,
|
281
288
|
exactness: {yes: Integer, no: Integer, maybe: Integer},
|
289
|
+
error: T::Class[StandardError],
|
282
290
|
branched: Integer
|
283
291
|
}
|
284
292
|
end
|
@@ -81,10 +81,14 @@ module OpenAI
|
|
81
81
|
#
|
82
82
|
# @param state [Hash{Symbol=>Object}] .
|
83
83
|
#
|
84
|
-
# @option state [Boolean
|
84
|
+
# @option state [Boolean] :translate_names
|
85
|
+
#
|
86
|
+
# @option state [Boolean] :strictness
|
85
87
|
#
|
86
88
|
# @option state [Hash{Symbol=>Object}] :exactness
|
87
89
|
#
|
90
|
+
# @option state [Class<StandardError>] :error
|
91
|
+
#
|
88
92
|
# @option state [Integer] :branched
|
89
93
|
#
|
90
94
|
# @return [Symbol, Object]
|
@@ -95,8 +99,12 @@ module OpenAI
|
|
95
99
|
if values.include?(val)
|
96
100
|
exactness[:yes] += 1
|
97
101
|
val
|
102
|
+
elsif values.first&.class == val.class
|
103
|
+
exactness[:maybe] += 1
|
104
|
+
value
|
98
105
|
else
|
99
|
-
exactness[
|
106
|
+
exactness[:no] += 1
|
107
|
+
state[:error] = TypeError.new("#{value.class} can't be coerced into #{self}")
|
100
108
|
value
|
101
109
|
end
|
102
110
|
end
|
@@ -45,10 +45,14 @@ module OpenAI
|
|
45
45
|
#
|
46
46
|
# @param state [Hash{Symbol=>Object}] .
|
47
47
|
#
|
48
|
-
# @option state [Boolean
|
48
|
+
# @option state [Boolean] :translate_names
|
49
|
+
#
|
50
|
+
# @option state [Boolean] :strictness
|
49
51
|
#
|
50
52
|
# @option state [Hash{Symbol=>Object}] :exactness
|
51
53
|
#
|
54
|
+
# @option state [Class<StandardError>] :error
|
55
|
+
#
|
52
56
|
# @option state [Integer] :branched
|
53
57
|
#
|
54
58
|
# @return [StringIO, Object]
|
@@ -62,6 +66,7 @@ module OpenAI
|
|
62
66
|
exactness[:yes] += 1
|
63
67
|
value
|
64
68
|
else
|
69
|
+
state[:error] = TypeError.new("#{value.class} can't be coerced into #{StringIO}")
|
65
70
|
exactness[:no] += 1
|
66
71
|
value
|
67
72
|
end
|