From 39fb2d1a3b03db085e7b0ae7d26d47a7a679147e Mon Sep 17 00:00:00 2001 From: Anush Date: Wed, 22 Oct 2025 22:49:32 +0530 Subject: [PATCH] feat(qdrant): Use Qdrant's built-in MMR search (#32302) --- .../qdrant/langchain_qdrant/qdrant.py | 25 ++++++------------- libs/partners/qdrant/pyproject.toml | 2 +- .../qdrant_vector_store/test_mmr.py | 2 +- libs/partners/qdrant/uv.lock | 4 +-- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/libs/partners/qdrant/langchain_qdrant/qdrant.py b/libs/partners/qdrant/langchain_qdrant/qdrant.py index 780f9d9eeca..c6952d6b143 100644 --- a/libs/partners/qdrant/langchain_qdrant/qdrant.py +++ b/libs/partners/qdrant/langchain_qdrant/qdrant.py @@ -10,14 +10,11 @@ from typing import ( Any, ) -import numpy as np from langchain_core.documents import Document from langchain_core.embeddings import Embeddings from langchain_core.vectorstores import VectorStore from qdrant_client import QdrantClient, models -from langchain_qdrant._utils import maximal_marginal_relevance - if TYPE_CHECKING: from collections.abc import Generator, Iterable, Sequence @@ -828,10 +825,13 @@ class QdrantVectorStore(VectorStore): """ results = self.client.query_points( collection_name=self.collection_name, - query=embedding, + query=models.NearestQuery( + nearest=embedding, + mmr=models.Mmr(diversity=lambda_mult, candidates_limit=fetch_k), + ), query_filter=filter, search_params=search_params, - limit=fetch_k, + limit=k, with_payload=True, with_vectors=True, score_threshold=score_threshold, @@ -840,26 +840,17 @@ class QdrantVectorStore(VectorStore): **kwargs, ).points - embeddings = [ - result.vector - if isinstance(result.vector, list) - else result.vector.get(self.vector_name) # type: ignore[union-attr] - for result in results - ] - mmr_selected = maximal_marginal_relevance( - np.array(embedding), embeddings, k=k, lambda_mult=lambda_mult - ) return [ ( self._document_from_point( - results[i], + result, self.collection_name, self.content_payload_key, self.metadata_payload_key, ), - results[i].score, + result.score, ) - for i in mmr_selected + for result in results ] def delete( # type: ignore[override] diff --git a/libs/partners/qdrant/pyproject.toml b/libs/partners/qdrant/pyproject.toml index 334a2dffe05..99e4e20f6df 100644 --- a/libs/partners/qdrant/pyproject.toml +++ b/libs/partners/qdrant/pyproject.toml @@ -7,7 +7,7 @@ authors = [] license = { text = "MIT" } requires-python = ">=3.10.0,<4.0.0" dependencies = [ - "qdrant-client>=1.10.1,<2.0.0", + "qdrant-client>=1.15.1,<2.0.0", "pydantic>=2.7.4,<3.0.0", "langchain-core>=1.0.0,<2.0.0", ] diff --git a/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_mmr.py b/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_mmr.py index 8e96fc70993..650bf45b4f7 100644 --- a/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_mmr.py +++ b/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_mmr.py @@ -63,7 +63,7 @@ def test_qdrant_mmr_search( output, [ Document(page_content="foo", metadata={"page": 0}), - Document(page_content="baz", metadata={"page": 2}), + Document(page_content="bar", metadata={"page": 1}), ], ) diff --git a/libs/partners/qdrant/uv.lock b/libs/partners/qdrant/uv.lock index 04d1b06c31e..0fd5f87a994 100644 --- a/libs/partners/qdrant/uv.lock +++ b/libs/partners/qdrant/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10.0, <4.0.0" resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", @@ -608,7 +608,7 @@ requires-dist = [ { name = "fastembed", marker = "python_full_version >= '3.9' and python_full_version < '3.13' and extra == 'fastembed'", specifier = ">=0.3.3,<1.0.0" }, { name = "langchain-core", editable = "../../core" }, { name = "pydantic", specifier = ">=2.7.4,<3.0.0" }, - { name = "qdrant-client", specifier = ">=1.10.1,<2.0.0" }, + { name = "qdrant-client", specifier = ">=1.15.1,<2.0.0" }, ] provides-extras = ["fastembed"]