Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions agent/anthropic_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,20 +831,38 @@ def read_hermes_oauth_credentials() -> Optional[Dict[str, Any]]:
# ---------------------------------------------------------------------------


def _is_bedrock_model_id(model: str) -> bool:
"""Return True if the model string is a Bedrock-native model identifier.

Bedrock IDs use dots as structural separators (e.g.
``global.anthropic.claude-opus-4-6-v1``, ``anthropic.claude-sonnet-4-6``).
These must NOT have their dots replaced with hyphens.
"""
lower = model.lower()
for prefix in ("global.", "us.", "eu.", "ap.", "apac.", "jp."):
if lower.startswith(prefix):
return True
if lower.startswith("anthropic.claude"):
return True
return False


def normalize_model_name(model: str, preserve_dots: bool = False) -> str:
"""Normalize a model name for the Anthropic API.

- Strips 'anthropic/' prefix (OpenRouter format, case-insensitive)
- Converts dots to hyphens in version numbers (OpenRouter uses dots,
Anthropic uses hyphens: claude-opus-4.6 → claude-opus-4-6), unless
preserve_dots is True (e.g. for Alibaba/DashScope: qwen3.5-plus).
- Preserves dots in Bedrock model IDs where dots are structural
separators (e.g. global.anthropic.claude-opus-4-6-v1).
"""
lower = model.lower()
if lower.startswith("anthropic/"):
model = model[len("anthropic/"):]
if _is_bedrock_model_id(model):
return model
if not preserve_dots:
# OpenRouter uses dots for version separators (claude-opus-4.6),
# Anthropic uses hyphens (claude-opus-4-6). Convert dots to hyphens.
model = model.replace(".", "-")
return model

Expand Down
37 changes: 37 additions & 0 deletions tests/agent/test_anthropic_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
convert_tools_to_anthropic,
is_claude_code_token_valid,
normalize_anthropic_response,
_is_bedrock_model_id,
normalize_model_name,
read_claude_code_credentials,
resolve_anthropic_token,
Expand Down Expand Up @@ -481,6 +482,42 @@ def test_preserve_dots_for_alibaba_dashscope(self):
assert normalize_model_name("anthropic/qwen3.5-plus", preserve_dots=True) == "qwen3.5-plus"
assert normalize_model_name("qwen3.5-flash", preserve_dots=True) == "qwen3.5-flash"

def test_preserves_dots_in_bedrock_model_ids(self):
"""Bedrock model IDs use dots as structural separators, not version delimiters."""
assert normalize_model_name("global.anthropic.claude-opus-4-6-v1") == "global.anthropic.claude-opus-4-6-v1"
assert normalize_model_name("global.anthropic.claude-opus-4-7") == "global.anthropic.claude-opus-4-7"
assert normalize_model_name("global.anthropic.claude-sonnet-4-6") == "global.anthropic.claude-sonnet-4-6"
assert normalize_model_name("apac.anthropic.claude-sonnet-4-20250514-v1:0") == "apac.anthropic.claude-sonnet-4-20250514-v1:0"
assert normalize_model_name("us.anthropic.claude-opus-4-6-v1") == "us.anthropic.claude-opus-4-6-v1"
assert normalize_model_name("eu.anthropic.claude-sonnet-4-6") == "eu.anthropic.claude-sonnet-4-6"
assert normalize_model_name("anthropic.claude-opus-4-6-v1") == "anthropic.claude-opus-4-6-v1"


class TestIsBedrockModelId:
def test_global_prefix(self):
assert _is_bedrock_model_id("global.anthropic.claude-opus-4-6-v1") is True

def test_regional_prefixes(self):
assert _is_bedrock_model_id("us.anthropic.claude-opus-4-6-v1") is True
assert _is_bedrock_model_id("eu.anthropic.claude-sonnet-4-6") is True
assert _is_bedrock_model_id("ap.anthropic.claude-sonnet-4-6") is True
assert _is_bedrock_model_id("apac.anthropic.claude-sonnet-4-20250514-v1:0") is True
assert _is_bedrock_model_id("jp.anthropic.claude-opus-4-7") is True

def test_foundation_model_prefix(self):
assert _is_bedrock_model_id("anthropic.claude-opus-4-6-v1") is True
assert _is_bedrock_model_id("anthropic.claude-sonnet-4-6") is True

def test_non_bedrock_ids(self):
assert _is_bedrock_model_id("claude-opus-4-6") is False
assert _is_bedrock_model_id("claude-opus-4.6") is False
assert _is_bedrock_model_id("anthropic/claude-opus-4.6") is False
assert _is_bedrock_model_id("qwen3.5-plus") is False

def test_case_insensitive(self):
assert _is_bedrock_model_id("Global.Anthropic.Claude-Opus-4-6-v1") is True
assert _is_bedrock_model_id("GLOBAL.ANTHROPIC.CLAUDE-OPUS-4-6-V1") is True


# ---------------------------------------------------------------------------
# Tool conversion
Expand Down