mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(auth): use bearer auth for MiniMax Anthropic endpoints (#4028)
MiniMax's /anthropic endpoints implement Anthropic's Messages API but require Authorization: Bearer instead of x-api-key. Without this fix, MiniMax users get 401 errors in gateway sessions. Adds _requires_bearer_auth() to detect MiniMax endpoints and route through auth_token in the Anthropic SDK. Check runs before OAuth token detection so MiniMax keys aren't misclassified as setup tokens. Co-authored-by: kshitijk4poor <kshitijk4poor@users.noreply.github.com>
This commit is contained in:
@@ -162,6 +162,21 @@ def _is_oauth_token(key: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _requires_bearer_auth(base_url: str | None) -> bool:
|
||||||
|
"""Return True for Anthropic-compatible providers that require Bearer auth.
|
||||||
|
|
||||||
|
Some third-party /anthropic endpoints implement Anthropic's Messages API but
|
||||||
|
require Authorization: Bearer instead of Anthropic's native x-api-key header.
|
||||||
|
MiniMax's global and China Anthropic-compatible endpoints follow this pattern.
|
||||||
|
"""
|
||||||
|
if not base_url:
|
||||||
|
return False
|
||||||
|
normalized = base_url.rstrip("/").lower()
|
||||||
|
return normalized.startswith("https://api.minimax.io/anthropic") or normalized.startswith(
|
||||||
|
"https://api.minimaxi.com/anthropic"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_anthropic_client(api_key: str, base_url: str = None):
|
def build_anthropic_client(api_key: str, base_url: str = None):
|
||||||
"""Create an Anthropic client, auto-detecting setup-tokens vs API keys.
|
"""Create an Anthropic client, auto-detecting setup-tokens vs API keys.
|
||||||
|
|
||||||
@@ -180,7 +195,17 @@ def build_anthropic_client(api_key: str, base_url: str = None):
|
|||||||
if base_url:
|
if base_url:
|
||||||
kwargs["base_url"] = base_url
|
kwargs["base_url"] = base_url
|
||||||
|
|
||||||
if _is_oauth_token(api_key):
|
if _requires_bearer_auth(base_url):
|
||||||
|
# Some Anthropic-compatible providers (e.g. MiniMax) expect the API key in
|
||||||
|
# Authorization: Bearer even for regular API keys. Route those endpoints
|
||||||
|
# through auth_token so the SDK sends Bearer auth instead of x-api-key.
|
||||||
|
# Check this before OAuth token shape detection because MiniMax secrets do
|
||||||
|
# not use Anthropic's sk-ant-api prefix and would otherwise be misread as
|
||||||
|
# Anthropic OAuth/setup tokens.
|
||||||
|
kwargs["auth_token"] = api_key
|
||||||
|
if _COMMON_BETAS:
|
||||||
|
kwargs["default_headers"] = {"anthropic-beta": ",".join(_COMMON_BETAS)}
|
||||||
|
elif _is_oauth_token(api_key):
|
||||||
# OAuth access token / setup-token → Bearer auth + Claude Code identity.
|
# OAuth access token / setup-token → Bearer auth + Claude Code identity.
|
||||||
# Anthropic routes OAuth requests based on user-agent and headers;
|
# Anthropic routes OAuth requests based on user-agent and headers;
|
||||||
# without Claude Code's fingerprint, requests get intermittent 500s.
|
# without Claude Code's fingerprint, requests get intermittent 500s.
|
||||||
|
|||||||
@@ -81,6 +81,19 @@ class TestBuildAnthropicClient:
|
|||||||
kwargs = mock_sdk.Anthropic.call_args[1]
|
kwargs = mock_sdk.Anthropic.call_args[1]
|
||||||
assert kwargs["base_url"] == "https://custom.api.com"
|
assert kwargs["base_url"] == "https://custom.api.com"
|
||||||
|
|
||||||
|
def test_minimax_anthropic_endpoint_uses_bearer_auth_for_regular_api_keys(self):
|
||||||
|
with patch("agent.anthropic_adapter._anthropic_sdk") as mock_sdk:
|
||||||
|
build_anthropic_client(
|
||||||
|
"minimax-secret-123",
|
||||||
|
base_url="https://api.minimax.io/anthropic",
|
||||||
|
)
|
||||||
|
kwargs = mock_sdk.Anthropic.call_args[1]
|
||||||
|
assert kwargs["auth_token"] == "minimax-secret-123"
|
||||||
|
assert "api_key" not in kwargs
|
||||||
|
assert kwargs["default_headers"] == {
|
||||||
|
"anthropic-beta": "interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestReadClaudeCodeCredentials:
|
class TestReadClaudeCodeCredentials:
|
||||||
def test_reads_valid_credentials(self, tmp_path, monkeypatch):
|
def test_reads_valid_credentials(self, tmp_path, monkeypatch):
|
||||||
|
|||||||
Reference in New Issue
Block a user