fix(bedrock): region-scoped model picker + geo-aware recommendations (#28156)

Bug 2 of #28156: the picker offered us./global. inference profiles to
EU-region endpoints (unroutable — AWS rejects them regardless of
credentials) and _RECOMMENDED hardcoded us.anthropic.* ids, so non-US
pickers pinned profiles their endpoint can't invoke.

- bedrock_model_routable_from_region(): geo-prefixed profiles are only
  offered in their own geography (full AWS prefix set incl. apac./jp./
  ca./sa./me./af.); bare ids and global.* pass everywhere; unknown
  region shapes hide nothing.
- Recommendations match geo-agnostically on the base model id, so an EU
  picker pins eu.anthropic.claude-sonnet-4-6; in-region geo profiles
  sort above global.* for the same model (addresses the global.*-first
  ordering complaint from the issue thread).
- Dedup generalized from (us., global.) to all profile prefixes.
This commit is contained in:
Teknium 2026-07-14 14:41:39 -07:00
parent b6f749af94
commit 3b15afafa2
2 changed files with 167 additions and 20 deletions

View file

@ -27,6 +27,54 @@ import subprocess
from hermes_cli.config import clear_model_endpoint_credentials
# AWS cross-region inference profile prefixes. Any geo-prefixed profile only
# routes from endpoints in its own geography, so the Bedrock picker must not
# offer (e.g.) us.* profiles to an eu-central-2 endpoint — selecting one
# produces a config AWS rejects regardless of credentials (#28156).
# global.* routes from everywhere. Full set per the AWS cross-region
# inference docs.
BEDROCK_GEO_PREFIXES = (
"us.", "eu.", "ap.", "apac.", "jp.", "ca.", "sa.", "me.", "af.",
)
def bedrock_region_geo_prefix(region_name: str) -> str:
"""Map an AWS region name to its inference-profile geo prefix ('' = unknown)."""
r = (region_name or "").lower()
for geo, region_prefixes in (
("us.", ("us-", "us_gov")),
("eu.", ("eu-",)),
("ap.", ("ap-",)),
("ca.", ("ca-",)),
("sa.", ("sa-",)),
("me.", ("me-",)),
("af.", ("af-",)),
):
if r.startswith(region_prefixes):
return geo
return ""
def bedrock_model_routable_from_region(model_id: str, region_name: str) -> bool:
"""True when *model_id* can be invoked from *region_name*'s endpoint.
Bare foundation-model ids and ``global.*`` profiles route from anywhere.
Geo-prefixed inference profiles (``us.*``, ``eu.*``, ...) only route from
endpoints in their own geography. Unknown region shapes hide nothing.
"""
mid = (model_id or "").lower()
matched_geo = next((p for p in BEDROCK_GEO_PREFIXES if mid.startswith(p)), None)
if matched_geo is None or mid.startswith("global."):
return True
geo = bedrock_region_geo_prefix(region_name)
if not geo:
return True
if geo == "ap.":
# Asia-Pacific regions can carry ap./apac./jp. profile spellings.
return matched_geo in ("ap.", "apac.", "jp.")
return matched_geo == geo
def _prune_replaced_custom_model_config_credentials(
base_url: str,
*,
@ -2265,6 +2313,8 @@ def _model_flow_bedrock(config, current_model=""):
"global.twelvelabs.",
)
_EXCLUDE_SUBSTRINGS = ("safeguard", "voxtral", "palmyra-vision")
filtered = []
for m in live_models:
mid = m["id"]
@ -2272,44 +2322,59 @@ def _model_flow_bedrock(config, current_model=""):
continue
if any(s in mid.lower() for s in _EXCLUDE_SUBSTRINGS):
continue
if not bedrock_model_routable_from_region(mid, region):
continue
filtered.append(m)
# Deduplicate: prefer inference profiles (us.*, global.*) over bare
# foundation model IDs.
# Deduplicate: prefer inference profiles (geo-prefixed or global.*)
# over bare foundation model IDs.
_PROFILE_PREFIXES = BEDROCK_GEO_PREFIXES + ("global.",)
profile_base_ids = set()
for m in filtered:
mid = m["id"]
if mid.startswith(("us.", "global.")):
base = mid.split(".", 1)[1] if "." in mid[3:] else mid
profile_base_ids.add(base)
_pp = next((p for p in _PROFILE_PREFIXES if mid.startswith(p)), None)
if _pp:
profile_base_ids.add(mid[len(_pp):])
deduped = []
for m in filtered:
mid = m["id"]
if not mid.startswith(("us.", "global.")) and mid in profile_base_ids:
if (
not mid.startswith(_PROFILE_PREFIXES)
and mid in profile_base_ids
):
continue
deduped.append(m)
_RECOMMENDED = [
"us.anthropic.claude-sonnet-4-6",
"us.anthropic.claude-opus-4-6",
"us.anthropic.claude-haiku-4-5",
"us.amazon.nova-pro",
"us.amazon.nova-lite",
"us.amazon.nova-micro",
# Recommended models, matched geo-agnostically so an EU (eu.*) or
# APAC (apac.*) picker pins its own region's profile of the same
# model rather than a us.* one it can't route to (#28156).
_RECOMMENDED_BASES = [
"anthropic.claude-sonnet-4-6",
"anthropic.claude-opus-4-6",
"anthropic.claude-haiku-4-5",
"amazon.nova-pro",
"amazon.nova-lite",
"amazon.nova-micro",
"deepseek.v3",
"us.meta.llama4-maverick",
"us.meta.llama4-scout",
"meta.llama4-maverick",
"meta.llama4-scout",
]
def _base_id(mid: str) -> str:
_pp = next((p for p in _PROFILE_PREFIXES if mid.startswith(p)), None)
return mid[len(_pp):] if _pp else mid
def _sort_key(m):
mid = m["id"]
for i, rec in enumerate(_RECOMMENDED):
if mid.startswith(rec):
return (0, i, mid)
base = _base_id(mid)
for i, rec in enumerate(_RECOMMENDED_BASES):
if base.startswith(rec):
# In-region geo profile beats global.* for the same model
return (0, i, 0 if not mid.startswith("global.") else 1, mid)
if mid.startswith("global."):
return (1, 0, mid)
return (2, 0, mid)
return (1, 0, 0, mid)
return (2, 0, 0, mid)
deduped.sort(key=_sort_key)
model_list = [m["id"] for m in deduped]

View file

@ -0,0 +1,82 @@
"""Regression tests for #28156 — Bedrock picker must be region-scoped.
Geo-prefixed cross-region inference profiles (us.*, eu.*, apac.*, ...) only
route from endpoints in their own geography. Offering us.* profiles to an
eu-central-2 picker produces configs AWS rejects regardless of credentials.
"""
from hermes_cli.model_setup_flows import (
BEDROCK_GEO_PREFIXES,
bedrock_model_routable_from_region,
bedrock_region_geo_prefix,
)
class TestRegionGeoPrefix:
def test_known_geographies(self):
assert bedrock_region_geo_prefix("us-east-1") == "us."
assert bedrock_region_geo_prefix("eu-central-2") == "eu."
assert bedrock_region_geo_prefix("ap-southeast-1") == "ap."
assert bedrock_region_geo_prefix("ca-central-1") == "ca."
assert bedrock_region_geo_prefix("sa-east-1") == "sa."
assert bedrock_region_geo_prefix("me-south-1") == "me."
assert bedrock_region_geo_prefix("af-south-1") == "af."
def test_unknown_region_is_empty(self):
assert bedrock_region_geo_prefix("") == ""
assert bedrock_region_geo_prefix("moon-base-1") == ""
class TestRoutableFromRegion:
def test_us_profile_not_offered_in_eu(self):
assert not bedrock_model_routable_from_region(
"us.anthropic.claude-sonnet-4-6", "eu-central-2"
)
def test_eu_profile_offered_in_eu(self):
assert bedrock_model_routable_from_region(
"eu.anthropic.claude-sonnet-4-6", "eu-central-2"
)
def test_global_profile_offered_everywhere(self):
for region in ("eu-central-2", "us-east-1", "ap-southeast-1"):
assert bedrock_model_routable_from_region(
"global.anthropic.claude-sonnet-4-6", region
)
def test_bare_foundation_id_offered_everywhere(self):
assert bedrock_model_routable_from_region(
"anthropic.claude-3-sonnet-20240229-v1:0", "eu-central-2"
)
def test_apac_spellings_route_in_ap_regions(self):
for prefix in ("ap.", "apac.", "jp."):
assert bedrock_model_routable_from_region(
f"{prefix}anthropic.claude-sonnet-4-6", "ap-northeast-1"
)
def test_eu_profile_not_offered_in_us(self):
assert not bedrock_model_routable_from_region(
"eu.anthropic.claude-sonnet-4-6", "us-east-1"
)
def test_unknown_region_hides_nothing(self):
assert bedrock_model_routable_from_region(
"us.anthropic.claude-sonnet-4-6", ""
)
class TestGeoPrefixContract:
def test_every_geo_prefix_maps_to_a_routable_region_or_is_alias(self):
"""Invariant: each geo prefix is either reachable from some region's
geo mapping or an ap-family alias no dead entries."""
mapped = {
bedrock_region_geo_prefix(r)
for r in (
"us-east-1", "eu-central-2", "ap-southeast-1",
"ca-central-1", "sa-east-1", "me-south-1", "af-south-1",
)
}
ap_aliases = {"apac.", "jp."}
for prefix in BEDROCK_GEO_PREFIXES:
assert prefix in mapped or prefix in ap_aliases