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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +51 -1
- data/lib/openai/client.rb +62 -8
- data/lib/openai/internal/provider.rb +31 -0
- data/lib/openai/internal/transport/base_client.rb +65 -21
- data/lib/openai/models/admin/organization/audit_log_list_params.rb +84 -0
- data/lib/openai/models/admin/organization/audit_log_list_response.rb +84 -0
- data/lib/openai/models/admin/organization/projects/project_service_account.rb +4 -3
- data/lib/openai/models/admin/organization/projects/service_account_create_params.rb +9 -1
- data/lib/openai/models/admin/organization/projects/service_account_create_response.rb +26 -6
- data/lib/openai/models/admin/organization/projects/service_accounts/api_key_create_params.rb +54 -0
- data/lib/openai/models/admin/organization/projects/service_accounts/api_key_create_response.rb +57 -0
- data/lib/openai/models/admin/organization/usage_audio_speeches_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_audio_transcriptions_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_code_interpreter_sessions_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_completions_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_costs_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_embeddings_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_file_search_calls_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_images_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_moderations_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_vector_stores_response.rb +88 -13
- data/lib/openai/models/admin/organization/usage_web_search_calls_response.rb +88 -13
- data/lib/openai/models/beta/beta_response_input_item.rb +16 -32
- data/lib/openai/provider.rb +18 -0
- data/lib/openai/providers/bedrock.rb +489 -0
- data/lib/openai/resources/admin/organization/projects/service_accounts/api_keys.rb +60 -0
- data/lib/openai/resources/admin/organization/projects/service_accounts.rb +10 -3
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +6 -0
- data/rbi/openai/client.rbi +17 -0
- data/rbi/openai/internal/provider.rbi +51 -0
- data/rbi/openai/internal/transport/base_client.rbi +32 -0
- data/rbi/openai/models/admin/organization/audit_log_list_params.rbi +420 -0
- data/rbi/openai/models/admin/organization/audit_log_list_response.rbi +420 -0
- data/rbi/openai/models/admin/organization/projects/project_service_account.rbi +8 -3
- data/rbi/openai/models/admin/organization/projects/service_account_create_params.rbi +8 -0
- data/rbi/openai/models/admin/organization/projects/service_account_create_response.rbi +52 -8
- data/rbi/openai/models/admin/organization/projects/service_accounts/api_key_create_params.rbi +84 -0
- data/rbi/openai/models/admin/organization/projects/service_accounts/api_key_create_response.rbi +80 -0
- data/rbi/openai/models/admin/organization/usage_audio_speeches_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_audio_transcriptions_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_code_interpreter_sessions_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_completions_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_costs_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_embeddings_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_file_search_calls_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_images_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_moderations_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_vector_stores_response.rbi +121 -16
- data/rbi/openai/models/admin/organization/usage_web_search_calls_response.rbi +121 -16
- data/rbi/openai/models/beta/beta_response_input_item.rbi +39 -46
- data/rbi/openai/provider.rbi +9 -0
- data/rbi/openai/providers.rbi +33 -0
- data/rbi/openai/resources/admin/organization/projects/service_accounts/api_keys.rbi +45 -0
- data/rbi/openai/resources/admin/organization/projects/service_accounts.rbi +12 -2
- data/sig/openai/client.rbs +7 -0
- data/sig/openai/internal/provider.rbs +29 -0
- data/sig/openai/internal/transport/base_client.rbs +17 -1
- data/sig/openai/models/admin/organization/audit_log_list_params.rbs +168 -0
- data/sig/openai/models/admin/organization/audit_log_list_response.rbs +168 -0
- data/sig/openai/models/admin/organization/projects/project_service_account.rbs +2 -1
- data/sig/openai/models/admin/organization/projects/service_account_create_params.rbs +9 -1
- data/sig/openai/models/admin/organization/projects/service_account_create_response.rbs +16 -5
- data/sig/openai/models/admin/organization/projects/service_accounts/api_key_create_params.rbs +53 -0
- data/sig/openai/models/admin/organization/projects/service_accounts/api_key_create_response.rbs +48 -0
- data/sig/openai/models/admin/organization/usage_audio_speeches_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_audio_transcriptions_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_code_interpreter_sessions_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_completions_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_costs_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_embeddings_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_file_search_calls_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_images_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_moderations_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_vector_stores_response.rbs +63 -0
- data/sig/openai/models/admin/organization/usage_web_search_calls_response.rbs +63 -0
- data/sig/openai/models/beta/beta_response_input_item.rbs +18 -24
- data/sig/openai/provider.rbs +5 -0
- data/sig/openai/providers.rbs +15 -0
- data/sig/openai/resources/admin/organization/projects/service_accounts/api_keys.rbs +23 -0
- data/sig/openai/resources/admin/organization/projects/service_accounts.rbs +3 -0
- 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
|
-
#
|
|
10
|
-
|
|
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
|
data/lib/openai/version.rb
CHANGED
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"
|
data/rbi/openai/client.rbi
CHANGED
|
@@ -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"],
|