Skip to content

Commit 8b32960

Browse files
committed
Add bearer token authentication for AWS Bedrock API keys
AWS Bedrock supports API key authentication via bearer tokens (Authorization: Bearer <token>), as an alternative to SigV4 signing. See: https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-use.html The existing Anthropic::BedrockClient only supports SigV4, so this adds a BearerClient that subclasses Anthropic::Client directly, reusing its built-in auth_token support while copying the Bedrock-specific request transformations (URL path rewriting, anthropic_version injection). The provider now checks for a bearer token first (via aws_bearer_token option or AWS_BEARER_TOKEN_BEDROCK env var) and falls back to the existing SigV4 path when no token is present. Also adds extra_headers and anthropic_beta to Bedrock::Options, which are required by the inherited AnthropicProvider at runtime.
1 parent d5cc97d commit 8b32960

File tree

4 files changed

+149
-16
lines changed

4 files changed

+149
-16
lines changed

lib/active_agent/providers/bedrock/_types.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative "options"
4+
require_relative "bearer_client"
45
require_relative "../anthropic/_types"
56

67
# Bedrock uses the same request/response types as Anthropic.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveAgent
4+
module Providers
5+
module Bedrock
6+
# Client for AWS Bedrock using bearer token (API key) authentication.
7+
#
8+
# Subclasses Anthropic::Client directly to reuse its built-in bearer
9+
# token support via the +auth_token+ parameter, while adding Bedrock-
10+
# specific request transformations (URL path rewriting, anthropic_version
11+
# injection) copied from Anthropic::BedrockClient.
12+
#
13+
# This avoids Anthropic::BedrockClient which requires SigV4 credentials
14+
# and would fail when only a bearer token is available.
15+
#
16+
# @see https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-use.html
17+
class BearerClient < ::Anthropic::Client
18+
BEDROCK_VERSION = "bedrock-2023-05-31"
19+
20+
# @return [String]
21+
attr_reader :aws_region
22+
23+
# @param aws_region [String] AWS region for the Bedrock endpoint
24+
# @param bearer_token [String] AWS Bedrock API key (bearer token)
25+
# @param base_url [String, nil] Override the default Bedrock endpoint
26+
# @param max_retries [Integer]
27+
# @param timeout [Float]
28+
# @param initial_retry_delay [Float]
29+
# @param max_retry_delay [Float]
30+
def initialize(
31+
aws_region:,
32+
bearer_token:,
33+
base_url: nil,
34+
max_retries: self.class::DEFAULT_MAX_RETRIES,
35+
timeout: self.class::DEFAULT_TIMEOUT_IN_SECONDS,
36+
initial_retry_delay: self.class::DEFAULT_INITIAL_RETRY_DELAY,
37+
max_retry_delay: self.class::DEFAULT_MAX_RETRY_DELAY
38+
)
39+
@aws_region = aws_region
40+
41+
base_url ||= "https://bedrock-runtime.#{aws_region}.amazonaws.com"
42+
43+
super(
44+
auth_token: bearer_token,
45+
api_key: nil,
46+
base_url: base_url,
47+
max_retries: max_retries,
48+
timeout: timeout,
49+
initial_retry_delay: initial_retry_delay,
50+
max_retry_delay: max_retry_delay
51+
)
52+
53+
@messages = ::Anthropic::Resources::Messages.new(client: self)
54+
@completions = ::Anthropic::Resources::Completions.new(client: self)
55+
@beta = ::Anthropic::Resources::Beta.new(client: self)
56+
end
57+
58+
private
59+
60+
# Intercepts request building to apply Bedrock-specific transformations
61+
# before the parent class processes the request.
62+
def build_request(req, opts)
63+
fit_req_to_bedrock_specs!(req)
64+
req = super
65+
body = req.fetch(:body)
66+
req[:body] = StringIO.new(body.to_a.join) if body.is_a?(Enumerator)
67+
req
68+
end
69+
70+
# Rewrites Anthropic API paths to Bedrock endpoint paths and injects
71+
# the Bedrock anthropic_version field.
72+
#
73+
# Adapted from Anthropic::Helpers::Bedrock::Client#fit_req_to_bedrock_specs!
74+
def fit_req_to_bedrock_specs!(request_components)
75+
if (body = request_components[:body]).is_a?(Hash)
76+
body[:anthropic_version] ||= BEDROCK_VERSION
77+
body.transform_keys!("anthropic-beta": :anthropic_beta)
78+
end
79+
80+
case request_components[:path]
81+
in %r{^v1/messages/batches}
82+
raise NotImplementedError, "The Batch API is not supported in Bedrock yet"
83+
in %r{v1/messages/count_tokens}
84+
raise NotImplementedError, "Token counting is not supported in Bedrock yet"
85+
in %r{v1/models\?beta=true}
86+
raise NotImplementedError,
87+
"Please instead use https://docs.anthropic.com/en/api/claude-on-amazon-bedrock#list-available-models " \
88+
"to list available models on Bedrock."
89+
else
90+
end
91+
92+
if %w[
93+
v1/complete
94+
v1/messages
95+
v1/messages?beta=true
96+
].include?(request_components[:path]) && request_components[:method] == :post && body.is_a?(Hash)
97+
model = body.delete(:model)
98+
model = URI.encode_www_form_component(model.to_s)
99+
stream = body.delete(:stream) || false
100+
request_components[:path] =
101+
stream ? "model/#{model}/invoke-with-response-stream" : "model/#{model}/invoke"
102+
end
103+
104+
request_components
105+
end
106+
end
107+
end
108+
end
109+
end

lib/active_agent/providers/bedrock/options.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ class Options < Common::BaseModel
3636
attribute :aws_secret_key, :string
3737
attribute :aws_session_token, :string
3838
attribute :aws_profile, :string
39+
attribute :aws_bearer_token, :string
3940
attribute :base_url, :string
41+
attribute :anthropic_beta, :string
4042

4143
attribute :max_retries, :integer, default: ::Anthropic::Client::DEFAULT_MAX_RETRIES
4244
attribute :timeout, :float, default: ::Anthropic::Client::DEFAULT_TIMEOUT_IN_SECONDS
@@ -51,15 +53,22 @@ def initialize(kwargs = {})
5153
aws_access_key: kwargs[:aws_access_key] || ENV["AWS_ACCESS_KEY_ID"],
5254
aws_secret_key: kwargs[:aws_secret_key] || ENV["AWS_SECRET_ACCESS_KEY"],
5355
aws_session_token: kwargs[:aws_session_token] || ENV["AWS_SESSION_TOKEN"],
54-
aws_profile: kwargs[:aws_profile] || ENV["AWS_PROFILE"]
56+
aws_profile: kwargs[:aws_profile] || ENV["AWS_PROFILE"],
57+
aws_bearer_token: kwargs[:aws_bearer_token] || ENV["AWS_BEARER_TOKEN_BEDROCK"]
5558
)))
5659
end
5760

61+
# Bedrock handles authentication at the client level (SigV4 or bearer token),
62+
# so no extra headers are needed in request options.
63+
def extra_headers
64+
{}
65+
end
66+
5867
# Excludes sensitive AWS credentials from serialized output.
5968
# The provider's client() method reads credentials directly from options attributes.
6069
def serialize
6170
attributes.symbolize_keys.except(
62-
:aws_access_key, :aws_secret_key, :aws_session_token, :aws_profile
71+
:aws_access_key, :aws_secret_key, :aws_session_token, :aws_profile, :aws_bearer_token
6372
)
6473
end
6574
end

lib/active_agent/providers/bedrock_provider.rb

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,37 @@ def self.prompt_request_type
4343
Anthropic::RequestType.new
4444
end
4545

46-
# Returns a configured Bedrock client using AWS credentials.
46+
# Returns a configured Bedrock client.
4747
#
48-
# Uses Anthropic::BedrockClient which handles SigV4 signing,
49-
# credential resolution, and Bedrock URL path rewriting internally.
48+
# When a bearer token is available (via +aws_bearer_token+ option or
49+
# +AWS_BEARER_TOKEN_BEDROCK+ env var), uses {Bedrock::BearerClient}
50+
# which sends an +Authorization: Bearer+ header.
5051
#
51-
# @return [Anthropic::Helpers::Bedrock::Client]
52+
# Otherwise, falls back to {Anthropic::BedrockClient} which handles
53+
# SigV4 signing, credential resolution, and Bedrock URL path rewriting.
54+
#
55+
# @return [Bedrock::BearerClient, Anthropic::Helpers::Bedrock::Client]
5256
def client
53-
@client ||= ::Anthropic::BedrockClient.new(
54-
aws_region: options.aws_region,
55-
aws_access_key: options.aws_access_key,
56-
aws_secret_key: options.aws_secret_key,
57-
aws_session_token: options.aws_session_token,
58-
aws_profile: options.aws_profile,
59-
base_url: options.base_url.presence,
60-
max_retries: options.max_retries,
61-
timeout: options.timeout
62-
)
57+
@client ||= if options.aws_bearer_token.present?
58+
Bedrock::BearerClient.new(
59+
aws_region: options.aws_region,
60+
bearer_token: options.aws_bearer_token,
61+
base_url: options.base_url.presence,
62+
max_retries: options.max_retries,
63+
timeout: options.timeout
64+
)
65+
else
66+
::Anthropic::BedrockClient.new(
67+
aws_region: options.aws_region,
68+
aws_access_key: options.aws_access_key,
69+
aws_secret_key: options.aws_secret_key,
70+
aws_session_token: options.aws_session_token,
71+
aws_profile: options.aws_profile,
72+
base_url: options.base_url.presence,
73+
max_retries: options.max_retries,
74+
timeout: options.timeout
75+
)
76+
end
6377
end
6478
end
6579
end

0 commit comments

Comments
 (0)