mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(agent): handle aws_sdk auth type in resolve_provider_client
Bedrock's aws_sdk auth_type had no matching branch in resolve_provider_client(), causing it to fall through to the "unhandled auth_type" warning and return (None, None). This broke all auxiliary tasks (compression, memory, summarization) for Bedrock users — the main conversation loop worked fine, but background context management silently failed. Add an aws_sdk branch that creates an AnthropicAuxiliaryClient via build_anthropic_bedrock_client(), using boto3's default credential chain (IAM roles, SSO, env vars, instance metadata). Default auxiliary model is Haiku for cost efficiency. Closes #13919
This commit is contained in:
parent
b290297d66
commit
7dc6eb9fbf
2 changed files with 128 additions and 0 deletions
|
|
@ -1993,6 +1993,39 @@ def resolve_provider_client(
|
||||||
"directly supported", provider)
|
"directly supported", provider)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
elif pconfig.auth_type == "aws_sdk":
|
||||||
|
# AWS SDK providers (Bedrock) — use the Anthropic Bedrock client via
|
||||||
|
# boto3's credential chain (IAM roles, SSO, env vars, instance metadata).
|
||||||
|
try:
|
||||||
|
from agent.bedrock_adapter import has_aws_credentials, resolve_bedrock_region
|
||||||
|
from agent.anthropic_adapter import build_anthropic_bedrock_client
|
||||||
|
except ImportError:
|
||||||
|
logger.warning("resolve_provider_client: bedrock requested but "
|
||||||
|
"boto3 or anthropic SDK not installed")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
if not has_aws_credentials():
|
||||||
|
logger.debug("resolve_provider_client: bedrock requested but "
|
||||||
|
"no AWS credentials found")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
region = resolve_bedrock_region()
|
||||||
|
default_model = "anthropic.claude-haiku-4-5-20251001-v1:0"
|
||||||
|
final_model = _normalize_resolved_model(model or default_model, provider)
|
||||||
|
try:
|
||||||
|
real_client = build_anthropic_bedrock_client(region)
|
||||||
|
except ImportError as exc:
|
||||||
|
logger.warning("resolve_provider_client: cannot create Bedrock "
|
||||||
|
"client: %s", exc)
|
||||||
|
return None, None
|
||||||
|
client = AnthropicAuxiliaryClient(
|
||||||
|
real_client, final_model, api_key="aws-sdk",
|
||||||
|
base_url=f"https://bedrock-runtime.{region}.amazonaws.com",
|
||||||
|
)
|
||||||
|
logger.debug("resolve_provider_client: bedrock (%s, %s)", final_model, region)
|
||||||
|
return (_to_async_client(client, final_model) if async_mode
|
||||||
|
else (client, final_model))
|
||||||
|
|
||||||
elif pconfig.auth_type in ("oauth_device_code", "oauth_external"):
|
elif pconfig.auth_type in ("oauth_device_code", "oauth_external"):
|
||||||
# OAuth providers — route through their specific try functions
|
# OAuth providers — route through their specific try functions
|
||||||
if provider == "nous":
|
if provider == "nous":
|
||||||
|
|
|
||||||
|
|
@ -493,3 +493,98 @@ class TestBedrockModelIdDetection:
|
||||||
preserve_dots=False,
|
preserve_dots=False,
|
||||||
)
|
)
|
||||||
assert kwargs["model"] == "anthropic.claude-opus-4-7"
|
assert kwargs["model"] == "anthropic.claude-opus-4-7"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# auxiliary_client Bedrock resolution — fix for #13919
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Before the fix, resolve_provider_client("bedrock", ...) fell through to the
|
||||||
|
# "unhandled auth_type" warning and returned (None, None), breaking all
|
||||||
|
# auxiliary tasks (compression, memory, summarization) for Bedrock users.
|
||||||
|
|
||||||
|
|
||||||
|
class TestAuxiliaryClientBedrockResolution:
|
||||||
|
"""Verify resolve_provider_client handles Bedrock's aws_sdk auth type."""
|
||||||
|
|
||||||
|
def test_bedrock_returns_client_with_credentials(self, monkeypatch):
|
||||||
|
"""With valid AWS credentials, Bedrock should return a usable client."""
|
||||||
|
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")
|
||||||
|
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
|
||||||
|
monkeypatch.setenv("AWS_REGION", "us-west-2")
|
||||||
|
|
||||||
|
mock_anthropic_bedrock = MagicMock()
|
||||||
|
with patch("agent.anthropic_adapter.build_anthropic_bedrock_client",
|
||||||
|
return_value=mock_anthropic_bedrock):
|
||||||
|
from agent.auxiliary_client import resolve_provider_client, AnthropicAuxiliaryClient
|
||||||
|
client, model = resolve_provider_client("bedrock", None)
|
||||||
|
|
||||||
|
assert client is not None, (
|
||||||
|
"resolve_provider_client('bedrock') returned None — "
|
||||||
|
"aws_sdk auth type is not handled"
|
||||||
|
)
|
||||||
|
assert isinstance(client, AnthropicAuxiliaryClient)
|
||||||
|
assert model is not None
|
||||||
|
assert client.api_key == "aws-sdk"
|
||||||
|
assert "us-west-2" in client.base_url
|
||||||
|
|
||||||
|
def test_bedrock_returns_none_without_credentials(self, monkeypatch):
|
||||||
|
"""Without AWS credentials, Bedrock should return (None, None) gracefully."""
|
||||||
|
with patch("agent.bedrock_adapter.has_aws_credentials", return_value=False):
|
||||||
|
from agent.auxiliary_client import resolve_provider_client
|
||||||
|
client, model = resolve_provider_client("bedrock", None)
|
||||||
|
|
||||||
|
assert client is None
|
||||||
|
assert model is None
|
||||||
|
|
||||||
|
def test_bedrock_uses_configured_region(self, monkeypatch):
|
||||||
|
"""Bedrock client base_url should reflect AWS_REGION."""
|
||||||
|
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")
|
||||||
|
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
|
||||||
|
monkeypatch.setenv("AWS_REGION", "eu-central-1")
|
||||||
|
|
||||||
|
with patch("agent.anthropic_adapter.build_anthropic_bedrock_client",
|
||||||
|
return_value=MagicMock()):
|
||||||
|
from agent.auxiliary_client import resolve_provider_client
|
||||||
|
client, _ = resolve_provider_client("bedrock", None)
|
||||||
|
|
||||||
|
assert client is not None
|
||||||
|
assert "eu-central-1" in client.base_url
|
||||||
|
|
||||||
|
def test_bedrock_respects_explicit_model(self, monkeypatch):
|
||||||
|
"""When caller passes an explicit model, it should be used."""
|
||||||
|
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")
|
||||||
|
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
|
||||||
|
|
||||||
|
with patch("agent.anthropic_adapter.build_anthropic_bedrock_client",
|
||||||
|
return_value=MagicMock()):
|
||||||
|
from agent.auxiliary_client import resolve_provider_client
|
||||||
|
_, model = resolve_provider_client(
|
||||||
|
"bedrock", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "claude-sonnet" in model
|
||||||
|
|
||||||
|
def test_bedrock_async_mode(self, monkeypatch):
|
||||||
|
"""Async mode should return an AsyncAnthropicAuxiliaryClient."""
|
||||||
|
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")
|
||||||
|
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
|
||||||
|
|
||||||
|
with patch("agent.anthropic_adapter.build_anthropic_bedrock_client",
|
||||||
|
return_value=MagicMock()):
|
||||||
|
from agent.auxiliary_client import resolve_provider_client, AsyncAnthropicAuxiliaryClient
|
||||||
|
client, model = resolve_provider_client("bedrock", None, async_mode=True)
|
||||||
|
|
||||||
|
assert client is not None
|
||||||
|
assert isinstance(client, AsyncAnthropicAuxiliaryClient)
|
||||||
|
|
||||||
|
def test_bedrock_default_model_is_haiku(self, monkeypatch):
|
||||||
|
"""Default auxiliary model for Bedrock should be Haiku (fast, cheap)."""
|
||||||
|
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")
|
||||||
|
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
|
||||||
|
|
||||||
|
with patch("agent.anthropic_adapter.build_anthropic_bedrock_client",
|
||||||
|
return_value=MagicMock()):
|
||||||
|
from agent.auxiliary_client import resolve_provider_client
|
||||||
|
_, model = resolve_provider_client("bedrock", None)
|
||||||
|
|
||||||
|
assert "haiku" in model.lower()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue