anthropic 1.25.0 → 1.27.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.md +1 -1
  4. data/lib/anthropic/aws.rb +5 -0
  5. data/lib/anthropic/errors.rb +11 -2
  6. data/lib/anthropic/helpers/aws/client.rb +104 -0
  7. data/lib/anthropic/helpers/aws_auth.rb +217 -0
  8. data/lib/anthropic/helpers/bedrock/client.rb +50 -26
  9. data/lib/anthropic/internal/util.rb +19 -6
  10. data/lib/anthropic/models/anthropic_beta.rb +3 -0
  11. data/lib/anthropic/models/beta/beta_message.rb +9 -1
  12. data/lib/anthropic/models/beta/beta_raw_message_delta_event.rb +9 -1
  13. data/lib/anthropic/models/beta/beta_refusal_stop_details.rb +60 -0
  14. data/lib/anthropic/models/message.rb +9 -1
  15. data/lib/anthropic/models/raw_message_delta_event.rb +9 -1
  16. data/lib/anthropic/models/refusal_stop_details.rb +56 -0
  17. data/lib/anthropic/models.rb +2 -0
  18. data/lib/anthropic/version.rb +1 -1
  19. data/lib/anthropic.rb +5 -0
  20. data/rbi/anthropic/aws.rbi +5 -0
  21. data/rbi/anthropic/errors.rbi +7 -2
  22. data/rbi/anthropic/helpers/aws/client.rbi +72 -0
  23. data/rbi/anthropic/internal/util.rbi +8 -0
  24. data/rbi/anthropic/models/anthropic_beta.rbi +2 -0
  25. data/rbi/anthropic/models/beta/beta_message.rbi +17 -0
  26. data/rbi/anthropic/models/beta/beta_raw_message_delta_event.rbi +18 -0
  27. data/rbi/anthropic/models/beta/beta_refusal_stop_details.rbi +115 -0
  28. data/rbi/anthropic/models/message.rbi +15 -0
  29. data/rbi/anthropic/models/raw_message_delta_event.rbi +15 -0
  30. data/rbi/anthropic/models/refusal_stop_details.rbi +93 -0
  31. data/rbi/anthropic/models.rbi +2 -0
  32. data/sig/anthropic/errors.rbs +4 -1
  33. data/sig/anthropic/internal/util.rbs +4 -0
  34. data/sig/anthropic/models/anthropic_beta.rbs +2 -0
  35. data/sig/anthropic/models/beta/beta_message.rbs +5 -0
  36. data/sig/anthropic/models/beta/beta_raw_message_delta_event.rbs +5 -0
  37. data/sig/anthropic/models/beta/beta_refusal_stop_details.rbs +45 -0
  38. data/sig/anthropic/models/message.rbs +5 -0
  39. data/sig/anthropic/models/raw_message_delta_event.rbs +5 -0
  40. data/sig/anthropic/models/refusal_stop_details.rbs +41 -0
  41. data/sig/anthropic/models.rbs +2 -0
  42. metadata +13 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bfde8fb8238b702db1198ffaaa13365d3274c4222f67795ef642cdf3400071b
4
- data.tar.gz: 971877b395273eca3fbadf30a18b30b94adb797b423ef0ed0a05e9bb53e487bb
3
+ metadata.gz: 72c6d580e9df64dc561e9dcd1b5ea9cd9ad18d46293c64dc25f8e6cd971ed8dc
4
+ data.tar.gz: a1124fb9148fc390060d8d9a6f333eb698fb65beaa5e81a7f57c5a02b95fcb16
5
5
  SHA512:
6
- metadata.gz: 991b93e8ddf7187a7b08598ca4f5b3b374f680337f1efb2a7ad5160e3ff0f2da5271320177a0abcf169e61a2502a56553d994449a1f493beebe80a49b0c3ed40
7
- data.tar.gz: b4778e9aec53cc412a43baadd0c382c130e3e16bc72f260eed7927bb0d04cd8707eec9582030abda572ccf104ce7537382bea6b771668354bcadad5ec0f78b2e
6
+ metadata.gz: a687549d5a637357f3ffd5b8da67529da52410168e7cab51e4485c3c120a15f4dbba7b07d957f5920866f78db44f6bd790cab398a6d07b5a4da3298d0c8038f0
7
+ data.tar.gz: 19245fd17062f33dbc20dc2ba59e5f6c33be3605bd37d3b27af34813cee0e4b37feedcc217d3cb3b7ef354492220dd39dacb71009eb43f7fe90920762e11adda
data/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.27.0 (2026-04-01)
4
+
5
+ Full Changelog: [v1.26.0...v1.27.0](https://github.com/anthropics/anthropic-sdk-ruby/compare/v1.26.0...v1.27.0)
6
+
7
+ ### Features
8
+
9
+ * **api:** add structured stop_details to message responses ([5e636fd](https://github.com/anthropics/anthropic-sdk-ruby/commit/5e636fd9ce945bd52f92d99e21e6bb8b9c212238))
10
+ * bedrock api key auth ([#880](https://github.com/anthropics/anthropic-sdk-ruby/issues/880)) ([93f9b87](https://github.com/anthropics/anthropic-sdk-ruby/commit/93f9b870184a3519270cacec10ceeaa85e9f548c))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * align path encoding with RFC 3986 section 3.3 ([280f489](https://github.com/anthropics/anthropic-sdk-ruby/commit/280f4894bb96af3890cb142df1e6bd0fae53fbcd))
16
+
17
+
18
+ ### Chores
19
+
20
+ * **internal:** client updates ([151043a](https://github.com/anthropics/anthropic-sdk-ruby/commit/151043a45a4f332fb176038799f499be358db44a))
21
+
22
+ ## 1.26.0 (2026-03-31)
23
+
24
+ Full Changelog: [v1.25.0...v1.26.0](https://github.com/anthropics/anthropic-sdk-ruby/compare/v1.25.0...v1.26.0)
25
+
26
+ ### Features
27
+
28
+ * add .type field to APIStatusError for uniform error identification ([#847](https://github.com/anthropics/anthropic-sdk-ruby/issues/847)) ([4c57783](https://github.com/anthropics/anthropic-sdk-ruby/commit/4c577837697516dc339100fe52be915afe544102))
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * **internal:** correct multipart form field name encoding ([0fed236](https://github.com/anthropics/anthropic-sdk-ruby/commit/0fed236eabd481a034739c6f7abab75bc3a875c2))
34
+ * variable name typo ([550b1ed](https://github.com/anthropics/anthropic-sdk-ruby/commit/550b1ed8af220d6749040c44f608b0b6b8910aaf))
35
+
36
+
37
+ ### Chores
38
+
39
+ * **ci:** run builds on CI even if only spec metadata changed ([8faa86f](https://github.com/anthropics/anthropic-sdk-ruby/commit/8faa86f76c7fbc80082ac07768e14af831566f62))
40
+ * **ci:** skip lint on metadata-only changes ([16064d4](https://github.com/anthropics/anthropic-sdk-ruby/commit/16064d40c434ba29c0943c6dd0402c0b58fcbb3d))
41
+ * **ci:** support opting out of skipping builds on metadata-only commits ([e184024](https://github.com/anthropics/anthropic-sdk-ruby/commit/e18402452192c17170a6933ab0bb060a7e0ba073))
42
+ * **internal:** update gitignore ([5f3d363](https://github.com/anthropics/anthropic-sdk-ruby/commit/5f3d363e741a966fa6ec3e04ed675eab27aedd77))
43
+ * **tests:** bump steady to v0.19.4 ([4a17d4d](https://github.com/anthropics/anthropic-sdk-ruby/commit/4a17d4deff2d696f78c9c503f99dcbd9c7146153))
44
+ * **tests:** bump steady to v0.19.5 ([a4bcfd7](https://github.com/anthropics/anthropic-sdk-ruby/commit/a4bcfd76f9384c02bfb074ed57de8aba2face020))
45
+ * **tests:** bump steady to v0.19.6 ([e34f523](https://github.com/anthropics/anthropic-sdk-ruby/commit/e34f5236c9d2aa6fa78c7562af7f89aa70fe373b))
46
+ * **tests:** bump steady to v0.19.7 ([577310d](https://github.com/anthropics/anthropic-sdk-ruby/commit/577310d77b14972f657d5946b661298cadbff31a))
47
+ * **tests:** bump steady to v0.20.1 ([dcf51d2](https://github.com/anthropics/anthropic-sdk-ruby/commit/dcf51d293d46d719c0fe09b4410dcb5445adcabc))
48
+ * **tests:** bump steady to v0.20.2 ([5c52306](https://github.com/anthropics/anthropic-sdk-ruby/commit/5c523068dadf801f50b1d39d53bbcce07a5d0722))
49
+
3
50
  ## 1.25.0 (2026-03-18)
4
51
 
5
52
  Full Changelog: [v1.24.0...v1.25.0](https://github.com/anthropics/anthropic-sdk-ruby/compare/v1.24.0...v1.25.0)
data/README.md CHANGED
@@ -15,7 +15,7 @@ Add to your application's Gemfile:
15
15
  <!-- x-release-please-start-version -->
16
16
 
17
17
  ```ruby
18
- gem "anthropic", "~> 1.25.0"
18
+ gem "anthropic", "~> 1.27.0"
19
19
  ```
20
20
 
21
21
  <!-- x-release-please-end -->
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ AWSClient = Anthropic::Helpers::AWS::Client
5
+ end
@@ -121,6 +121,9 @@ module Anthropic
121
121
  end
122
122
 
123
123
  class APIStatusError < Anthropic::Errors::APIError
124
+ # @return [Anthropic::ErrorType::TaggedSymbol, nil]
125
+ attr_reader :type
126
+
124
127
  # @api private
125
128
  #
126
129
  # @param url [URI::Generic]
@@ -133,6 +136,9 @@ module Anthropic
133
136
  #
134
137
  # @return [self]
135
138
  def self.for(url:, status:, headers:, body:, request:, response:, message: nil)
139
+ error_type = body.dig(:error, :type) if body.is_a?(Hash)
140
+ error_type = error_type.to_sym if error_type.is_a?(String)
141
+
136
142
  kwargs =
137
143
  {
138
144
  url: url,
@@ -141,7 +147,8 @@ module Anthropic
141
147
  body: body,
142
148
  request: request,
143
149
  response: response,
144
- message: message
150
+ message: message,
151
+ type: error_type
145
152
  }
146
153
 
147
154
  case status
@@ -179,7 +186,9 @@ module Anthropic
179
186
  # @param request [nil]
180
187
  # @param response [nil]
181
188
  # @param message [String, nil]
182
- def initialize(url:, status:, headers:, body:, request:, response:, message: nil)
189
+ # @param type [Anthropic::ErrorType::TaggedSymbol, nil]
190
+ def initialize(url:, status:, headers:, body:, request:, response:, message: nil, type: nil)
191
+ @type = type
183
192
  message ||= {url: url.to_s, status: status, body: body}
184
193
  super(
185
194
  url: url,
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Helpers
5
+ module AWS
6
+ class Client < Anthropic::Client
7
+ include Anthropic::Helpers::AWSAuth
8
+
9
+ # @return [String, nil]
10
+ attr_reader :aws_region
11
+
12
+ # @return [String, nil]
13
+ attr_reader :workspace_id
14
+
15
+ # Creates and returns a new client for interacting with Anthropic models via the AWS gateway.
16
+ #
17
+ # AWS credentials are resolved in priority order:
18
+ # 1. `api_key` constructor arg → API key mode (x-api-key header, no SigV4)
19
+ # 2. `aws_access_key` + `aws_secret_access_key` args → SigV4 with explicit credentials
20
+ # 3. `aws_profile` arg → SigV4 with named AWS profile
21
+ # 4. `ANTHROPIC_AWS_API_KEY` env var → API key mode (only when no platform auth args given)
22
+ # 5. Default AWS credential chain (env vars → shared config → instance profile) → SigV4
23
+ #
24
+ # When `skip_auth` is true, all auth is skipped and workspace_id is not required.
25
+ #
26
+ # @param api_key [String, nil] Anthropic API key (bypasses SigV4, uses x-api-key header)
27
+ #
28
+ # @param aws_access_key [String, nil] AWS access key ID for SigV4 signing
29
+ #
30
+ # @param aws_secret_access_key [String, nil] AWS secret access key for SigV4 signing
31
+ #
32
+ # @param aws_session_token [String, nil] Optional AWS session token for temporary credentials
33
+ #
34
+ # @param aws_profile [String, nil] AWS profile name (uses SigV4 with named profile)
35
+ #
36
+ # @param aws_region [String, nil] AWS region for the gateway URL and SigV4 signing.
37
+ # Defaults to `ENV["AWS_REGION"]`, then `ENV["AWS_DEFAULT_REGION"]`. Required in SigV4 mode.
38
+ #
39
+ # @param workspace_id [String, nil] Anthropic workspace ID, sent as `anthropic-workspace-id` header.
40
+ # Defaults to `ENV["ANTHROPIC_AWS_WORKSPACE_ID"]`
41
+ #
42
+ # @param skip_auth [Boolean] When true, skip all auth (no SigV4, no API key) and do not
43
+ # require workspace_id. Useful for testing or when auth is handled externally.
44
+ #
45
+ # @param base_url [String, nil] Override the default base URL for the API.
46
+ # Defaults to `ENV["ANTHROPIC_AWS_BASE_URL"]`, then `https://aws-external-anthropic.{region}.api.aws`.
47
+ #
48
+ # @param max_retries [Integer] Max number of retries to attempt after a failed retryable request.
49
+ #
50
+ # @param timeout [Float]
51
+ #
52
+ # @param initial_retry_delay [Float]
53
+ #
54
+ # @param max_retry_delay [Float]
55
+ #
56
+ def initialize(
57
+ api_key: nil,
58
+ aws_access_key: nil,
59
+ aws_secret_access_key: nil,
60
+ aws_session_token: nil,
61
+ aws_profile: nil,
62
+ aws_region: ENV["AWS_REGION"] || ENV["AWS_DEFAULT_REGION"],
63
+ workspace_id: ENV["ANTHROPIC_AWS_WORKSPACE_ID"],
64
+ skip_auth: false,
65
+ base_url: ENV["ANTHROPIC_AWS_BASE_URL"],
66
+ max_retries: self.class::DEFAULT_MAX_RETRIES,
67
+ timeout: self.class::DEFAULT_TIMEOUT_IN_SECONDS,
68
+ initial_retry_delay: self.class::DEFAULT_INITIAL_RETRY_DELAY,
69
+ max_retry_delay: self.class::DEFAULT_MAX_RETRY_DELAY
70
+ )
71
+ effective_api_key, resolved_base_url = setup_aws_auth(
72
+ api_key: api_key,
73
+ aws_access_key: aws_access_key,
74
+ aws_secret_access_key: aws_secret_access_key,
75
+ aws_session_token: aws_session_token,
76
+ aws_profile: aws_profile,
77
+ aws_region: aws_region,
78
+ workspace_id: workspace_id,
79
+ skip_auth: skip_auth,
80
+ base_url: base_url,
81
+ service_name: "aws-external-anthropic",
82
+ env_api_key: "ANTHROPIC_AWS_API_KEY",
83
+ env_workspace_id: "ANTHROPIC_AWS_WORKSPACE_ID",
84
+ derive_base_url: ->(region) { "https://aws-external-anthropic.#{region}.api.aws" }
85
+ )
86
+
87
+ super(
88
+ api_key: effective_api_key,
89
+ base_url: resolved_base_url,
90
+ max_retries: max_retries,
91
+ timeout: timeout,
92
+ initial_retry_delay: initial_retry_delay,
93
+ max_retry_delay: max_retry_delay
94
+ )
95
+ end
96
+
97
+ # @api private
98
+ private def transform_request(request)
99
+ aws_auth_transform_request(request)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Helpers
5
+ # Shared AWS authentication module used by both the AWS gateway client and the
6
+ # Bedrock Mantle client. Provides SigV4 signing, credential resolution, and
7
+ # config handling parameterized by service name and environment variable names.
8
+ module AWSAuth
9
+ # Sets up AWS authentication state. Call from the including class's `initialize`.
10
+ #
11
+ # @param api_key [String, nil]
12
+ # @param aws_access_key [String, nil]
13
+ # @param aws_secret_access_key [String, nil]
14
+ # @param aws_session_token [String, nil]
15
+ # @param aws_profile [String, nil]
16
+ # @param aws_region [String, nil]
17
+ # @param workspace_id [String, nil]
18
+ # @param skip_auth [Boolean]
19
+ # @param base_url [String, nil]
20
+ # @param service_name [String] AWS service name for SigV4 signing
21
+ # @param env_api_key [String] Primary env var for API key
22
+ # @param env_api_key_fallback [String, nil] Fallback env var for API key
23
+ # @param env_workspace_id [String] Primary env var for workspace ID
24
+ # @param env_workspace_id_fallback [String, nil] Fallback env var for workspace ID
25
+ # @param derive_base_url [Proc] Lambda that takes region and returns default base URL
26
+ #
27
+ # @return [Array(String, String)] [effective_api_key, resolved_base_url]
28
+ private def setup_aws_auth(
29
+ api_key:,
30
+ aws_access_key:,
31
+ aws_secret_access_key:,
32
+ aws_session_token:,
33
+ aws_profile:,
34
+ aws_region:,
35
+ workspace_id:,
36
+ skip_auth:,
37
+ base_url:,
38
+ service_name:,
39
+ env_api_key:,
40
+ env_workspace_id:,
41
+ derive_base_url:,
42
+ env_api_key_fallback: nil,
43
+ env_workspace_id_fallback: nil
44
+ )
45
+ begin
46
+ require("aws-sdk-core")
47
+ rescue LoadError
48
+ message = <<~MSG
49
+
50
+ In order to use Anthropic AWS clients you must require the `aws-sdk-core` gem.
51
+ You can install it by adding the following to your Gemfile:
52
+
53
+ gem "aws-sdk-core"
54
+
55
+ and then running `bundle install`.
56
+
57
+ Alternatively, if you are not using Bundler, simply run:
58
+
59
+ gem install aws-sdk-core
60
+ MSG
61
+
62
+ raise RuntimeError.new(message)
63
+ end
64
+
65
+ if (aws_access_key && !aws_secret_access_key) || (!aws_access_key && aws_secret_access_key)
66
+ raise ArgumentError.new("Both aws_access_key and aws_secret_access_key must be provided together")
67
+ end
68
+
69
+ @skip_auth = skip_auth
70
+
71
+ if @skip_auth
72
+ effective_api_key = nil
73
+ @use_sig_v4 = false
74
+ else
75
+ # Resolve auth mode. Explicit platform auth args suppress the API key env var check.
76
+ has_platform_auth = aws_access_key || aws_secret_access_key || aws_profile
77
+ env_key = env_lookup(env_api_key, env_api_key_fallback)
78
+ effective_api_key = api_key || (has_platform_auth ? nil : env_key)
79
+ @use_sig_v4 = effective_api_key.nil?
80
+ end
81
+
82
+ if @use_sig_v4
83
+ if aws_region.to_s.empty?
84
+ # rubocop:disable Layout/LineLength
85
+ raise ArgumentError.new("No aws_region was given. Set the `aws_region` argument or the `AWS_REGION` / `AWS_DEFAULT_REGION` environment variable.")
86
+ # rubocop:enable Layout/LineLength
87
+ end
88
+
89
+ @aws_region = aws_region
90
+ credentials = resolve_credentials(
91
+ aws_access_key: aws_access_key,
92
+ aws_secret_access_key: aws_secret_access_key,
93
+ aws_session_token: aws_session_token,
94
+ aws_profile: aws_profile
95
+ )
96
+ @signer = Aws::Sigv4::Signer.new(
97
+ service: service_name,
98
+ region: @aws_region,
99
+ credentials_provider: credentials
100
+ )
101
+ end
102
+
103
+ # Workspace ID: constructor > primary env > fallback env
104
+ resolved_workspace_id = workspace_id
105
+ if resolved_workspace_id.to_s.empty?
106
+ resolved_workspace_id = env_lookup(env_workspace_id, env_workspace_id_fallback)
107
+ end
108
+ @workspace_id = resolved_workspace_id.to_s.empty? ? nil : resolved_workspace_id.to_s
109
+
110
+ unless @skip_auth
111
+ if @workspace_id.nil?
112
+ # rubocop:disable Layout/LineLength
113
+ raise ArgumentError.new("No workspace ID found. Set the `workspace_id` argument or the `#{env_workspace_id}` environment variable.")
114
+ # rubocop:enable Layout/LineLength
115
+ end
116
+ end
117
+
118
+ if base_url.nil? && !aws_region.to_s.empty?
119
+ @aws_region ||= aws_region
120
+ base_url = derive_base_url.call(aws_region)
121
+ end
122
+
123
+ if base_url.nil?
124
+ # rubocop:disable Layout/LineLength
125
+ raise ArgumentError.new("No base_url was given and no aws_region is available to derive one. Set the `base_url` argument or provide an `aws_region`.")
126
+ # rubocop:enable Layout/LineLength
127
+ end
128
+
129
+ [effective_api_key, base_url]
130
+ end
131
+
132
+ # Override the base client's auth_headers to prevent ANTHROPIC_API_KEY
133
+ # from leaking as an x-api-key header in SigV4 mode. The server rejects
134
+ # requests that contain both Authorization (SigV4) and x-api-key headers.
135
+ #
136
+ # @return [Hash{String=>String}]
137
+ private def auth_headers
138
+ return {} if @use_sig_v4
139
+ super
140
+ end
141
+
142
+ # Applies workspace-id header and SigV4 signing to the request.
143
+ # Call from the including class's `transform_request`.
144
+ #
145
+ # @param request [Hash{Symbol=>Object}]
146
+ # @return [Hash{Symbol=>Object}]
147
+ private def aws_auth_transform_request(request)
148
+ headers = request.fetch(:headers)
149
+
150
+ headers["anthropic-workspace-id"] = @workspace_id if @workspace_id
151
+
152
+ return request unless @use_sig_v4
153
+
154
+ sliced = request.slice(:method, :url, :body).transform_keys(method: :http_method)
155
+ signed = @signer.sign_request({**sliced, headers: headers})
156
+ headers = Anthropic::Internal::Util.normalized_headers(headers, signed.headers)
157
+ headers.delete("connection")
158
+ {**request, headers: headers}
159
+ end
160
+
161
+ # Resolves AWS credentials from explicit args, profile, env vars, or default chain.
162
+ #
163
+ # @param aws_access_key [String, nil]
164
+ # @param aws_secret_access_key [String, nil]
165
+ # @param aws_session_token [String, nil]
166
+ # @param aws_profile [String, nil]
167
+ #
168
+ # @return [Aws::Credentials, Aws::SharedCredentials, Aws::InstanceProfileCredentials]
169
+ private def resolve_credentials(
170
+ aws_access_key:,
171
+ aws_secret_access_key:,
172
+ aws_session_token:,
173
+ aws_profile:
174
+ )
175
+ if aws_access_key && aws_secret_access_key
176
+ return Aws::Credentials.new(aws_access_key, aws_secret_access_key, aws_session_token)
177
+ end
178
+
179
+ if aws_profile
180
+ return Aws::SharedCredentials.new(profile_name: aws_profile)
181
+ end
182
+
183
+ # Default credential chain: env vars → shared config file → instance profile
184
+ if ENV["AWS_ACCESS_KEY_ID"] && ENV["AWS_SECRET_ACCESS_KEY"]
185
+ return Aws::Credentials.new(
186
+ ENV["AWS_ACCESS_KEY_ID"],
187
+ ENV["AWS_SECRET_ACCESS_KEY"],
188
+ ENV["AWS_SESSION_TOKEN"]
189
+ )
190
+ end
191
+
192
+ begin
193
+ shared = Aws::SharedCredentials.new
194
+ return shared if shared.set?
195
+ rescue StandardError
196
+ nil
197
+ end
198
+
199
+ # Fall back to instance profile (EC2/ECS/Lambda metadata service)
200
+ Aws::InstanceProfileCredentials.new
201
+ end
202
+
203
+ # Returns the first non-empty value from the given env var names.
204
+ #
205
+ # @param names [Array<String, nil>]
206
+ # @return [String, nil]
207
+ private def env_lookup(*names)
208
+ names.each do |name|
209
+ next if name.nil?
210
+ val = ENV[name]
211
+ return val unless val.to_s.empty?
212
+ end
213
+ nil
214
+ end
215
+ end
216
+ end
217
+ end
@@ -21,6 +21,9 @@ module Anthropic
21
21
  # @return [Aws::Credentials]
22
22
  attr_reader :aws_credentials
23
23
 
24
+ # @return [String, nil]
25
+ def api_key = @auth_token
26
+
24
27
  # Creates and returns a new client for interacting with the AWS Bedrock API for Anthropic models.
25
28
  #
26
29
  # AWS credentials are resolved according to the AWS SDK's default resolution order, described at
@@ -37,6 +40,9 @@ module Anthropic
37
40
  #
38
41
  # @param aws_profile [String, nil] Optional AWS profile to use for authentication. Overrides the credential provider chain
39
42
  #
43
+ # @param api_key [String, nil] Optional API key for Bearer token authentication. Mutually exclusive with AWS
44
+ # credentials. Falls back to the `AWS_BEARER_TOKEN_BEDROCK` environment variable if not provided.
45
+ #
40
46
  # @param base_url [String, nil] Override the default base URL for the API, e.g., `"https://api.example.com/v2/"`
41
47
  #
42
48
  # @param max_retries [Integer] The maximum number of times to retry a request if it fails
@@ -57,41 +63,55 @@ module Anthropic
57
63
  aws_access_key: nil,
58
64
  aws_secret_key: nil,
59
65
  aws_session_token: nil,
60
- aws_profile: nil
66
+ aws_profile: nil,
67
+ api_key: nil
61
68
  )
62
- begin
63
- require("aws-sdk-bedrockruntime")
64
- rescue LoadError
65
- message = <<~MSG
69
+ api_key ||= ENV["AWS_BEARER_TOKEN_BEDROCK"]
66
70
 
67
- In order to access Anthropic models on Bedrock you must require the `aws-sdk-bedrockruntime` gem.
68
- You can install it by adding the following to your Gemfile:
71
+ has_aws_credentials = !aws_access_key.nil? || !aws_secret_key.nil? || !aws_session_token.nil? || !aws_profile.nil?
72
+ if !api_key.nil? && has_aws_credentials
73
+ raise ArgumentError.new(
74
+ "Cannot specify both `api_key` and AWS credentials (`aws_access_key`, `aws_secret_key`, `aws_session_token`, `aws_profile`)"
75
+ )
76
+ end
69
77
 
70
- gem "aws-sdk-bedrockruntime"
78
+ if api_key.nil?
79
+ begin
80
+ require("aws-sdk-bedrockruntime")
81
+ rescue LoadError
82
+ message = <<~MSG
71
83
 
72
- and then running `bundle install`.
84
+ In order to access Anthropic models on Bedrock you must require the `aws-sdk-bedrockruntime` gem.
85
+ You can install it by adding the following to your Gemfile:
73
86
 
74
- Alternatively, if you are not using Bundler, simply run:
87
+ gem "aws-sdk-bedrockruntime"
75
88
 
76
- gem install aws-sdk-bedrockruntime
77
- MSG
89
+ and then running `bundle install`.
78
90
 
79
- raise RuntimeError.new(message)
80
- end
91
+ Alternatively, if you are not using Bundler, simply run:
81
92
 
82
- @aws_region, @aws_credentials = resolve_region_and_credentials(
83
- aws_region: aws_region,
84
- aws_secret_key: aws_secret_key,
85
- aws_access_key: aws_access_key,
86
- aws_session_token: aws_session_token,
87
- aws_profile: aws_profile
88
- )
93
+ gem install aws-sdk-bedrockruntime
94
+ MSG
89
95
 
90
- @signer = Aws::Sigv4::Signer.new(
91
- service: "bedrock",
92
- region: @aws_region,
93
- credentials: @aws_credentials
94
- )
96
+ raise RuntimeError.new(message)
97
+ end
98
+
99
+ @aws_region, @aws_credentials = resolve_region_and_credentials(
100
+ aws_region: aws_region,
101
+ aws_secret_key: aws_secret_key,
102
+ aws_access_key: aws_access_key,
103
+ aws_session_token: aws_session_token,
104
+ aws_profile: aws_profile
105
+ )
106
+
107
+ @signer = Aws::Sigv4::Signer.new(
108
+ service: "bedrock",
109
+ region: @aws_region,
110
+ credentials: @aws_credentials
111
+ )
112
+ else
113
+ @aws_region = aws_region
114
+ end
95
115
 
96
116
  base_url ||= ENV.fetch(
97
117
  "ANTHROPIC_BEDROCK_BASE_URL",
@@ -99,6 +119,8 @@ module Anthropic
99
119
  )
100
120
 
101
121
  super(
122
+ api_key: nil,
123
+ auth_token: api_key,
102
124
  base_url: base_url,
103
125
  timeout: timeout,
104
126
  max_retries: max_retries,
@@ -172,6 +194,8 @@ module Anthropic
172
194
  #
173
195
  # @return [Hash{Symbol, Object}]
174
196
  private def transform_request(request)
197
+ return request if @auth_token
198
+
175
199
  headers = request.fetch(:headers)
176
200
  sliced = super.slice(:method, :url, :body).transform_keys(method: :http_method)
177
201
  signed = @signer.sign_request({**sliced, headers: headers})
@@ -157,7 +157,7 @@ module Anthropic
157
157
  in Hash | nil => coerced
158
158
  coerced
159
159
  else
160
- message = "Expected a #{Hash} or #{Anthropic::Internal::Type::BaseModel}, got #{data.inspect}"
160
+ message = "Expected a #{Hash} or #{Anthropic::Internal::Type::BaseModel}, got #{input.inspect}"
161
161
  raise ArgumentError.new(message)
162
162
  end
163
163
  end
@@ -237,6 +237,11 @@ module Anthropic
237
237
  end
238
238
  end
239
239
 
240
+ # @type [Regexp]
241
+ #
242
+ # https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
243
+ RFC_3986_NOT_PCHARS = /[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/
244
+
240
245
  class << self
241
246
  # @api private
242
247
  #
@@ -247,6 +252,15 @@ module Anthropic
247
252
  "#{uri.scheme}://#{uri.host}#{":#{uri.port}" unless uri.port == uri.default_port}"
248
253
  end
249
254
 
255
+ # @api private
256
+ #
257
+ # @param path [String, Integer]
258
+ #
259
+ # @return [String]
260
+ def encode_path(path)
261
+ path.to_s.gsub(Anthropic::Internal::Util::RFC_3986_NOT_PCHARS) { ERB::Util.url_encode(_1) }
262
+ end
263
+
250
264
  # @api private
251
265
  #
252
266
  # @param path [String, Array<String>]
@@ -259,7 +273,7 @@ module Anthropic
259
273
  in []
260
274
  ""
261
275
  in [String => p, *interpolations]
262
- encoded = interpolations.map { ERB::Util.url_encode(_1) }
276
+ encoded = interpolations.map { encode_path(_1) }
263
277
  format(p, *encoded)
264
278
  end
265
279
  end
@@ -571,16 +585,15 @@ module Anthropic
571
585
  y << "Content-Disposition: form-data"
572
586
 
573
587
  unless key.nil?
574
- name = ERB::Util.url_encode(key.to_s)
575
- y << "; name=\"#{name}\""
588
+ y << "; name=\"#{key}\""
576
589
  end
577
590
 
578
591
  case val
579
592
  in Anthropic::FilePart unless val.filename.nil?
580
- filename = ERB::Util.url_encode(val.filename)
593
+ filename = encode_path(val.filename)
581
594
  y << "; filename=\"#{filename}\""
582
595
  in Pathname | IO
583
- filename = ERB::Util.url_encode(::File.basename(val.to_path))
596
+ filename = encode_path(::File.basename(val.to_path))
584
597
  y << "; filename=\"#{filename}\""
585
598
  else
586
599
  end
@@ -47,6 +47,8 @@ module Anthropic
47
47
 
48
48
  variant const: -> { Anthropic::Models::AnthropicBeta::FAST_MODE_2026_02_01 }
49
49
 
50
+ variant const: -> { Anthropic::Models::AnthropicBeta::OUTPUT_300K_2026_03_24 }
51
+
50
52
  # @!method self.variants
51
53
  # @return [Array(String, Symbol)]
52
54
 
@@ -76,6 +78,7 @@ module Anthropic
76
78
  MODEL_CONTEXT_WINDOW_EXCEEDED_2025_08_26 = :"model-context-window-exceeded-2025-08-26"
77
79
  SKILLS_2025_10_02 = :"skills-2025-10-02"
78
80
  FAST_MODE_2026_02_01 = :"fast-mode-2026-02-01"
81
+ OUTPUT_300K_2026_03_24 = :"output-300k-2026-03-24"
79
82
 
80
83
  # @!endgroup
81
84
  end
@@ -95,6 +95,12 @@ module Anthropic
95
95
  # @return [Symbol, :assistant]
96
96
  required :role, const: :assistant
97
97
 
98
+ # @!attribute stop_details
99
+ # Structured information about a refusal.
100
+ #
101
+ # @return [Anthropic::Models::Beta::BetaRefusalStopDetails, nil]
102
+ required :stop_details, -> { Anthropic::Beta::BetaRefusalStopDetails }, nil?: true
103
+
98
104
  # @!attribute stop_reason
99
105
  # The reason that we stopped.
100
106
  #
@@ -152,7 +158,7 @@ module Anthropic
152
158
  # @return [Anthropic::Models::Beta::BetaUsage]
153
159
  required :usage, -> { Anthropic::Beta::BetaUsage }
154
160
 
155
- # @!method initialize(id:, container:, content:, context_management:, model:, stop_reason:, stop_sequence:, usage:, role: :assistant, type: :message)
161
+ # @!method initialize(id:, container:, content:, context_management:, model:, stop_details:, stop_reason:, stop_sequence:, usage:, role: :assistant, type: :message)
156
162
  # Some parameter documentations has been truncated, see
157
163
  # {Anthropic::Models::Beta::BetaMessage} for more details.
158
164
  #
@@ -166,6 +172,8 @@ module Anthropic
166
172
  #
167
173
  # @param model [Symbol, String, Anthropic::Models::Model] The model that will complete your prompt.\n\nSee [models](https://docs.anthropic
168
174
  #
175
+ # @param stop_details [Anthropic::Models::Beta::BetaRefusalStopDetails, nil] Structured information about a refusal.
176
+ #
169
177
  # @param stop_reason [Symbol, Anthropic::Models::Beta::BetaStopReason, nil] The reason that we stopped.
170
178
  #
171
179
  # @param stop_sequence [String, nil] Which custom stop sequence was generated, if any.