openai 0.70.0 → 0.71.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +51 -1
  4. data/lib/openai/client.rb +62 -8
  5. data/lib/openai/internal/provider.rb +31 -0
  6. data/lib/openai/internal/transport/base_client.rb +65 -21
  7. data/lib/openai/models/admin/organization/audit_log_list_params.rb +84 -0
  8. data/lib/openai/models/admin/organization/audit_log_list_response.rb +84 -0
  9. data/lib/openai/models/admin/organization/projects/project_service_account.rb +4 -3
  10. data/lib/openai/models/admin/organization/projects/service_account_create_params.rb +9 -1
  11. data/lib/openai/models/admin/organization/projects/service_account_create_response.rb +26 -6
  12. data/lib/openai/models/admin/organization/projects/service_accounts/api_key_create_params.rb +54 -0
  13. data/lib/openai/models/admin/organization/projects/service_accounts/api_key_create_response.rb +57 -0
  14. data/lib/openai/models/admin/organization/usage_audio_speeches_response.rb +88 -13
  15. data/lib/openai/models/admin/organization/usage_audio_transcriptions_response.rb +88 -13
  16. data/lib/openai/models/admin/organization/usage_code_interpreter_sessions_response.rb +88 -13
  17. data/lib/openai/models/admin/organization/usage_completions_response.rb +88 -13
  18. data/lib/openai/models/admin/organization/usage_costs_response.rb +88 -13
  19. data/lib/openai/models/admin/organization/usage_embeddings_response.rb +88 -13
  20. data/lib/openai/models/admin/organization/usage_file_search_calls_response.rb +88 -13
  21. data/lib/openai/models/admin/organization/usage_images_response.rb +88 -13
  22. data/lib/openai/models/admin/organization/usage_moderations_response.rb +88 -13
  23. data/lib/openai/models/admin/organization/usage_vector_stores_response.rb +88 -13
  24. data/lib/openai/models/admin/organization/usage_web_search_calls_response.rb +88 -13
  25. data/lib/openai/models/beta/beta_response_input_item.rb +16 -32
  26. data/lib/openai/provider.rb +18 -0
  27. data/lib/openai/providers/bedrock.rb +489 -0
  28. data/lib/openai/resources/admin/organization/projects/service_accounts/api_keys.rb +60 -0
  29. data/lib/openai/resources/admin/organization/projects/service_accounts.rb +10 -3
  30. data/lib/openai/version.rb +1 -1
  31. data/lib/openai.rb +6 -0
  32. data/rbi/openai/client.rbi +17 -0
  33. data/rbi/openai/internal/provider.rbi +51 -0
  34. data/rbi/openai/internal/transport/base_client.rbi +32 -0
  35. data/rbi/openai/models/admin/organization/audit_log_list_params.rbi +420 -0
  36. data/rbi/openai/models/admin/organization/audit_log_list_response.rbi +420 -0
  37. data/rbi/openai/models/admin/organization/projects/project_service_account.rbi +8 -3
  38. data/rbi/openai/models/admin/organization/projects/service_account_create_params.rbi +8 -0
  39. data/rbi/openai/models/admin/organization/projects/service_account_create_response.rbi +52 -8
  40. data/rbi/openai/models/admin/organization/projects/service_accounts/api_key_create_params.rbi +84 -0
  41. data/rbi/openai/models/admin/organization/projects/service_accounts/api_key_create_response.rbi +80 -0
  42. data/rbi/openai/models/admin/organization/usage_audio_speeches_response.rbi +121 -16
  43. data/rbi/openai/models/admin/organization/usage_audio_transcriptions_response.rbi +121 -16
  44. data/rbi/openai/models/admin/organization/usage_code_interpreter_sessions_response.rbi +121 -16
  45. data/rbi/openai/models/admin/organization/usage_completions_response.rbi +121 -16
  46. data/rbi/openai/models/admin/organization/usage_costs_response.rbi +121 -16
  47. data/rbi/openai/models/admin/organization/usage_embeddings_response.rbi +121 -16
  48. data/rbi/openai/models/admin/organization/usage_file_search_calls_response.rbi +121 -16
  49. data/rbi/openai/models/admin/organization/usage_images_response.rbi +121 -16
  50. data/rbi/openai/models/admin/organization/usage_moderations_response.rbi +121 -16
  51. data/rbi/openai/models/admin/organization/usage_vector_stores_response.rbi +121 -16
  52. data/rbi/openai/models/admin/organization/usage_web_search_calls_response.rbi +121 -16
  53. data/rbi/openai/models/beta/beta_response_input_item.rbi +39 -46
  54. data/rbi/openai/provider.rbi +9 -0
  55. data/rbi/openai/providers.rbi +33 -0
  56. data/rbi/openai/resources/admin/organization/projects/service_accounts/api_keys.rbi +45 -0
  57. data/rbi/openai/resources/admin/organization/projects/service_accounts.rbi +12 -2
  58. data/sig/openai/client.rbs +7 -0
  59. data/sig/openai/internal/provider.rbs +29 -0
  60. data/sig/openai/internal/transport/base_client.rbs +17 -1
  61. data/sig/openai/models/admin/organization/audit_log_list_params.rbs +168 -0
  62. data/sig/openai/models/admin/organization/audit_log_list_response.rbs +168 -0
  63. data/sig/openai/models/admin/organization/projects/project_service_account.rbs +2 -1
  64. data/sig/openai/models/admin/organization/projects/service_account_create_params.rbs +9 -1
  65. data/sig/openai/models/admin/organization/projects/service_account_create_response.rbs +16 -5
  66. data/sig/openai/models/admin/organization/projects/service_accounts/api_key_create_params.rbs +53 -0
  67. data/sig/openai/models/admin/organization/projects/service_accounts/api_key_create_response.rbs +48 -0
  68. data/sig/openai/models/admin/organization/usage_audio_speeches_response.rbs +63 -0
  69. data/sig/openai/models/admin/organization/usage_audio_transcriptions_response.rbs +63 -0
  70. data/sig/openai/models/admin/organization/usage_code_interpreter_sessions_response.rbs +63 -0
  71. data/sig/openai/models/admin/organization/usage_completions_response.rbs +63 -0
  72. data/sig/openai/models/admin/organization/usage_costs_response.rbs +63 -0
  73. data/sig/openai/models/admin/organization/usage_embeddings_response.rbs +63 -0
  74. data/sig/openai/models/admin/organization/usage_file_search_calls_response.rbs +63 -0
  75. data/sig/openai/models/admin/organization/usage_images_response.rbs +63 -0
  76. data/sig/openai/models/admin/organization/usage_moderations_response.rbs +63 -0
  77. data/sig/openai/models/admin/organization/usage_vector_stores_response.rbs +63 -0
  78. data/sig/openai/models/admin/organization/usage_web_search_calls_response.rbs +63 -0
  79. data/sig/openai/models/beta/beta_response_input_item.rbs +18 -24
  80. data/sig/openai/provider.rbs +5 -0
  81. data/sig/openai/providers.rbs +15 -0
  82. data/sig/openai/resources/admin/organization/projects/service_accounts/api_keys.rbs +23 -0
  83. data/sig/openai/resources/admin/organization/projects/service_accounts.rbs +3 -0
  84. metadata +20 -2
@@ -0,0 +1,489 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenAI
4
+ module Providers
5
+ # @api private
6
+ module Bedrock
7
+ SERVICE = "bedrock-mantle"
8
+ CANONICAL_HOST = /\Abedrock-mantle\.([a-z0-9-]+)\.api\.aws\z/i
9
+ SIGNING_HEADERS = %w[authorization x-amz-content-sha256 x-amz-date x-amz-security-token].freeze
10
+ BEARER_AUTH_MARKER = :openai_bedrock_bearer
11
+ SIGV4_AUTH_MARKER = :openai_bedrock_sigv4
12
+ MISSING_REGION_MESSAGE =
13
+ "Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` " \
14
+ "or `AWS_DEFAULT_REGION`."
15
+ MISSING_CREDENTIALS_MESSAGE =
16
+ "Could not find credentials for Bedrock. Pass a bearer credential or AWS credentials " \
17
+ "to `bedrock(...)`, set `AWS_BEARER_TOKEN_BEDROCK`, or configure the default AWS credential chain."
18
+ CREDENTIAL_RESOLUTION_MESSAGE =
19
+ "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment " \
20
+ "variables, or runtime identity configuration and try again."
21
+ NON_REPLAYABLE_BODY_MESSAGE =
22
+ "Bedrock SigV4 authentication requires a replayable request body. Buffer the body " \
23
+ "before sending or use bearer authentication."
24
+ MISSING_DEPENDENCY_MESSAGE =
25
+ "Bedrock AWS authentication requires optional AWS dependencies. Add `gem \"aws-sdk-core\"` " \
26
+ "to your Gemfile, run `bundle install`, and try again."
27
+ PARTIAL_STATIC_CREDENTIALS_MESSAGE =
28
+ "Static AWS credentials require both `access_key_id` and `secret_access_key`. " \
29
+ "A `session_token` may only be used with both."
30
+ AMBIGUOUS_AWS_AUTH_MESSAGE =
31
+ "Bedrock authentication is ambiguous. Configure exactly one explicit AWS mode: " \
32
+ "static credentials, profile, or credential provider."
33
+ AMBIGUOUS_AUTH_MESSAGE =
34
+ "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer " \
35
+ "credential, static AWS credentials, profile, or credential provider."
36
+
37
+ class Definition
38
+ attr_reader :name
39
+
40
+ def initialize(
41
+ region:,
42
+ base_url:,
43
+ bearer_provider:,
44
+ profile:,
45
+ access_key_id:,
46
+ secret_access_key:,
47
+ session_token:,
48
+ credentials_provider:,
49
+ default_chain:
50
+ )
51
+ @name = "bedrock"
52
+ @region = region&.freeze
53
+ @base_url = base_url&.freeze
54
+ @bearer_provider = bearer_provider
55
+ @profile = profile&.freeze
56
+ @access_key_id = access_key_id&.freeze
57
+ @secret_access_key = secret_access_key&.freeze
58
+ @session_token = session_token&.freeze
59
+ @credentials_provider = credentials_provider
60
+ @default_chain = default_chain
61
+ end
62
+
63
+ def configure
64
+ if @bearer_provider
65
+ base_url = Bedrock.resolve_base_url(@base_url, @region)
66
+ auth = BearerAuth.new(token_provider: @bearer_provider, base_url: base_url)
67
+ else
68
+ Bedrock.load_aws!
69
+ region = @region || Bedrock.profile_region(@profile)
70
+ raise ArgumentError, MISSING_REGION_MESSAGE if region.nil?
71
+
72
+ base_url = Bedrock.resolve_base_url(@base_url, region)
73
+ credentials =
74
+ if @access_key_id
75
+ Aws::Credentials.new(@access_key_id, @secret_access_key, @session_token)
76
+ elsif @credentials_provider
77
+ CustomCredentialsProvider.new(@credentials_provider)
78
+ elsif @profile
79
+ ProfileCredentialsProvider.new(@profile, region: region)
80
+ else
81
+ DefaultCredentialsProvider.new
82
+ end
83
+ auth = SigV4Auth.new(
84
+ region: region,
85
+ base_url: base_url,
86
+ credentials_provider: credentials,
87
+ default_chain: @default_chain
88
+ )
89
+ end
90
+
91
+ OpenAI::Internal::Provider::Runtime.new(
92
+ name: name,
93
+ base_url: base_url,
94
+ prepare_request: auth.method(:prepare_request)
95
+ )
96
+ end
97
+ end
98
+
99
+ class BearerAuth
100
+ def initialize(token_provider:, base_url:)
101
+ @token_provider = token_provider
102
+ @base_url = URI(base_url)
103
+ end
104
+
105
+ def prepare_request(request)
106
+ Bedrock.validate_origin!(request.fetch(:url), @base_url, action: "authenticate")
107
+ headers = Bedrock.provider_headers(request, marker: BEARER_AUTH_MARKER)
108
+ token = resolve_token
109
+
110
+ request.merge(
111
+ headers: headers.merge("authorization" => "Bearer #{token}"),
112
+ provider_auth: BEARER_AUTH_MARKER
113
+ )
114
+ end
115
+
116
+ private def resolve_token
117
+ token = @token_provider.call
118
+ unless token.is_a?(String) && !token.strip.empty?
119
+ raise OpenAI::Errors::Error,
120
+ "The Bedrock bearer credential provider must return a non-empty string."
121
+ end
122
+ token
123
+ rescue OpenAI::Errors::Error
124
+ raise
125
+ rescue StandardError => e
126
+ raise OpenAI::Errors::Error.new("Failed to resolve a bearer credential for Bedrock."), cause: e
127
+ end
128
+ end
129
+
130
+ class SigV4Auth
131
+ def initialize(region:, base_url:, credentials_provider:, default_chain:)
132
+ @region = region
133
+ @base_url = URI(base_url)
134
+ @default_chain = default_chain
135
+ @signer = Aws::Sigv4::Signer.new(
136
+ service: SERVICE,
137
+ region: region,
138
+ credentials_provider: credentials_provider
139
+ )
140
+ end
141
+
142
+ def prepare_request(request)
143
+ url = request.fetch(:url)
144
+ Bedrock.validate_origin!(url, @base_url, action: "sign")
145
+ Bedrock.validate_endpoint_region!(url, @region)
146
+ body = request[:body]
147
+ unless body.nil? || body.is_a?(String)
148
+ raise OpenAI::Errors::Error, NON_REPLAYABLE_BODY_MESSAGE
149
+ end
150
+
151
+ headers = Bedrock.provider_headers(request, marker: SIGV4_AUTH_MARKER)
152
+ SIGNING_HEADERS.each { headers.delete(_1) }
153
+ signature = sign_request(
154
+ http_method: request.fetch(:method).to_s.upcase,
155
+ url: url.to_s,
156
+ headers: headers,
157
+ body: body
158
+ )
159
+
160
+ request.merge(
161
+ headers: OpenAI::Internal::Util.normalized_headers(headers, signature.headers),
162
+ follow_redirects: false,
163
+ provider_auth: SIGV4_AUTH_MARKER
164
+ )
165
+ end
166
+
167
+ private def sign_request(**options)
168
+ @signer.sign_request(**options)
169
+ rescue OpenAI::Errors::Error
170
+ raise
171
+ rescue StandardError => e
172
+ message =
173
+ if @default_chain
174
+ MISSING_CREDENTIALS_MESSAGE
175
+ else
176
+ CREDENTIAL_RESOLUTION_MESSAGE
177
+ end
178
+ raise OpenAI::Errors::Error.new(message), cause: e
179
+ end
180
+ end
181
+
182
+ class CustomCredentialsProvider
183
+ def initialize(provider) = @provider = provider
184
+
185
+ def credentials
186
+ value = @provider.respond_to?(:call) ? @provider.call : @provider.credentials
187
+ value = value.credentials if !value.respond_to?(:access_key_id) && value.respond_to?(:credentials)
188
+ Bedrock.validate_credentials!(value)
189
+ rescue OpenAI::Errors::Error
190
+ raise
191
+ rescue StandardError => e
192
+ raise OpenAI::Errors::Error.new(CREDENTIAL_RESOLUTION_MESSAGE), cause: e
193
+ end
194
+ end
195
+
196
+ class DefaultCredentialsProvider
197
+ def initialize
198
+ @mutex = Mutex.new
199
+ @provider = nil
200
+ end
201
+
202
+ def credentials
203
+ provider = @mutex.synchronize { @provider ||= Aws::CredentialProviderChain.new.resolve }
204
+ raise OpenAI::Errors::Error, MISSING_CREDENTIALS_MESSAGE unless provider
205
+ Bedrock.validate_credentials!(provider.credentials)
206
+ end
207
+ end
208
+
209
+ class ProfileCredentialsProvider
210
+ def initialize(profile, region:)
211
+ @profile = profile
212
+ @region = region
213
+ @mutex = Mutex.new
214
+ @provider = nil
215
+ end
216
+
217
+ def credentials
218
+ provider = @mutex.synchronize { @provider ||= resolve }
219
+ Bedrock.validate_credentials!(provider.credentials)
220
+ rescue OpenAI::Errors::Error
221
+ raise
222
+ rescue StandardError => e
223
+ raise OpenAI::Errors::Error.new(CREDENTIAL_RESOLUTION_MESSAGE), cause: e
224
+ end
225
+
226
+ private def resolve
227
+ config = Aws.shared_config
228
+ providers = []
229
+ if config.config_enabled?
230
+ providers << config.assume_role_web_identity_credentials_from_config(
231
+ profile: @profile,
232
+ region: @region
233
+ )
234
+ providers << config.sso_credentials_from_config(profile: @profile)
235
+ providers << config.assume_role_credentials_from_config(profile: @profile, region: @region)
236
+ end
237
+
238
+ begin
239
+ providers << Aws::SharedCredentials.new(profile_name: @profile)
240
+ rescue Aws::Errors::NoSuchProfileError
241
+ nil
242
+ end
243
+
244
+ if config.config_enabled?
245
+ if config.respond_to?(:login_credentials_from_config)
246
+ providers << config.login_credentials_from_config(profile: @profile, region: @region)
247
+ end
248
+ process = config.credential_process(profile: @profile)
249
+ providers << Aws::ProcessCredentials.new([process]) if process
250
+ end
251
+
252
+ providers.compact.find(&:set?) || raise(OpenAI::Errors::Error, CREDENTIAL_RESOLUTION_MESSAGE)
253
+ end
254
+ end
255
+
256
+ class << self
257
+ def load_aws!
258
+ require("aws-sdk-core")
259
+ require("aws-sigv4")
260
+ rescue LoadError => e
261
+ raise OpenAI::Errors::Error.new(MISSING_DEPENDENCY_MESSAGE), cause: e
262
+ end
263
+
264
+ def resolve_base_url(base_url, region)
265
+ return base_url if base_url
266
+ raise ArgumentError, MISSING_REGION_MESSAGE if region.nil?
267
+ "https://bedrock-mantle.#{region}.api.aws/v1"
268
+ end
269
+
270
+ def normalize_base_url(base_url)
271
+ uri = URI(base_url)
272
+ unless uri.is_a?(URI::HTTP) && uri.host
273
+ raise ArgumentError, "The Bedrock `base_url` must be an absolute HTTP or HTTPS URL."
274
+ end
275
+ uri.path = uri.path.sub(%r{/responses(?:/.*)?\z}, "")
276
+ uri.path = "" if uri.path == "/"
277
+ uri.to_s.sub(%r{/\z}, "")
278
+ rescue URI::InvalidURIError => e
279
+ message = "The Bedrock `base_url` must be an absolute HTTP or HTTPS URL."
280
+ raise ArgumentError.new(message), cause: e
281
+ end
282
+
283
+ def profile_region(profile)
284
+ configured_profile = profile || ENV["AWS_PROFILE"] || ENV["AWS_DEFAULT_PROFILE"] || "default"
285
+ region = Aws.shared_config.region(profile: configured_profile)
286
+ normalize_optional_string(region)
287
+ rescue StandardError => e
288
+ raise OpenAI::Errors::Error.new(CREDENTIAL_RESOLUTION_MESSAGE), cause: e
289
+ end
290
+
291
+ def provider_headers(request, marker:)
292
+ headers = request.fetch(:headers).dup
293
+ if request[:provider_auth] == marker
294
+ headers.delete("authorization")
295
+ elsif headers.key?("authorization")
296
+ raise OpenAI::Errors::Error,
297
+ "Bedrock provider authentication cannot be combined with a custom `Authorization` header."
298
+ end
299
+ headers
300
+ end
301
+
302
+ def validate_origin!(url, base_url, action:)
303
+ return if OpenAI::Internal::Util.uri_origin(url) == OpenAI::Internal::Util.uri_origin(base_url)
304
+ message =
305
+ "Refusing to #{action} a Bedrock request for an origin other than the configured " \
306
+ "provider URL."
307
+ raise OpenAI::Errors::Error,
308
+ message
309
+ end
310
+
311
+ def validate_endpoint_region!(url, region)
312
+ endpoint_region = CANONICAL_HOST.match(url.host)&.[](1)
313
+ return if endpoint_region.nil? || endpoint_region == region
314
+ message =
315
+ "The Bedrock endpoint region `#{endpoint_region}` does not match the SigV4 " \
316
+ "region `#{region}`."
317
+ raise OpenAI::Errors::Error,
318
+ message
319
+ end
320
+
321
+ def validate_credentials!(credentials)
322
+ access_key_id = credentials&.access_key_id
323
+ secret_access_key = credentials&.secret_access_key
324
+ session_token = credentials&.session_token if credentials.respond_to?(:session_token)
325
+ valid =
326
+ access_key_id.is_a?(String) && !access_key_id.strip.empty? &&
327
+ secret_access_key.is_a?(String) && !secret_access_key.strip.empty? &&
328
+ (session_token.nil? || (session_token.is_a?(String) && !session_token.strip.empty?))
329
+ return credentials if valid
330
+ raise OpenAI::Errors::Error, CREDENTIAL_RESOLUTION_MESSAGE
331
+ end
332
+
333
+ def normalize_optional_string(value)
334
+ return nil if value.nil?
335
+ return nil unless value.is_a?(String)
336
+ normalized = value.strip
337
+ normalized unless normalized.empty?
338
+ end
339
+ end
340
+ end
341
+
342
+ class << self
343
+ # Configure the standard OpenAI client for Amazon Bedrock Mantle.
344
+ #
345
+ # Authentication precedence is an explicit bearer or AWS mode, then
346
+ # AWS_BEARER_TOKEN_BEDROCK, then the standard AWS credential chain.
347
+ #
348
+ # @param region [String, nil] AWS signing region. Defaults to `AWS_REGION`,
349
+ # `AWS_DEFAULT_REGION`, or the selected profile's configured region.
350
+ #
351
+ # @param base_url [String, nil] Bedrock API root. Defaults to
352
+ # `AWS_BEDROCK_BASE_URL` or the regional Bedrock Mantle endpoint.
353
+ #
354
+ # @param api_key [String, nil] Explicit Bedrock bearer credential. Passing
355
+ # `nil` explicitly skips the `AWS_BEARER_TOKEN_BEDROCK` fallback.
356
+ #
357
+ # @param token_provider [#call, nil] Callable returning a fresh bearer
358
+ # credential before each request attempt.
359
+ #
360
+ # @param access_key_id [String, nil] Explicit AWS access key ID. Must be
361
+ # paired with `secret_access_key`.
362
+ #
363
+ # @param secret_access_key [String, nil] Explicit AWS secret access key.
364
+ #
365
+ # @param session_token [String, nil] Optional session token for explicit
366
+ # temporary AWS credentials.
367
+ #
368
+ # @param profile [String, nil] Explicit AWS shared-config profile.
369
+ #
370
+ # @param credentials_provider [#credentials, #call, nil] Refreshable AWS
371
+ # credentials provider. A callable must return an AWS credential identity.
372
+ #
373
+ # @return [OpenAI::Provider]
374
+ def bedrock(
375
+ region: nil,
376
+ base_url: OpenAI::Internal::OMIT,
377
+ api_key: OpenAI::Internal::OMIT,
378
+ token_provider: nil,
379
+ access_key_id: nil,
380
+ secret_access_key: nil,
381
+ session_token: nil,
382
+ profile: nil,
383
+ credentials_provider: nil
384
+ )
385
+ normalized_region = Bedrock.normalize_optional_string(region)
386
+ if !region.nil? && normalized_region.nil?
387
+ raise ArgumentError, "The Bedrock AWS `region` must not be empty."
388
+ end
389
+ normalized_region ||= Bedrock.normalize_optional_string(ENV["AWS_REGION"]) ||
390
+ Bedrock.normalize_optional_string(ENV["AWS_DEFAULT_REGION"])
391
+
392
+ normalized_profile = Bedrock.normalize_optional_string(profile)
393
+ if !profile.nil? && normalized_profile.nil?
394
+ raise ArgumentError, "The Bedrock AWS `profile` must not be empty."
395
+ end
396
+
397
+ configured_base_url =
398
+ if base_url.equal?(OpenAI::Internal::OMIT)
399
+ Bedrock.normalize_optional_string(ENV["AWS_BEDROCK_BASE_URL"])
400
+ elsif base_url.nil?
401
+ nil
402
+ else
403
+ normalized = Bedrock.normalize_optional_string(base_url)
404
+ raise(ArgumentError, "The Bedrock `base_url` must not be empty.") unless normalized
405
+ normalized
406
+ end
407
+ configured_base_url = Bedrock.normalize_base_url(configured_base_url) if configured_base_url
408
+
409
+ has_access_key = !access_key_id.nil?
410
+ has_secret_key = !secret_access_key.nil?
411
+ if has_access_key != has_secret_key || (!session_token.nil? && !has_access_key)
412
+ raise ArgumentError, Bedrock::PARTIAL_STATIC_CREDENTIALS_MESSAGE
413
+ end
414
+ normalized_access_key_id = Bedrock.normalize_optional_string(access_key_id)
415
+ normalized_secret_access_key = Bedrock.normalize_optional_string(secret_access_key)
416
+ normalized_session_token = Bedrock.normalize_optional_string(session_token)
417
+ if has_access_key && [normalized_access_key_id, normalized_secret_access_key].any?(&:nil?)
418
+ raise ArgumentError,
419
+ "Static AWS credentials require non-empty `access_key_id` and `secret_access_key` values."
420
+ end
421
+ if !session_token.nil? && normalized_session_token.nil?
422
+ raise ArgumentError, "A static AWS `session_token` must not be empty when provided."
423
+ end
424
+
425
+ explicit_api_key = !api_key.equal?(OpenAI::Internal::OMIT) && !api_key.nil?
426
+ normalized_api_key = Bedrock.normalize_optional_string(api_key)
427
+ if explicit_api_key && normalized_api_key.nil?
428
+ raise ArgumentError, "The Bedrock bearer credential must not be empty."
429
+ end
430
+ if explicit_api_key && !token_provider.nil?
431
+ raise ArgumentError,
432
+ "The `api_key` and `token_provider` options are mutually exclusive. Configure only one."
433
+ end
434
+ if !token_provider.nil? && !token_provider.respond_to?(:call)
435
+ raise ArgumentError, "The Bedrock `token_provider` must respond to `call`."
436
+ end
437
+ if !credentials_provider.nil? &&
438
+ !credentials_provider.respond_to?(:call) &&
439
+ !credentials_provider.respond_to?(:credentials)
440
+ raise ArgumentError,
441
+ "The Bedrock `credentials_provider` must respond to `call` or `credentials`."
442
+ end
443
+
444
+ explicit_bearer = explicit_api_key || !token_provider.nil?
445
+ aws_modes = [has_access_key, !normalized_profile.nil?, !credentials_provider.nil?].count(true)
446
+ if aws_modes > 1
447
+ raise ArgumentError, Bedrock::AMBIGUOUS_AWS_AUTH_MESSAGE
448
+ end
449
+ if explicit_bearer && aws_modes.positive?
450
+ raise ArgumentError, Bedrock::AMBIGUOUS_AUTH_MESSAGE
451
+ end
452
+
453
+ skip_environment_bearer = !api_key.equal?(OpenAI::Internal::OMIT) && api_key.nil?
454
+ environment_bearer =
455
+ !explicit_bearer && aws_modes.zero? && !skip_environment_bearer &&
456
+ !Bedrock.normalize_optional_string(ENV["AWS_BEARER_TOKEN_BEDROCK"]).nil?
457
+ bearer_provider =
458
+ if explicit_api_key
459
+ normalized_api_key.freeze
460
+ -> { normalized_api_key }
461
+ elsif !token_provider.nil?
462
+ token_provider
463
+ elsif environment_bearer
464
+ lambda do
465
+ ENV["AWS_BEARER_TOKEN_BEDROCK"] ||
466
+ raise(OpenAI::Errors::Error, Bedrock::MISSING_CREDENTIALS_MESSAGE)
467
+ end
468
+ end
469
+
470
+ if bearer_provider && configured_base_url.nil? && normalized_region.nil?
471
+ raise ArgumentError, Bedrock::MISSING_REGION_MESSAGE
472
+ end
473
+
474
+ definition = Bedrock::Definition.new(
475
+ region: normalized_region,
476
+ base_url: configured_base_url,
477
+ bearer_provider: bearer_provider,
478
+ profile: normalized_profile,
479
+ access_key_id: normalized_access_key_id,
480
+ secret_access_key: normalized_secret_access_key,
481
+ session_token: normalized_session_token,
482
+ credentials_provider: credentials_provider,
483
+ default_chain: aws_modes.zero?
484
+ )
485
+ OpenAI::Internal::Provider.create(definition)
486
+ end
487
+ end
488
+ end
489
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenAI
4
+ module Resources
5
+ class Admin
6
+ class Organization
7
+ class Projects
8
+ class ServiceAccounts
9
+ class APIKeys
10
+ # Creates an API key for a service account in the project.
11
+ #
12
+ # @overload create(service_account_id, project_id:, name: nil, scopes: nil, request_options: {})
13
+ #
14
+ # @param service_account_id [String] Path param: The ID of the service account.
15
+ #
16
+ # @param project_id [String] Path param: The ID of the project.
17
+ #
18
+ # @param name [String] Body param: API key name.
19
+ #
20
+ # @param scopes [Array<String>] Body param: API key scopes.
21
+ #
22
+ # @param request_options [OpenAI::RequestOptions, Hash{Symbol=>Object}, nil]
23
+ #
24
+ # @return [OpenAI::Models::Admin::Organization::Projects::ServiceAccounts::APIKeyCreateResponse]
25
+ #
26
+ # @see OpenAI::Models::Admin::Organization::Projects::ServiceAccounts::APIKeyCreateParams
27
+ def create(service_account_id, params)
28
+ parsed, options =
29
+ OpenAI::Admin::Organization::Projects::ServiceAccounts::APIKeyCreateParams.dump_request(params)
30
+ project_id =
31
+ parsed.delete(:project_id) do
32
+ raise ArgumentError.new("missing required path argument #{_1}")
33
+ end
34
+ @client.request(
35
+ method: :post,
36
+ path: [
37
+ "organization/projects/%1$s/service_accounts/%2$s/api_keys",
38
+ project_id,
39
+ service_account_id
40
+ ],
41
+ body: parsed,
42
+ model: OpenAI::Models::Admin::Organization::Projects::ServiceAccounts::APIKeyCreateResponse,
43
+ security: {admin_api_key_auth: true},
44
+ options: options
45
+ )
46
+ end
47
+
48
+ # @api private
49
+ #
50
+ # @param client [OpenAI::Client]
51
+ def initialize(client:)
52
+ @client = client
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -6,15 +6,20 @@ module OpenAI
6
6
  class Organization
7
7
  class Projects
8
8
  class ServiceAccounts
9
- # Creates a new service account in the project. This also returns an unredacted
10
- # API key for the service account.
9
+ # @return [OpenAI::Resources::Admin::Organization::Projects::ServiceAccounts::APIKeys]
10
+ attr_reader :api_keys
11
+
12
+ # Creates a new service account in the project. By default, this also returns an
13
+ # unredacted API key for the service account.
11
14
  #
12
- # @overload create(project_id, name:, request_options: {})
15
+ # @overload create(project_id, name:, create_service_account_only: nil, request_options: {})
13
16
  #
14
17
  # @param project_id [String] The ID of the project.
15
18
  #
16
19
  # @param name [String] The name of the service account being created.
17
20
  #
21
+ # @param create_service_account_only [Boolean, nil] Create the service account without default roles or an API key.
22
+ #
18
23
  # @param request_options [OpenAI::RequestOptions, Hash{Symbol=>Object}, nil]
19
24
  #
20
25
  # @return [OpenAI::Models::Admin::Organization::Projects::ServiceAccountCreateResponse]
@@ -163,6 +168,8 @@ module OpenAI
163
168
  # @param client [OpenAI::Client]
164
169
  def initialize(client:)
165
170
  @client = client
171
+ @api_keys =
172
+ OpenAI::Resources::Admin::Organization::Projects::ServiceAccounts::APIKeys.new(client: client)
166
173
  end
167
174
  end
168
175
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenAI
4
- VERSION = "0.70.0"
4
+ VERSION = "0.71.0"
5
5
  end
data/lib/openai.rb CHANGED
@@ -53,6 +53,9 @@ require_relative "openai/internal"
53
53
  require_relative "openai/request_options"
54
54
  require_relative "openai/file_part"
55
55
  require_relative "openai/errors"
56
+ require_relative "openai/provider"
57
+ require_relative "openai/internal/provider"
58
+ require_relative "openai/providers/bedrock"
56
59
  require_relative "openai/internal/transport/base_client"
57
60
  require_relative "openai/internal/transport/pooled_net_requester"
58
61
  require_relative "openai/client"
@@ -205,6 +208,8 @@ require_relative "openai/models/admin/organization/projects/service_account_dele
205
208
  require_relative "openai/models/admin/organization/projects/service_account_delete_response"
206
209
  require_relative "openai/models/admin/organization/projects/service_account_list_params"
207
210
  require_relative "openai/models/admin/organization/projects/service_account_retrieve_params"
211
+ require_relative "openai/models/admin/organization/projects/service_accounts/api_key_create_params"
212
+ require_relative "openai/models/admin/organization/projects/service_accounts/api_key_create_response"
208
213
  require_relative "openai/models/admin/organization/projects/service_account_update_params"
209
214
  require_relative "openai/models/admin/organization/projects/spend_alert_create_params"
210
215
  require_relative "openai/models/admin/organization/projects/spend_alert_delete_params"
@@ -1162,6 +1167,7 @@ require_relative "openai/resources/admin/organization/projects/model_permissions
1162
1167
  require_relative "openai/resources/admin/organization/projects/rate_limits"
1163
1168
  require_relative "openai/resources/admin/organization/projects/roles"
1164
1169
  require_relative "openai/resources/admin/organization/projects/service_accounts"
1170
+ require_relative "openai/resources/admin/organization/projects/service_accounts/api_keys"
1165
1171
  require_relative "openai/resources/admin/organization/projects/spend_alerts"
1166
1172
  require_relative "openai/resources/admin/organization/projects/users"
1167
1173
  require_relative "openai/resources/admin/organization/projects/users/roles"
@@ -132,14 +132,29 @@ module OpenAI
132
132
  private def admin_api_key_auth
133
133
  end
134
134
 
135
+ # @api private
136
+ sig do
137
+ override
138
+ .params(
139
+ request: OpenAI::Internal::Transport::BaseClient::RequestInput,
140
+ redirect_count: Integer,
141
+ retry_count: Integer
142
+ )
143
+ .returns(OpenAI::Internal::Transport::BaseClient::RequestInput)
144
+ end
145
+ private def prepare_request(request, redirect_count:, retry_count:)
146
+ end
147
+
135
148
  # Creates and returns a new client for interacting with the API.
136
149
  sig do
137
150
  params(
138
151
  api_key: T.nilable(String),
139
152
  admin_api_key: T.nilable(String),
153
+ workload_identity: T.nilable(OpenAI::Auth::WorkloadIdentity),
140
154
  organization: T.nilable(String),
141
155
  project: T.nilable(String),
142
156
  webhook_secret: T.nilable(String),
157
+ provider: T.nilable(OpenAI::Provider),
143
158
  base_url: T.nilable(String),
144
159
  max_retries: Integer,
145
160
  timeout: Float,
@@ -152,12 +167,14 @@ module OpenAI
152
167
  api_key: ENV["OPENAI_API_KEY"],
153
168
  # Defaults to `ENV["OPENAI_ADMIN_KEY"]`
154
169
  admin_api_key: ENV["OPENAI_ADMIN_KEY"],
170
+ workload_identity: nil,
155
171
  # Defaults to `ENV["OPENAI_ORG_ID"]`
156
172
  organization: ENV["OPENAI_ORG_ID"],
157
173
  # Defaults to `ENV["OPENAI_PROJECT_ID"]`
158
174
  project: ENV["OPENAI_PROJECT_ID"],
159
175
  # Defaults to `ENV["OPENAI_WEBHOOK_SECRET"]`
160
176
  webhook_secret: ENV["OPENAI_WEBHOOK_SECRET"],
177
+ provider: nil,
161
178
  # Override the default base URL for the API, e.g.,
162
179
  # `"https://api.example.com/v2/"`. Defaults to `ENV["OPENAI_BASE_URL"]`
163
180
  base_url: ENV["OPENAI_BASE_URL"],