Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions qdrant_client/async_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,8 @@ async def delete_shard_key(

async def info(self) -> types.VersionInfo:
raise NotImplementedError()

async def cluster_collection_update(
self, collection_name: str, cluster_operation: types.ClusterOperations, **kwargs: Any
) -> bool:
raise NotImplementedError()
15 changes: 15 additions & 0 deletions qdrant_client/async_qdrant_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2286,3 +2286,18 @@ async def info(self) -> types.VersionInfo:

"""
return await self._client.info()

async def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
assert len(kwargs) == 0, f"Unknown arguments: {list(kwargs.keys())}"
return await self._client.cluster_collection_update(
collection_name=collection_name,
cluster_operation=cluster_operation,
timeout=timeout,
**kwargs,
)
17 changes: 17 additions & 0 deletions qdrant_client/async_qdrant_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -2394,3 +2394,20 @@ async def info(self) -> types.VersionInfo:
version_info = await self.rest.service_api.root()
assert version_info is not None, "Healthcheck returned None"
return version_info

async def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
update_result = (
await self.rest.distributed_api.update_collection_cluster(
collection_name=collection_name,
cluster_operations=cluster_operation,
timeout=timeout,
)
).result
assert update_result is not None, "Cluster collection update returned None"
return update_result
8 changes: 8 additions & 0 deletions qdrant_client/client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,11 @@ def delete_shard_key(

def info(self) -> types.VersionInfo:
raise NotImplementedError()

def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
**kwargs: Any,
) -> bool:
raise NotImplementedError()
2 changes: 2 additions & 0 deletions qdrant_client/conversions/common_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ def get_args_subscribed(tp): # type: ignore
VersionInfo: TypeAlias = rest.VersionInfo

ReplicaState: TypeAlias = rest.ReplicaState
ClusterOperations: TypeAlias = rest.ClusterOperations

# we can't use `nptyping` package due to numpy/python-version incompatibilities
# thus we need to define precise type annotations while we support python3.7
_np_numeric = Union[
Expand Down
78 changes: 12 additions & 66 deletions qdrant_client/embed/_inspection_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3150,28 +3150,10 @@
"title": "Fusion",
"type": "string",
},
"ShardTransferMethodOneOf": {
"description": "Stream all shard records in batches until the whole shard is transferred.",
"enum": ["stream_records"],
"title": "ShardTransferMethodOneOf",
"type": "string",
},
"ShardTransferMethodOneOf1": {
"description": "Snapshot the shard, transfer and restore it on the receiver.",
"enum": ["snapshot"],
"title": "ShardTransferMethodOneOf1",
"type": "string",
},
"ShardTransferMethodOneOf2": {
"description": "Attempt to transfer shard difference by WAL delta.",
"enum": ["wal_delta"],
"title": "ShardTransferMethodOneOf2",
"type": "string",
},
"ShardTransferMethodOneOf3": {
"description": "Shard transfer for resharding: stream all records in batches until all points are transferred.",
"enum": ["resharding_stream_records"],
"title": "ShardTransferMethodOneOf3",
"ShardTransferMethod": {
"description": "Methods for transferring a shard from one node to another. - `stream_records` - Stream all shard records in batches until the whole shard is transferred. - `snapshot` - Snapshot the shard, transfer and restore it on the receiver. - `wal_delta` - Attempt to transfer shard difference by WAL delta. - `resharding_stream_records` - Shard transfer for resharding: stream all records in batches until all points are transferred.",
"enum": ["stream_records", "snapshot", "wal_delta", "resharding_stream_records"],
"title": "ShardTransferMethod",
"type": "string",
},
"MoveShard": {
Expand All @@ -3181,16 +3163,9 @@
"to_peer_id": {"description": "", "title": "To Peer Id", "type": "integer"},
"from_peer_id": {"description": "", "title": "From Peer Id", "type": "integer"},
"method": {
"anyOf": [
{"$ref": "#/$defs/ShardTransferMethodOneOf"},
{"$ref": "#/$defs/ShardTransferMethodOneOf1"},
{"$ref": "#/$defs/ShardTransferMethodOneOf2"},
{"$ref": "#/$defs/ShardTransferMethodOneOf3"},
{"type": "null"},
],
"anyOf": [{"$ref": "#/$defs/ShardTransferMethod"}, {"type": "null"}],
"default": None,
"description": "Method for transferring the shard from one node to another",
"title": "Method",
},
},
"required": ["shard_id", "to_peer_id", "from_peer_id"],
Expand Down Expand Up @@ -4031,16 +4006,9 @@
"to_peer_id": {"description": "", "title": "To Peer Id", "type": "integer"},
"from_peer_id": {"description": "", "title": "From Peer Id", "type": "integer"},
"method": {
"anyOf": [
{"$ref": "#/$defs/ShardTransferMethodOneOf"},
{"$ref": "#/$defs/ShardTransferMethodOneOf1"},
{"$ref": "#/$defs/ShardTransferMethodOneOf2"},
{"$ref": "#/$defs/ShardTransferMethodOneOf3"},
{"type": "null"},
],
"anyOf": [{"$ref": "#/$defs/ShardTransferMethod"}, {"type": "null"}],
"default": None,
"description": "Method for transferring the shard from one node to another",
"title": "Method",
},
},
"required": ["shard_id", "to_peer_id", "from_peer_id"],
Expand All @@ -4053,16 +4021,7 @@
"shard_id": {"description": "", "title": "Shard Id", "type": "integer"},
"from_peer_id": {"description": "", "title": "From Peer Id", "type": "integer"},
"to_peer_id": {"description": "", "title": "To Peer Id", "type": "integer"},
"method": {
"anyOf": [
{"$ref": "#/$defs/ShardTransferMethodOneOf"},
{"$ref": "#/$defs/ShardTransferMethodOneOf1"},
{"$ref": "#/$defs/ShardTransferMethodOneOf2"},
{"$ref": "#/$defs/ShardTransferMethodOneOf3"},
],
"description": "",
"title": "Method",
},
"method": {"$ref": "#/$defs/ShardTransferMethod", "description": ""},
},
"required": ["shard_id", "from_peer_id", "to_peer_id", "method"],
"title": "RestartTransfer",
Expand Down Expand Up @@ -4186,29 +4145,16 @@
"title": "SnapshotPriority",
"type": "string",
},
"ReshardingDirectionOneOf": {
"description": "Scale up, add a new shard",
"enum": ["up"],
"title": "ReshardingDirectionOneOf",
"type": "string",
},
"ReshardingDirectionOneOf1": {
"description": "Scale down, remove a shard",
"enum": ["down"],
"title": "ReshardingDirectionOneOf1",
"ReshardingDirection": {
"description": "Resharding direction, scale up or down in number of shards - `up` - Scale up, add a new shard - `down` - Scale down, remove a shard",
"enum": ["up", "down"],
"title": "ReshardingDirection",
"type": "string",
},
"StartResharding": {
"additionalProperties": False,
"properties": {
"direction": {
"anyOf": [
{"$ref": "#/$defs/ReshardingDirectionOneOf"},
{"$ref": "#/$defs/ReshardingDirectionOneOf1"},
],
"description": "",
"title": "Direction",
},
"direction": {"$ref": "#/$defs/ReshardingDirection", "description": ""},
"peer_id": {
"anyOf": [{"type": "integer"}, {"type": "null"}],
"default": None,
Expand Down
58 changes: 4 additions & 54 deletions qdrant_client/http/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2425,25 +2425,15 @@ class RequestsTelemetry(BaseModel):
grpc: "GrpcTelemetry" = Field(..., description="")


class ReshardingDirectionOneOf(str, Enum):
class ReshardingDirection(str, Enum):
"""
Scale up, add a new shard
Resharding direction, scale up or down in number of shards - `up` - Scale up, add a new shard - `down` - Scale down, remove a shard
"""

def __str__(self) -> str:
return str(self.value)

UP = "up"


class ReshardingDirectionOneOf1(str, Enum):
"""
Scale down, remove a shard
"""

def __str__(self) -> str:
return str(self.value)

DOWN = "down"


Expand Down Expand Up @@ -2834,47 +2824,17 @@ class ShardTransferInfo(BaseModel):
)


class ShardTransferMethodOneOf(str, Enum):
class ShardTransferMethod(str, Enum):
"""
Stream all shard records in batches until the whole shard is transferred.
Methods for transferring a shard from one node to another. - `stream_records` - Stream all shard records in batches until the whole shard is transferred. - `snapshot` - Snapshot the shard, transfer and restore it on the receiver. - `wal_delta` - Attempt to transfer shard difference by WAL delta. - `resharding_stream_records` - Shard transfer for resharding: stream all records in batches until all points are transferred.
"""

def __str__(self) -> str:
return str(self.value)

STREAM_RECORDS = "stream_records"


class ShardTransferMethodOneOf1(str, Enum):
"""
Snapshot the shard, transfer and restore it on the receiver.
"""

def __str__(self) -> str:
return str(self.value)

SNAPSHOT = "snapshot"


class ShardTransferMethodOneOf2(str, Enum):
"""
Attempt to transfer shard difference by WAL delta.
"""

def __str__(self) -> str:
return str(self.value)

WAL_DELTA = "wal_delta"


class ShardTransferMethodOneOf3(str, Enum):
"""
Shard transfer for resharding: stream all records in batches until all points are transferred.
"""

def __str__(self) -> str:
return str(self.value)

RESHARDING_STREAM_RECORDS = "resharding_stream_records"


Expand Down Expand Up @@ -3745,10 +3705,6 @@ def __str__(self) -> str:
StrictInt,
ReadConsistencyType,
]
ReshardingDirection = Union[
ReshardingDirectionOneOf,
ReshardingDirectionOneOf1,
]
ShardCleanStatusTelemetry = Union[
ShardCleanStatusTelemetryOneOf,
ShardCleanStatusTelemetryOneOf1,
Expand All @@ -3761,12 +3717,6 @@ def __str__(self) -> str:
ShardSnapshotLocation = Union[
StrictStr,
]
ShardTransferMethod = Union[
ShardTransferMethodOneOf,
ShardTransferMethodOneOf1,
ShardTransferMethodOneOf2,
ShardTransferMethodOneOf3,
]
SparseIndexType = Union[
SparseIndexTypeOneOf,
SparseIndexTypeOneOf1,
Expand Down
11 changes: 11 additions & 0 deletions qdrant_client/local/async_qdrant_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -941,3 +941,14 @@ async def info(self) -> types.VersionInfo:
return rest_models.VersionInfo(
title="qdrant - vector search engine", version=version, commit=None
)

async def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
raise NotImplementedError(
"Cluster collection updates is not supported in the local Qdrant. Please use server Qdrant if you need a cluster"
)
12 changes: 12 additions & 0 deletions qdrant_client/local/qdrant_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,3 +1009,15 @@ def info(self) -> types.VersionInfo:
return rest_models.VersionInfo(
title="qdrant - vector search engine", version=version, commit=None
)

def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
raise NotImplementedError(
"Cluster collection updates is not supported in the local Qdrant. "
"Please use server Qdrant if you need a cluster"
)
16 changes: 16 additions & 0 deletions qdrant_client/qdrant_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2415,3 +2415,19 @@ def info(self) -> types.VersionInfo:

"""
return self._client.info()

def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
assert len(kwargs) == 0, f"Unknown arguments: {list(kwargs.keys())}"

return self._client.cluster_collection_update(
collection_name=collection_name,
cluster_operation=cluster_operation,
timeout=timeout,
**kwargs,
)
13 changes: 13 additions & 0 deletions qdrant_client/qdrant_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -2580,3 +2580,16 @@ def info(self) -> types.VersionInfo:
version_info = self.rest.service_api.root()
assert version_info is not None, "Healthcheck returned None"
return version_info

def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
update_result = self.rest.distributed_api.update_collection_cluster(
collection_name=collection_name, cluster_operations=cluster_operation, timeout=timeout
).result
assert update_result is not None, "Cluster collection update returned None"
return update_result
Comment on lines +2584 to +2595
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Return type bug and unused kwargs: return the boolean and validate kwargs.

Like the async variant, this currently returns the full InlineResponse200. Return its .result and assert no stray kwargs.

 def cluster_collection_update(
     self,
     collection_name: str,
     cluster_operation: types.ClusterOperations,
     timeout: Optional[int] = None,
-    **kwargs: Any,
+    **kwargs: Any,
 ) -> bool:
-    update_result = self.rest.distributed_api.update_collection_cluster(
+    assert len(kwargs) == 0, f"Unknown arguments: {list(kwargs.keys())}"
+    update_response = self.rest.distributed_api.update_collection_cluster(
         collection_name=collection_name, cluster_operations=cluster_operation, timeout=timeout
     )
-    assert update_result is not None, "Cluster collection update returned None"
-    return update_result
+    result = getattr(update_response, "result", None)
+    assert result is not None, "Cluster collection update returned None"
+    return result

Also consider adding the timeout parameter to the abstract base (QdrantBase) for signature parity with concrete implementations.


🏁 Script executed:

#!/bin/bash
# Find the cluster_collection_update method (both sync and async variants)
rg -n "def cluster_collection_update" --type=py

Length of output: 618


🏁 Script executed:

#!/bin/bash
# Search for update_collection_cluster calls and return type patterns
rg -n "update_collection_cluster" --type=py -A 2 -B 2

Length of output: 2740


🏁 Script executed:

#!/bin/bash
# Look for InlineResponse200 imports and usage in the file
rg -n "InlineResponse200" --type=py -C 3

Length of output: 116495


🏁 Script executed:

#!/bin/bash
# Check QdrantBase class to see base method signatures
rg -n "class QdrantBase" --type=py -A 30 | head -50

Length of output: 1835


🏁 Script executed:

#!/bin/bash
# Find other similar update_ methods in qdrant_remote.py to see pattern
rg -n "def.*_update\(" qdrant_client/qdrant_remote.py --type=py -A 5 | head -60

Length of output: 222


🏁 Script executed:

#!/bin/bash
# Check the full async variant implementation
cat qdrant_client/async_qdrant_remote.py | sed -n '2398,2415p'

Length of output: 518


🏁 Script executed:

#!/bin/bash
# Look for cluster_collection_update in QdrantBase and AsyncQdrantBase
rg -n "def cluster_collection_update" qdrant_client/client_base.py qdrant_client/async_client_base.py -A 6

Length of output: 780


🏁 Script executed:

#!/bin/bash
# Check if other similar methods validate kwargs
rg -n "assert len\(kwargs\)" qdrant_client/qdrant_remote.py -B 3 -A 1 | head -40

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if the current code at 2584-2595 already exists or needs to be created
cat qdrant_client/qdrant_remote.py | sed -n '2584,2595p'

Length of output: 506


Fix return type and validate kwargs parameters.

The method currently returns the full InlineResponse200 object instead of extracting the boolean .result field, violating its -> bool signature. The unused **kwargs parameter should be validated:

 def cluster_collection_update(
     self,
     collection_name: str,
     cluster_operation: types.ClusterOperations,
     timeout: Optional[int] = None,
     **kwargs: Any,
 ) -> bool:
+    assert not kwargs, f"Unexpected keyword arguments: {list(kwargs.keys())}"
-    update_result = self.rest.distributed_api.update_collection_cluster(
+    update_response = self.rest.distributed_api.update_collection_cluster(
         collection_name=collection_name, cluster_operations=cluster_operation, timeout=timeout
     )
-    assert update_result is not None, "Cluster collection update returned None"
-    return update_result
+    result = update_response.result
+    assert result is not None, "Cluster collection update returned None"
+    return result

Also apply identical fix to the async variant at async_qdrant_remote.py:2398–2413, and consider adding timeout parameter to base class signatures (client_base.py:398 and async_client_base.py:371) for signature consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
update_result = self.rest.distributed_api.update_collection_cluster(
collection_name=collection_name, cluster_operations=cluster_operation, timeout=timeout
)
assert update_result is not None, "Cluster collection update returned None"
return update_result
def cluster_collection_update(
self,
collection_name: str,
cluster_operation: types.ClusterOperations,
timeout: Optional[int] = None,
**kwargs: Any,
) -> bool:
assert not kwargs, f"Unexpected keyword arguments: {list(kwargs.keys())}"
update_response = self.rest.distributed_api.update_collection_cluster(
collection_name=collection_name, cluster_operations=cluster_operation, timeout=timeout
)
result = update_response.result
assert result is not None, "Cluster collection update returned None"
return result
🧰 Tools
🪛 Ruff (0.14.4)

2589-2589: Unused method argument: kwargs

(ARG002)

🤖 Prompt for AI Agents
In qdrant_client/qdrant_remote.py around lines 2584–2595, the method currently
returns the full InlineResponse200 object and ignores **kwargs; change it to
extract and return the boolean result field (e.g., return update_result.result)
to match the -> bool signature, and validate kwargs by raising a TypeError or
ValueError if any unexpected kwargs are passed (or explicitly accept and
document supported keys); apply the identical change to the async variant in
async_qdrant_remote.py around lines 2398–2413 so it returns the boolean result
and validates kwargs as well; finally, update the base class method signatures
in client_base.py (around line ~398) and async_client_base.py (around line ~371)
to include the timeout parameter for signature consistency.

Loading