fix(auxiliary): propagate explicit_api_key to _try_anthropic()

_try_anthropic() lacked the explicit_api_key parameter added to
_try_openrouter() in #18768. When resolve_provider_client() is called
with provider="anthropic" and an explicit key (e.g. from a fallback_model
entry with api_key set), the key was silently ignored — _try_anthropic()
always fell back to resolve_anthropic_token(), so the fallback returned
None,None for users without a default Anthropic credential configured.

Fix: add explicit_api_key: str = None to _try_anthropic() and use
explicit_api_key or <pool/env fallback> in both the pool-present and
no-pool paths. Pass explicit_api_key=explicit_api_key at the call site
in resolve_provider_client(). Symmetric with the _try_openrouter() fix.
No behavior change when explicit_api_key is None.
This commit is contained in:
nftpoetrist 2026-05-04 00:12:58 +03:00 committed by Teknium
parent 74636f9c4a
commit 808fee151d
2 changed files with 54 additions and 4 deletions

View file

@ -1529,7 +1529,7 @@ def _build_codex_client(model: str) -> Tuple[Optional[Any], Optional[str]]:
return CodexAuxiliaryClient(real_client, model), model return CodexAuxiliaryClient(real_client, model), model
def _try_anthropic() -> Tuple[Optional[Any], Optional[str]]: def _try_anthropic(explicit_api_key: str = None) -> Tuple[Optional[Any], Optional[str]]:
try: try:
from agent.anthropic_adapter import build_anthropic_client, resolve_anthropic_token from agent.anthropic_adapter import build_anthropic_client, resolve_anthropic_token
except ImportError: except ImportError:
@ -1539,10 +1539,10 @@ def _try_anthropic() -> Tuple[Optional[Any], Optional[str]]:
if pool_present: if pool_present:
if entry is None: if entry is None:
return None, None return None, None
token = _pool_runtime_api_key(entry) token = explicit_api_key or _pool_runtime_api_key(entry)
else: else:
entry = None entry = None
token = resolve_anthropic_token() token = explicit_api_key or resolve_anthropic_token()
if not token: if not token:
return None, None return None, None
@ -2336,7 +2336,7 @@ def resolve_provider_client(
if pconfig.auth_type == "api_key": if pconfig.auth_type == "api_key":
if provider == "anthropic": if provider == "anthropic":
client, default_model = _try_anthropic() client, default_model = _try_anthropic(explicit_api_key=explicit_api_key)
if client is None: if client is None:
logger.warning("resolve_provider_client: anthropic requested but no Anthropic credentials found") logger.warning("resolve_provider_client: anthropic requested but no Anthropic credentials found")
return None, None return None, None

View file

@ -1893,3 +1893,53 @@ class TestOpenRouterExplicitApiKey:
assert call_kwargs["api_key"] == "env-fallback-key", ( assert call_kwargs["api_key"] == "env-fallback-key", (
f"Expected env fallback key to be used when explicit_api_key is None, got: {call_kwargs['api_key']}" f"Expected env fallback key to be used when explicit_api_key is None, got: {call_kwargs['api_key']}"
) )
class TestAnthropicExplicitApiKey:
"""Test that explicit_api_key is correctly propagated to _try_anthropic().
Parity with the OpenRouter fix in #18768: resolve_provider_client() passes
explicit_api_key to _try_openrouter(), but the anthropic branch was not
updated _try_anthropic() always fell back to resolve_anthropic_token()
even when an explicit key was supplied (e.g. from a fallback_model entry).
"""
def test_try_anthropic_uses_explicit_api_key_over_env(self):
"""_try_anthropic(explicit_api_key) must use the supplied key, not the env fallback."""
with patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="env-fallback-key"), \
patch("agent.anthropic_adapter.build_anthropic_client") as mock_build, \
patch("agent.auxiliary_client._select_pool_entry", return_value=(False, None)):
mock_build.return_value = MagicMock()
from agent.auxiliary_client import _try_anthropic
client, model = _try_anthropic("explicit-pool-key")
assert client is not None
assert mock_build.call_args.args[0] == "explicit-pool-key", (
f"Expected explicit_api_key to be passed, got: {mock_build.call_args.args[0]}"
)
assert mock_build.call_args.args[0] != "env-fallback-key"
def test_try_anthropic_without_explicit_key_falls_back_to_resolve(self):
"""Without explicit_api_key, _try_anthropic falls back to resolve_anthropic_token."""
with patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="env-fallback-key"), \
patch("agent.anthropic_adapter.build_anthropic_client") as mock_build, \
patch("agent.auxiliary_client._select_pool_entry", return_value=(False, None)):
mock_build.return_value = MagicMock()
from agent.auxiliary_client import _try_anthropic
client, model = _try_anthropic()
assert client is not None
assert mock_build.call_args.args[0] == "env-fallback-key"
def test_resolve_provider_client_passes_explicit_api_key_to_anthropic(self):
"""resolve_provider_client(provider='anthropic', explicit_api_key=...) must propagate the key."""
with patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="env-key"), \
patch("agent.anthropic_adapter.build_anthropic_client") as mock_build, \
patch("agent.auxiliary_client._select_pool_entry", return_value=(False, None)):
mock_build.return_value = MagicMock()
client, model = resolve_provider_client(
provider="anthropic",
explicit_api_key="explicit-fallback-key",
)
assert client is not None
assert mock_build.call_args.args[0] == "explicit-fallback-key", (
"resolve_provider_client must forward explicit_api_key to _try_anthropic()"
)