Skip to content

Python: Qdrant vector search fails with qdrant-client 1.16+ (search removed) and with single-comparison filters #14561

Description

What happens

QdrantCollection._inner_search in python/semantic_kernel/connectors/qdrant.py has two problems that break Qdrant vector search.

1. AsyncQdrantClient.search no longer exists. The vector search path calls self.qdrant_client.search(...). That method was removed in qdrant-client 1.16.0. I compared the wheels: async def search( is in 1.15.1 and not in 1.16.0, and query_points is in both. The extra is declared as qdrant-client ~= 1.9 (python/pyproject.toml line 139), so a fresh install resolves to 1.19.1 and every vector search fails with:

VectorSearchExecutionException: An error occurred during the search: 'AsyncQdrantClient' object has no attribute 'search'

uv.lock pins 1.12.1, and tests/unit/connectors/memory/test_qdrant.py mocks AsyncQdrantClient.search, so CI does not see it. With qdrant-client 1.16 or newer the existing unit tests error at setup, because patch("...AsyncQdrantClient.search") has nothing to patch. Hybrid search already uses query_points (added in qdrant-client 1.10.0).

2. A single-comparison filter is passed as a bare FieldCondition. A filter such as lambda x: x.temperature > 1 is parsed to a FieldCondition, and _inner_search hands it to Qdrant unchanged:

filters: Filter | list[Filter] | None = self._build_filter(options.filter)  # type: ignore
filter: Filter | None = Filter(must=filters) if filters and isinstance(filters, list) else filters  # type: ignore

Only a list gets wrapped. Qdrant expects a Filter here. The local client fails with 'FieldCondition' object has no attribute 'must', and the qdrant-client request models reject it too (models.Prefetch(filter=FieldCondition(...)) raises ValidationError: Input should be a valid dictionary or instance of Filter). Hybrid search fails the same way at keyword_filter.must. Filters built with and, or or not already return a Filter, so they work. test_search_filter asserts that the bare FieldCondition is passed through.

Where

python/semantic_kernel/connectors/qdrant.py QdrantCollection._inner_search on current main (64005c7), the self.qdrant_client.search( call at line 294 and the filters and filter lines above it.

Steps to reproduce

Uses the in-process Qdrant that ships with qdrant-client, no server needed.

import asyncio
from dataclasses import dataclass
from typing import Annotated

from semantic_kernel.connectors.qdrant import QdrantCollection
from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel


@vectorstoremodel
@dataclass
class Record:
    id: Annotated[int, VectorStoreField("key")]
    temperature: Annotated[int, VectorStoreField("data", is_indexed=True)]
    vector: Annotated[list[float] | None, VectorStoreField("vector", dimensions=2)] = None


async def main():
    collection = QdrantCollection(record_type=Record, collection_name="t", location=":memory:")
    await collection.ensure_collection_exists()
    await collection.upsert([Record(1, 1, [1.0, 0.2]), Record(2, 5, [0.0, 1.0]), Record(3, 9, [1.0, 1.0])])
    for kwargs in ({}, {"filter": "lambda x: x.temperature > 1"}):
        try:
            results = await collection.search(vector=[1.0, 1.0], **kwargs)
            print("search", kwargs, [r.record.id async for r in results.results])
        except Exception as ex:
            print("search", kwargs, type(ex).__name__, ex)


asyncio.run(main())

Actual

On main:

qdrant-client 1.12.1 / 1.15.1
  search {}                                        [3, 1, 2]
  search {'filter': 'lambda x: x.temperature > 1'} VectorSearchExecutionException: ... 'FieldCondition' object has no attribute 'must'
  hybrid_search with the same filter               VectorSearchExecutionException: ... 'FieldCondition' object has no attribute 'must'

qdrant-client 1.16.2 / 1.19.1
  search {}                                        VectorSearchExecutionException: ... 'AsyncQdrantClient' object has no attribute 'search'
  search {'filter': 'lambda x: x.temperature > 1'} VectorSearchExecutionException: ... 'AsyncQdrantClient' object has no attribute 'search'
  hybrid_search with the same filter               VectorSearchExecutionException: ... 'FieldCondition' object has no attribute 'must'

Expected

  • Vector search uses query_points(query=vector, using=<vector name or None>, ...) and reads .points, so it works on every qdrant-client release that has query_points (1.10 and newer). The lower bound of the extra becomes ~= 1.10, which hybrid search needs already.
  • A single comparison filter is wrapped as Filter(must=[condition]), so x.temperature > 1 returns records 3 and 2 for both search and hybrid_search.

I checked open and closed issues and PRs for Qdrant, query_points, FieldCondition and qdrant-client versions and found nothing covering this. I am opening a PR right after filing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonPull requests for the Python Semantic Kerneltriage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions