This is an automated email from the ASF dual-hosted git repository.

guan404ming pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new db6c95ae92e Reject invalid Amazon Bedrock model IDs in the Anthropic 
provider (#69404)
db6c95ae92e is described below

commit db6c95ae92eb7611fa0c5b1fa761f8f6f9c58918
Author: Guan-Ming Chiu <[email protected]>
AuthorDate: Mon Jul 6 22:40:53 2026 +0800

    Reject invalid Amazon Bedrock model IDs in the Anthropic provider (#69404)
---
 .../airflow/providers/anthropic/hooks/anthropic.py | 16 +++++++++-
 .../tests/unit/anthropic/hooks/test_anthropic.py   | 34 ++++++++++++++++++++++
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git 
a/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py 
b/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
index dbefdf84d5d..4d13240422c 100644
--- a/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
+++ b/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
@@ -297,6 +297,20 @@ class AnthropicHook(BaseHook):
             kwargs["scope"] = wif["scope"]
         return WorkloadIdentityCredentials(**kwargs)
 
+    def _resolve_model(self, model: str | None) -> str:
+        """Resolve the effective model id; Bedrock rejects a bare id, so 
require its prefix."""
+        resolved = model or self.default_model
+        # Valid Bedrock ids either start with the ``anthropic.`` provider 
prefix or carry a
+        # region/profile prefix as a dotted component (e.g. ``us.anthropic.``, 
``global.anthropic.``).
+        is_bedrock_model_id = resolved.startswith("anthropic.") or 
".anthropic." in resolved
+        if self.platform == "bedrock" and not is_bedrock_model_id:
+            raise AnthropicError(
+                f"Model {resolved!r} is not a valid Amazon Bedrock model id. 
Bedrock ids carry a "
+                "provider/region prefix (e.g. 
'global.anthropic.claude-opus-4-6-v1'); set one via "
+                "the 'model' argument or the connection's extra['model']."
+            )
+        return resolved
+
     def _require_first_party(self, feature: str) -> None:
         if self.platform not in FIRST_PARTY_PLATFORMS:
             raise AnthropicError(
@@ -348,7 +362,7 @@ class AnthropicHook(BaseHook):
         :param system: Optional system prompt.
         """
         params: dict[str, Any] = {
-            "model": model or self.default_model,
+            "model": self._resolve_model(model),
             "max_tokens": max_tokens,
             "messages": messages,
             **kwargs,
diff --git a/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py 
b/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
index 1f0974d540c..44a448b2011 100644
--- a/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
+++ b/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
@@ -185,6 +185,40 @@ class TestDefaultModel:
         hook.create_agent(name="a")
         assert client.beta.agents.create.call_args.kwargs["model"] == 
"claude-sonnet-4-6"
 
+    @pytest.mark.parametrize(
+        "model",
+        [None, "my-anthropic.model"],  # bare default id and a substring that 
is not a real prefix
+    )
+    def test_bedrock_rejects_invalid_model_id(self, model):
+        extra = {"platform": "bedrock", **({"model": model} if model else {})}
+        hook, client = _make_hook(extra=extra)
+        with pytest.raises(AnthropicError, match="not a valid Amazon Bedrock 
model id"):
+            hook.create_message([{"role": "user", "content": "hi"}])
+        client.messages.create.assert_not_called()
+
+    @pytest.mark.parametrize(
+        ("platform", "model", "expected"),
+        [
+            (
+                "bedrock",
+                "anthropic.claude-sonnet-4-5-20250929-v1:0",
+                "anthropic.claude-sonnet-4-5-20250929-v1:0",
+            ),
+            ("bedrock", "global.anthropic.claude-opus-4-6-v1", 
"global.anthropic.claude-opus-4-6-v1"),
+            (
+                "bedrock",
+                "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
+                "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
+            ),
+            ("vertex", None, DEFAULT_MODEL),  # non-Bedrock platforms skip the 
guard
+        ],
+    )
+    def test_accepts_valid_model_id(self, platform, model, expected):
+        extra = {"platform": platform, **({"model": model} if model else {})}
+        hook, client = _make_hook(extra=extra)
+        hook.create_message([{"role": "user", "content": "hi"}])
+        assert client.messages.create.call_args.kwargs["model"] == expected
+
 
 class TestManagedAgentsHook:
     def _hook_with_client(self, extra=None):

Reply via email to