Skip to content

Allow query_points to accept Sequence[float]#1150

Open
veeceey wants to merge 4 commits intoqdrant:masterfrom
veeceey:fix/issue-1074-query-points-sequence-float
Open

Allow query_points to accept Sequence[float]#1150
veeceey wants to merge 4 commits intoqdrant:masterfrom
veeceey:fix/issue-1074-query-points-sequence-float

Conversation

@veeceey
Copy link
Copy Markdown

@veeceey veeceey commented Feb 8, 2026

Summary

  • Updates query_points and query_points_groups to accept Sequence[float] instead of list[float]
  • Allows tuples, lists, and other sequence types as query vectors
  • Matches the deprecated search() method API for better compatibility
  • Updates both sync (QdrantClient) and async (AsyncQdrantClient) clients
  • Includes comprehensive tests for tuple, list, and numpy array inputs

Motivation

The deprecated search() method accepted Sequence[float] for query vectors, but query_points() only accepted list[float]. This created an API inconsistency when migrating from search() to query_points().

With this change, users can now pass tuples and other sequence types:

# Now works with tuples
query_tuple = tuple([1.0] * 10)
result = client.query_points(
    collection_name="test",
    query=query_tuple,
    limit=5,
)

Changes

  • Updated type hints in client_base.py and async_client_base.py
  • Updated _resolve_query method in qdrant_fastembed.py and async_qdrant_fastembed.py to handle tuple conversion
  • Added test coverage for tuple, list, and numpy array inputs

Test Plan

  • Tests for sync client with tuple input
  • Tests for async client with tuple input
  • Tests for query_points_groups with tuple input
  • Backward compatibility tests with list and numpy array inputs
  • All tests pass locally

Fixes

- Update type hints to accept Sequence[float] instead of list[float]
- Allows tuples, lists, and other sequence types as query vectors
- Matches the deprecated search() method API for better compatibility
- Update both sync (QdrantClient) and async (AsyncQdrantClient) clients
- Update _resolve_query to handle tuple conversion
- Add comprehensive tests for tuple, list, and numpy array inputs

Fixes qdrant#1074
@netlify
Copy link
Copy Markdown

netlify bot commented Feb 8, 2026

Deploy Preview for poetic-froyo-8baba7 ready!

Name Link
🔨 Latest commit 36d6d6a
🔍 Latest deploy log https://app.netlify.com/projects/poetic-froyo-8baba7/deploys/69b2423243e8e4000861dd6e
😎 Deploy Preview https://deploy-preview-1150--poetic-froyo-8baba7.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6094fd54-e4f0-424b-a683-f046f38feaaa

📥 Commits

Reviewing files that changed from the base of the PR and between b5f5be9 and 36d6d6a.

📒 Files selected for processing (1)
  • tests/test_query_points_sequence.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_query_points_sequence.py

📝 Walkthrough

Walkthrough

Type annotations for numeric query inputs were widened from list[float] to Sequence[float] in sync and async client base files and in fastembed mixins. _resolve_query (sync and async fastembed) now accepts tuple and generic Sequence[float] (e.g., deque) by converting them to models.NearestQuery, and explicitly rejects raw byte-like and range inputs with ValueError. Two async public method signatures had their query parameter type broadened. Tests were added to verify tuple, deque, and numpy-array inputs for query_points and query_points_groups in both sync and async clients. No other control-flow or return-type changes were made.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: allowing query_points to accept Sequence[float] instead of list[float], which is the primary objective of the PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering motivation, changes made, test plan, and the issue it fixes.
Linked Issues check ✅ Passed The PR fully implements the requirement from issue #1074 to allow query_points to accept Sequence[float] for both sync and async clients, with comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are in scope: type hint updates in client_base.py and async_client_base.py, runtime handling in _resolve_query methods, guard conditions for unsupported types, and comprehensive test coverage for the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@veeceey
Copy link
Copy Markdown
Author

veeceey commented Feb 8, 2026

All checks passing, ready for maintainer review

@veeceey
Copy link
Copy Markdown
Author

veeceey commented Feb 19, 2026

Thanks for the review feedback, CodeRabbit! Here's my plan to address the suggestions:

  1. Async test for query_points_groups: Good catch on the asymmetric test coverage. I'll add an async variant of the query_points_groups tuple test to ensure the tuple handling works in both the sync (qdrant_fastembed.py) and async (async_qdrant_fastembed.py) code paths.

  2. Generic Sequence fallback in _resolve_query: Agreed that accepting Sequence[float] in the type hint but only handling list/tuple/ndarray at runtime is a gap. I'll add a generic Sequence fallback (converting to list) so that any conforming Sequence type works without hitting the ValueError.

  3. Out of scope formatting changes in async_client_base.py: Fair point -- I'll split these out into a separate PR to keep this one focused on the Sequence[float] feature.

  4. Docstring coverage: I'll add docstrings to the new functions to meet the coverage threshold.

Will push updates shortly.

…rmatting changes

- Add async test for query_points_groups with tuple input
- Add generic Sequence fallback in _resolve_query for both sync and async
- Remove out-of-scope formatting changes from async_client_base.py,
  keeping only the Sequence[float] type hint updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@veeceey
Copy link
Copy Markdown
Author

veeceey commented Feb 20, 2026

Pushed the updates addressing review feedback:

  1. Async test for query_points_groups: Added async variant of the tuple test to ensure coverage for both sync and async code paths.
  2. Generic Sequence fallback: Added a Sequence fallback in _resolve_query (both sync and async) so any conforming Sequence type works without hitting the ValueError.
  3. Removed out-of-scope formatting changes: Reverted the formatting-only changes in async_client_base.py, keeping only the Sequence[float] type hint updates.

All 4 tests pass.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/test_query_points_sequence.py (1)

118-192: Consider adding a test for the generic Sequence fallback.

The PR adds a generic Sequence fallback in _resolve_query (converting any Sequence[float] to list), but no test exercises that path. A test using e.g. collections.deque would confirm the fallback works end-to-end:

from collections import deque

def test_query_points_with_deque():
    """Test that query_points accepts deque (generic Sequence[float])"""
    client = QdrantClient(":memory:")
    collection_name = "test_query_deque"
    client.create_collection(
        collection_name=collection_name,
        vectors_config=models.VectorParams(size=10, distance=models.Distance.COSINE),
    )
    client.upsert(
        collection_name=collection_name,
        points=[
            models.PointStruct(id=i, vector=[float(i)] * 10, payload={"value": i})
            for i in range(5)
        ],
    )
    query_deque = deque([1.0] * 10)
    result = client.query_points(
        collection_name=collection_name, query=query_deque, limit=5,
    )
    assert len(result.points) == 5
    client.delete_collection(collection_name)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_query_points_sequence.py` around lines 118 - 192, Add a test that
exercises the generic Sequence fallback in _resolve_query by passing a
collections.deque as the query vector to query_points (and its async
counterpart); create a collection with VectorParams(size=10), upsert a few
PointStructs, call client.query_points (and await for AsyncQdrantClient) with
query=deque([1.0]*10) and assert the returned points length matches the limit,
then delete the collection—this will verify the Sequence-to-list conversion path
used by _resolve_query for both QdrantClient.query_points and
AsyncQdrantClient.query_points.
qdrant_client/qdrant_fastembed.py (1)

854-857: isinstance(query, Sequence) matches bytes, bytearray, and range — consider adding defensive checks.

str is correctly caught as a PointId first (line 842 checks isinstance(query, get_args(types.PointId)), which includes str). However, the fallback at line 854 will accept any Sequence, including bytes, bytearray, and range, which would be converted to lists without validation. This relies entirely on upstream type hints for safety.

Optional: Add an element-type check before conversion:

Suggested refinement
         if isinstance(query, Sequence):
-            # Generic Sequence fallback - convert any Sequence[float] to list
-            return models.NearestQuery(nearest=list(query))
+            # Generic Sequence fallback - convert any Sequence of numbers to list.
+            # str is already handled as PointId above; guard against bytes/bytearray/range.
+            if len(query) > 0 and not isinstance(query[0], (int, float)):
+                raise ValueError(f"Unsupported query type: {type(query)}")
+            return models.NearestQuery(nearest=list(query))

Note: async_qdrant_fastembed.py (line 792) has identical code that should be updated in parallel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@qdrant_client/qdrant_fastembed.py` around lines 854 - 857, The Sequence
fallback in the nearest-query conversion accepts bytes/bytearray/range and
blindly converts to list; update the branch that returns
models.NearestQuery(nearest=list(query)) to first guard against unwanted
Sequence types (e.g., if isinstance(query, (bytes, bytearray, range)): raise
TypeError(...) or skip) and/or validate element types (e.g., ensure all items
are int/float or castable to float via a quick all(isinstance(x, (int, float))
for x in query) check) before converting; apply the same defensive check/change
to the identical code in async_qdrant_fastembed.py so models.NearestQuery is
only created from valid numeric sequences.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@qdrant_client/qdrant_fastembed.py`:
- Around line 854-857: The Sequence fallback in the nearest-query conversion
accepts bytes/bytearray/range and blindly converts to list; update the branch
that returns models.NearestQuery(nearest=list(query)) to first guard against
unwanted Sequence types (e.g., if isinstance(query, (bytes, bytearray, range)):
raise TypeError(...) or skip) and/or validate element types (e.g., ensure all
items are int/float or castable to float via a quick all(isinstance(x, (int,
float)) for x in query) check) before converting; apply the same defensive
check/change to the identical code in async_qdrant_fastembed.py so
models.NearestQuery is only created from valid numeric sequences.

In `@tests/test_query_points_sequence.py`:
- Around line 118-192: Add a test that exercises the generic Sequence fallback
in _resolve_query by passing a collections.deque as the query vector to
query_points (and its async counterpart); create a collection with
VectorParams(size=10), upsert a few PointStructs, call client.query_points (and
await for AsyncQdrantClient) with query=deque([1.0]*10) and assert the returned
points length matches the limit, then delete the collection—this will verify the
Sequence-to-list conversion path used by _resolve_query for both
QdrantClient.query_points and AsyncQdrantClient.query_points.

- Reject bytes, bytearray, and range before the generic Sequence fallback
  in both sync and async _resolve_query methods
- Add sync and async tests using collections.deque to exercise the
  generic Sequence[float] fallback path
@veeceey
Copy link
Copy Markdown
Author

veeceey commented Mar 12, 2026

Addressed the latest review nitpicks:

  1. Guard against non-numeric Sequence types: Added explicit rejection of bytes, bytearray, and range before the generic Sequence fallback in both sync and async _resolve_query methods. These types technically satisfy isinstance(x, Sequence) but aren't valid numeric vectors.

  2. Deque tests: Added sync and async tests using collections.deque to exercise the generic Sequence fallback path end-to-end, confirming it works for Sequence types beyond list/tuple/ndarray.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
qdrant_client/qdrant_fastembed.py (1)

854-861: Use TypeError for unsupported query type arguments.

These branches reject based on argument type, not invalid values. Current ValueError usage diverges from Python semantics where TypeError should signal type contract violations. This makes it harder for callers to distinguish between type errors and value validation errors.

Also update the docstring from "Raises: ValueError" to "Raises: TypeError" to match the implementation. Keep the autogenerated async copy in sync as well.

♻️ Proposed fix
-        if isinstance(query, (bytes, bytearray, range)):
-            raise ValueError(f"Unsupported query type: {type(query)}")
+        if isinstance(query, (bytes, bytearray, range)):
+            raise TypeError(f"Unsupported query type: {type(query)}")
@@
-        raise ValueError(f"Unsupported query type: {type(query)}")
+        raise TypeError(f"Unsupported query type: {type(query)}")

Update docstring in both _resolve_query methods:

-            ValueError: if query is not of supported type
+            TypeError: if query is not of supported type
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@qdrant_client/qdrant_fastembed.py` around lines 854 - 861, Replace ValueError
with TypeError in the _resolve_query implementation(s): change the two raise
ValueError(...) lines inside _resolve_query to raise TypeError(...) because
these branches reject by argument type, and update the method docstring "Raises:
ValueError" to "Raises: TypeError" accordingly; do the same for the
autogenerated async copy of _resolve_query so both synchronous and async
variants use TypeError in raises and docstrings and remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_query_points_sequence.py`:
- Around line 195-265: Extend the existing tests
(test_query_points_with_deque_sync and test_query_points_with_deque_async) to
also assert the new reject path by passing invalid Sequence types (bytes,
bytearray, range) into client.query_points and verifying they raise the expected
exception (TypeError/ValueError) for both QdrantClient and AsyncQdrantClient;
locate the call sites to client.query_points in those functions and add
assertions that calling query_points with query=b"bytes..." ,
query=bytearray(...), and query=range(...) raises the same error the library
uses for unsupported sequence types so the guard branches are covered.

---

Nitpick comments:
In `@qdrant_client/qdrant_fastembed.py`:
- Around line 854-861: Replace ValueError with TypeError in the _resolve_query
implementation(s): change the two raise ValueError(...) lines inside
_resolve_query to raise TypeError(...) because these branches reject by argument
type, and update the method docstring "Raises: ValueError" to "Raises:
TypeError" accordingly; do the same for the autogenerated async copy of
_resolve_query so both synchronous and async variants use TypeError in raises
and docstrings and remain consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cbe8786f-4858-459b-88cc-d94767d4377c

📥 Commits

Reviewing files that changed from the base of the PR and between c3d7dec and b5f5be9.

📒 Files selected for processing (3)
  • qdrant_client/async_qdrant_fastembed.py
  • qdrant_client/qdrant_fastembed.py
  • tests/test_query_points_sequence.py

Comment thread tests/test_query_points_sequence.py
@veeceey
Copy link
Copy Markdown
Author

veeceey commented Apr 9, 2026

hey, just checking in on this one - anything else needed?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow query_points query param to accept Sequence[float]

1 participant