mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-08-01 19:34:24 +03:00
feat(llm): support Bedrock API-key auth via AWS_BEARER_TOKEN_BEDROCK
- pass the token to ChatBedrockConverse as api_key so langchain-aws prefers bearer auth and an ambient AWS_PROFILE can't override it; no AWS access keys required #1103
This commit is contained in:
@@ -35,12 +35,46 @@ def test_helpful_error_when_langchain_aws_absent(monkeypatch):
|
||||
create_llm_client("bedrock", "m").get_llm()
|
||||
|
||||
|
||||
def _capture_kwargs(monkeypatch):
|
||||
"""Stub _bedrock_class so the constructor kwargs are testable without the
|
||||
optional langchain-aws extra installed."""
|
||||
import tradingagents.llm_clients.bedrock_client as bc
|
||||
captured = {}
|
||||
|
||||
class _FakeChat:
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
|
||||
monkeypatch.setattr(bc, "_bedrock_class", lambda: _FakeChat)
|
||||
return captured
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_bearer_token_passed_as_api_key(monkeypatch):
|
||||
# #1103: a Bedrock API key authenticates without AWS access keys.
|
||||
captured = _capture_kwargs(monkeypatch)
|
||||
monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "bt-secret")
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", "us-east-1")
|
||||
create_llm_client("bedrock", "us.anthropic.claude-opus-4-8-v1:0").get_llm()
|
||||
assert captured["api_key"] == "bt-secret"
|
||||
assert captured["region_name"] == "us-east-1"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_no_bearer_token_omits_api_key(monkeypatch):
|
||||
# Without a token, fall back to the AWS credential chain (no api_key kwarg).
|
||||
captured = _capture_kwargs(monkeypatch)
|
||||
monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False)
|
||||
create_llm_client("bedrock", "us.anthropic.claude-opus-4-8-v1:0").get_llm()
|
||||
assert "api_key" not in captured
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_construction_when_extra_installed(monkeypatch):
|
||||
pytest.importorskip("langchain_aws")
|
||||
import tradingagents.llm_clients.bedrock_client as bc
|
||||
monkeypatch.setattr(bc, "_BEDROCK_CLASS", None)
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", "eu-west-1")
|
||||
llm = create_llm_client("bedrock", "us.anthropic.claude-sonnet-4-6-v1:0").get_llm()
|
||||
llm = create_llm_client("bedrock", "us.anthropic.claude-sonnet-5").get_llm()
|
||||
assert type(llm).__name__ == "NormalizedChatBedrockConverse"
|
||||
assert llm.region_name == "eu-west-1"
|
||||
|
||||
@@ -41,11 +41,12 @@ def _bedrock_class():
|
||||
class BedrockClient(BaseLLMClient):
|
||||
"""Client for Amazon Bedrock via the Converse API (langchain-aws).
|
||||
|
||||
Authentication uses the standard AWS credential chain (env vars,
|
||||
``~/.aws/credentials``, or an IAM role); set ``AWS_REGION`` /
|
||||
``AWS_DEFAULT_REGION`` and optionally ``AWS_PROFILE``. The model name is a
|
||||
Bedrock model ID or cross-region inference profile ID, e.g.
|
||||
``us.anthropic.claude-opus-4-8-v1:0``.
|
||||
Authentication is either a Bedrock API key (bearer token) via
|
||||
``AWS_BEARER_TOKEN_BEDROCK`` — no AWS access keys required — or the standard
|
||||
AWS credential chain (env vars, ``~/.aws/credentials``, or an IAM role) with
|
||||
optional ``AWS_PROFILE``. Set ``AWS_REGION`` / ``AWS_DEFAULT_REGION`` either
|
||||
way (the token carries no region). The model name is a Bedrock model ID or
|
||||
cross-region inference profile ID, e.g. ``us.anthropic.claude-opus-4-8-v1:0``.
|
||||
"""
|
||||
|
||||
def get_llm(self) -> Any:
|
||||
@@ -59,6 +60,12 @@ class BedrockClient(BaseLLMClient):
|
||||
or _DEFAULT_REGION
|
||||
)
|
||||
llm_kwargs = {"model": self.model, "region_name": region}
|
||||
# A Bedrock API key authenticates without AWS access keys. Passing it as
|
||||
# api_key makes langchain-aws prefer bearer auth, so an ambient
|
||||
# AWS_PROFILE / SigV4 credentials can't override it (#1103).
|
||||
bearer_token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK")
|
||||
if bearer_token:
|
||||
llm_kwargs["api_key"] = bearer_token
|
||||
for key in ("temperature", "max_tokens", "max_retries", "callbacks"):
|
||||
if key in self.kwargs:
|
||||
llm_kwargs[key] = self.kwargs[key]
|
||||
|
||||
Reference in New Issue
Block a user