This is an automated email from the ASF dual-hosted git repository.
amoghrajesh 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 5677af31e96 Migrate anthropic provider to use anthropic 1.x SDK
(#72094)
5677af31e96 is described below
commit 5677af31e96a40c088ffdb454d2f7842cd47a921
Author: Amogh Desai <[email protected]>
AuthorDate: Tue Sep 8 03:17:31 2026 -0500
Migrate anthropic provider to use anthropic 1.x SDK (#72094)
---
providers/anthropic/README.rst | 20 +++++------
providers/anthropic/docs/connections.rst | 3 +-
providers/anthropic/docs/index.rst | 20 +++++------
providers/anthropic/pyproject.toml | 17 +++------
.../airflow/providers/anthropic/hooks/anthropic.py | 28 ++++++++++++---
.../tests/unit/anthropic/hooks/test_anthropic.py | 41 +++++++++++++++++++---
uv.lock | 17 +++++----
7 files changed, 95 insertions(+), 51 deletions(-)
diff --git a/providers/anthropic/README.rst b/providers/anthropic/README.rst
index aa2a8a925e3..eefc65dc614 100644
--- a/providers/anthropic/README.rst
+++ b/providers/anthropic/README.rst
@@ -52,24 +52,24 @@ The package supports the following python versions:
3.10,3.11,3.12,3.13,3.14
Requirements
------------
-========================================== ====================
+========================================== ==================
PIP package Version required
-========================================== ====================
+========================================== ==================
``apache-airflow`` ``>=3.0.0``
``apache-airflow-providers-common-compat`` ``>=1.12.0``
-``anthropic`` ``>=0.121.0,<1.0.0``
-========================================== ====================
+``anthropic`` ``>=1.0.0``
+========================================== ==================
Optional dependencies
----------------------
-=========== ===============================
+=========== =============================
Extra Dependencies
-=========== ===============================
-``bedrock`` ``anthropic[bedrock]>=0.121.0``
-``vertex`` ``anthropic[vertex]>=0.121.0``
-``aws`` ``anthropic[aws]>=0.121.0``
-=========== ===============================
+=========== =============================
+``bedrock`` ``anthropic[bedrock]>=1.0.0``
+``vertex`` ``anthropic[vertex]>=1.0.0``
+``aws`` ``anthropic[aws]>=1.0.0``
+=========== =============================
The changelog for the provider package can be found in the
`changelog
<https://airflow.apache.org/docs/apache-airflow-providers-anthropic/0.3.0/changelog.html>`_.
diff --git a/providers/anthropic/docs/connections.rst
b/providers/anthropic/docs/connections.rst
index 55725e29ccf..71c87a0d912 100644
--- a/providers/anthropic/docs/connections.rst
+++ b/providers/anthropic/docs/connections.rst
@@ -49,7 +49,8 @@ Extra (optional)
``model`` (e.g. ``hook.create_message(...)``,
``hook.create_agent(...)``). Set it here
to change the model without editing Dags; falls back to the provider
default
(``claude-opus-4-8``).
- * ``aws_region`` — AWS region for the ``bedrock`` and ``aws`` platforms.
+ * ``aws_region`` — AWS region for the ``bedrock`` and ``aws`` platforms.
Required unless the
+ worker resolves a region from ``AWS_REGION`` / ``AWS_DEFAULT_REGION`` or
its AWS profile.
* ``project_id`` / ``region`` — GCP project and region for the ``vertex``
platform.
* ``resource`` — Azure resource name for the ``foundry`` platform.
* ``anthropic_client_kwargs`` — a nested dictionary forwarded verbatim to
the client
diff --git a/providers/anthropic/docs/index.rst
b/providers/anthropic/docs/index.rst
index fd9c4ef97f7..f8c1b8a2db2 100644
--- a/providers/anthropic/docs/index.rst
+++ b/providers/anthropic/docs/index.rst
@@ -133,13 +133,13 @@ Requirements
The minimum Apache Airflow version supported by this provider distribution is
``3.0.0``.
-========================================== ====================
+========================================== ==================
PIP package Version required
-========================================== ====================
+========================================== ==================
``apache-airflow`` ``>=3.0.0``
``apache-airflow-providers-common-compat`` ``>=1.12.0``
-``anthropic`` ``>=0.121.0,<1.0.0``
-========================================== ====================
+``anthropic`` ``>=1.0.0``
+========================================== ==================
Optional dependencies
---------------------
@@ -152,13 +152,13 @@ Install them when installing from PyPI. For example:
pip install apache-airflow-providers-anthropic[bedrock]
-=========== ======================================
+=========== =============================
Extra Dependencies
-=========== ======================================
-``bedrock`` ``anthropic[bedrock]>=0.121.0,<1.0.0``
-``vertex`` ``anthropic[vertex]>=0.121.0,<1.0.0``
-``aws`` ``anthropic[aws]>=0.121.0,<1.0.0``
-=========== ======================================
+=========== =============================
+``bedrock`` ``anthropic[bedrock]>=1.0.0``
+``vertex`` ``anthropic[vertex]>=1.0.0``
+``aws`` ``anthropic[aws]>=1.0.0``
+=========== =============================
Downloading official packages
-----------------------------
diff --git a/providers/anthropic/pyproject.toml
b/providers/anthropic/pyproject.toml
index a967811f13f..ba030a4dcf9 100644
--- a/providers/anthropic/pyproject.toml
+++ b/providers/anthropic/pyproject.toml
@@ -61,24 +61,15 @@ requires-python = ">=3.10"
dependencies = [
"apache-airflow>=3.0.0",
"apache-airflow-providers-common-compat>=1.12.0",
- # 0.121.0 is the first release whose ``sessions.create`` accepts
``budget``, which is
- # how the guide tells users to set a session ceiling. Reading a
``budget_reached`` stop
- # reason happens to work further back -- older clients mis-build the
discriminated
- # union into the wrong variant class while preserving ``.type`` -- but
only with
- # response validation left non-strict, so it is not something to depend on.
- # anthropic 1.0.0 replaces httpx with httpx2 and is a major release the
provider
- # has not been validated against. Remove the <1.0.0 cap (here and in the
extras
- # below) after migrating to the 1.x SDK;
- # tracked at https://github.com/apache/airflow/issues/72071
- "anthropic>=0.121.0,<1.0.0",
+ "anthropic>=1.0.0",
]
# The optional dependencies should be modified in place in the generated file
# Any change in the dependencies is preserved when the file is regenerated
[project.optional-dependencies]
-"bedrock" = ["anthropic[bedrock]>=0.121.0,<1.0.0"]
-"vertex" = ["anthropic[vertex]>=0.121.0,<1.0.0"]
-"aws" = ["anthropic[aws]>=0.121.0,<1.0.0"]
+"bedrock" = ["anthropic[bedrock]>=1.0.0"]
+"vertex" = ["anthropic[vertex]>=1.0.0"]
+"aws" = ["anthropic[aws]>=1.0.0"]
[dependency-groups]
dev = [
diff --git
a/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
b/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
index 864a6bbb813..023729f2636 100644
--- a/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
+++ b/providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py
@@ -50,7 +50,7 @@ from airflow.providers.common.compat.sdk import
AirflowSkipException, BaseHook
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
- from collections.abc import Iterable, Iterator
+ from collections.abc import Callable, Iterable, Iterator
from anthropic.types import Message
from anthropic.types.beta import (
@@ -295,7 +295,8 @@ class AnthropicHook(BaseHook):
- ``platform``: one of ``anthropic`` (default), ``bedrock``, ``vertex``,
``aws``, ``foundry``.
- ``model``: default model id used when an operator/hook call omits
``model`` (lets you
change the model without editing Dags); falls back to
:data:`DEFAULT_MODEL`.
- - ``aws_region``: region for the ``bedrock`` and ``aws`` platforms.
+ - ``aws_region``: region for the ``bedrock`` and ``aws`` platforms.
Required unless the
+ worker resolves one from ``AWS_REGION`` / ``AWS_DEFAULT_REGION`` or its
AWS profile.
- ``project_id`` / ``region``: project and region for the ``vertex``
platform.
- ``resource``: Azure resource name for the ``foundry`` platform.
- ``anthropic_client_kwargs``: extra keyword arguments forwarded to the
client
@@ -342,6 +343,25 @@ class AnthropicHook(BaseHook):
"""Return the Anthropic client for the configured platform."""
return self.get_conn()
+ def _build_aws_client(
+ self,
+ factory: Callable[..., AnthropicClient],
+ aws_region: str | None,
+ client_kwargs: dict[str, Any],
+ ) -> AnthropicClient:
+ try:
+ return factory(aws_region=aws_region, **client_kwargs)
+ except ValueError as exc:
+ # anthropic 1.x dropped the implicit us-east-1 fallback, so an
unset region now
+ # fails at client construction. Point at the connection rather
than the SDK.
+ if aws_region:
+ raise
+ raise AnthropicError(
+ f"No AWS region configured for the {self.platform!r} platform.
Set 'aws_region' in the "
+ f"extra of connection {self.conn_id!r}, or set AWS_REGION /
AWS_DEFAULT_REGION (or a "
+ "region on the AWS profile) on the worker."
+ ) from exc
+
def get_conn(self) -> AnthropicClient:
"""Build and return the Anthropic client for the configured
platform."""
conn = self._connection
@@ -350,13 +370,13 @@ class AnthropicHook(BaseHook):
platform = self.platform
self.log.debug("Building Anthropic client for platform %r
(conn_id=%s)", platform, self.conn_id)
if platform == "bedrock":
- return AnthropicBedrock(aws_region=extras.get("aws_region"),
**client_kwargs)
+ return self._build_aws_client(AnthropicBedrock,
extras.get("aws_region"), client_kwargs)
if platform == "vertex":
return AnthropicVertex(
project_id=extras.get("project_id"),
region=extras.get("region"), **client_kwargs
)
if platform == "aws":
- return AnthropicAWS(aws_region=extras.get("aws_region"),
**client_kwargs)
+ return self._build_aws_client(AnthropicAWS,
extras.get("aws_region"), client_kwargs)
if platform == "foundry":
api_key = client_kwargs.pop("api_key", None) or conn.password
return AnthropicFoundry(api_key=api_key,
resource=extras.get("resource"), **client_kwargs)
diff --git a/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
b/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
index 816b238d401..3ba0842d7e6 100644
--- a/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
+++ b/providers/anthropic/tests/unit/anthropic/hooks/test_anthropic.py
@@ -45,7 +45,6 @@ from airflow.providers.anthropic.hooks.anthropic import (
pytest.importorskip("anthropic")
-import httpx
from anthropic import BadRequestError
from anthropic.types import BetaMonetaryAmount
from anthropic.types.beta import BetaManagedAgentsServerToolUsage,
BetaManagedAgentsSessionUsage
@@ -281,10 +280,17 @@ class TestBuildBudget:
build_budget(amount)
+class _StubResponse:
+ """The only response attributes ``APIStatusError`` reads, so no HTTP
library is needed."""
+
+ status_code = 400
+ headers: dict[str, str] = {}
+ request = None
+
+
def _make_bad_request(message="cannot be archived while its status is
running") -> BadRequestError:
"""A real SDK BadRequestError -- archive_session only interrupts on this,
not on any error."""
- request = httpx.Request("POST",
"https://api.anthropic.com/v1/sessions/sess_1/archive")
- return BadRequestError(message, response=httpx.Response(400,
request=request), body=None)
+ return BadRequestError(message, response=_StubResponse(), body=None) #
type: ignore[arg-type]
class TestArchiveSession:
@@ -679,11 +685,38 @@ class TestAnthropicHookGetConn:
@mock.patch(f"{HOOK_PATH}.AnthropicAWS", autospec=True)
@mock.patch.object(AnthropicHook, "get_connection")
def test_aws_platform_without_region(self, mock_get_connection, mock_aws):
- # No aws_region configured -> pass None so the SDK falls back to its
own resolution.
+ # No aws_region configured -> pass None so the SDK can still resolve
one from the
+ # environment or the AWS profile; it only fails when nothing supplies
a region.
mock_get_connection.return_value = _conn(extra={"platform": "aws"})
AnthropicHook().get_conn()
mock_aws.assert_called_once_with(aws_region=None)
+ @pytest.mark.parametrize(
+ ("platform", "client_name"),
+ [("bedrock", "AnthropicBedrock"), ("aws", "AnthropicAWS")],
+ )
+ @mock.patch.object(AnthropicHook, "get_connection")
+ def test_unresolvable_aws_region_points_at_the_connection(
+ self, mock_get_connection, platform, client_name
+ ):
+ mock_get_connection.return_value = _conn(extra={"platform": platform})
+ sdk_error = ValueError("No AWS region was provided.")
+ with mock.patch(f"{HOOK_PATH}.{client_name}", side_effect=sdk_error):
+ with pytest.raises(AnthropicError) as exc_info:
+ AnthropicHook().get_conn()
+ message = str(exc_info.value)
+ assert f"No AWS region configured for the {platform!r} platform" in
message
+ assert "'aws_region'" in message
+ assert "'anthropic_default'" in message
+ assert exc_info.value.__cause__ is sdk_error
+
+ @mock.patch.object(AnthropicHook, "get_connection")
+ def test_sdk_value_error_is_not_masked_when_a_region_is_configured(self,
mock_get_connection):
+ mock_get_connection.return_value = _conn(extra={"platform": "bedrock",
"aws_region": "us-east-1"})
+ with mock.patch(f"{HOOK_PATH}.AnthropicBedrock",
side_effect=ValueError("bad profile")):
+ with pytest.raises(ValueError, match="bad profile"):
+ AnthropicHook().get_conn()
+
@mock.patch(f"{HOOK_PATH}.AnthropicFoundry")
@mock.patch.object(AnthropicHook, "get_connection")
def test_foundry_platform(self, mock_get_connection, mock_foundry):
diff --git a/uv.lock b/uv.lock
index 7e52ab5f4e4..fb803c564a5 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1097,21 +1097,20 @@ wheels = [
[[package]]
name = "anthropic"
-version = "0.125.0"
+version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
- { name = "distro" },
{ name = "docstring-parser" },
- { name = "httpx" },
+ { name = "httpx2" },
{ name = "jiter" },
{ name = "pydantic" },
{ name = "sniffio" },
{ name = "typing-extensions" },
]
-sdist = { url =
"https://files.pythonhosted.org/packages/62/f8/6f0560884b5363848347bd640b6c1d04abc25e7aa61787a232f790c6b60a/anthropic-0.125.0.tar.gz",
hash =
"sha256:e0cdd336580cb7411c1cdab69f80973e9bf4bff7f8e08141811d46307d45c682", size
= 1112593, upload-time = "2026-08-19T22:00:42.837Z" }
+sdist = { url =
"https://files.pythonhosted.org/packages/25/aa/4978e58035bd6c638c7b483450a68b7ef2d732ab78885e27bb9db0cff1a2/anthropic-1.0.0.tar.gz",
hash =
"sha256:42be3c97604af7252c5898413aee076ace6c46e9bca0d0d90ceb77c7d3719027", size
= 1077769, upload-time = "2026-08-20T19:59:00.565Z" }
wheels = [
- { url =
"https://files.pythonhosted.org/packages/2f/1a/b1bd30cda3790557e8791bec5922a6ec8fabb6fa8b008c76a39cf7be6152/anthropic-0.125.0-py3-none-any.whl",
hash =
"sha256:3486013602eca76d8b12540764e53654f02cf4951110bca86cf06e67428a9f21", size
= 1184067, upload-time = "2026-08-19T22:00:44.596Z" },
+ { url =
"https://files.pythonhosted.org/packages/ad/5b/db4a854aebf5d33a5ab714c46af6eb85ee44f390ed29b7b325c00b9f11ed/anthropic-1.0.0-py3-none-any.whl",
hash =
"sha256:32dd52e9e1d774393b27182f451398ba4262287a4d0eab30887f89f1481b3ae4", size
= 1171725, upload-time = "2026-08-20T19:58:58.725Z" },
]
[package.optional-dependencies]
@@ -3442,10 +3441,10 @@ docs = [
[package.metadata]
requires-dist = [
- { name = "anthropic", specifier = ">=0.121.0,<1.0.0" },
- { name = "anthropic", extras = ["aws"], marker = "extra == 'aws'",
specifier = ">=0.121.0,<1.0.0" },
- { name = "anthropic", extras = ["bedrock"], marker = "extra == 'bedrock'",
specifier = ">=0.121.0,<1.0.0" },
- { name = "anthropic", extras = ["vertex"], marker = "extra == 'vertex'",
specifier = ">=0.121.0,<1.0.0" },
+ { name = "anthropic", specifier = ">=1.0.0" },
+ { name = "anthropic", extras = ["aws"], marker = "extra == 'aws'",
specifier = ">=1.0.0" },
+ { name = "anthropic", extras = ["bedrock"], marker = "extra == 'bedrock'",
specifier = ">=1.0.0" },
+ { name = "anthropic", extras = ["vertex"], marker = "extra == 'vertex'",
specifier = ">=1.0.0" },
{ name = "apache-airflow", editable = "." },
{ name = "apache-airflow-providers-common-compat", editable =
"providers/common/compat" },
]