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

potiuk 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 bc50c58768 Add Qdrant Provider (#36805)
bc50c58768 is described below

commit bc50c58768f436916f1bb7ceb6cfae038683c358
Author: Anush <[email protected]>
AuthorDate: Thu Feb 8 19:30:30 2024 +0530

    Add Qdrant Provider (#36805)
    
    
    Co-authored-by: Elad Kalif <[email protected]>
    Co-authored-by: Josh Fell <[email protected]>
---
 .../airflow_providers_bug_report.yml               |   1 +
 .github/boring-cyborg.yml                          |   6 +
 .github/workflows/ci.yml                           |   3 +
 INSTALL                                            |   7 +-
 airflow/providers/qdrant/CHANGELOG.rst             |  26 +++++
 airflow/providers/qdrant/__init__.py               |  16 +++
 airflow/providers/qdrant/hooks/__init__.py         |  16 +++
 airflow/providers/qdrant/hooks/qdrant.py           | 128 ++++++++++++++++++++
 airflow/providers/qdrant/operators/__init__.py     |  16 +++
 airflow/providers/qdrant/operators/qdrant.py       | 109 +++++++++++++++++
 airflow/providers/qdrant/provider.yaml             |  55 +++++++++
 airflow/utils/db.py                                |   9 ++
 .../12_airflow_dependencies_and_extras.rst         |   7 +-
 contributing-docs/testing/integration_tests.rst    |   2 +
 dev/breeze/doc/images/output-commands.svg          |   2 +-
 dev/breeze/doc/images/output_build-docs.svg        |   6 +-
 dev/breeze/doc/images/output_build-docs.txt        |   2 +-
 ...tput_release-management_add-back-references.svg |   6 +-
 ...tput_release-management_add-back-references.txt |   2 +-
 ...management_generate-issue-content-providers.svg |   6 +-
 ...management_generate-issue-content-providers.txt |   2 +-
 ...e-management_prepare-provider-documentation.svg |   6 +-
 ...e-management_prepare-provider-documentation.txt |   2 +-
 ...elease-management_prepare-provider-packages.svg |   6 +-
 ...elease-management_prepare-provider-packages.txt |   2 +-
 .../output_release-management_publish-docs.svg     |   6 +-
 .../output_release-management_publish-docs.txt     |   2 +-
 ...output_sbom_generate-providers-requirements.svg |   6 +-
 ...output_sbom_generate-providers-requirements.txt |   2 +-
 dev/breeze/doc/images/output_shell.svg             |   2 +-
 dev/breeze/doc/images/output_shell.txt             |   2 +-
 dev/breeze/doc/images/output_start-airflow.svg     |   2 +-
 dev/breeze/doc/images/output_start-airflow.txt     |   2 +-
 .../images/output_testing_integration-tests.svg    |   2 +-
 .../images/output_testing_integration-tests.txt    |   2 +-
 dev/breeze/doc/images/output_testing_tests.svg     |   2 +-
 dev/breeze/doc/images/output_testing_tests.txt     |   2 +-
 dev/breeze/src/airflow_breeze/global_constants.py  |  16 +--
 docs/apache-airflow-providers-qdrant/changelog.rst |  18 +++
 docs/apache-airflow-providers-qdrant/commits.rst   |  19 +++
 .../connections.rst                                |  55 +++++++++
 docs/apache-airflow-providers-qdrant/index.rst     |  98 ++++++++++++++++
 .../installing-providers-from-sources.rst          |  18 +++
 .../operators/qdrant.rst                           |  40 +++++++
 docs/apache-airflow-providers-qdrant/security.rst  |  18 +++
 docs/apache-airflow/extra-packages-ref.rst         |   2 +
 docs/spelling_wordlist.txt                         |   3 +
 generated/provider_dependencies.json               |  10 ++
 pyproject.toml                                     |   5 +
 scripts/ci/docker-compose/integration-qdrant.yml   |  34 ++++++
 scripts/in_container/check_environment.sh          |   7 ++
 tests/integration/providers/qdrant/__init__.py     |  16 +++
 .../integration/providers/qdrant/hooks/__init__.py |  16 +++
 .../providers/qdrant/hooks/test_qdrant.py          |  69 +++++++++++
 .../providers/qdrant/operators/__init__.py         |  16 +++
 .../qdrant/operators/test_qdrant_ingest.py         |  71 ++++++++++++
 tests/providers/qdrant/__init__.py                 |  16 +++
 tests/providers/qdrant/hooks/__init__.py           |  16 +++
 tests/providers/qdrant/hooks/test_qdrant.py        | 129 +++++++++++++++++++++
 tests/providers/qdrant/operators/__init__.py       |  16 +++
 tests/providers/qdrant/operators/test_qdrant.py    |  64 ++++++++++
 tests/system/providers/qdrant/__init__.py          |  16 +++
 .../system/providers/qdrant/example_dag_qdrant.py  |  49 ++++++++
 63 files changed, 1257 insertions(+), 55 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/airflow_providers_bug_report.yml 
b/.github/ISSUE_TEMPLATE/airflow_providers_bug_report.yml
index 3213bf8b3c..eeb201cbb0 100644
--- a/.github/ISSUE_TEMPLATE/airflow_providers_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/airflow_providers_bug_report.yml
@@ -90,6 +90,7 @@ body:
         - pinecone
         - postgres
         - presto
+        - qdrant
         - redis
         - salesforce
         - samba
diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml
index 4fbcfcf85e..c18afde719 100644
--- a/.github/boring-cyborg.yml
+++ b/.github/boring-cyborg.yml
@@ -403,6 +403,12 @@ labelPRBasedOnFilePath:
     - tests/providers/presto/**/*
     - tests/system/providers/presto/**/*
 
+  provider:qdrant:
+    - airflow/providers/qdrant/**/*
+    - docs/apache-airflow-providers-qdrant/**/*
+    - tests/providers/qdrant/**/*
+    - tests/system/providers/qdrant/**/*
+
   provider:redis:
     - airflow/providers/redis/**/*
     - docs/apache-airflow-providers-redis/**/*
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 153b00fed1..8147ee77ef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1540,6 +1540,9 @@ jobs:
           breeze testing integration-tests --integration kafka
           breeze down
         if: needs.build-info.outputs.is-airflow-runner != 'true'
+      - name: "Integration Tests Postgres: Qdrant"
+        run: breeze testing integration-tests --integration qdrant
+        if: needs.build-info.outputs.is-airflow-runner == 'true'
       - name: "Integration Tests Postgres: all-testable"
         run: breeze testing integration-tests --integration all-testable
         if: needs.build-info.outputs.is-airflow-runner == 'true'
diff --git a/INSTALL b/INSTALL
index 173442e485..082858264c 100644
--- a/INSTALL
+++ b/INSTALL
@@ -253,9 +253,10 @@ gcp_api, github, github-enterprise, google, google-auth, 
graphviz, grpc, hashico
 http, imap, influxdb, jdbc, jenkins, kerberos, kubernetes, ldap, leveldb, 
microsoft-azure,
 microsoft-mssql, microsoft-psrp, microsoft-winrm, mongo, mssql, mysql, neo4j, 
odbc, openai,
 openfaas, openlineage, opensearch, opsgenie, oracle, otel, pagerduty, pandas, 
papermill, password,
-pgvector, pinecone, pinot, postgres, presto, rabbitmq, redis, s3, s3fs, 
salesforce, samba, saml,
-segment, sendgrid, sentry, sftp, singularity, slack, smtp, snowflake, spark, 
sqlite, ssh, statsd,
-tableau, tabular, telegram, trino, vertica, virtualenv, weaviate, webhdfs, 
winrm, yandex, zendesk
+pgvector, pinecone, pinot, postgres, presto, qdrant, rabbitmq, redis, s3, 
s3fs, salesforce, samba,
+saml, segment, sendgrid, sentry, sftp, singularity, slack, smtp, snowflake, 
spark, sqlite, ssh,
+statsd, tableau, tabular, telegram, trino, vertica, virtualenv, weaviate, 
webhdfs, winrm, yandex,
+zendesk
 
 # END REGULAR EXTRAS HERE
 
diff --git a/airflow/providers/qdrant/CHANGELOG.rst 
b/airflow/providers/qdrant/CHANGELOG.rst
new file mode 100644
index 0000000000..460738e87f
--- /dev/null
+++ b/airflow/providers/qdrant/CHANGELOG.rst
@@ -0,0 +1,26 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+``apache-airflow-providers-qdrant``
+
+Changelog
+---------
+
+1.0.0
+.....
+
+* ``Initial version of the provider. (#36805)``
diff --git a/airflow/providers/qdrant/__init__.py 
b/airflow/providers/qdrant/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/airflow/providers/qdrant/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/airflow/providers/qdrant/hooks/__init__.py 
b/airflow/providers/qdrant/hooks/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/airflow/providers/qdrant/hooks/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/airflow/providers/qdrant/hooks/qdrant.py 
b/airflow/providers/qdrant/hooks/qdrant.py
new file mode 100644
index 0000000000..494e4d57ed
--- /dev/null
+++ b/airflow/providers/qdrant/hooks/qdrant.py
@@ -0,0 +1,128 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from functools import cached_property
+from typing import Any
+
+from grpc import RpcError
+from qdrant_client import QdrantClient
+from qdrant_client.http.exceptions import UnexpectedResponse
+
+from airflow.hooks.base import BaseHook
+
+
+class QdrantHook(BaseHook):
+    """
+    Hook for interfacing with a Qdrant instance.
+
+    :param conn_id: The connection id to use when connecting to Qdrant. 
Defaults to `qdrant_default`.
+    """
+
+    conn_name_attr = "conn_id"
+    conn_type = "qdrant"
+    default_conn_name = "qdrant_default"
+    hook_name = "Qdrant"
+
+    @classmethod
+    def get_connection_form_widgets(cls) -> dict[str, Any]:
+        """Returns connection widgets to add to connection form."""
+        from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
+        from flask_babel import lazy_gettext
+        from wtforms import BooleanField, IntegerField, StringField
+
+        return {
+            "url": StringField(
+                lazy_gettext("URL"),
+                widget=BS3TextFieldWidget(),
+                description="Optional. Qualified URL of the Qdrant instance."
+                "Example: 
https://xyz-example.eu-central.aws.cloud.qdrant.io:6333";,
+            ),
+            "grpc_port": IntegerField(
+                lazy_gettext("GPRC Port"),
+                widget=BS3TextFieldWidget(),
+                description="Optional. Port of the gRPC interface.",
+                default=6334,
+            ),
+            "prefer_gprc": BooleanField(
+                lazy_gettext("Prefer GRPC"),
+                widget=BS3TextFieldWidget(),
+                description="Optional. Whether to use gPRC interface whenever 
possible in custom methods.",
+                default=False,
+            ),
+            "https": BooleanField(
+                lazy_gettext("HTTPS"),
+                widget=BS3TextFieldWidget(),
+                description="Optional. Whether to use HTTPS(SSL) protocol.",
+            ),
+            "prefix": StringField(
+                lazy_gettext("Prefix"),
+                widget=BS3TextFieldWidget(),
+                description="Optional. Prefix to the REST URL path."
+                "Example: `service/v1` will result in 
http://localhost:6333/service/v1/{qdrant-endpoint} for REST API.",
+            ),
+        }
+
+    @classmethod
+    def get_ui_field_behaviour(cls) -> dict[str, Any]:
+        """Returns custom field behaviour."""
+        return {
+            "hidden_fields": ["schema", "login", "extra"],
+            "relabeling": {"password": "API Key"},
+        }
+
+    def __init__(self, conn_id: str = default_conn_name, **kwargs) -> None:
+        super().__init__(**kwargs)
+        self.conn_id = conn_id
+
+    def get_conn(self) -> QdrantClient:
+        """Get a Qdrant client instance for interfacing with the database."""
+        connection = self.get_connection(self.conn_id)
+        host = connection.host or None
+        port = connection.port or 6333
+        api_key = connection.password
+        extra = connection.extra_dejson
+        url = extra.get("url", None)
+        grpc_port = extra.get("grpc_port", 6334)
+        prefer_gprc = extra.get("prefer_gprc", False)
+        https = extra.get("https", None)
+        prefix = extra.get("prefix", None)
+
+        return QdrantClient(
+            host=host,
+            port=port,
+            url=url,
+            api_key=api_key,
+            grpc_port=grpc_port,
+            prefer_grpc=prefer_gprc,
+            https=https,
+            prefix=prefix,
+        )
+
+    @cached_property
+    def conn(self) -> QdrantClient:
+        """Get a Qdrant client instance for interfacing with the database."""
+        return self.get_conn()
+
+    def verify_connection(self) -> tuple[bool, str]:
+        """Check the connection to the Qdrant instance."""
+        try:
+            self.conn.get_collections()
+            return True, "Connection established!"
+        except (UnexpectedResponse, RpcError, ValueError) as e:
+            return False, str(e)
diff --git a/airflow/providers/qdrant/operators/__init__.py 
b/airflow/providers/qdrant/operators/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/airflow/providers/qdrant/operators/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/airflow/providers/qdrant/operators/qdrant.py 
b/airflow/providers/qdrant/operators/qdrant.py
new file mode 100644
index 0000000000..6cc71d864b
--- /dev/null
+++ b/airflow/providers/qdrant/operators/qdrant.py
@@ -0,0 +1,109 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from functools import cached_property
+from typing import TYPE_CHECKING, Any, Iterable, Sequence
+
+from airflow.models import BaseOperator
+from airflow.providers.qdrant.hooks.qdrant import QdrantHook
+
+if TYPE_CHECKING:
+    from qdrant_client.models import VectorStruct
+
+    from airflow.utils.context import Context
+
+
+class QdrantIngestOperator(BaseOperator):
+    """
+    Upload points to a Qdrant collection.
+
+    .. seealso::
+        For more information on how to use this operator, take a look at the 
guide:
+        :ref:`howto/operator:QdrantIngestOperator`
+
+    :param conn_id: The connection id to connect to a Qdrant instance.
+    :param collection_name: The name of the collection to ingest data into.
+    :param vectors: An iterable over vectors to upload.
+    :param payload: Iterable of vector payloads, Optional. Defaults to None.
+    :param ids: Iterable of custom vector ids, Optional. Defaults to None.
+    :param batch_size: Number of points to upload per-request. Defaults to 64.
+    :param parallel: Number of parallel upload processes. Defaults to 1.
+    :param method: Start method for parallel processes. Defaults to 
'forkserver'.
+    :param max_retries: Number of retries for failed requests. Defaults to 3.
+    :param wait: Await for the results to be applied on the server side. 
Defaults to True.
+    :param kwargs: Additional keyword arguments passed to the BaseOperator 
constructor.
+    """
+
+    template_fields: Sequence[str] = (
+        "collection_name",
+        "vectors",
+        "payload",
+        "ids",
+        "batch_size",
+        "parallel",
+        "method",
+        "max_retries",
+        "wait",
+    )
+
+    def __init__(
+        self,
+        *,
+        conn_id: str = QdrantHook.default_conn_name,
+        collection_name: str,
+        vectors: Iterable[VectorStruct],
+        payload: Iterable[dict[str, Any]] | None = None,
+        ids: Iterable[int | str] | None = None,
+        batch_size: int = 64,
+        parallel: int = 1,
+        method: str | None = None,
+        max_retries: int = 3,
+        wait: bool = True,
+        **kwargs: Any,
+    ) -> None:
+        super().__init__(**kwargs)
+        self.conn_id = conn_id
+        self.collection_name = collection_name
+        self.vectors = vectors
+        self.payload = payload
+        self.ids = ids
+        self.batch_size = batch_size
+        self.parallel = parallel
+        self.method = method
+        self.max_retries = max_retries
+        self.wait = wait
+
+    @cached_property
+    def hook(self) -> QdrantHook:
+        """Return an instance of QdrantHook."""
+        return QdrantHook(conn_id=self.conn_id)
+
+    def execute(self, context: Context) -> None:
+        """Upload points to a Qdrant collection."""
+        self.hook.conn.upload_collection(
+            collection_name=self.collection_name,
+            vectors=self.vectors,
+            payload=self.payload,
+            ids=self.ids,
+            batch_size=self.batch_size,
+            parallel=self.parallel,
+            method=self.method,
+            max_retries=self.max_retries,
+            wait=self.wait,
+        )
diff --git a/airflow/providers/qdrant/provider.yaml 
b/airflow/providers/qdrant/provider.yaml
new file mode 100644
index 0000000000..433fe08f9c
--- /dev/null
+++ b/airflow/providers/qdrant/provider.yaml
@@ -0,0 +1,55 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+---
+package-name: apache-airflow-providers-qdrant
+
+name: Qdrant
+
+description: |
+    `Qdrant <https://qdrant.tech/documentation>`__
+
+state: ready
+source-date-epoch: 1705379899
+
+versions:
+  - 1.0.0
+
+integrations:
+  - integration-name: Qdrant
+    external-doc-url: https://qdrant.tech/documentation
+    how-to-guide:
+      - /docs/apache-airflow-providers-qdrant/operators/qdrant.rst
+    tags: [software]
+
+dependencies:
+  - qdrant_client>=1.7.0
+  - apache-airflow>=2.7.0
+
+hooks:
+  - integration-name: Qdrant
+    python-modules:
+      - airflow.providers.qdrant.hooks.qdrant
+
+connection-types:
+  - hook-class-name: airflow.providers.qdrant.hooks.qdrant.QdrantHook
+    connection-type: qdrant
+
+operators:
+  - integration-name: Qdrant
+    python-modules:
+      - airflow.providers.qdrant.operators.qdrant
diff --git a/airflow/utils/db.py b/airflow/utils/db.py
index b9ee832361..e2de7666d1 100644
--- a/airflow/utils/db.py
+++ b/airflow/utils/db.py
@@ -541,6 +541,15 @@ def create_default_connections(session: Session = 
NEW_SESSION):
         ),
         session,
     )
+    merge_conn(
+        Connection(
+            conn_id="qdrant_default",
+            conn_type="qdrant",
+            host="qdrant",
+            port=6333,
+        ),
+        session,
+    )
     merge_conn(
         Connection(
             conn_id="redis_default",
diff --git a/contributing-docs/12_airflow_dependencies_and_extras.rst 
b/contributing-docs/12_airflow_dependencies_and_extras.rst
index c9124d1ccb..270f567afd 100644
--- a/contributing-docs/12_airflow_dependencies_and_extras.rst
+++ b/contributing-docs/12_airflow_dependencies_and_extras.rst
@@ -209,9 +209,10 @@ gcp_api, github, github-enterprise, google, google-auth, 
graphviz, grpc, hashico
 http, imap, influxdb, jdbc, jenkins, kerberos, kubernetes, ldap, leveldb, 
microsoft-azure,
 microsoft-mssql, microsoft-psrp, microsoft-winrm, mongo, mssql, mysql, neo4j, 
odbc, openai,
 openfaas, openlineage, opensearch, opsgenie, oracle, otel, pagerduty, pandas, 
papermill, password,
-pgvector, pinecone, pinot, postgres, presto, rabbitmq, redis, s3, s3fs, 
salesforce, samba, saml,
-segment, sendgrid, sentry, sftp, singularity, slack, smtp, snowflake, spark, 
sqlite, ssh, statsd,
-tableau, tabular, telegram, trino, vertica, virtualenv, weaviate, webhdfs, 
winrm, yandex, zendesk
+pgvector, pinecone, pinot, postgres, presto, qdrant, rabbitmq, redis, s3, 
s3fs, salesforce, samba,
+saml, segment, sendgrid, sentry, sftp, singularity, slack, smtp, snowflake, 
spark, sqlite, ssh,
+statsd, tableau, tabular, telegram, trino, vertica, virtualenv, weaviate, 
webhdfs, winrm, yandex,
+zendesk
 
   .. END REGULAR EXTRAS HERE
 
diff --git a/contributing-docs/testing/integration_tests.rst 
b/contributing-docs/testing/integration_tests.rst
index 2d698f6cb8..8650df1b50 100644
--- a/contributing-docs/testing/integration_tests.rst
+++ b/contributing-docs/testing/integration_tests.rst
@@ -64,6 +64,8 @@ The following integrations are available:
 +--------------+----------------------------------------------------+
 | pinot        | Integration required for Apache Pinot hooks.       |
 +--------------+----------------------------------------------------+
+| qdrant       | Integration required for Qdrant tests.             |
++--------------+----------------------------------------------------+
 | statsd       | Integration required for Statsd hooks.             |
 +--------------+----------------------------------------------------+
 | trino        | Integration required for Trino hooks.              |
diff --git a/dev/breeze/doc/images/output-commands.svg 
b/dev/breeze/doc/images/output-commands.svg
index 3837045fc3..f0886818f2 100644
--- a/dev/breeze/doc/images/output-commands.svg
+++ b/dev/breeze/doc/images/output-commands.svg
@@ -285,7 +285,7 @@
 </text><text class="breeze-help-r5" x="0" y="166.4" textLength="12.2" 
clip-path="url(#breeze-help-line-6)">│</text><text class="breeze-help-r5" 
x="414.8" y="166.4" textLength="732" 
clip-path="url(#breeze-help-line-6)">[default:&#160;3.8]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
 </text><text class="breeze-help-r5" x="0" y="190.8" textLength="12.2" 
clip-path="url(#breeze-help-line-7)">│</text><text class="breeze-help-r4" 
x="24.4" y="190.8" textLength="12.2" 
clip-path="url(#breeze-help-line-7)">-</text><text class="breeze-help-r4" 
x="36.6" y="190.8" textLength="146.4" 
clip-path="url(#breeze-help-line-7)">-integration</text><text 
class="breeze-help-r1" x="414.8" y="190.8" textLength="1024.8" 
clip-path="url(#breeze-help-line-7)">Integration(s)&#160;to&#160;enable&#1 [...]
 </text><text class="breeze-help-r5" x="0" y="215.2" textLength="12.2" 
clip-path="url(#breeze-help-line-8)">│</text><text class="breeze-help-r7" 
x="414.8" y="215.2" textLength="1024.8" 
clip-path="url(#breeze-help-line-8)">(all&#160;|&#160;all-testable&#160;|&#160;cassandra&#160;|&#160;celery&#160;|&#160;kafka&#160;|&#160;kerberos&#160;|&#160;mongo&#160;|&#160;openlineage&#160;|&#160;</text><text
 class="breeze-help-r5" x="1451.8" y="215.2" textLength="12.2" 
clip-path="url(#breeze-help-line [...]
-</text><text class="breeze-help-r5" x="0" y="239.6" textLength="12.2" 
clip-path="url(#breeze-help-line-9)">│</text><text class="breeze-help-r7" 
x="414.8" y="239.6" textLength="1024.8" 
clip-path="url(#breeze-help-line-9)">otel&#160;|&#160;pinot&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&
 [...]
+</text><text class="breeze-help-r5" x="0" y="239.6" textLength="12.2" 
clip-path="url(#breeze-help-line-9)">│</text><text class="breeze-help-r7" 
x="414.8" y="239.6" textLength="1024.8" 
clip-path="url(#breeze-help-line-9)">otel&#160;|&#160;pinot&#160;|&#160;qdrant&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 [...]
 </text><text class="breeze-help-r5" x="0" y="264" textLength="12.2" 
clip-path="url(#breeze-help-line-10)">│</text><text class="breeze-help-r4" 
x="24.4" y="264" textLength="12.2" 
clip-path="url(#breeze-help-line-10)">-</text><text class="breeze-help-r4" 
x="36.6" y="264" textLength="134.2" 
clip-path="url(#breeze-help-line-10)">-standalone</text><text 
class="breeze-help-r4" x="170.8" y="264" textLength="170.8" 
clip-path="url(#breeze-help-line-10)">-dag-processor</text><text 
class="breeze-he [...]
 </text><text class="breeze-help-r5" x="0" y="288.4" textLength="12.2" 
clip-path="url(#breeze-help-line-11)">│</text><text class="breeze-help-r4" 
x="24.4" y="288.4" textLength="12.2" 
clip-path="url(#breeze-help-line-11)">-</text><text class="breeze-help-r4" 
x="36.6" y="288.4" textLength="109.8" 
clip-path="url(#breeze-help-line-11)">-database</text><text 
class="breeze-help-r4" x="146.4" y="288.4" textLength="122" 
clip-path="url(#breeze-help-line-11)">-isolation</text><text class="breeze-he 
[...]
 </text><text class="breeze-help-r5" x="0" y="312.8" textLength="1464" 
clip-path="url(#breeze-help-line-12)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-help-r1" x="1464" y="312.8" textLength="12.2" 
clip-path="url(#breeze-help-line-12)">
diff --git a/dev/breeze/doc/images/output_build-docs.svg 
b/dev/breeze/doc/images/output_build-docs.svg
index dabcb66854..bab81e920a 100644
--- a/dev/breeze/doc/images/output_build-docs.svg
+++ b/dev/breeze/doc/images/output_build-docs.svg
@@ -182,9 +182,9 @@
 </text><text class="breeze-build-docs-r4" x="12.2" y="190.8" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-7)">dbt</text><text 
class="breeze-build-docs-r1" x="48.8" y="190.8" textLength="12.2" 
clip-path="url(#breeze-build-docs-line-7)">.</text><text 
class="breeze-build-docs-r4" x="61" y="190.8" textLength="61" 
clip-path="url(#breeze-build-docs-line-7)">cloud</text><text 
class="breeze-build-docs-r1" x="122" y="190.8" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-7)"> [...]
 </text><text class="breeze-build-docs-r4" x="12.2" y="215.2" textLength="73.2" 
clip-path="url(#breeze-build-docs-line-8)">google</text><text 
class="breeze-build-docs-r1" x="85.4" y="215.2" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-8)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="122" y="215.2" textLength="48.8" 
clip-path="url(#breeze-build-docs-line-8)">grpc</text><text 
class="breeze-build-docs-r1" x="170.8" y="215.2" textLength="36.6" 
clip-path="url(#breeze-b [...]
 </text><text class="breeze-build-docs-r4" x="12.2" y="239.6" 
textLength="109.8" 
clip-path="url(#breeze-build-docs-line-9)">microsoft</text><text 
class="breeze-build-docs-r1" x="122" y="239.6" textLength="12.2" 
clip-path="url(#breeze-build-docs-line-9)">.</text><text 
class="breeze-build-docs-r4" x="134.2" y="239.6" textLength="48.8" 
clip-path="url(#breeze-build-docs-line-9)">psrp</text><text 
class="breeze-build-docs-r1" x="183" y="239.6" textLength="36.6" 
clip-path="url(#breeze-build-docs [...]
-</text><text class="breeze-build-docs-r4" x="12.2" y="264" textLength="97.6" 
clip-path="url(#breeze-build-docs-line-10)">opsgenie</text><text 
class="breeze-build-docs-r1" x="109.8" y="264" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-10)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="146.4" y="264" textLength="73.2" 
clip-path="url(#breeze-build-docs-line-10)">oracle</text><text 
class="breeze-build-docs-r1" x="219.6" y="264" textLength="36.6" 
clip-path="url(#breeze [...]
-</text><text class="breeze-build-docs-r4" x="12.2" y="288.4" textLength="85.4" 
clip-path="url(#breeze-build-docs-line-11)">segment</text><text 
class="breeze-build-docs-r1" x="97.6" y="288.4" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-11)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="134.2" y="288.4" textLength="97.6" 
clip-path="url(#breeze-build-docs-line-11)">sendgrid</text><text 
class="breeze-build-docs-r1" x="231.8" y="288.4" textLength="36.6" 
clip-path="url [...]
-</text><text class="breeze-build-docs-r4" x="12.2" y="312.8" textLength="61" 
clip-path="url(#breeze-build-docs-line-12)">trino</text><text 
class="breeze-build-docs-r1" x="73.2" y="312.8" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-12)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="109.8" y="312.8" textLength="85.4" 
clip-path="url(#breeze-build-docs-line-12)">vertica</text><text 
class="breeze-build-docs-r1" x="195.2" y="312.8" textLength="36.6" 
clip-path="url(#bre [...]
+</text><text class="breeze-build-docs-r4" x="12.2" y="264" textLength="97.6" 
clip-path="url(#breeze-build-docs-line-10)">opsgenie</text><text 
class="breeze-build-docs-r1" x="109.8" y="264" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-10)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="146.4" y="264" textLength="73.2" 
clip-path="url(#breeze-build-docs-line-10)">oracle</text><text 
class="breeze-build-docs-r1" x="219.6" y="264" textLength="36.6" 
clip-path="url(#breeze [...]
+</text><text class="breeze-build-docs-r4" x="12.2" y="288.4" textLength="61" 
clip-path="url(#breeze-build-docs-line-11)">samba</text><text 
class="breeze-build-docs-r1" x="73.2" y="288.4" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-11)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="109.8" y="288.4" textLength="85.4" 
clip-path="url(#breeze-build-docs-line-11)">segment</text><text 
class="breeze-build-docs-r1" x="195.2" y="288.4" textLength="36.6" 
clip-path="url(#bre [...]
+</text><text class="breeze-build-docs-r4" x="12.2" y="312.8" textLength="97.6" 
clip-path="url(#breeze-build-docs-line-12)">telegram</text><text 
class="breeze-build-docs-r1" x="109.8" y="312.8" textLength="36.6" 
clip-path="url(#breeze-build-docs-line-12)">&#160;|&#160;</text><text 
class="breeze-build-docs-r4" x="146.4" y="312.8" textLength="61" 
clip-path="url(#breeze-build-docs-line-12)">trino</text><text 
class="breeze-build-docs-r1" x="207.4" y="312.8" textLength="36.6" 
clip-path="url(#b [...]
 </text><text class="breeze-build-docs-r1" x="1464" y="337.2" textLength="12.2" 
clip-path="url(#breeze-build-docs-line-13)">
 </text><text class="breeze-build-docs-r1" x="12.2" y="361.6" 
textLength="195.2" 
clip-path="url(#breeze-build-docs-line-14)">Build&#160;documents.</text><text 
class="breeze-build-docs-r1" x="1464" y="361.6" textLength="12.2" 
clip-path="url(#breeze-build-docs-line-14)">
 </text><text class="breeze-build-docs-r1" x="1464" y="386" textLength="12.2" 
clip-path="url(#breeze-build-docs-line-15)">
diff --git a/dev/breeze/doc/images/output_build-docs.txt 
b/dev/breeze/doc/images/output_build-docs.txt
index b580d9d01c..1a93849cc2 100644
--- a/dev/breeze/doc/images/output_build-docs.txt
+++ b/dev/breeze/doc/images/output_build-docs.txt
@@ -1 +1 @@
-b4bc09e22159b362651e9dd299f795c2
+8f5e4f90611d600cbd02ae9e79d60df2
diff --git 
a/dev/breeze/doc/images/output_release-management_add-back-references.svg 
b/dev/breeze/doc/images/output_release-management_add-back-references.svg
index a13066ce0d..7492e1d03f 100644
--- a/dev/breeze/doc/images/output_release-management_add-back-references.svg
+++ b/dev/breeze/doc/images/output_release-management_add-back-references.svg
@@ -144,9 +144,9 @@
 </text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-7)">dbt</text><text
 class="breeze-release-management-add-back-references-r1" x="48.8" y="190.8" 
textLength="12.2" 
clip-path="url(#breeze-release-management-add-back-references-line-7)">.</text><text
 class="breeze-release-management-add-back-references-r4" x="61" y="190.8" 
textLength="61" clip-path="url(#breeze-releas [...]
 </text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="215.2" textLength="73.2" 
clip-path="url(#breeze-release-management-add-back-references-line-8)">google</text><text
 class="breeze-release-management-add-back-references-r1" x="85.4" y="215.2" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-8)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="122" y="215.2" 
textLength="48.8" clip-path=" [...]
 </text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="239.6" textLength="109.8" 
clip-path="url(#breeze-release-management-add-back-references-line-9)">microsoft</text><text
 class="breeze-release-management-add-back-references-r1" x="122" y="239.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-add-back-references-line-9)">.</text><text
 class="breeze-release-management-add-back-references-r4" x="134.2" y="239.6" 
textLength="48.8" clip-path="url(#br [...]
-</text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="264" textLength="97.6" 
clip-path="url(#breeze-release-management-add-back-references-line-10)">opsgenie</text><text
 class="breeze-release-management-add-back-references-r1" x="109.8" y="264" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="146.4" y="264" 
textLength="73.2" clip-path= [...]
-</text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="288.4" textLength="85.4" 
clip-path="url(#breeze-release-management-add-back-references-line-11)">segment</text><text
 class="breeze-release-management-add-back-references-r1" x="97.6" y="288.4" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="134.2" y="288.4" 
textLength="97.6" clip-p [...]
-</text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="312.8" textLength="61" 
clip-path="url(#breeze-release-management-add-back-references-line-12)">trino</text><text
 class="breeze-release-management-add-back-references-r1" x="73.2" y="312.8" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-12)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="109.8" y="312.8" 
textLength="85.4" clip-path= [...]
+</text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="264" textLength="97.6" 
clip-path="url(#breeze-release-management-add-back-references-line-10)">opsgenie</text><text
 class="breeze-release-management-add-back-references-r1" x="109.8" y="264" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="146.4" y="264" 
textLength="73.2" clip-path= [...]
+</text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="288.4" textLength="61" 
clip-path="url(#breeze-release-management-add-back-references-line-11)">samba</text><text
 class="breeze-release-management-add-back-references-r1" x="73.2" y="288.4" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="109.8" y="288.4" 
textLength="85.4" clip-path= [...]
+</text><text class="breeze-release-management-add-back-references-r4" x="12.2" 
y="312.8" textLength="97.6" 
clip-path="url(#breeze-release-management-add-back-references-line-12)">telegram</text><text
 class="breeze-release-management-add-back-references-r1" x="109.8" y="312.8" 
textLength="36.6" 
clip-path="url(#breeze-release-management-add-back-references-line-12)">&#160;|&#160;</text><text
 class="breeze-release-management-add-back-references-r4" x="146.4" y="312.8" 
textLength="61" clip-p [...]
 </text><text class="breeze-release-management-add-back-references-r1" x="1464" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-add-back-references-line-13)">
 </text><text class="breeze-release-management-add-back-references-r1" x="12.2" 
y="361.6" textLength="976" 
clip-path="url(#breeze-release-management-add-back-references-line-14)">Command&#160;to&#160;add&#160;back&#160;references&#160;for&#160;documentation&#160;to&#160;make&#160;it&#160;backward&#160;compatible.</text><text
 class="breeze-release-management-add-back-references-r1" x="1464" y="361.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-add-back-references-line-14)">
 </text><text class="breeze-release-management-add-back-references-r1" x="1464" 
y="386" textLength="12.2" 
clip-path="url(#breeze-release-management-add-back-references-line-15)">
diff --git 
a/dev/breeze/doc/images/output_release-management_add-back-references.txt 
b/dev/breeze/doc/images/output_release-management_add-back-references.txt
index c7d57a09d3..bd8679b924 100644
--- a/dev/breeze/doc/images/output_release-management_add-back-references.txt
+++ b/dev/breeze/doc/images/output_release-management_add-back-references.txt
@@ -1 +1 @@
-6ceda71ff8edfe80c678c2a6c844f22d
+af0db4105f4aec228083f240d550bda3
diff --git 
a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
 
b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
index e8e193e1d1..0646eb5871 100644
--- 
a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
+++ 
b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.svg
@@ -144,9 +144,9 @@
 </text><text 
class="breeze-release-management-generate-issue-content-providers-r1" x="12.2" 
y="166.4" textLength="24.4" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-6)">|&#160;</text><text
 class="breeze-release-management-generate-issue-content-providers-r4" x="36.6" 
y="166.4" textLength="73.2" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-6)">common</text><text
 class="breeze-release-management-generate-issue-content-pr [...]
 </text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-7)">ftp</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" x="48.8" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-7)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue-content [...]
 </text><text 
class="breeze-release-management-generate-issue-content-providers-r1" x="12.2" 
y="215.2" textLength="24.4" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-8)">|&#160;</text><text
 class="breeze-release-management-generate-issue-content-providers-r4" x="36.6" 
y="215.2" textLength="109.8" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-8)">microsoft</text><text
 class="breeze-release-management-generate-issue-conten [...]
-</text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="239.6" textLength="97.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-9)">opsgenie</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" 
x="109.8" y="239.6" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-9)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue-c [...]
-</text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="264" textLength="85.4" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-10)">segment</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" x="97.6" 
y="264" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue-conte [...]
-</text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="288.4" textLength="61" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-11)">trino</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" x="73.2" 
y="288.4" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue-conte [...]
+</text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="239.6" textLength="97.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-9)">opsgenie</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" 
x="109.8" y="239.6" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-9)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue-c [...]
+</text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="264" textLength="61" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-10)">samba</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" x="73.2" 
y="264" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue-content-p [...]
+</text><text 
class="breeze-release-management-generate-issue-content-providers-r4" x="12.2" 
y="288.4" textLength="97.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-11)">telegram</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" 
x="109.8" y="288.4" textLength="36.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-generate-issue [...]
 </text><text 
class="breeze-release-management-generate-issue-content-providers-r1" x="1464" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-12)">
 </text><text 
class="breeze-release-management-generate-issue-content-providers-r1" x="12.2" 
y="337.2" textLength="585.6" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-13)">Generates&#160;content&#160;for&#160;issue&#160;to&#160;test&#160;the&#160;release.</text><text
 class="breeze-release-management-generate-issue-content-providers-r1" x="1464" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-13)">
 </text><text 
class="breeze-release-management-generate-issue-content-providers-r1" x="1464" 
y="361.6" textLength="12.2" 
clip-path="url(#breeze-release-management-generate-issue-content-providers-line-14)">
diff --git 
a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
 
b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
index aa42535eac..36d7be3c99 100644
--- 
a/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
+++ 
b/dev/breeze/doc/images/output_release-management_generate-issue-content-providers.txt
@@ -1 +1 @@
-8109ff3d327a5e6921b15402f9c15bb6
+97f88e5ddbf7bd0f8de4fb734c8a2386
diff --git 
a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
 
b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
index 561b4a627d..34893298db 100644
--- 
a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
+++ 
b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
@@ -180,9 +180,9 @@
 </text><text 
class="breeze-release-management-prepare-provider-documentation-r1" x="12.2" 
y="166.4" textLength="24.4" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-6)">|&#160;</text><text
 class="breeze-release-management-prepare-provider-documentation-r4" x="36.6" 
y="166.4" textLength="73.2" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-6)">common</text><text
 class="breeze-release-management-prepare-provider-documentation-r1 [...]
 </text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-7)">ftp</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="48.8" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-7)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-documentation [...]
 </text><text 
class="breeze-release-management-prepare-provider-documentation-r1" x="12.2" 
y="215.2" textLength="24.4" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-8)">|&#160;</text><text
 class="breeze-release-management-prepare-provider-documentation-r4" x="36.6" 
y="215.2" textLength="109.8" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-8)">microsoft</text><text
 class="breeze-release-management-prepare-provider-documentatio [...]
-</text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="239.6" textLength="97.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-9)">opsgenie</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="109.8" 
y="239.6" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-9)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-documen [...]
-</text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="264" textLength="85.4" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-10)">segment</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="97.6" 
y="264" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-documentati [...]
-</text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="288.4" textLength="61" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-11)">trino</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="73.2" 
y="288.4" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-documentati [...]
+</text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="239.6" textLength="97.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-9)">opsgenie</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="109.8" 
y="239.6" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-9)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-documen [...]
+</text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="264" textLength="61" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-10)">samba</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="73.2" 
y="264" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-documentation-r [...]
+</text><text 
class="breeze-release-management-prepare-provider-documentation-r4" x="12.2" 
y="288.4" textLength="97.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-11)">telegram</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="109.8" 
y="288.4" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-docum [...]
 </text><text 
class="breeze-release-management-prepare-provider-documentation-r1" x="1464" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-12)">
 </text><text 
class="breeze-release-management-prepare-provider-documentation-r1" x="12.2" 
y="337.2" textLength="780.8" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-13)">Prepare&#160;CHANGELOG,&#160;README&#160;and&#160;COMMITS&#160;information&#160;for&#160;providers.</text><text
 class="breeze-release-management-prepare-provider-documentation-r1" x="1464" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-prepare-provider-documentation- [...]
 </text><text 
class="breeze-release-management-prepare-provider-documentation-r1" x="1464" 
y="361.6" textLength="12.2" 
clip-path="url(#breeze-release-management-prepare-provider-documentation-line-14)">
diff --git 
a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
 
b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
index 6708878a18..83b69b7ea7 100644
--- 
a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
+++ 
b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
@@ -1 +1 @@
-f4dbf1109bcdcca01230e5eb5331fa26
+663614748d86a8e2e8df08417e9b9307
diff --git 
a/dev/breeze/doc/images/output_release-management_prepare-provider-packages.svg 
b/dev/breeze/doc/images/output_release-management_prepare-provider-packages.svg
index 970ef40924..0104899650 100644
--- 
a/dev/breeze/doc/images/output_release-management_prepare-provider-packages.svg
+++ 
b/dev/breeze/doc/images/output_release-management_prepare-provider-packages.svg
@@ -165,9 +165,9 @@
 </text><text class="breeze-release-management-prepare-provider-packages-r1" 
x="12.2" y="166.4" textLength="24.4" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-6)">|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="36.6" 
y="166.4" textLength="73.2" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-6)">common</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="109.8" 
y="166.4" tex [...]
 </text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-7)">ftp</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="48.8" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-7)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="85.4" 
y="190.8" t [...]
 </text><text class="breeze-release-management-prepare-provider-packages-r1" 
x="12.2" y="215.2" textLength="24.4" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-8)">|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="36.6" 
y="215.2" textLength="109.8" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-8)">microsoft</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="146.4" 
y="215.2" [...]
-</text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="239.6" textLength="97.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-9)">opsgenie</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="109.8" 
y="239.6" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-9)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="146.4" y="2 
[...]
-</text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="264" textLength="85.4" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-10)">segment</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="97.6" 
y="264" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="134.2" 
y="264"  [...]
-</text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="288.4" textLength="61" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-11)">trino</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="73.2" 
y="288.4" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="109.8" 
y="288.4 [...]
+</text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="239.6" textLength="97.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-9)">opsgenie</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="109.8" 
y="239.6" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-9)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="146.4" y="2 
[...]
+</text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="264" textLength="61" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-10)">samba</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="73.2" 
y="264" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="109.8" 
y="264" text [...]
+</text><text class="breeze-release-management-prepare-provider-packages-r4" 
x="12.2" y="288.4" textLength="97.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-11)">telegram</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="109.8" 
y="288.4" textLength="36.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-prepare-provider-packages-r4" x="146.4" y= 
[...]
 </text><text class="breeze-release-management-prepare-provider-packages-r1" 
x="1464" y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-12)">
 </text><text class="breeze-release-management-prepare-provider-packages-r1" 
x="12.2" y="337.2" textLength="585.6" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-13)">Prepare&#160;sdist/whl&#160;packages&#160;of&#160;Airflow&#160;Providers.</text><text
 class="breeze-release-management-prepare-provider-packages-r1" x="1464" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-13)">
 </text><text class="breeze-release-management-prepare-provider-packages-r1" 
x="1464" y="361.6" textLength="12.2" 
clip-path="url(#breeze-release-management-prepare-provider-packages-line-14)">
diff --git 
a/dev/breeze/doc/images/output_release-management_prepare-provider-packages.txt 
b/dev/breeze/doc/images/output_release-management_prepare-provider-packages.txt
index 50a99b308f..a65dfccd0b 100644
--- 
a/dev/breeze/doc/images/output_release-management_prepare-provider-packages.txt
+++ 
b/dev/breeze/doc/images/output_release-management_prepare-provider-packages.txt
@@ -1 +1 @@
-e82f390815da62d3927de3f6cb9704f1
+c233e9c9a308ce97422dfb26a4125ada
diff --git a/dev/breeze/doc/images/output_release-management_publish-docs.svg 
b/dev/breeze/doc/images/output_release-management_publish-docs.svg
index 1448a8a594..0f8a6216c4 100644
--- a/dev/breeze/doc/images/output_release-management_publish-docs.svg
+++ b/dev/breeze/doc/images/output_release-management_publish-docs.svg
@@ -190,9 +190,9 @@
 </text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="190.8" textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-7)">dbt</text><text 
class="breeze-release-management-publish-docs-r1" x="48.8" y="190.8" 
textLength="12.2" 
clip-path="url(#breeze-release-management-publish-docs-line-7)">.</text><text 
class="breeze-release-management-publish-docs-r4" x="61" y="190.8" 
textLength="61" 
clip-path="url(#breeze-release-management-publish-docs-line-7)"> [...]
 </text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="215.2" textLength="73.2" 
clip-path="url(#breeze-release-management-publish-docs-line-8)">google</text><text
 class="breeze-release-management-publish-docs-r1" x="85.4" y="215.2" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-8)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="122" y="215.2" 
textLength="48.8" clip-path="url(#breeze-release-management-publ [...]
 </text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="239.6" textLength="109.8" 
clip-path="url(#breeze-release-management-publish-docs-line-9)">microsoft</text><text
 class="breeze-release-management-publish-docs-r1" x="122" y="239.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-publish-docs-line-9)">.</text><text 
class="breeze-release-management-publish-docs-r4" x="134.2" y="239.6" 
textLength="48.8" clip-path="url(#breeze-release-management-publish-doc [...]
-</text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="264" textLength="97.6" 
clip-path="url(#breeze-release-management-publish-docs-line-10)">opsgenie</text><text
 class="breeze-release-management-publish-docs-r1" x="109.8" y="264" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="146.4" y="264" 
textLength="73.2" clip-path="url(#breeze-release-management-pub [...]
-</text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="288.4" textLength="85.4" 
clip-path="url(#breeze-release-management-publish-docs-line-11)">segment</text><text
 class="breeze-release-management-publish-docs-r1" x="97.6" y="288.4" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="134.2" y="288.4" 
textLength="97.6" clip-path="url(#breeze-release-management [...]
-</text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="312.8" textLength="61" 
clip-path="url(#breeze-release-management-publish-docs-line-12)">trino</text><text
 class="breeze-release-management-publish-docs-r1" x="73.2" y="312.8" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-12)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="109.8" y="312.8" 
textLength="85.4" clip-path="url(#breeze-release-management-pub [...]
+</text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="264" textLength="97.6" 
clip-path="url(#breeze-release-management-publish-docs-line-10)">opsgenie</text><text
 class="breeze-release-management-publish-docs-r1" x="109.8" y="264" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-10)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="146.4" y="264" 
textLength="73.2" clip-path="url(#breeze-release-management-pub [...]
+</text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="288.4" textLength="61" 
clip-path="url(#breeze-release-management-publish-docs-line-11)">samba</text><text
 class="breeze-release-management-publish-docs-r1" x="73.2" y="288.4" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-11)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="109.8" y="288.4" 
textLength="85.4" clip-path="url(#breeze-release-management-pub [...]
+</text><text class="breeze-release-management-publish-docs-r4" x="12.2" 
y="312.8" textLength="97.6" 
clip-path="url(#breeze-release-management-publish-docs-line-12)">telegram</text><text
 class="breeze-release-management-publish-docs-r1" x="109.8" y="312.8" 
textLength="36.6" 
clip-path="url(#breeze-release-management-publish-docs-line-12)">&#160;|&#160;</text><text
 class="breeze-release-management-publish-docs-r4" x="146.4" y="312.8" 
textLength="61" clip-path="url(#breeze-release-management [...]
 </text><text class="breeze-release-management-publish-docs-r1" x="1464" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-publish-docs-line-13)">
 </text><text class="breeze-release-management-publish-docs-r1" x="12.2" 
y="361.6" textLength="707.6" 
clip-path="url(#breeze-release-management-publish-docs-line-14)">Command&#160;to&#160;publish&#160;generated&#160;documentation&#160;to&#160;airflow-site</text><text
 class="breeze-release-management-publish-docs-r1" x="1464" y="361.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-publish-docs-line-14)">
 </text><text class="breeze-release-management-publish-docs-r1" x="1464" 
y="386" textLength="12.2" 
clip-path="url(#breeze-release-management-publish-docs-line-15)">
diff --git a/dev/breeze/doc/images/output_release-management_publish-docs.txt 
b/dev/breeze/doc/images/output_release-management_publish-docs.txt
index f07d4889c6..d3a965a4bb 100644
--- a/dev/breeze/doc/images/output_release-management_publish-docs.txt
+++ b/dev/breeze/doc/images/output_release-management_publish-docs.txt
@@ -1 +1 @@
-babcac730a3ede766b87ba14ab3484e1
+30bde47bb7c648532bcadd4c53ff3d1e
diff --git 
a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg 
b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg
index dbc947ad26..6505980e87 100644
--- a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg
+++ b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.svg
@@ -191,9 +191,9 @@
 </text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="361.6" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-14)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="361.6" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-14)">|&#160;facebook&#160;|&#160;ftp&#160;|&#160;github&#160;|&#160;google&#160;|&#160;grpc&#160;|&#160;hashicorp&#160;|&#160;http&#160;|&#160;ima
 [...]
 </text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="386" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-15)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="386" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-15)">jenkins&#160;|&#160;microsoft.azure&#160;|&#160;microsoft.mssql&#160;|&#160;microsoft.psrp&#160;|&#160;microsoft.winrm&#160;|&#160;mongo&#160;|&#
 [...]
 </text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="410.4" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-16)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="410.4" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-16)">|&#160;neo4j&#160;|&#160;odbc&#160;|&#160;openai&#160;|&#160;openfaas&#160;|&#160;openlineage&#160;|&#160;opensearch&#160;|&#160;opsgenie&#16
 [...]
-</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="434.8" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-17)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="434.8" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-17)">|&#160;papermill&#160;|&#160;pgvector&#160;|&#160;pinecone&#160;|&#160;postgres&#160;|&#160;presto&#160;|&#160;redis&#160;|&#160;salesforce&#
 [...]
-</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="459.2" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-18)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="459.2" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-18)">sendgrid&#160;|&#160;sftp&#160;|&#160;singularity&#160;|&#160;slack&#160;|&#160;smtp&#160;|&#160;snowflake&#160;|&#160;sqlite&#160;|&#160;ssh
 [...]
-</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="483.6" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-19)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="483.6" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-19)">telegram&#160;|&#160;trino&#160;|&#160;vertica&#160;|&#160;weaviate&#160;|&#160;yandex&#160;|&#160;zendesk)&#160;&#160;&#160;&#160;&#160;&#16
 [...]
+</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="434.8" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-17)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="434.8" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-17)">|&#160;papermill&#160;|&#160;pgvector&#160;|&#160;pinecone&#160;|&#160;postgres&#160;|&#160;presto&#160;|&#160;qdrant&#160;|&#160;redis&#160;
 [...]
+</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="459.2" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-18)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="459.2" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-18)">segment&#160;|&#160;sendgrid&#160;|&#160;sftp&#160;|&#160;singularity&#160;|&#160;slack&#160;|&#160;smtp&#160;|&#160;snowflake&#160;|&#160;sq
 [...]
+</text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="483.6" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-19)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="483.6" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-19)">tabular&#160;|&#160;telegram&#160;|&#160;trino&#160;|&#160;vertica&#160;|&#160;weaviate&#160;|&#160;yandex&#160;|&#160;zendesk)&#160;&#160;&#
 [...]
 </text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="508" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-20)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r4" x="24.4" y="508" 
textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-20)">-</text><text
 class="breeze-sbom-generate-providers-requirements-r4" x="36.6" y="508" 
textLength="109.8" clip-path="url(#breeze-sbom-generate-provid [...]
 </text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="532.4" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-21)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r1" x="292.8" y="532.4" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-21)">value&#160;to&#160;account&#160;for&#160;the&#160;most&#160;recent&#160;version&#160;of&#160;the&#160;provider&#160;&#160;&#160;&#160;&#160;&
 [...]
 </text><text class="breeze-sbom-generate-providers-requirements-r5" x="0" 
y="556.8" textLength="12.2" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-22)">│</text><text
 class="breeze-sbom-generate-providers-requirements-r6" x="292.8" y="556.8" 
textLength="1146.8" 
clip-path="url(#breeze-sbom-generate-providers-requirements-line-22)">(TEXT)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
diff --git 
a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt 
b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt
index 56eadeec24..6eab2584f8 100644
--- a/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt
+++ b/dev/breeze/doc/images/output_sbom_generate-providers-requirements.txt
@@ -1 +1 @@
-84d46887b3f47bc209014ec5cb26406c
+75d28480ee1900ffd878862190585efc
diff --git a/dev/breeze/doc/images/output_shell.svg 
b/dev/breeze/doc/images/output_shell.svg
index ff0289a6c7..22111e4033 100644
--- a/dev/breeze/doc/images/output_shell.svg
+++ b/dev/breeze/doc/images/output_shell.svg
@@ -431,7 +431,7 @@
 </text><text class="breeze-shell-r5" x="0" y="215.2" textLength="12.2" 
clip-path="url(#breeze-shell-line-8)">│</text><text class="breeze-shell-r5" 
x="414.8" y="215.2" textLength="732" 
clip-path="url(#breeze-shell-line-8)">[default:&#160;3.8]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160
 [...]
 </text><text class="breeze-shell-r5" x="0" y="239.6" textLength="12.2" 
clip-path="url(#breeze-shell-line-9)">│</text><text class="breeze-shell-r4" 
x="24.4" y="239.6" textLength="12.2" 
clip-path="url(#breeze-shell-line-9)">-</text><text class="breeze-shell-r4" 
x="36.6" y="239.6" textLength="146.4" 
clip-path="url(#breeze-shell-line-9)">-integration</text><text 
class="breeze-shell-r1" x="414.8" y="239.6" textLength="1024.8" 
clip-path="url(#breeze-shell-line-9)">Integration(s)&#160;to&#160;e [...]
 </text><text class="breeze-shell-r5" x="0" y="264" textLength="12.2" 
clip-path="url(#breeze-shell-line-10)">│</text><text class="breeze-shell-r7" 
x="414.8" y="264" textLength="1024.8" 
clip-path="url(#breeze-shell-line-10)">(all&#160;|&#160;all-testable&#160;|&#160;cassandra&#160;|&#160;celery&#160;|&#160;kafka&#160;|&#160;kerberos&#160;|&#160;mongo&#160;|&#160;openlineage&#160;|&#160;</text><text
 class="breeze-shell-r5" x="1451.8" y="264" textLength="12.2" 
clip-path="url(#breeze-shell-li [...]
-</text><text class="breeze-shell-r5" x="0" y="288.4" textLength="12.2" 
clip-path="url(#breeze-shell-line-11)">│</text><text class="breeze-shell-r7" 
x="414.8" y="288.4" textLength="1024.8" 
clip-path="url(#breeze-shell-line-11)">otel&#160;|&#160;pinot&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&
 [...]
+</text><text class="breeze-shell-r5" x="0" y="288.4" textLength="12.2" 
clip-path="url(#breeze-shell-line-11)">│</text><text class="breeze-shell-r7" 
x="414.8" y="288.4" textLength="1024.8" 
clip-path="url(#breeze-shell-line-11)">otel&#160;|&#160;pinot&#160;|&#160;qdrant&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 [...]
 </text><text class="breeze-shell-r5" x="0" y="312.8" textLength="12.2" 
clip-path="url(#breeze-shell-line-12)">│</text><text class="breeze-shell-r4" 
x="24.4" y="312.8" textLength="12.2" 
clip-path="url(#breeze-shell-line-12)">-</text><text class="breeze-shell-r4" 
x="36.6" y="312.8" textLength="134.2" 
clip-path="url(#breeze-shell-line-12)">-standalone</text><text 
class="breeze-shell-r4" x="170.8" y="312.8" textLength="170.8" 
clip-path="url(#breeze-shell-line-12)">-dag-processor</text><text  [...]
 </text><text class="breeze-shell-r5" x="0" y="337.2" textLength="12.2" 
clip-path="url(#breeze-shell-line-13)">│</text><text class="breeze-shell-r4" 
x="24.4" y="337.2" textLength="12.2" 
clip-path="url(#breeze-shell-line-13)">-</text><text class="breeze-shell-r4" 
x="36.6" y="337.2" textLength="109.8" 
clip-path="url(#breeze-shell-line-13)">-database</text><text 
class="breeze-shell-r4" x="146.4" y="337.2" textLength="122" 
clip-path="url(#breeze-shell-line-13)">-isolation</text><text class="b [...]
 </text><text class="breeze-shell-r5" x="0" y="361.6" textLength="1464" 
clip-path="url(#breeze-shell-line-14)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-shell-r1" x="1464" y="361.6" textLength="12.2" 
clip-path="url(#breeze-shell-line-14)">
diff --git a/dev/breeze/doc/images/output_shell.txt 
b/dev/breeze/doc/images/output_shell.txt
index ca43ac6ab7..2a342786af 100644
--- a/dev/breeze/doc/images/output_shell.txt
+++ b/dev/breeze/doc/images/output_shell.txt
@@ -1 +1 @@
-7658b53f007e54deb8da7c69edd878c3
+a5cb9724e8240f658d00282c8d30f125
diff --git a/dev/breeze/doc/images/output_start-airflow.svg 
b/dev/breeze/doc/images/output_start-airflow.svg
index 18922ae8f7..eab034f663 100644
--- a/dev/breeze/doc/images/output_start-airflow.svg
+++ b/dev/breeze/doc/images/output_start-airflow.svg
@@ -388,7 +388,7 @@
 </text><text class="breeze-start-airflow-r5" x="0" y="264" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-10)">│</text><text 
class="breeze-start-airflow-r4" x="24.4" y="264" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-10)">-</text><text 
class="breeze-start-airflow-r4" x="36.6" y="264" textLength="109.8" 
clip-path="url(#breeze-start-airflow-line-10)">-platform</text><text 
class="breeze-start-airflow-r1" x="414.8" y="264" textLength="329.4" 
clip-path="url(#bree [...]
 </text><text class="breeze-start-airflow-r5" x="0" y="288.4" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-11)">│</text><text 
class="breeze-start-airflow-r4" x="24.4" y="288.4" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-11)">-</text><text 
class="breeze-start-airflow-r4" x="36.6" y="288.4" textLength="146.4" 
clip-path="url(#breeze-start-airflow-line-11)">-integration</text><text 
class="breeze-start-airflow-r1" x="414.8" y="288.4" textLength="1024.8" 
clip-pat [...]
 </text><text class="breeze-start-airflow-r5" x="0" y="312.8" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-12)">│</text><text 
class="breeze-start-airflow-r7" x="414.8" y="312.8" textLength="1024.8" 
clip-path="url(#breeze-start-airflow-line-12)">(all&#160;|&#160;all-testable&#160;|&#160;cassandra&#160;|&#160;celery&#160;|&#160;kafka&#160;|&#160;kerberos&#160;|&#160;mongo&#160;|&#160;openlineage&#160;|&#160;</text><text
 class="breeze-start-airflow-r5" x="1451.8" y="312.8" tex [...]
-</text><text class="breeze-start-airflow-r5" x="0" y="337.2" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-13)">│</text><text 
class="breeze-start-airflow-r7" x="414.8" y="337.2" textLength="1024.8" 
clip-path="url(#breeze-start-airflow-line-13)">otel&#160;|&#160;pinot&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160
 [...]
+</text><text class="breeze-start-airflow-r5" x="0" y="337.2" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-13)">│</text><text 
class="breeze-start-airflow-r7" x="414.8" y="337.2" textLength="1024.8" 
clip-path="url(#breeze-start-airflow-line-13)">otel&#160;|&#160;pinot&#160;|&#160;qdrant&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#16
 [...]
 </text><text class="breeze-start-airflow-r5" x="0" y="361.6" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-14)">│</text><text 
class="breeze-start-airflow-r4" x="24.4" y="361.6" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-14)">-</text><text 
class="breeze-start-airflow-r4" x="36.6" y="361.6" textLength="134.2" 
clip-path="url(#breeze-start-airflow-line-14)">-standalone</text><text 
class="breeze-start-airflow-r4" x="170.8" y="361.6" textLength="170.8" 
clip-path= [...]
 </text><text class="breeze-start-airflow-r5" x="0" y="386" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-15)">│</text><text 
class="breeze-start-airflow-r4" x="24.4" y="386" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-15)">-</text><text 
class="breeze-start-airflow-r4" x="36.6" y="386" textLength="109.8" 
clip-path="url(#breeze-start-airflow-line-15)">-database</text><text 
class="breeze-start-airflow-r4" x="146.4" y="386" textLength="122" 
clip-path="url(#breeze [...]
 </text><text class="breeze-start-airflow-r5" x="0" y="410.4" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-16)">│</text><text 
class="breeze-start-airflow-r4" x="24.4" y="410.4" textLength="12.2" 
clip-path="url(#breeze-start-airflow-line-16)">-</text><text 
class="breeze-start-airflow-r4" x="36.6" y="410.4" textLength="61" 
clip-path="url(#breeze-start-airflow-line-16)">-load</text><text 
class="breeze-start-airflow-r4" x="97.6" y="410.4" textLength="158.6" 
clip-path="url(#bree [...]
diff --git a/dev/breeze/doc/images/output_start-airflow.txt 
b/dev/breeze/doc/images/output_start-airflow.txt
index d8b5dd0f70..c2dfd51ff7 100644
--- a/dev/breeze/doc/images/output_start-airflow.txt
+++ b/dev/breeze/doc/images/output_start-airflow.txt
@@ -1 +1 @@
-1664c1bda5204995062f50c4e97b087d
+e40d63b96551e328aee3fe8f1a3aedf2
diff --git a/dev/breeze/doc/images/output_testing_integration-tests.svg 
b/dev/breeze/doc/images/output_testing_integration-tests.svg
index 35beec897b..b4e8af9aff 100644
--- a/dev/breeze/doc/images/output_testing_integration-tests.svg
+++ b/dev/breeze/doc/images/output_testing_integration-tests.svg
@@ -197,7 +197,7 @@
 </text><text class="breeze-testing-integration-tests-r5" x="0" y="361.6" 
textLength="24.4" 
clip-path="url(#breeze-testing-integration-tests-line-14)">╭─</text><text 
class="breeze-testing-integration-tests-r5" x="24.4" y="361.6" 
textLength="219.6" 
clip-path="url(#breeze-testing-integration-tests-line-14)">&#160;Test&#160;environment&#160;</text><text
 class="breeze-testing-integration-tests-r5" x="244" y="361.6" 
textLength="1195.6" clip-path="url(#breeze-testing-integration-tests-line-14)" 
[...]
 </text><text class="breeze-testing-integration-tests-r5" x="0" y="386" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-15)">│</text><text 
class="breeze-testing-integration-tests-r4" x="24.4" y="386" textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-15)">-</text><text 
class="breeze-testing-integration-tests-r4" x="36.6" y="386" textLength="146.4" 
clip-path="url(#breeze-testing-integration-tests-line-15)">-integration</text><text
 class="breeze- [...]
 </text><text class="breeze-testing-integration-tests-r5" x="0" y="410.4" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-16)">│</text><text 
class="breeze-testing-integration-tests-r6" x="353.8" y="410.4" 
textLength="1085.8" 
clip-path="url(#breeze-testing-integration-tests-line-16)">(all&#160;|&#160;all-testable&#160;|&#160;cassandra&#160;|&#160;celery&#160;|&#160;kafka&#160;|&#160;kerberos&#160;|&#160;mongo&#160;|&#160;openlineage&#160;|&#160;otel&#160;</text><tex
 [...]
-</text><text class="breeze-testing-integration-tests-r5" x="0" y="434.8" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-17)">│</text><text 
class="breeze-testing-integration-tests-r6" x="353.8" y="434.8" 
textLength="1085.8" 
clip-path="url(#breeze-testing-integration-tests-line-17)">|&#160;pinot&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
+</text><text class="breeze-testing-integration-tests-r5" x="0" y="434.8" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-17)">│</text><text 
class="breeze-testing-integration-tests-r6" x="353.8" y="434.8" 
textLength="1085.8" 
clip-path="url(#breeze-testing-integration-tests-line-17)">|&#160;pinot&#160;|&#160;qdrant&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
 </text><text class="breeze-testing-integration-tests-r5" x="0" y="459.2" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-18)">│</text><text 
class="breeze-testing-integration-tests-r4" x="24.4" y="459.2" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-18)">-</text><text 
class="breeze-testing-integration-tests-r4" x="36.6" y="459.2" 
textLength="97.6" 
clip-path="url(#breeze-testing-integration-tests-line-18)">-backend</text><text 
class="breeze [...]
 </text><text class="breeze-testing-integration-tests-r5" x="0" y="483.6" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-19)">│</text><text 
class="breeze-testing-integration-tests-r1" x="353.8" y="483.6" 
textLength="1085.8" 
clip-path="url(#breeze-testing-integration-tests-line-19)">configuration&#160;and&#160;no&#160;database&#160;and&#160;any&#160;attempts&#160;to&#160;connect&#160;to&#160;Airflow&#160;DB&#160;will&#160;fail.&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
 </text><text class="breeze-testing-integration-tests-r5" x="0" y="508" 
textLength="12.2" 
clip-path="url(#breeze-testing-integration-tests-line-20)">│</text><text 
class="breeze-testing-integration-tests-r6" x="353.8" y="508" 
textLength="1085.8" 
clip-path="url(#breeze-testing-integration-tests-line-20)">(&gt;sqlite&lt;&#160;|&#160;mysql&#160;|&#160;postgres&#160;|&#160;none)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
diff --git a/dev/breeze/doc/images/output_testing_integration-tests.txt 
b/dev/breeze/doc/images/output_testing_integration-tests.txt
index 2270869eb9..bab4263ef6 100644
--- a/dev/breeze/doc/images/output_testing_integration-tests.txt
+++ b/dev/breeze/doc/images/output_testing_integration-tests.txt
@@ -1 +1 @@
-ae82cfad12e55c5f0f847234992d3877
+d2fca4bbac3f1d53f1b56a47652997eb
diff --git a/dev/breeze/doc/images/output_testing_tests.svg 
b/dev/breeze/doc/images/output_testing_tests.svg
index ff52a515a7..2732943b90 100644
--- a/dev/breeze/doc/images/output_testing_tests.svg
+++ b/dev/breeze/doc/images/output_testing_tests.svg
@@ -368,7 +368,7 @@
 </text><text class="breeze-testing-tests-r5" x="0" y="1020.4" 
textLength="24.4" clip-path="url(#breeze-testing-tests-line-41)">╭─</text><text 
class="breeze-testing-tests-r5" x="24.4" y="1020.4" textLength="219.6" 
clip-path="url(#breeze-testing-tests-line-41)">&#160;Test&#160;environment&#160;</text><text
 class="breeze-testing-tests-r5" x="244" y="1020.4" textLength="1195.6" 
clip-path="url(#breeze-testing-tests-line-41)">────────────────────────────────────────────────────────────────────
 [...]
 </text><text class="breeze-testing-tests-r5" x="0" y="1044.8" 
textLength="12.2" clip-path="url(#breeze-testing-tests-line-42)">│</text><text 
class="breeze-testing-tests-r4" x="24.4" y="1044.8" textLength="12.2" 
clip-path="url(#breeze-testing-tests-line-42)">-</text><text 
class="breeze-testing-tests-r4" x="36.6" y="1044.8" textLength="146.4" 
clip-path="url(#breeze-testing-tests-line-42)">-integration</text><text 
class="breeze-testing-tests-r1" x="353.8" y="1044.8" textLength="1085.8" clip 
[...]
 </text><text class="breeze-testing-tests-r5" x="0" y="1069.2" 
textLength="12.2" clip-path="url(#breeze-testing-tests-line-43)">│</text><text 
class="breeze-testing-tests-r7" x="353.8" y="1069.2" textLength="1085.8" 
clip-path="url(#breeze-testing-tests-line-43)">(all&#160;|&#160;all-testable&#160;|&#160;cassandra&#160;|&#160;celery&#160;|&#160;kafka&#160;|&#160;kerberos&#160;|&#160;mongo&#160;|&#160;openlineage&#160;|&#160;otel&#160;</text><text
 class="breeze-testing-tests-r5" x="1451.8" y [...]
-</text><text class="breeze-testing-tests-r5" x="0" y="1093.6" 
textLength="12.2" clip-path="url(#breeze-testing-tests-line-44)">│</text><text 
class="breeze-testing-tests-r7" x="353.8" y="1093.6" textLength="1085.8" 
clip-path="url(#breeze-testing-tests-line-44)">|&#160;pinot&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&
 [...]
+</text><text class="breeze-testing-tests-r5" x="0" y="1093.6" 
textLength="12.2" clip-path="url(#breeze-testing-tests-line-44)">│</text><text 
class="breeze-testing-tests-r7" x="353.8" y="1093.6" textLength="1085.8" 
clip-path="url(#breeze-testing-tests-line-44)">|&#160;pinot&#160;|&#160;qdrant&#160;|&#160;statsd&#160;|&#160;trino)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
 [...]
 </text><text class="breeze-testing-tests-r5" x="0" y="1118" textLength="12.2" 
clip-path="url(#breeze-testing-tests-line-45)">│</text><text 
class="breeze-testing-tests-r4" x="24.4" y="1118" textLength="12.2" 
clip-path="url(#breeze-testing-tests-line-45)">-</text><text 
class="breeze-testing-tests-r4" x="36.6" y="1118" textLength="97.6" 
clip-path="url(#breeze-testing-tests-line-45)">-backend</text><text 
class="breeze-testing-tests-r6" x="305" y="1118" textLength="24.4" 
clip-path="url(#breez [...]
 </text><text class="breeze-testing-tests-r5" x="0" y="1142.4" 
textLength="12.2" clip-path="url(#breeze-testing-tests-line-46)">│</text><text 
class="breeze-testing-tests-r1" x="353.8" y="1142.4" textLength="1085.8" 
clip-path="url(#breeze-testing-tests-line-46)">configuration&#160;and&#160;no&#160;database&#160;and&#160;any&#160;attempts&#160;to&#160;connect&#160;to&#160;Airflow&#160;DB&#160;will&#160;fail.&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text
 class="breeze-testing-tests-r [...]
 </text><text class="breeze-testing-tests-r5" x="0" y="1166.8" 
textLength="12.2" clip-path="url(#breeze-testing-tests-line-47)">│</text><text 
class="breeze-testing-tests-r7" x="353.8" y="1166.8" textLength="1085.8" 
clip-path="url(#breeze-testing-tests-line-47)">(&gt;sqlite&lt;&#160;|&#160;mysql&#160;|&#160;postgres&#160;|&#160;none)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#1
 [...]
diff --git a/dev/breeze/doc/images/output_testing_tests.txt 
b/dev/breeze/doc/images/output_testing_tests.txt
index 0b631fb981..f4864ad515 100644
--- a/dev/breeze/doc/images/output_testing_tests.txt
+++ b/dev/breeze/doc/images/output_testing_tests.txt
@@ -1 +1 @@
-ebfb809a4ef0e366f21b39495a20c7dd
+5b4d64d2d8331c5fb9bd803ad9af53ef
diff --git a/dev/breeze/src/airflow_breeze/global_constants.py 
b/dev/breeze/src/airflow_breeze/global_constants.py
index c867a86a60..324176c446 100644
--- a/dev/breeze/src/airflow_breeze/global_constants.py
+++ b/dev/breeze/src/airflow_breeze/global_constants.py
@@ -50,7 +50,7 @@ ALLOWED_ARCHITECTURES = [Architecture.X86_64, 
Architecture.ARM]
 ALLOWED_BACKENDS = ["sqlite", "mysql", "postgres", "none"]
 ALLOWED_PROD_BACKENDS = ["mysql", "postgres"]
 DEFAULT_BACKEND = ALLOWED_BACKENDS[0]
-TESTABLE_INTEGRATIONS = ["cassandra", "celery", "kerberos", "mongo", "pinot", 
"trino", "kafka"]
+TESTABLE_INTEGRATIONS = ["cassandra", "celery", "kerberos", "mongo", "pinot", 
"trino", "kafka", "qdrant"]
 OTHER_INTEGRATIONS = ["statsd", "otel", "openlineage"]
 ALLOWED_DEBIAN_VERSIONS = ["bookworm", "bullseye"]
 ALL_INTEGRATIONS = sorted(
@@ -365,15 +365,7 @@ def get_airflow_extras():
 
 
 # Initialize integrations
-AVAILABLE_INTEGRATIONS = [
-    "cassandra",
-    "kerberos",
-    "mongo",
-    "pinot",
-    "celery",
-    "statsd",
-    "trino",
-]
+AVAILABLE_INTEGRATIONS = ["cassandra", "kerberos", "mongo", "pinot", "celery", 
"statsd", "trino", "qdrant"]
 ALL_PROVIDER_YAML_FILES = Path(AIRFLOW_SOURCES_ROOT, "airflow", 
"providers").rglob("provider.yaml")
 PROVIDER_RUNTIME_DATA_SCHEMA_PATH = AIRFLOW_SOURCES_ROOT / "airflow" / 
"provider_info.schema.json"
 
@@ -468,12 +460,12 @@ BASE_PROVIDERS_COMPATIBILITY_CHECKS: list[dict[str, str]] 
= [
     {
         "python-version": "3.8",
         "airflow-version": "2.6.0",
-        "remove-providers": _exclusion(["openlineage", "common.io", "cohere", 
"fab"]),
+        "remove-providers": _exclusion(["openlineage", "common.io", "cohere", 
"fab", "qdrant"]),
     },
     {
         "python-version": "3.9",
         "airflow-version": "2.6.0",
-        "remove-providers": _exclusion(["openlineage", "common.io", "fab"]),
+        "remove-providers": _exclusion(["openlineage", "common.io", "fab", 
"qdrant"]),
     },
     {
         "python-version": "3.8",
diff --git a/docs/apache-airflow-providers-qdrant/changelog.rst 
b/docs/apache-airflow-providers-qdrant/changelog.rst
new file mode 100644
index 0000000000..b4d67b06a0
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/changelog.rst
@@ -0,0 +1,18 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+.. include:: ../../airflow/providers/qdrant/CHANGELOG.rst
diff --git a/docs/apache-airflow-providers-qdrant/commits.rst 
b/docs/apache-airflow-providers-qdrant/commits.rst
new file mode 100644
index 0000000000..c5206dab50
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/commits.rst
@@ -0,0 +1,19 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Package apache-airflow-providers-qdrant
+-------------------------------------------
diff --git a/docs/apache-airflow-providers-qdrant/connections.rst 
b/docs/apache-airflow-providers-qdrant/connections.rst
new file mode 100644
index 0000000000..2eadc0146d
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/connections.rst
@@ -0,0 +1,55 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+.. _howto/connection:qdrant:
+
+Qdrant Connection
+===================
+
+The `Qdrant <https://qdrant.tech/>`__ connection type enables access to Qdrant 
clusters.
+
+Default Connection IDs
+----------------------
+
+The Qdrant hook use the ``qdrant_default`` connection ID by default.
+
+Configuring the Connection
+--------------------------
+
+Host (optional)
+    Host of the Qdrant instance to connect to.
+
+API key (optional)
+    Qdrant API Key for authentication.
+
+Port (optional)
+    REST port of the Qdrant instance to connect to. Defaults to ``6333``.
+
+URL (optional)
+    URL of the Qdrant instance to connect to. If specified, it overrides the 
``host`` and ``port`` parameters.
+
+GRPC Port (optional)
+    GRPC port of the Qdrant instance to connect to. Defaults to ``6334``.
+
+Prefer GRPC (optional)
+    Whether to use GRPC for custom methods. Defaults to ``False``.
+
+HTTPS (optional)
+    Whether to use HTTPS for requests. Defaults to ``True`` if an API key is 
provided. ``False`` otherwise.
+
+Prefix (optional)
+    Prefix to add to the REST URL endpoints. Defaults to ``None``.
diff --git a/docs/apache-airflow-providers-qdrant/index.rst 
b/docs/apache-airflow-providers-qdrant/index.rst
new file mode 100644
index 0000000000..c2f8fc473a
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/index.rst
@@ -0,0 +1,98 @@
+
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+``apache-airflow-providers-qdrant``
+======================================
+
+
+.. toctree::
+    :hidden:
+    :maxdepth: 1
+    :caption: Basics
+
+    Home <self>
+    Changelog <changelog>
+    Security <security>
+
+.. toctree::
+    :hidden:
+    :maxdepth: 1
+    :caption: Guides
+
+    Connection types <connections>
+    Operators <operators/qdrant>
+
+
+.. toctree::
+    :hidden:
+    :maxdepth: 1
+    :caption: Commits
+
+    Detailed list of commits <commits>
+
+
+.. toctree::
+    :hidden:
+    :maxdepth: 1
+    :caption: Resources
+
+    Python API <_api/airflow/providers/qdrant/index>
+    PyPI Repository <https://pypi.org/project/apache-airflow-providers-qdrant/>
+    Installing from sources <installing-providers-from-sources>
+
+.. toctree::
+    :hidden:
+    :maxdepth: 1
+    :caption: System tests
+
+    System Tests <_api/tests/system/providers/qdrant/index>
+
+Package apache-airflow-providers-qdrant
+-----------------------------------------
+
+`Qdrant <https://qdrant.tech/>`__
+
+
+Release: 1.0.0
+
+Provider package
+----------------
+
+This is a provider package for ``Qdrant`` APIs. All classes for this provider 
package
+are in ``airflow.providers.qdrant`` python module.
+
+Installation
+------------
+
+You can install this package on top of an existing Airflow 2 installation (see 
``Requirements`` below)
+for the minimum Airflow version supported) via
+``pip install apache-airflow-providers-qdrant``
+
+
+Requirements
+------------
+
+The minimum Apache Airflow version supported by this provider package is 
``2.5.0``.
+
+
+===================  ==================
+PIP package          Version required
+===================  ==================
+``apache-airflow``    ``>=2.7.0``
+``qdrant_client``     ``>=1.7.0``
+===================  ==================
diff --git 
a/docs/apache-airflow-providers-qdrant/installing-providers-from-sources.rst 
b/docs/apache-airflow-providers-qdrant/installing-providers-from-sources.rst
new file mode 100644
index 0000000000..b4e730f4ff
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/installing-providers-from-sources.rst
@@ -0,0 +1,18 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+.. include:: ../exts/includes/installing-providers-from-sources.rst
diff --git a/docs/apache-airflow-providers-qdrant/operators/qdrant.rst 
b/docs/apache-airflow-providers-qdrant/operators/qdrant.rst
new file mode 100644
index 0000000000..22d82d5958
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/operators/qdrant.rst
@@ -0,0 +1,40 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+.. _howto/operator:QdrantIngestOperator:
+
+QdrantIngestOperator
+======================
+
+Use the 
:class:`~airflow.providers.qdrant.operators.qdrant.QdrantIngestOperator` to
+ingest data into a Qdrant instance.
+
+
+Using the Operator
+^^^^^^^^^^^^^^^^^^
+
+The QdrantIngestOperator requires the ``vectors`` as an input ingest into 
Qdrant. Use the ``conn_id`` parameter to
+specify the Qdrant connection to connect to Qdrant instance. The vectors could 
also contain metadata referencing
+the original text corresponding to the vectors that could be ingested into the 
database.
+
+An example using the operator in this way:
+
+.. exampleinclude:: /../../tests/system/providers/qdrant/example_dag_qdrant.py
+    :language: python
+    :dedent: 4
+    :start-after: [START howto_operator_qdrant_ingest]
+    :end-before: [END howto_operator_qdrant_ingest]
diff --git a/docs/apache-airflow-providers-qdrant/security.rst 
b/docs/apache-airflow-providers-qdrant/security.rst
new file mode 100644
index 0000000000..afa13dac6f
--- /dev/null
+++ b/docs/apache-airflow-providers-qdrant/security.rst
@@ -0,0 +1,18 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+.. include:: ../exts/includes/security.rst
diff --git a/docs/apache-airflow/extra-packages-ref.rst 
b/docs/apache-airflow/extra-packages-ref.rst
index 8c1c1143c5..ea813aeb49 100644
--- a/docs/apache-airflow/extra-packages-ref.rst
+++ b/docs/apache-airflow/extra-packages-ref.rst
@@ -210,6 +210,8 @@ These are extras that add dependencies needed for 
integration with external serv
 
+---------------------+-----------------------------------------------------+-----------------------------------------------------+
 | pinecone            | ``pip install 'apache-airflow[pinecone]'``          | 
Pinecone Operators and Hooks                        |
 
+---------------------+-----------------------------------------------------+-----------------------------------------------------+
+| qdrant              | ``pip install 'apache-airflow[qdrant]'``            | 
Qdrant Operators and Hooks                          |
++---------------------+-----------------------------------------------------+-----------------------------------------------------+
 | salesforce          | ``pip install 'apache-airflow[salesforce]'``        | 
Salesforce hook                                     |
 
+---------------------+-----------------------------------------------------+-----------------------------------------------------+
 | sendgrid            | ``pip install 'apache-airflow[sendgrid]'``          | 
Send email using sendgrid                           |
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 343c09a57c..2d168708a8 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -634,6 +634,7 @@ fn
 fo
 followsa
 forecasted
+forkserver
 formatter
 formatters
 Formaturas
@@ -1269,6 +1270,8 @@ pythonic
 PythonOperator
 pythonpath
 pywinrm
+Qdrant
+qdrant
 qds
 Qingping
 Qplum
diff --git a/generated/provider_dependencies.json 
b/generated/provider_dependencies.json
index f916a2d2ab..5991796ee5 100644
--- a/generated/provider_dependencies.json
+++ b/generated/provider_dependencies.json
@@ -936,6 +936,16 @@
     "excluded-python-versions": [],
     "state": "ready"
   },
+  "qdrant": {
+    "deps": [
+      "apache-airflow>=2.7.0",
+      "qdrant_client>=1.7.0"
+    ],
+    "devel-deps": [],
+    "cross-providers-deps": [],
+    "excluded-python-versions": [],
+    "state": "ready"
+  },
   "redis": {
     "deps": [
       "apache-airflow>=2.6.0",
diff --git a/pyproject.toml b/pyproject.toml
index 0f7652dd24..68ea02694a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -873,6 +873,9 @@ presto = [ # source: airflow/providers/presto/provider.yaml
   "pandas>=1.2.5",
   "presto-python-client>=0.8.4",
 ]
+qdrant = [ # source: airflow/providers/qdrant/provider.yaml
+  "qdrant_client>=1.7.0",
+]
 redis = [ # source: airflow/providers/redis/provider.yaml
   "redis>=4.5.2,<5.0.0,!=4.5.5",
 ]
@@ -1036,6 +1039,7 @@ all = [
     "apache-airflow[pinecone]",
     "apache-airflow[postgres]",
     "apache-airflow[presto]",
+    "apache-airflow[qdrant]",
     "apache-airflow[redis]",
     "apache-airflow[salesforce]",
     "apache-airflow[samba]",
@@ -1135,6 +1139,7 @@ devel-all = [
     "apache-airflow[pinecone]",
     "apache-airflow[postgres]",
     "apache-airflow[presto]",
+    "apache-airflow[qdrant]",
     "apache-airflow[redis]",
     "apache-airflow[salesforce]",
     "apache-airflow[samba]",
diff --git a/scripts/ci/docker-compose/integration-qdrant.yml 
b/scripts/ci/docker-compose/integration-qdrant.yml
new file mode 100644
index 0000000000..724c1e92cf
--- /dev/null
+++ b/scripts/ci/docker-compose/integration-qdrant.yml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+---
+version: "3.8"
+services:
+  qdrant:
+    image: qdrant/qdrant:latest
+    labels:
+      breeze.description: "Integration required for Qdrant tests."
+    ports:
+      - "6333:6333"
+      - "6334:6334"
+    restart: "on-failure"
+
+  airflow:
+    environment:
+      - INTEGRATION_QDRANT=true
+    depends_on:
+      qdrant:
+        condition: service_started
diff --git a/scripts/in_container/check_environment.sh 
b/scripts/in_container/check_environment.sh
index 187490dee8..73686e1368 100755
--- a/scripts/in_container/check_environment.sh
+++ b/scripts/in_container/check_environment.sh
@@ -177,6 +177,13 @@ if [[ ${INTEGRATION_PINOT} == "true" ]]; then
     CMD="curl --max-time 1 -X GET 'http://pinot:8000/health' -H 'accept: 
text/plain' | grep OK"
     check_service "Pinot (Broker API)" "${CMD}" 50
 fi
+
+if [[ ${INTEGRATION_QDRANT} == "true" ]]; then
+    check_service "Qdrant" "run_nc qdrant 6333" 50
+    CMD="curl -f -X GET 'http://qdrant:6333/collections'"
+    check_service "Qdrant (Collections API)" "${CMD}" 50
+fi
+
 if [[ ${INTEGRATION_KAFKA} == "true" ]]; then
     check_service "Kafka Cluster" "run_nc broker 9092" 50
 fi
diff --git a/tests/integration/providers/qdrant/__init__.py 
b/tests/integration/providers/qdrant/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/integration/providers/qdrant/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/integration/providers/qdrant/hooks/__init__.py 
b/tests/integration/providers/qdrant/hooks/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/integration/providers/qdrant/hooks/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/integration/providers/qdrant/hooks/test_qdrant.py 
b/tests/integration/providers/qdrant/hooks/test_qdrant.py
new file mode 100644
index 0000000000..296df3bed5
--- /dev/null
+++ b/tests/integration/providers/qdrant/hooks/test_qdrant.py
@@ -0,0 +1,69 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+import random
+
+import numpy as np
+import pytest
+from qdrant_client import models
+
+from airflow.providers.qdrant.hooks.qdrant import QdrantHook
+
+
[email protected]("qdrant")
+class TestQdrant:
+    def setup_method(self):
+        self.test_collection_name = "test-hook-collection"
+        self.test_collection_dimension = random.randint(100, 2000)
+        self.hook = QdrantHook()
+
+        self.hook.conn.recreate_collection(
+            self.test_collection_name,
+            vectors_config=models.VectorParams(
+                size=self.test_collection_dimension, 
distance=models.Distance.MANHATTAN
+            ),
+        )
+
+    def test_connection(self):
+        response, message = self.hook.verify_connection()
+        assert response and message == "Connection established!", 
"Successfully connected to Qdrant."
+
+    def test_upsert_points(self):
+        vectors = np.random.rand(100, self.test_collection_dimension)
+        self.hook.conn.upsert(
+            self.test_collection_name,
+            points=[
+                models.PointStruct(
+                    id=idx, vector=vector.tolist(), payload={"color": "red", 
"rand_number": idx % 10}
+                )
+                for idx, vector in enumerate(vectors)
+            ],
+        )
+
+        assert self.hook.conn.count(self.test_collection_name).count == 100
+
+    def test_delete_points(self):
+        self.hook.conn.delete(
+            self.test_collection_name,
+            points_selector=models.Filter(
+                must=[models.FieldCondition(key="color", 
match=models.MatchValue(value="red"))]
+            ),
+        )
+
+        assert self.hook.conn.count(self.test_collection_name).count == 0
diff --git a/tests/integration/providers/qdrant/operators/__init__.py 
b/tests/integration/providers/qdrant/operators/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/integration/providers/qdrant/operators/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/integration/providers/qdrant/operators/test_qdrant_ingest.py 
b/tests/integration/providers/qdrant/operators/test_qdrant_ingest.py
new file mode 100644
index 0000000000..eed4bd8e78
--- /dev/null
+++ b/tests/integration/providers/qdrant/operators/test_qdrant_ingest.py
@@ -0,0 +1,71 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import random
+from unittest.mock import MagicMock
+
+import pytest
+from qdrant_client.models import Distance, VectorParams
+
+from airflow.models.dag import DAG
+from airflow.providers.qdrant.operators.qdrant import QdrantIngestOperator
+from airflow.utils import timezone
+
+DEFAULT_DATE = timezone.datetime(2024, 1, 1)
+
+
[email protected]("qdrant")
+class TestQdrantIngestOperator:
+    def setup_method(self):
+        args = {"owner": "airflow", "start_date": DEFAULT_DATE}
+
+        self.dag = DAG("test_qdrant_dag_id", default_args=args)
+
+        self.mock_context = MagicMock()
+        self.channel = "test"
+
+    def test_execute_hello(self):
+        collection_name = "test-operator-collection"
+        dimensions = 384
+        points_count = 100
+        vectors = [[random.uniform(0, 1) for _ in range(dimensions)] for _ in 
range(points_count)]
+        ids = random.sample(range(100, 10000), points_count)
+        payload = [{"some_number": i % 10} for i in range(points_count)]
+
+        operator = QdrantIngestOperator(
+            task_id="qdrant_ingest",
+            conn_id="qdrant_default",
+            collection_name=collection_name,
+            vectors=vectors,
+            ids=ids,
+            payload=payload,
+            batch_size=1,
+        )
+
+        hook = operator.hook
+
+        hook.conn.recreate_collection(
+            collection_name, vectors_config=VectorParams(size=dimensions, 
distance=Distance.COSINE)
+        )
+
+        operator.execute(self.mock_context)
+
+        assert (
+            hook.conn.count(collection_name=collection_name).count == 
points_count
+        ), f"Added {points_count} points to the Qdrant collection"
diff --git a/tests/providers/qdrant/__init__.py 
b/tests/providers/qdrant/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/providers/qdrant/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/providers/qdrant/hooks/__init__.py 
b/tests/providers/qdrant/hooks/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/providers/qdrant/hooks/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/providers/qdrant/hooks/test_qdrant.py 
b/tests/providers/qdrant/hooks/test_qdrant.py
new file mode 100644
index 0000000000..ab66aaf7ad
--- /dev/null
+++ b/tests/providers/qdrant/hooks/test_qdrant.py
@@ -0,0 +1,129 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from unittest.mock import Mock, patch
+
+from airflow.providers.qdrant.hooks.qdrant import QdrantHook
+
+
+class TestQdrantHook:
+    def setup_method(self):
+        """Set up the test connection for the QdrantHook."""
+        with patch("airflow.models.Connection.get_connection_from_secrets") as 
mock_get_connection:
+            mock_conn = Mock()
+            mock_conn.host = "localhost"
+            mock_conn.port = 6333
+            mock_conn.extra_dejson = {}
+            mock_conn.password = "some_test_api_key"
+            mock_get_connection.return_value = mock_conn
+            self.qdrant_hook = QdrantHook()
+
+            self.collection_name = "test_collection"
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_verify_connection(self, mock_conn):
+        """Test the verify_connection of the QdrantHook."""
+        self.qdrant_hook.verify_connection()
+
+        mock_conn.get_collections.assert_called_once()
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_upsert(self, conn):
+        """Test the upsert method of the QdrantHook with appropriate 
arguments."""
+        vectors = [[0.732, 0.611, 0.289], [0.217, 0.526, 0.416], [0.326, 
0.483, 0.376]]
+        ids = [32, 21, "b626f6a9-b14d-4af9-b7c3-43d8deb719a6"]
+        payloads = [{"meta": "data"}, {"meta": "data_2"}, {"meta": "data_3", 
"extra": "data"}]
+        parallel = 2
+        self.qdrant_hook.conn.upsert(
+            collection_name=self.collection_name,
+            vectors=vectors,
+            ids=ids,
+            payloads=payloads,
+            parallel=parallel,
+        )
+        conn.upsert.assert_called_once_with(
+            collection_name=self.collection_name,
+            vectors=vectors,
+            ids=ids,
+            payloads=payloads,
+            parallel=parallel,
+        )
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_list_collections(self, conn):
+        """Test that the list_collections is called correctly."""
+        self.qdrant_hook.conn.list_collections()
+        conn.list_collections.assert_called_once()
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_create_collection(self, conn):
+        """Test that the create_collection is called with correct arguments."""
+
+        from qdrant_client.models import Distance, VectorParams
+
+        self.qdrant_hook.conn.create_collection(
+            collection_name=self.collection_name,
+            vectors_config=VectorParams(size=384, distance=Distance.COSINE),
+        )
+        conn.create_collection.assert_called_once_with(
+            collection_name=self.collection_name,
+            vectors_config=VectorParams(size=384, distance=Distance.COSINE),
+        )
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_delete(self, conn):
+        """Test that the delete is called with correct arguments."""
+
+        self.qdrant_hook.conn.delete(
+            collection_name=self.collection_name, points_selector=[32, 21], 
wait=False
+        )
+
+        conn.delete.assert_called_once_with(
+            collection_name=self.collection_name, points_selector=[32, 21], 
wait=False
+        )
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_search(self, conn):
+        """Test that the search is called with correct arguments."""
+
+        self.qdrant_hook.conn.search(
+            collection_name=self.collection_name,
+            query_vector=[1.0, 2.0, 3.0],
+            limit=10,
+            with_vectors=True,
+        )
+
+        conn.search.assert_called_once_with(
+            collection_name=self.collection_name, query_vector=[1.0, 2.0, 
3.0], limit=10, with_vectors=True
+        )
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_get_collection(self, conn):
+        """Test that the get_collection is called with correct arguments."""
+
+        
self.qdrant_hook.conn.get_collection(collection_name=self.collection_name)
+
+        
conn.get_collection.assert_called_once_with(collection_name=self.collection_name)
+
+    @patch("airflow.providers.qdrant.hooks.qdrant.QdrantHook.conn")
+    def test_delete_collection(self, conn):
+        """Test that the delete_collection is called with correct arguments."""
+
+        
self.qdrant_hook.conn.delete_collection(collection_name=self.collection_name)
+
+        
conn.delete_collection.assert_called_once_with(collection_name=self.collection_name)
diff --git a/tests/providers/qdrant/operators/__init__.py 
b/tests/providers/qdrant/operators/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/providers/qdrant/operators/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/providers/qdrant/operators/test_qdrant.py 
b/tests/providers/qdrant/operators/test_qdrant.py
new file mode 100644
index 0000000000..54b2bc1b3a
--- /dev/null
+++ b/tests/providers/qdrant/operators/test_qdrant.py
@@ -0,0 +1,64 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from unittest.mock import patch
+
+import pytest
+
+from airflow.providers.qdrant.operators.qdrant import QdrantIngestOperator
+
+
+class TestQdrantIngestOperator:
+    @pytest.mark.db_test
+    def test_operator_execution(self, dag_maker):
+        """
+        Test the execution of the QdrantIngestOperator.
+        Ensures that the upsert method on the hook is correctly called.
+        """
+        with dag_maker(dag_id="test_dag") as dummy_dag:
+            vectors = [[0.732, 0.611, 0.289], [0.217, 0.526, 0.416], [0.326, 
0.483, 0.376]]
+            ids = [32, 21, "b626f6a9-b14d-4af9-b7c3-43d8deb719a6"]
+            payload = [{"meta": "data"}, {"meta": "data_2"}, {"meta": 
"data_3", "extra": "data"}]
+
+            task = QdrantIngestOperator(
+                task_id="ingest_vectors",
+                collection_name="test_collection",
+                vectors=vectors,
+                ids=ids,
+                payload=payload,
+                wait=False,
+                max_retries=1,
+                parallel=3,
+                dag=dummy_dag,
+            )
+
+        with patch(
+            
"airflow.providers.qdrant.operators.qdrant.QdrantIngestOperator.hook"
+        ) as mock_hook_instance:
+            task.execute(context={})
+            mock_hook_instance.conn.upload_collection.assert_called_once_with(
+                collection_name="test_collection",
+                vectors=vectors,
+                ids=ids,
+                payload=payload,
+                batch_size=64,
+                wait=False,
+                max_retries=1,
+                parallel=3,
+                method=None,
+            )
diff --git a/tests/system/providers/qdrant/__init__.py 
b/tests/system/providers/qdrant/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/tests/system/providers/qdrant/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/tests/system/providers/qdrant/example_dag_qdrant.py 
b/tests/system/providers/qdrant/example_dag_qdrant.py
new file mode 100644
index 0000000000..8f55d2d72c
--- /dev/null
+++ b/tests/system/providers/qdrant/example_dag_qdrant.py
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from datetime import datetime
+
+from airflow import DAG
+from airflow.providers.qdrant.operators.qdrant import QdrantIngestOperator
+
+with DAG(
+    "example_qdrant_ingest",
+    schedule=None,
+    start_date=datetime(2024, 1, 1),
+    catchup=False,
+) as dag:
+    # [START howto_operator_qdrant_ingest]
+    vectors = [[0.732, 0.611, 0.289, 0.421], [0.217, 0.526, 0.416, 0.981], 
[0.326, 0.483, 0.376, 0.136]]
+    ids: list[str | int] = [32, 21, "b626f6a9-b14d-4af9-b7c3-43d8deb719a6"]
+    payload = [{"meta": "data"}, {"meta": "data_2"}, {"meta": "data_3", 
"extra": "data"}]
+
+    QdrantIngestOperator(
+        task_id="qdrant_ingest",
+        collection_name="test_collection",
+        vectors=vectors,
+        ids=ids,
+        payload=payload,
+        batch_size=1,
+    )
+    # [END howto_operator_qdrant_ingest]
+
+
+from tests.system.utils import get_test_run
+
+# Needed to run the example DAG with pytest (see: 
tests/system/README.md#run_via_pytest)
+test_run = get_test_run(dag)

Reply via email to