Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fastembed integration #210

Merged
merged 32 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ca7a1ac
Add skeleton for fastvector registration
NirantK Jul 12, 2023
d090edc
* fix(qdrant_client.py): fix import statement for QdrantClientMixin f…
NirantK Jul 31, 2023
ec48a5a
Merge branch 'master' into fastvector
NirantK Jul 31, 2023
8aa718e
* chore(README.md): add bullet point for extensive test coverage
NirantK Jul 31, 2023
f04024b
* fix(qdrant_client.py): remove unused imports and code
NirantK Aug 3, 2023
40ce7bc
* feat(qdrant_client.py): add support for fastembed.qdrant_mixin.Qdra…
NirantK Aug 3, 2023
9ec14d0
* fix(qdrant_client.py): remove unused import of SearchParams
NirantK Aug 3, 2023
c54ebca
* fix(qdrant_client.py): fix import statement for SearchParams class …
NirantK Aug 3, 2023
b8d824b
* feat(models.py): add QueryResponse model for handling query responses
NirantK Aug 16, 2023
20c073b
* feat(qdrant_client.py): add batch_iterable method to QdrantClient c…
NirantK Aug 16, 2023
1e4666c
* chore(qdrant_client.py): reformat code for better readability
NirantK Aug 16, 2023
8a0d05e
* chore(test_qdrant_client.py): add type hinting to fastembed_setup f…
NirantK Aug 16, 2023
f021d7a
* chore(.gitignore): add pattern for ignoring .tar.gz files
NirantK Aug 16, 2023
d959430
Merge branch 'master' into fastvector
NirantK Aug 16, 2023
33e6bef
* refactor(test_qdrant_client.py): remove unused imports and variables
NirantK Aug 16, 2023
d20c406
* chore(test_qdrant_client.py): remove unused fixtures and test cases
NirantK Aug 16, 2023
8e7a78d
* chore(qdrant_client.py): remove unused imports
NirantK Aug 16, 2023
34c99a0
Add test skeleton
NirantK Aug 16, 2023
e3ab7dc
* chore(.gitignore): add local_cache/*/* to ignore local cache files
NirantK Aug 16, 2023
deed7f6
* fix(qdrant_client.py): change import statement for fastembed to use…
NirantK Aug 16, 2023
9e92818
* fix(qdrant_client.py): import missing models in qdrant_client.py
NirantK Aug 16, 2023
b9e53c4
* test(test_fast_embed.py): add assertion to verify count of document…
NirantK Aug 16, 2023
ca95dd3
* chore(pyproject.toml): add optional fastembed group to poetry depen…
NirantK Aug 16, 2023
4493aca
* chore(qdrant_client.py): add check for fastembed installation in Qd…
NirantK Aug 16, 2023
062e8e8
* fix(test_fast_embed.py): skip test if FastEmbed is not installed
NirantK Aug 16, 2023
acf3a1e
Delete unused usage file
NirantK Aug 16, 2023
4ff71e3
* chore(models.py): remove unused QueryResponse class
NirantK Aug 17, 2023
91c6323
* refactor(qdrant_client.py): organize imports and fix type hinting f…
NirantK Aug 17, 2023
e4062a2
review fixes
generall Aug 17, 2023
f567835
make mypy happy
generall Aug 17, 2023
3afa5f4
review fixes
generall Aug 18, 2023
8835f08
fix mypy
generall Aug 18, 2023
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
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pip install qdrant-client
- Local mode - use same API without running server
- REST and gRPC support
- Minimal dependencies
- Extensive Test Coverage

## Local mode

Expand All @@ -66,10 +67,36 @@ Local mode is useful for development, prototyping and testing.
- Run it in Colab or Jupyter Notebook, no extra dependencies required. See an [example](https://colab.research.google.com/drive/1Bz8RSVHwnNDaNtDwotfPj0w7AYzsdXZ-?usp=sharing)
- When you need to scale, simply switch to server mode.

### How it works?
## Fast Embeddings + Simpler API

We just implemented Qdrant API in pure Python.
We covered it with tests extensively to be sure it works the same as the server version.
```
pip install fastembed qdrant-client
```

FastEmbed is a library for creating fast vector embeddings on CPU. It is based on ONNX Runtime and allows to run inference on CPU with GPU-like performance.

Qdrant Client can use FastEmbed to create embeddings and upload them to Qdrant. This allows to simplify API and make it more intuitive.

```python
from qdrant_client import QdrantClient

# Initialize the client
client = QdrantClient(":memory:") # or QdrantClient(path="path/to/db")

# Prepare your documents, metadata, and IDs
docs = ["Qdrant has Langchain integrations", "Qdrant also has Llama Index integrations"]
metadatas = [
{"source": "Langchain-docs"},
{"source": "Linkedin-docs"},
]
ids = [42, 2]

# Use the new add method
client.add(collection_name="demo_collection", docs={"documents": docs, "metadatas": metadatas, "ids": ids})

search_result = client.query(collection_name="demo_collection", query_texts=["This is a query document"])
print(search_result)
```

## Connect to Qdrant server

Expand Down
49 changes: 40 additions & 9 deletions qdrant_client/qdrant_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ def __init__(
path: Optional[str] = None,
**kwargs: Any,
):
# Check if fastvector is installed
try:
from fastembed.qdrant_mixin import QdrantClientMixin

# If it is, add the mixin methods to this instance
for name, method in QdrantClientMixin.__dict__.items():
NirantK marked this conversation as resolved.
Show resolved Hide resolved
if callable(method):
setattr(self, name, method.__get__(self, self.__class__))
except ImportError:
# If it's not, do nothing
pass

self._client: QdrantBase

if location == ":memory:":
Expand Down Expand Up @@ -115,7 +127,9 @@ def grpc_collections(self) -> grpc.CollectionsStub:
if isinstance(self._client, QdrantRemote):
return self._client.grpc_collections

raise NotImplementedError(f"gRPC client is not supported for {type(self._client)}")
raise NotImplementedError(
f"gRPC client is not supported for {type(self._client)}"
)

@property
def grpc_points(self) -> grpc.PointsStub:
Expand All @@ -127,7 +141,9 @@ def grpc_points(self) -> grpc.PointsStub:
if isinstance(self._client, QdrantRemote):
return self._client.grpc_points

raise NotImplementedError(f"gRPC client is not supported for {type(self._client)}")
raise NotImplementedError(
f"gRPC client is not supported for {type(self._client)}"
)

@property
def async_grpc_points(self) -> grpc.PointsStub:
Expand All @@ -139,7 +155,9 @@ def async_grpc_points(self) -> grpc.PointsStub:
if isinstance(self._client, QdrantRemote):
return self._client.async_grpc_points

raise NotImplementedError(f"gRPC client is not supported for {type(self._client)}")
raise NotImplementedError(
f"gRPC client is not supported for {type(self._client)}"
)

@property
def async_grpc_collections(self) -> grpc.CollectionsStub:
Expand All @@ -151,7 +169,9 @@ def async_grpc_collections(self) -> grpc.CollectionsStub:
if isinstance(self._client, QdrantRemote):
return self._client.async_grpc_collections

raise NotImplementedError(f"gRPC client is not supported for {type(self._client)}")
raise NotImplementedError(
f"gRPC client is not supported for {type(self._client)}"
)

@property
def rest(self) -> SyncApis[ApiClient]:
Expand All @@ -163,7 +183,9 @@ def rest(self) -> SyncApis[ApiClient]:
if isinstance(self._client, QdrantRemote):
return self._client.rest

raise NotImplementedError(f"REST client is not supported for {type(self._client)}")
raise NotImplementedError(
f"REST client is not supported for {type(self._client)}"
)

@property
def http(self) -> SyncApis[ApiClient]:
Expand All @@ -175,7 +197,9 @@ def http(self) -> SyncApis[ApiClient]:
if isinstance(self._client, QdrantRemote):
return self._client.http

raise NotImplementedError(f"REST client is not supported for {type(self._client)}")
raise NotImplementedError(
f"REST client is not supported for {type(self._client)}"
)

def search_batch(
self,
Expand Down Expand Up @@ -1173,7 +1197,9 @@ def get_collection_aliases(
"""
assert len(kwargs) == 0, f"Unknown arguments: {list(kwargs.keys())}"

return self._client.get_collection_aliases(collection_name=collection_name, **kwargs)
return self._client.get_collection_aliases(
collection_name=collection_name, **kwargs
)

def get_aliases(self, **kwargs: Any) -> types.CollectionsAliasesResponse:
"""Get all aliases
Expand All @@ -1195,7 +1221,9 @@ def get_collections(self, **kwargs: Any) -> types.CollectionsResponse:

return self._client.get_collections(**kwargs)

def get_collection(self, collection_name: str, **kwargs: Any) -> types.CollectionInfo:
def get_collection(
self, collection_name: str, **kwargs: Any
) -> types.CollectionInfo:
"""Get detailed information about specified existing collection

Args:
Expand Down Expand Up @@ -1601,7 +1629,9 @@ def create_snapshot(

return self._client.create_snapshot(collection_name=collection_name, **kwargs)

def delete_snapshot(self, collection_name: str, snapshot_name: str, **kwargs: Any) -> bool:
def delete_snapshot(
self, collection_name: str, snapshot_name: str, **kwargs: Any
) -> bool:
"""Delete snapshot for a given collection.

Args:
Expand Down Expand Up @@ -1699,3 +1729,4 @@ def get_locks(self, **kwargs: Any) -> types.LocksOption:
assert len(kwargs) == 0, f"Unknown arguments: {list(kwargs.keys())}"

return self._client.get_locks(**kwargs)
return self._client.get_locks(**kwargs)
NirantK marked this conversation as resolved.
Show resolved Hide resolved