Skip to content

Commit d409a44

Browse files
helix4ukshitijk4poor
authored andcommitted
fix(model): avoid bedrock credential probe in provider picker
1 parent 5d3be89 commit d409a44

2 files changed

Lines changed: 67 additions & 6 deletions

File tree

hermes_cli/model_switch.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,45 @@ def _record_builtin_endpoint(slug: str) -> None:
10571057
if normed:
10581058
_builtin_endpoints.add(normed)
10591059

1060+
def _has_fast_aws_sdk_signal() -> bool:
1061+
"""Return True when explicit AWS auth config is present.
1062+
1063+
This intentionally avoids botocore's full credential chain. Provider
1064+
picker/model-switch discovery can run for non-Bedrock providers, and
1065+
botocore may otherwise probe EC2 IMDS (169.254.169.254) on local
1066+
machines before returning no credentials.
1067+
"""
1068+
if os.environ.get("AWS_BEARER_TOKEN_BEDROCK", "").strip():
1069+
return True
1070+
if (
1071+
os.environ.get("AWS_ACCESS_KEY_ID", "").strip()
1072+
and os.environ.get("AWS_SECRET_ACCESS_KEY", "").strip()
1073+
):
1074+
return True
1075+
return any(
1076+
os.environ.get(name, "").strip()
1077+
for name in (
1078+
"AWS_PROFILE",
1079+
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI",
1080+
"AWS_CONTAINER_CREDENTIALS_FULL_URI",
1081+
"AWS_WEB_IDENTITY_TOKEN_FILE",
1082+
)
1083+
)
1084+
1085+
def _has_aws_sdk_creds_for_listing(slug: str) -> bool:
1086+
"""Credential check for AWS SDK providers in non-runtime discovery."""
1087+
slug_norm = str(slug or "").strip().lower()
1088+
current_norm = str(current_provider or "").strip().lower()
1089+
if _has_fast_aws_sdk_signal():
1090+
return True
1091+
if slug_norm != current_norm:
1092+
return False
1093+
try:
1094+
from agent.bedrock_adapter import has_aws_credentials
1095+
return bool(has_aws_credentials())
1096+
except Exception:
1097+
return False
1098+
10601099
data = fetch_models_dev()
10611100

10621101
# Build curated model lists keyed by hermes provider ID
@@ -1184,7 +1223,9 @@ def _record_builtin_endpoint(slug: str) -> None:
11841223

11851224
# Check if credentials exist
11861225
has_creds = False
1187-
if overlay.extra_env_vars:
1226+
if overlay.auth_type == "aws_sdk":
1227+
has_creds = _has_aws_sdk_creds_for_listing(hermes_slug)
1228+
elif overlay.extra_env_vars:
11881229
has_creds = any(os.environ.get(ev) for ev in overlay.extra_env_vars)
11891230
# Also check api_key_env_vars from PROVIDER_REGISTRY for api_key auth_type
11901231
if not has_creds and overlay.auth_type == "api_key":
@@ -1324,11 +1365,7 @@ def _record_builtin_endpoint(slug: str) -> None:
13241365
# credentials come from the boto3 credential chain (env vars,
13251366
# ~/.aws/credentials, instance roles, etc.)
13261367
if not _cp_has_creds and _cp_config and getattr(_cp_config, "auth_type", "") == "aws_sdk":
1327-
try:
1328-
from agent.bedrock_adapter import has_aws_credentials
1329-
_cp_has_creds = has_aws_credentials()
1330-
except Exception:
1331-
pass
1368+
_cp_has_creds = _has_aws_sdk_creds_for_listing(_cp.slug)
13321369

13331370
if not _cp_has_creds:
13341371
continue

tests/hermes_cli/test_bedrock_model_picker.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,30 @@ def test_bedrock_not_shown_without_credentials(self, monkeypatch):
203203
bedrock = next((p for p in providers if p["slug"] == "bedrock"), None)
204204
assert bedrock is None, "bedrock should NOT appear when AWS credentials are absent"
205205

206+
def test_non_bedrock_picker_does_not_probe_full_aws_chain(self, monkeypatch):
207+
"""Non-Bedrock provider discovery must not touch boto3's full credential chain."""
208+
from hermes_cli.model_switch import list_authenticated_providers
209+
210+
monkeypatch.delenv("AWS_PROFILE", raising=False)
211+
monkeypatch.delenv("AWS_ACCESS_KEY_ID", raising=False)
212+
monkeypatch.delenv("AWS_SECRET_ACCESS_KEY", raising=False)
213+
monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False)
214+
monkeypatch.delenv("AWS_WEB_IDENTITY_TOKEN_FILE", raising=False)
215+
monkeypatch.delenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", raising=False)
216+
monkeypatch.delenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", raising=False)
217+
218+
calls = {"has_aws_credentials": 0}
219+
220+
def _has_aws_credentials():
221+
calls["has_aws_credentials"] += 1
222+
return False
223+
224+
with patch("agent.bedrock_adapter.has_aws_credentials", side_effect=_has_aws_credentials):
225+
providers = list_authenticated_providers(current_provider="openrouter", max_models=0)
226+
227+
assert calls["has_aws_credentials"] == 0
228+
assert all(p["slug"] != "bedrock" for p in providers)
229+
206230
def test_bedrock_falls_back_to_curated_when_discovery_fails(self, monkeypatch):
207231
"""When discover_bedrock_models() raises, fall back to curated list without crashing."""
208232
from hermes_cli.model_switch import list_authenticated_providers

0 commit comments

Comments
 (0)