You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by pa...@apache.org on 2026/09/22 07:06:59 UTC

(airflow) branch main updated: Replace retired gemini-2.0-flash example model in common.ai Vertex docs (#73516)

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

pankajastro 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 3eabbb59586 Replace retired gemini-2.0-flash example model in common.ai Vertex docs (#73516)
3eabbb59586 is described below

commit 3eabbb59586f3c4f321894a31a423256513e2244
Author: Pankaj Singh <98...@users.noreply.github.com>
AuthorDate: Tue Sep 22 12:36:47 2026 +0530

    Replace retired gemini-2.0-flash example model in common.ai Vertex docs (#73516)
    
    Google shut this model down on both Vertex AI and the Generative
    Language API, so the connection form's placeholder and every doc page
    that copied it sent a first-time user straight into a 404. The
    existing tests only checked that the google-cloud prefix was a
    provider pydantic-ai recognizes, never that the specific model after
    the colon was actually servable, which is how a retired name went
    unnoticed.
---
 .../common/ai/docs/connections/pydantic_ai.rst     |  4 +-
 .../ai/docs/connections/pydantic_ai_vertex.rst     |  8 +--
 providers/common/ai/provider.yaml                  |  4 +-
 .../providers/common/ai/get_provider_info.py       |  4 +-
 .../providers/common/ai/hooks/pydantic_ai.py       |  8 +--
 .../tests/unit/common/ai/hooks/test_pydantic_ai.py | 75 ++++++++++++++++++----
 6 files changed, 75 insertions(+), 28 deletions(-)

diff --git a/providers/common/ai/docs/connections/pydantic_ai.rst b/providers/common/ai/docs/connections/pydantic_ai.rst
index f82f5d4042b..ba1447a5bed 100644
--- a/providers/common/ai/docs/connections/pydantic_ai.rst
+++ b/providers/common/ai/docs/connections/pydantic_ai.rst
@@ -44,7 +44,7 @@ Model
     naming this connection rather than being resolved automatically.
 
     Examples: ``openai:gpt-5.6-sol``, ``anthropic:claude-sonnet-5``,
-    ``bedrock:us.anthropic.claude-opus-4-6-v1:0``, ``google:gemini-2.0-flash``
+    ``bedrock:us.anthropic.claude-opus-4-6-v1:0``, ``google:gemini-2.5-flash``
 
     See `Anthropic's models overview <https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison>`__
     for the current list of Claude model IDs across the Claude API, Amazon Bedrock, and Google Cloud.
@@ -145,7 +145,7 @@ in the environment:
 
     {
         "conn_type": "pydanticai",
-        "extra": "{\"model\": \"google:gemini-2.0-flash\"}"
+        "extra": "{\"model\": \"google:gemini-2.5-flash\"}"
     }
 
 This connects to the Gemini API (Google AI Studio), not Vertex AI — pydantic-ai's
diff --git a/providers/common/ai/docs/connections/pydantic_ai_vertex.rst b/providers/common/ai/docs/connections/pydantic_ai_vertex.rst
index 03503960491..bbf210e79b8 100644
--- a/providers/common/ai/docs/connections/pydantic_ai_vertex.rst
+++ b/providers/common/ai/docs/connections/pydantic_ai_vertex.rst
@@ -63,8 +63,8 @@ Configuring the Connection
 All fields below are ``extra`` (JSON) fields.
 
 Model
-    Google model identifier (e.g. ``google-cloud:gemini-2.0-flash``, or the
-    bare ``gemini-2.0-flash``). A bare name is automatically resolved to
+    Google model identifier (e.g. ``google-cloud:gemini-2.5-flash``, or the
+    bare ``gemini-2.5-flash``). A bare name is automatically resolved to
     ``google-cloud:<name>``, instantiating the ``GoogleCloudProvider`` that
     accepts this hook's ``project`` / ``location`` / ``service_account_info``
     fields (see "Credentials" below) -- Vertex AI is this connection type's
@@ -152,7 +152,7 @@ environment:
 
     {
         "conn_type": "pydanticai_vertex",
-        "extra": "{\"model\": \"google-cloud:gemini-2.0-flash\", \"project\": \"my-gcp-project\", \"location\": \"us-central1\"}"
+        "extra": "{\"model\": \"google-cloud:gemini-2.5-flash\", \"project\": \"my-gcp-project\", \"location\": \"us-central1\"}"
     }
 
 **Inline service account**
@@ -161,5 +161,5 @@ environment:
 
     {
         "conn_type": "pydanticai_vertex",
-        "extra": "{\"model\": \"google-cloud:gemini-2.0-flash\", \"project\": \"my-gcp-project\", \"location\": \"us-central1\", \"service_account_info\": {\"type\": \"service_account\", \"project_id\": \"my-gcp-project\", \"private_key\": \"<contents of the service account JSON key's private_key field>\", \"client_email\": \"sa@my-gcp-project.iam.gserviceaccount.com\"}}"
+        "extra": "{\"model\": \"google-cloud:gemini-2.5-flash\", \"project\": \"my-gcp-project\", \"location\": \"us-central1\", \"service_account_info\": {\"type\": \"service_account\", \"project_id\": \"my-gcp-project\", \"private_key\": \"<contents of the service account JSON key's private_key field>\", \"client_email\": \"sa@my-gcp-project.iam.gserviceaccount.com\"}}"
     }
diff --git a/providers/common/ai/provider.yaml b/providers/common/ai/provider.yaml
index 75b5f133e03..5dfd9867c37 100644
--- a/providers/common/ai/provider.yaml
+++ b/providers/common/ai/provider.yaml
@@ -345,13 +345,13 @@ connection-types:
         - password
       relabeling: {}
       placeholders:
-        extra: '{"model": "google-cloud:gemini-2.0-flash", "project": "my-project", "location":
+        extra: '{"model": "google-cloud:gemini-2.5-flash", "project": "my-project", "location":
           "us-central1"}  — add service_account_info (object) for SA auth; omit both to use Application
           Default Credentials'
     conn-fields:
       model:
         label: Model
-        description: "Google model identifier (e.g. google-cloud:gemini-2.0-flash)"
+        description: "Google model identifier (e.g. google-cloud:gemini-2.5-flash)"
         schema:
           type:
             - string
diff --git a/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py b/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
index 658f1f10e77..316b43f107f 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/get_provider_info.py
@@ -273,13 +273,13 @@ def get_provider_info():
                     "hidden-fields": ["schema", "port", "login", "host", "password"],
                     "relabeling": {},
                     "placeholders": {
-                        "extra": '{"model": "google-cloud:gemini-2.0-flash", "project": "my-project", "location": "us-central1"}  — add service_account_info (object) for SA auth; omit both to use Application Default Credentials'
+                        "extra": '{"model": "google-cloud:gemini-2.5-flash", "project": "my-project", "location": "us-central1"}  — add service_account_info (object) for SA auth; omit both to use Application Default Credentials'
                     },
                 },
                 "conn-fields": {
                     "model": {
                         "label": "Model",
-                        "description": "Google model identifier (e.g. google-cloud:gemini-2.0-flash)",
+                        "description": "Google model identifier (e.g. google-cloud:gemini-2.5-flash)",
                         "schema": {"type": ["string", "null"]},
                     },
                     "fallback_conn_ids": {
diff --git a/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py b/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
index 9d9e15d0c16..f4167487d25 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/hooks/pydantic_ai.py
@@ -795,7 +795,7 @@ class PydanticAIVertexHook(PydanticAIHook):
         - **extra** JSON::
 
             {
-                "model": "google-cloud:gemini-2.0-flash",
+                "model": "google-cloud:gemini-2.5-flash",
                 "project": "my-gcp-project",
                 "location": "us-central1",
                 "service_account_info": {...},
@@ -820,8 +820,8 @@ class PydanticAIVertexHook(PydanticAIHook):
     works today.
 
     :param llm_conn_id: Airflow connection ID.
-    :param model_id: Model identifier, e.g. ``"google-cloud:gemini-2.0-flash"``. A
-        bare name (e.g. ``"gemini-2.0-flash"``) defaults to Vertex AI; prefix with
+    :param model_id: Model identifier, e.g. ``"google-cloud:gemini-2.5-flash"``. A
+        bare name (e.g. ``"gemini-2.5-flash"``) defaults to Vertex AI; prefix with
         ``google:`` for the Generative Language API.
     """
 
@@ -838,7 +838,7 @@ class PydanticAIVertexHook(PydanticAIHook):
             "relabeling": {},
             "placeholders": {
                 "extra": (
-                    '{"model": "google-cloud:gemini-2.0-flash", '
+                    '{"model": "google-cloud:gemini-2.5-flash", '
                     '"project": "my-project", "location": "us-central1"}'
                     "  — add service_account_info (object) for SA auth;"
                     " omit both to use Application Default Credentials"
diff --git a/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py b/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
index 9db95fce059..da1670f3dc8 100644
--- a/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
+++ b/providers/common/ai/tests/unit/common/ai/hooks/test_pydantic_ai.py
@@ -46,7 +46,7 @@ from airflow.providers.common.ai.hooks.pydantic_ai import (
 from airflow.providers.common.compat.sdk import AirflowNotFoundException
 
 # Matches the `google...` provider key pydantic-ai expects before the `:model-name`
-# separator, e.g. "google-cloud" out of "google-cloud:gemini-2.0-flash".
+# separator, e.g. "google-cloud" out of "google-cloud:gemini-2.5-flash".
 _GOOGLE_MODEL_PREFIX_RE = re.compile(r"google[\w-]*(?=:)")
 
 
@@ -84,6 +84,33 @@ def _extract_google_cloud_prefix(text: str) -> str:
     return match.group()
 
 
+_KNOWN_SERVABLE_VERTEX_GEMINI_MODELS = frozenset({"gemini-2.5-flash"})
+
+_GOOGLE_CLOUD_MODEL_NAME_RE = re.compile(r"google-cloud:([\w.-]+)")
+
+
+def _assert_model_name_is_known_servable(text: str) -> None:
+    """
+    Extract the model name following a ``google-cloud:`` prefix in ``text`` and
+    assert it is in ``_KNOWN_SERVABLE_VERTEX_GEMINI_MODELS``.
+
+    The prefix-only checks above (``_assert_prefix_is_known_provider``) only
+    confirm pydantic-ai recognizes ``google-cloud`` as a provider; they never
+    check whether the specific model advertised after the colon is actually
+    servable, which is how a retired model name went unnoticed in the docs and
+    connection form.
+    """
+    match = _GOOGLE_CLOUD_MODEL_NAME_RE.search(text)
+    assert match, f"no google-cloud:<model> found in: {text!r}"
+    model_name = match.group(1)
+    assert model_name in _KNOWN_SERVABLE_VERTEX_GEMINI_MODELS, (
+        f"{model_name!r} is not in the maintained allowlist of known-servable Vertex "
+        f"Gemini models ({sorted(_KNOWN_SERVABLE_VERTEX_GEMINI_MODELS)}). If it has been "
+        "confirmed retired, replace it with a current model everywhere it's documented; "
+        "if it's a new, confirmed-servable model, add it to the allowlist."
+    )
+
+
 class TestPydanticAIHookInit:
     def test_default_conn_id(self):
         hook = PydanticAIHook()
@@ -416,11 +443,11 @@ class TestPydanticAIHookModelProviderResolution:
             "primary",
             conn_type="pydanticai_vertex",
             hook_class=PydanticAIVertexHook,
-            extra={"model": "gemini-2.0-flash", "api_key": "some-key"},
+            extra={"model": "gemini-2.5-flash", "api_key": "some-key"},
         )
         hook = PydanticAIVertexHook(llm_conn_id="primary")
 
-        assert hook.get_conn() is infer_model_stub.models["google-cloud:gemini-2.0-flash"]
+        assert hook.get_conn() is infer_model_stub.models["google-cloud:gemini-2.5-flash"]
 
     def test_bedrock_bare_model_id_with_embedded_colon_gets_platform_prefix(self, registry, infer_model_stub):
         """A ``:`` alone doesn't pin a platform -- Bedrock's own ids contain one.
@@ -534,14 +561,14 @@ class TestPydanticAIHookModelProviderResolution:
             "primary",
             conn_type="pydanticai_vertex",
             hook_class=PydanticAIVertexHook,
-            extra={"model": "google-vertex:gemini-2.0-flash", "api_key": "some-key"},
+            extra={"model": "google-vertex:gemini-2.5-flash", "api_key": "some-key"},
         )
         hook = PydanticAIVertexHook(llm_conn_id="primary")
 
         with caplog.at_level(logging.WARNING):
             model = hook.get_conn()
 
-        assert model is infer_model_stub.models["google-cloud:google-vertex:gemini-2.0-flash"]
+        assert model is infer_model_stub.models["google-cloud:google-vertex:gemini-2.5-flash"]
         assert any("google-vertex" in r.message and "typo" in r.message for r in caplog.records)
 
     def test_bedrock_native_id_does_not_warn(self, registry, infer_model_stub, caplog):
@@ -1689,7 +1716,7 @@ class TestPydanticAIVertexHook:
             None,
             None,
             {
-                "model": "google-cloud:gemini-2.0-flash",
+                "model": "google-cloud:gemini-2.5-flash",
                 "project": "my-project",
                 "location": "us-central1",
             },
@@ -1706,7 +1733,7 @@ class TestPydanticAIVertexHook:
         result = hook._get_provider_kwargs(
             None,
             None,
-            {"model": "google:gemini-2.0-flash", "api_key": "gla-key"},
+            {"model": "google:gemini-2.5-flash", "api_key": "gla-key"},
         )
         assert result["api_key"] == "gla-key"
 
@@ -1725,7 +1752,7 @@ class TestPydanticAIVertexHook:
             None,
             None,
             {
-                "model": "google-cloud:gemini-2.0-flash",
+                "model": "google-cloud:gemini-2.5-flash",
                 "project": "my-project",
                 "location": "us-central1",
                 "vertexai": vertexai_value,
@@ -1759,7 +1786,7 @@ class TestPydanticAIVertexHook:
                 None,
                 None,
                 {
-                    "model": "google-cloud:gemini-2.0-flash",
+                    "model": "google-cloud:gemini-2.5-flash",
                     "service_account_info": sa_info_dict,
                 },
             )
@@ -1774,7 +1801,7 @@ class TestPydanticAIVertexHook:
     def test_get_provider_kwargs_returns_empty_for_adc(self):
         """When no keys are in extra, return {} so ADC path is taken."""
         hook = PydanticAIVertexHook.__new__(PydanticAIVertexHook)
-        result = hook._get_provider_kwargs(None, None, {"model": "google-cloud:gemini-2.0-flash"})
+        result = hook._get_provider_kwargs(None, None, {"model": "google-cloud:gemini-2.5-flash"})
         assert result == {}
 
     @patch("airflow.providers.common.ai.hooks.pydantic_ai.infer_model", autospec=True)
@@ -1784,12 +1811,12 @@ class TestPydanticAIVertexHook:
         conn = Connection(
             conn_id="vertex_test",
             conn_type="pydanticai_vertex",
-            extra=json.dumps({"model": "google-cloud:gemini-2.0-flash"}),
+            extra=json.dumps({"model": "google-cloud:gemini-2.5-flash"}),
         )
         with patch.object(hook, "get_connection", return_value=conn):
             hook.get_conn()
 
-        mock_infer_model.assert_called_once_with("google-cloud:gemini-2.0-flash")
+        mock_infer_model.assert_called_once_with("google-cloud:gemini-2.5-flash")
 
     @patch("airflow.providers.common.ai.hooks.pydantic_ai.infer_model", autospec=True)
     @patch("airflow.providers.common.ai.hooks.pydantic_ai.infer_provider_class", autospec=True)
@@ -1804,7 +1831,7 @@ class TestPydanticAIVertexHook:
             conn_type="pydanticai_vertex",
             extra=json.dumps(
                 {
-                    "model": "google-cloud:gemini-2.0-flash",
+                    "model": "google-cloud:gemini-2.5-flash",
                     "project": "my-project",
                     "location": "europe-west4",
                 }
@@ -1862,7 +1889,7 @@ class TestPydanticAIVertexHook:
             conn_type="pydanticai_vertex",
             extra=json.dumps(
                 {
-                    "model": "google-cloud:gemini-2.0-flash",
+                    "model": "google-cloud:gemini-2.5-flash",
                     "project": "my-project",
                     "location": "us-central1",
                     "vertexai": True,
@@ -1937,6 +1964,26 @@ class TestPydanticAIVertexHook:
         prefix = _extract_google_cloud_prefix(placeholder)
         _assert_prefix_is_known_provider(prefix)
 
+    def test_conn_fields_model_description_names_a_servable_model(self):
+        connection_types = get_provider_info()["connection-types"]
+        vertex_conn_fields = next(
+            c["conn-fields"] for c in connection_types if c["connection-type"] == "pydanticai_vertex"
+        )
+        description = vertex_conn_fields["model"]["description"]
+        _assert_model_name_is_known_servable(description)
+
+    def test_conn_types_ui_field_behaviour_placeholder_names_a_servable_model(self):
+        connection_types = get_provider_info()["connection-types"]
+        vertex_connection_type = next(
+            c for c in connection_types if c["connection-type"] == "pydanticai_vertex"
+        )
+        placeholder = vertex_connection_type["ui-field-behaviour"]["placeholders"]["extra"]
+        _assert_model_name_is_known_servable(placeholder)
+
+    def test_ui_field_behaviour_placeholder_names_a_servable_model(self):
+        placeholder = PydanticAIVertexHook.get_ui_field_behaviour()["placeholders"]["extra"]
+        _assert_model_name_is_known_servable(placeholder)
+
 
 ALL_HOOKS = [PydanticAIHook, PydanticAIAzureHook, PydanticAIBedrockHook, PydanticAIVertexHook]
 DECLARED_CONNECTION_TYPES = [c["connection-type"] for c in get_provider_info()["connection-types"]]