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

Lee-W pushed a commit to branch ci-upgrade-main
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 07a9df0bacb26f3e44f88214fae2cd8ca22c5bca
Author: Wei Lee <[email protected]>
AuthorDate: Mon Nov 9 16:48:14 2026 +0800

    Fix Redis integration, common.ai fingerprint and OpenAI tests after 
dependency upgrade
    
    The CI dependency upgrade pulls in redis-py 8 and kombu 5.7, which speak
    RESP3 by default and send HELLO on connect; the Redis 5.0.1 integration
    image rejects it. It also pulls in pydantic-ai 2.66, which re-raises the
    unhashable-type TypeError from tool-return serialization as a
    PydanticSerializationError, so durable fingerprinting returned None
    instead of falling back, and openai 3.46, which adds an optional field to
    the usage details that the tests pinned.
---
 .../providers/common/ai/durable/fingerprint.py       |  8 +++++++-
 .../tests/unit/openai/operators/test_openai.py       | 20 ++++----------------
 scripts/ci/docker-compose/integration-redis.yml      |  2 +-
 3 files changed, 12 insertions(+), 18 deletions(-)

diff --git 
a/providers/common/ai/src/airflow/providers/common/ai/durable/fingerprint.py 
b/providers/common/ai/src/airflow/providers/common/ai/durable/fingerprint.py
index bf001af966e..9904be74abf 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/durable/fingerprint.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/durable/fingerprint.py
@@ -105,7 +105,7 @@ from typing import TYPE_CHECKING, Any, Literal
 from pydantic import TypeAdapter
 from pydantic_ai.messages import ModelMessagesTypeAdapter
 from pydantic_ai.models import ModelRequestParameters
-from pydantic_core import to_jsonable_python
+from pydantic_core import PydanticSerializationError, to_jsonable_python
 
 from airflow.providers.common.ai.utils.masking import mask_secrets
 from airflow.providers.common.ai.utils.prompt_cache import 
PROMPT_CACHE_SETTING_NAMES
@@ -473,6 +473,12 @@ def _guide(adapter: TypeAdapter[Any], payload: Any) -> 
tuple[Any, bool]:
         guide = adapter.dump_python(payload)
     except TypeError:
         return None, False
+    except PydanticSerializationError as exc:
+        # pydantic-ai 2.66 serializes a tool return through a wrapping field 
serializer, and pydantic
+        # re-raises the ``TypeError`` from inside it as this error, keeping 
only its message.
+        if "unhashable type" not in str(exc):
+            raise
+        return None, False
     return guide, _check_guide(guide)
 
 
diff --git a/providers/openai/tests/unit/openai/operators/test_openai.py 
b/providers/openai/tests/unit/openai/operators/test_openai.py
index 39381a117f5..f65d4bcc9bd 100644
--- a/providers/openai/tests/unit/openai/operators/test_openai.py
+++ b/providers/openai/tests/unit/openai/operators/test_openai.py
@@ -157,17 +157,12 @@ def 
test_openai_response_operator_execute_xcom_push(do_xcom_push, expected_push_
     # slipping through assert_any_call, which only checks presence, not 
exhaustiveness.
     assert context["ti"].xcom_push.call_count == expected_push_count
     if do_xcom_push:
+        # Expected value is derived from the SDK model so the test does not 
pin optional fields that
+        # newer openai releases add to the usage details (e.g. 
``cache_write_tokens_details``).
         context["ti"].xcom_push.assert_any_call(key="response_id", 
value="resp_123")
         context["ti"].xcom_push.assert_any_call(
             key="usage",
-            value={
-                "input_tokens": 5,
-                "input_tokens_details": {"cache_write_tokens": 0, 
"cached_tokens": 1},
-                "output_tokens": 7,
-                "output_tokens_details": {"reasoning_tokens": 2},
-                "total_tokens": 12,
-                "try_number": 1,
-            },
+            value={**usage.model_dump(mode="json"), "try_number": 1},
         )
 
 
@@ -200,14 +195,7 @@ def 
test_openai_response_operator_execute_records_try_number():
 
     context["ti"].xcom_push.assert_any_call(
         key="usage",
-        value={
-            "input_tokens": 5,
-            "input_tokens_details": {"cache_write_tokens": 0, "cached_tokens": 
1},
-            "output_tokens": 7,
-            "output_tokens_details": {"reasoning_tokens": 2},
-            "total_tokens": 12,
-            "try_number": 3,
-        },
+        value={**usage.model_dump(mode="json"), "try_number": 3},
     )
 
 
diff --git a/scripts/ci/docker-compose/integration-redis.yml 
b/scripts/ci/docker-compose/integration-redis.yml
index a1bca55eff9..959d9445842 100644
--- a/scripts/ci/docker-compose/integration-redis.yml
+++ b/scripts/ci/docker-compose/integration-redis.yml
@@ -17,7 +17,7 @@
 ---
 services:
   redis:
-    image: redis:5.0.1
+    image: redis:7-alpine
     labels:
       org.apache.airflow.breeze: "true"
       org.apache.airflow.breeze.worktree: "${BREEZE_WORKTREE_PATH:-}"

Reply via email to