fix(models): make Claude family fallback recognize Bedrock ids

#1651 added the family-aware `provider_fallback_pair` via `_claude_family_of`,
but the helper only matched `anthropic:claude-*` ids. This fork serves Claude
through Bedrock (`bedrock_converse:us.anthropic.claude-*`), so the family logic
was dead code: a dropped Bedrock Sonnet fell back to the Bedrock Opus that sits
first in the list instead of staying in the Sonnet family.

Teach `_claude_family_of` to parse `bedrock_converse` ids and add regression
coverage for the Sonnet-stays-on-Sonnet case.
This commit is contained in:
Adam Moussa 2026-07-02 20:10:23 -04:00
parent 2a226e2cd1
commit c16fb915f5
No known key found for this signature in database
2 changed files with 20 additions and 2 deletions

View file

@ -99,9 +99,18 @@ def _provider_of(model_id: str) -> str | None:
def _claude_family_of(model_id: str) -> str | None: def _claude_family_of(model_id: str) -> str | None:
provider, _, name = model_id.partition(":") provider, _, name = model_id.partition(":")
if provider != "anthropic" or not name.startswith("claude-"): if provider == "anthropic":
claude = name
elif provider == "bedrock_converse":
# Bedrock ids embed the model as a region-prefixed path, e.g.
# "us.anthropic.claude-sonnet-5" — take the trailing "claude-*" segment so
# our Bedrock Claude models get the same family-aware fallback.
claude = name.rpartition(".")[2]
else:
return None return None
parts = name.split("-") if not claude.startswith("claude-"):
return None
parts = claude.split("-")
if len(parts) < 2: if len(parts) < 2:
return None return None
return "-".join(parts[:2]) return "-".join(parts[:2])

View file

@ -12,12 +12,21 @@ from agent.dashboard.team_settings import get_team_default_model
STALE_ANTHROPIC = "bedrock_converse:us.anthropic.claude-opus-4-7" STALE_ANTHROPIC = "bedrock_converse:us.anthropic.claude-opus-4-7"
SUPPORTED_ANTHROPIC = "bedrock_converse:us.anthropic.claude-opus-4-8" SUPPORTED_ANTHROPIC = "bedrock_converse:us.anthropic.claude-opus-4-8"
STALE_SONNET = "bedrock_converse:us.anthropic.claude-sonnet-4-9"
SUPPORTED_SONNET = "bedrock_converse:us.anthropic.claude-sonnet-5"
def test_provider_fallback_preserves_provider_and_effort() -> None: def test_provider_fallback_preserves_provider_and_effort() -> None:
assert provider_fallback_pair(STALE_ANTHROPIC, "xhigh") == (SUPPORTED_ANTHROPIC, "xhigh") assert provider_fallback_pair(STALE_ANTHROPIC, "xhigh") == (SUPPORTED_ANTHROPIC, "xhigh")
def test_provider_fallback_keeps_bedrock_sonnet_in_family() -> None:
# A dropped Bedrock Sonnet must prefer the current Bedrock Sonnet, not cross to
# the Bedrock Opus that happens to sit first in the provider's list. Requires
# _claude_family_of to understand bedrock_converse ids, not just anthropic:.
assert provider_fallback_pair(STALE_SONNET, "high") == (SUPPORTED_SONNET, "high")
def test_provider_fallback_uses_default_effort_when_unsupported() -> None: def test_provider_fallback_uses_default_effort_when_unsupported() -> None:
assert provider_fallback_pair(STALE_ANTHROPIC, "bogus") == (SUPPORTED_ANTHROPIC, "high") assert provider_fallback_pair(STALE_ANTHROPIC, "bogus") == (SUPPORTED_ANTHROPIC, "high")
assert provider_fallback_pair(STALE_ANTHROPIC, None) == (SUPPORTED_ANTHROPIC, "high") assert provider_fallback_pair(STALE_ANTHROPIC, None) == (SUPPORTED_ANTHROPIC, "high")