From 7aa7428f9c24c13cd8a7393b781885a960640726 Mon Sep 17 00:00:00 2001 From: Madhavan Sridharan Date: Fri, 4 Apr 2025 16:16:22 -0400 Subject: [PATCH 1/4] Initial implementation of Cassandra engine --- .gitignore | 3 + engine/clients/cassandra/README.md | 55 +++++ engine/clients/cassandra/__init__.py | 9 + engine/clients/cassandra/config.py | 7 + engine/clients/cassandra/configure.py | 96 ++++++++ engine/clients/cassandra/parser.py | 92 ++++++++ engine/clients/cassandra/search.py | 116 +++++++++ engine/clients/cassandra/upload.py | 95 ++++++++ engine/clients/client_factory.py | 8 + .../cassandra-single-node/docker-compose.yaml | 29 +++ .../configurations/cassandra-single-node.json | 25 ++ poetry.lock | 222 ++++++++++++++++-- pyproject.toml | 1 + 13 files changed, 735 insertions(+), 23 deletions(-) create mode 100644 engine/clients/cassandra/README.md create mode 100644 engine/clients/cassandra/__init__.py create mode 100644 engine/clients/cassandra/config.py create mode 100644 engine/clients/cassandra/configure.py create mode 100644 engine/clients/cassandra/parser.py create mode 100644 engine/clients/cassandra/search.py create mode 100644 engine/clients/cassandra/upload.py create mode 100644 engine/servers/cassandra-single-node/docker-compose.yaml create mode 100644 experiments/configurations/cassandra-single-node.json diff --git a/.gitignore b/.gitignore index e21c3643..8cb6a349 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ NOTES.md results/* tools/custom/data.json + +.venv +*.DS_Store diff --git a/engine/clients/cassandra/README.md b/engine/clients/cassandra/README.md new file mode 100644 index 00000000..f4f6c1bd --- /dev/null +++ b/engine/clients/cassandra/README.md @@ -0,0 +1,55 @@ +# Apache Cassandra® + +## Setup +### Pre-requisites +Adjust the configuration file [`cassandra-single-node.json`](../../../experiments/configurations/cassandra-single-node.json) to your needs. The default configuration is set to use the default Apache Cassandra® settings. +### Start up the server +Run the following command to start the server (alternatively, run `docker compose up -d` to run in detached mode): +```bash +$ docker compose up + +[+] Running 1/1 + ✔ cassandra Pulled 1.4s +[+] Running 1/1 + ✔ Container cassandra-benchmark Recreated 0.1s +Attaching to cassandra-benchmark +cassandra-benchmark | CompileCommand: dontinline org/apache/cassandra/db/Columns$Serializer.deserializeLargeSubset(Lorg/apache/cassandra/io/util/DataInputPlus;Lorg/apache/cassandra/db/Columns;I)Lorg/apache/cassandra/db/Columns; bool dontinline = true +... +cassandra-benchmark | INFO [main] 2025-04-04 22:27:38,592 StorageService.java:957 - Cassandra version: 5.0.3 +cassandra-benchmark | INFO [main] 2025-04-04 22:27:38,592 StorageService.java:958 - Git SHA: b0226c8ea122c3e5ea8680efb0744d33924fd732 +cassandra-benchmark | INFO [main] 2025-04-04 22:27:38,592 StorageService.java:959 - CQL version: 3.4.7 +... +cassandra-benchmark | INFO [main] 2025-04-04 22:28:25,091 StorageService.java:3262 - Node /172.18.0.2:7000 state jump to NORMAL +``` +> [!TIP] +> Other helpful commands: +> - Run `docker exec -it cassandra-benchmark cqlsh` to access the Cassandra shell. +> - Run `docker compose logs --follow --tail 10` to view & follow the logs of the container running Cassandra. + +### Start up the client benchmark +Run the following command to start the client benchmark using `glove-25-angular` dataset as an example: +```bash +% python3 -m run --engines cassandra-single-node --datasets glove-25-angular +``` +and you'll see the following output: +```bash +Running experiment: cassandra-single-node - glove-25-angular +Downloading http://ann-benchmarks.com/glove-25-angular.hdf5... +... +Experiment stage: Configure +Experiment stage: Upload +1183514it [mm:ss, <...>it/s] +Upload time: <...> +Index is not ready yet, sleeping for 2 minutes... +... +Total import time: <...> +Experiment stage: Search +10000it [mm:ss, <...>it/s] +10000it [mm:ss, <...>it/s] +Experiment stage: Done +Results saved to: /path/to/repository/results +... +``` + +> [!TIP] +> If you want to see the detailed results, set environment variable `DETAILED_RESULTS=1` before running the benchmark. \ No newline at end of file diff --git a/engine/clients/cassandra/__init__.py b/engine/clients/cassandra/__init__.py new file mode 100644 index 00000000..4e3e7e54 --- /dev/null +++ b/engine/clients/cassandra/__init__.py @@ -0,0 +1,9 @@ +from engine.clients.cassandra.configure import CassandraConfigurator +from engine.clients.cassandra.search import CassandraSearcher +from engine.clients.cassandra.upload import CassandraUploader + +__all__ = [ + "CassandraConfigurator", + "CassandraSearcher", + "CassandraUploader" +] \ No newline at end of file diff --git a/engine/clients/cassandra/config.py b/engine/clients/cassandra/config.py new file mode 100644 index 00000000..ca3ff758 --- /dev/null +++ b/engine/clients/cassandra/config.py @@ -0,0 +1,7 @@ +import os + +CASSANDRA_KEYSPACE = os.getenv("CASSANDRA_KEYSPACE", "benchmark") +CASSANDRA_TABLE = os.getenv("CASSANDRA_TABLE", "vectors") +ASTRA_API_ENDPOINT = os.getenv("ASTRA_API_ENDPOINT", None) +ASTRA_API_KEY = os.getenv("ASTRA_API_KEY", None) +ASTRA_SCB_PATH = os.getenv("ASTRA_SCB_PATH", None) \ No newline at end of file diff --git a/engine/clients/cassandra/configure.py b/engine/clients/cassandra/configure.py new file mode 100644 index 00000000..5fe7093c --- /dev/null +++ b/engine/clients/cassandra/configure.py @@ -0,0 +1,96 @@ +from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT +from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy, ExponentialReconnectionPolicy +from cassandra import ConsistencyLevel, ProtocolVersion + +from benchmark.dataset import Dataset +from engine.base_client.configure import BaseConfigurator +from engine.base_client.distances import Distance +from engine.clients.cassandra.config import CASSANDRA_KEYSPACE, CASSANDRA_TABLE + + +class CassandraConfigurator(BaseConfigurator): + SPARSE_VECTOR_SUPPORT = False + DISTANCE_MAPPING = { + Distance.L2: "euclidean", + Distance.COSINE: "cosine", + Distance.DOT: "dot_product" + } + + def __init__(self, host, collection_params: dict, connection_params: dict): + super().__init__(host, collection_params, connection_params) + + # Set up execution profiles for consistency and performance + profile = ExecutionProfile( + load_balancing_policy=TokenAwarePolicy(DCAwareRoundRobinPolicy()), + consistency_level=ConsistencyLevel.LOCAL_QUORUM, + request_timeout=60 + ) + + # Initialize Cassandra cluster connection + self.cluster = Cluster( + contact_points=[host], + execution_profiles={EXEC_PROFILE_DEFAULT: profile}, + protocol_version=ProtocolVersion.V4, + reconnection_policy=ExponentialReconnectionPolicy(base_delay=1, max_delay=60), + **connection_params + ) + self.session = self.cluster.connect() + + def clean(self): + """Drop the keyspace if it exists""" + self.session.execute(f"DROP KEYSPACE IF EXISTS {CASSANDRA_KEYSPACE}") + + def recreate(self, dataset: Dataset, collection_params): + """Create keyspace and table for vector search""" + # Create keyspace if not exists + self.session.execute( + f"""CREATE KEYSPACE IF NOT EXISTS {CASSANDRA_KEYSPACE} + WITH REPLICATION = {{ 'class': 'SimpleStrategy', 'replication_factor': 1 }}""" + ) + + # Use the keyspace + self.session.execute(f"USE {CASSANDRA_KEYSPACE}") + + # Get the distance metric + distance_metric = self.DISTANCE_MAPPING.get(dataset.config.distance) + vector_size = dataset.config.vector_size + + # Create vector table + # Using a simple schema that supports vector similarity search + self.session.execute( + f"""CREATE TABLE IF NOT EXISTS {CASSANDRA_TABLE} ( + id int PRIMARY KEY, + embedding vector, + metadata map + )""" + ) + + # Create vector index using the appropriate distance metric + self.session.execute( + f"""CREATE CUSTOM INDEX IF NOT EXISTS vector_index ON {CASSANDRA_TABLE}(embedding) + USING 'StorageAttachedIndex' + WITH OPTIONS = {{ 'similarity_function': '{distance_metric}' }}""" + ) + + # Add additional schema fields based on collection_params if needed + for field_name, field_type in dataset.config.schema.items(): + if field_type in ["keyword", "text"]: + # For text fields, we would typically add them to metadata + pass + elif field_type in ["int", "float"]: + # For numeric fields that need separate indexing + # In a real implementation, we might alter the table to add these columns + pass + + return collection_params + + def execution_params(self, distance, vector_size) -> dict: + """Return any execution parameters needed for the dataset""" + return {"normalize": distance == Distance.COSINE} + + def delete_client(self): + """Close the Cassandra connection""" + if hasattr(self, 'session') and self.session: + self.session.shutdown() + if hasattr(self, 'cluster') and self.cluster: + self.cluster.shutdown() \ No newline at end of file diff --git a/engine/clients/cassandra/parser.py b/engine/clients/cassandra/parser.py new file mode 100644 index 00000000..363e5c92 --- /dev/null +++ b/engine/clients/cassandra/parser.py @@ -0,0 +1,92 @@ +from typing import Any, List, Optional + +from engine.base_client.parser import BaseConditionParser, FieldValue + + +class CassandraConditionParser(BaseConditionParser): + def build_condition( + self, and_subfilters: Optional[List[Any]], or_subfilters: Optional[List[Any]] + ) -> Optional[Any]: + """ + Build a CQL condition expression that combines AND and OR subfilters + """ + conditions = [] + + # Add AND conditions + if and_subfilters and len(and_subfilters) > 0: + and_conds = " AND ".join([f"({cond})" for cond in and_subfilters if cond]) + if and_conds: + conditions.append(f"({and_conds})") + + # Add OR conditions + if or_subfilters and len(or_subfilters) > 0: + or_conds = " OR ".join([f"({cond})" for cond in or_subfilters if cond]) + if or_conds: + conditions.append(f"({or_conds})") + + # Combine all conditions + if not conditions: + return None + + return " AND ".join(conditions) + + def build_exact_match_filter(self, field_name: str, value: FieldValue) -> Any: + """ + Build a CQL exact match condition for metadata fields + For Cassandra, we format metadata as a map with string values + """ + if isinstance(value, str): + return f"metadata['{field_name}'] = '{value}'" + else: + return f"metadata['{field_name}'] = '{str(value)}'" + + def build_range_filter( + self, + field_name: str, + lt: Optional[FieldValue], + gt: Optional[FieldValue], + lte: Optional[FieldValue], + gte: Optional[FieldValue], + ) -> Any: + """ + Build a CQL range filter condition + """ + conditions = [] + + if lt is not None: + if isinstance(lt, str): + conditions.append(f"metadata['{field_name}'] < '{lt}'") + else: + conditions.append(f"metadata['{field_name}'] < '{str(lt)}'") + + if gt is not None: + if isinstance(gt, str): + conditions.append(f"metadata['{field_name}'] > '{gt}'") + else: + conditions.append(f"metadata['{field_name}'] > '{str(gt)}'") + + if lte is not None: + if isinstance(lte, str): + conditions.append(f"metadata['{field_name}'] <= '{lte}'") + else: + conditions.append(f"metadata['{field_name}'] <= '{str(lte)}'") + + if gte is not None: + if isinstance(gte, str): + conditions.append(f"metadata['{field_name}'] >= '{gte}'") + else: + conditions.append(f"metadata['{field_name}'] >= '{str(gte)}'") + + return " AND ".join(conditions) + + def build_geo_filter( + self, field_name: str, lat: float, lon: float, radius: float + ) -> Any: + """ + Build a CQL geo filter condition + Note: Basic Cassandra doesn't have built-in geo filtering. + This is a simplified approach that won't actually work without extensions. + """ + # In a real implementation with a geo extension, we'd implement proper geo filtering + # For this benchmark, we'll return a placeholder condition that doesn't filter + return "1=1" # Always true condition as a placeholder \ No newline at end of file diff --git a/engine/clients/cassandra/search.py b/engine/clients/cassandra/search.py new file mode 100644 index 00000000..8cb9443b --- /dev/null +++ b/engine/clients/cassandra/search.py @@ -0,0 +1,116 @@ +import multiprocessing as mp +from typing import List, Tuple + +from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT +from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy, ExponentialReconnectionPolicy +from cassandra import ConsistencyLevel, ProtocolVersion + +from dataset_reader.base_reader import Query +from engine.base_client.distances import Distance +from engine.base_client.search import BaseSearcher +from engine.clients.cassandra.config import CASSANDRA_KEYSPACE, CASSANDRA_TABLE +from engine.clients.cassandra.parser import CassandraConditionParser + + +class CassandraSearcher(BaseSearcher): + search_params = {} + session = None + cluster = None + parser = CassandraConditionParser() + + @classmethod + def init_client(cls, host, distance, connection_params: dict, search_params: dict): + # Set up execution profiles for consistency and performance + profile = ExecutionProfile( + load_balancing_policy=TokenAwarePolicy(DCAwareRoundRobinPolicy()), + consistency_level=ConsistencyLevel.LOCAL_ONE, + request_timeout=60 + ) + + # Initialize Cassandra cluster connection + cls.cluster = Cluster( + contact_points=[host], + execution_profiles={EXEC_PROFILE_DEFAULT: profile}, + reconnection_policy=ExponentialReconnectionPolicy(base_delay=1, max_delay=60), + protocol_version=ProtocolVersion.V4, + **connection_params + ) + cls.session = cls.cluster.connect(CASSANDRA_KEYSPACE) + cls.search_params = search_params + + # Update prepared statements with current search parameters + cls.update_prepared_statements(distance) + + @classmethod + def get_mp_start_method(cls): + return "fork" if "fork" in mp.get_all_start_methods() else "spawn" + + @classmethod + def update_prepared_statements(cls, distance): + """Create prepared statements for vector searches""" + # Prepare a vector similarity search query + limit = cls.search_params.get("top", 10) + + if distance == Distance.COSINE: + SIMILARITY_FUNC = "similarity_cosine" + elif distance == Distance.L2: + SIMILARITY_FUNC = "similarity_euclidean" + elif distance == Distance.DOT: + SIMILARITY_FUNC = "similarity_dot_product" + else: + raise ValueError(f"Unsupported distance metric: {distance}") + + cls.ann_search_stmt = cls.session.prepare( + f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance + FROM {CASSANDRA_TABLE} + ORDER BY embedding ANN OF ? + LIMIT {limit}""" + ) + + # Prepare a statement for filtered vector search + cls.filtered_search_query_template = ( + f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance + FROM {CASSANDRA_TABLE} + WHERE {{conditions}} + ORDER BY embedding ANN OF ? + LIMIT {limit}""" + ) + + @classmethod + def search_one(cls, query: Query, top: int) -> List[Tuple[int, float]]: + """Execute a vector similarity search with optional filters""" + # Convert query vector to a format Cassandra can use + query_vector = query.vector.tolist() if hasattr(query.vector, 'tolist') else query.vector + + # Generate filter conditions if metadata conditions exist + filter_conditions = cls.parser.parse(query.meta_conditions) + + try: + if filter_conditions: + # Use the filtered search query + query_with_conditions = cls.filtered_search_query_template.format(conditions=filter_conditions) + results = cls.session.execute( + cls.session.prepare(query_with_conditions), + (query_vector, query_vector) + ) + else: + # Use the basic ANN search query + results = cls.session.execute( + cls.ann_search_stmt, + (query_vector, query_vector) + ) + + # Extract and return results + return [(row.id, row.distance) for row in results] + + except Exception as ex: + print(f"Error during Cassandra vector search: {ex}") + raise ex + + @classmethod + def delete_client(cls): + """Close the Cassandra connection""" + if cls.session: + cls.session.shutdown() + if cls.cluster: + cls.cluster.shutdown() \ No newline at end of file diff --git a/engine/clients/cassandra/upload.py b/engine/clients/cassandra/upload.py new file mode 100644 index 00000000..1879465c --- /dev/null +++ b/engine/clients/cassandra/upload.py @@ -0,0 +1,95 @@ +import time +from typing import List + +from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT, ResultSet +from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy, ExponentialReconnectionPolicy +from cassandra import ConsistencyLevel, ProtocolVersion + +from dataset_reader.base_reader import Record +from engine.base_client.upload import BaseUploader +from engine.clients.cassandra.config import CASSANDRA_KEYSPACE, CASSANDRA_TABLE + +class CassandraUploader(BaseUploader): + client = None + upload_params = {} + + @classmethod + def init_client(cls, host, distance, connection_params, upload_params): + # Set up execution profiles for consistency and performance + profile = ExecutionProfile( + load_balancing_policy=TokenAwarePolicy(DCAwareRoundRobinPolicy()), + consistency_level=ConsistencyLevel.LOCAL_QUORUM, + request_timeout=60 + ) + + # Initialize Cassandra cluster connection + cls.cluster = Cluster( + contact_points=[host], + execution_profiles={EXEC_PROFILE_DEFAULT: profile}, + protocol_version=ProtocolVersion.V4, + reconnection_policy=ExponentialReconnectionPolicy(base_delay=1, max_delay=60), + **connection_params + ) + cls.session = cls.cluster.connect(CASSANDRA_KEYSPACE) + cls.upload_params = upload_params + + # Prepare statements for faster uploads + cls.insert_stmt = cls.session.prepare( + f"""INSERT INTO {CASSANDRA_TABLE} (id, embedding, metadata) VALUES (?, ?, ?)""" + ) + + @classmethod + def upload_batch(cls, batch: List[Record]): + """Upload a batch of records to Cassandra""" + for point in batch: + # Convert metadata to a map format that Cassandra can store + metadata = {} + if point.metadata: + for key, value in point.metadata.items(): + # Convert all values to strings for simplicity + metadata[str(key)] = str(value) + + # Cassandra vector type requires a list of float values + vector = point.vector.tolist() if hasattr(point.vector, 'tolist') else point.vector + + # Execute the prepared statement + cls.session.execute( + cls.insert_stmt, + (int(point.id), vector, metadata) + ) + + @classmethod + def check_index_status(cls) -> ResultSet: + """ + Check the status of the index + See https://docs.datastax.com/en/cql/cassandra-5.0/develop/indexing/sai/sai-monitor.html + """ + assert cls.session is not None, "CQL session is not initialized" + return cls.session.execute(f""" + SELECT is_queryable, is_building + FROM system_views.sai_column_indexes + WHERE keyspace_name='{CASSANDRA_KEYSPACE}' AND table_name='{CASSANDRA_TABLE}' AND index_name='vector_index'; + """ + ).one() + + @classmethod + def post_upload(cls, _distance): + """Post-upload operations, like waiting for indexing to complete""" + # Cassandra vector indexes are automatically built when data is inserted + # so the wait time must be very quick + while True: + result = cls.check_index_status() + idx_ready = result and result.is_queryable and not result.is_building + if idx_ready: + break + print("Index is not ready yet, sleeping for 30 seconds...") + time.sleep(30) + return {} + + @classmethod + def delete_client(cls): + """Close the Cassandra connection""" + if hasattr(cls, 'session') and cls.session: + cls.session.shutdown() + if hasattr(cls, 'cluster') and cls.cluster: + cls.cluster.shutdown() \ No newline at end of file diff --git a/engine/clients/client_factory.py b/engine/clients/client_factory.py index a74df2ab..371604a2 100644 --- a/engine/clients/client_factory.py +++ b/engine/clients/client_factory.py @@ -30,6 +30,11 @@ WeaviateSearcher, WeaviateUploader, ) +from engine.clients.cassandra import ( + CassandraConfigurator, + CassandraSearcher, + CassandraUploader, +) ENGINE_CONFIGURATORS = { "qdrant": QdrantConfigurator, @@ -39,6 +44,7 @@ "opensearch": OpenSearchConfigurator, "redis": RedisConfigurator, "pgvector": PgVectorConfigurator, + "cassandra": CassandraConfigurator, } ENGINE_UPLOADERS = { @@ -49,6 +55,7 @@ "opensearch": OpenSearchUploader, "redis": RedisUploader, "pgvector": PgVectorUploader, + "cassandra": CassandraUploader, } ENGINE_SEARCHERS = { @@ -59,6 +66,7 @@ "opensearch": OpenSearchSearcher, "redis": RedisSearcher, "pgvector": PgVectorSearcher, + "cassandra": CassandraSearcher, } diff --git a/engine/servers/cassandra-single-node/docker-compose.yaml b/engine/servers/cassandra-single-node/docker-compose.yaml new file mode 100644 index 00000000..093d9b9f --- /dev/null +++ b/engine/servers/cassandra-single-node/docker-compose.yaml @@ -0,0 +1,29 @@ +version: '3.8' + +services: + cassandra: + image: cassandra:latest + container_name: cassandra-benchmark + ports: + - "9042:9042" # CQL native transport port + environment: + - CASSANDRA_CLUSTER_NAME=VectorBenchmark + - MAX_HEAP_SIZE=4G + - HEAP_NEWSIZE=800M + volumes: + - cassandra-data:/var/lib/cassandra + # Healthcheck to verify when Cassandra is ready + healthcheck: + test: ["CMD-SHELL", "cqlsh -e 'describe keyspaces' || exit 1"] + interval: 30s + timeout: 10s + retries: 10 + restart: unless-stopped + logging: + driver: "json-file" + options: + max-file: "1" + max-size: "10m" + +volumes: + cassandra-data: \ No newline at end of file diff --git a/experiments/configurations/cassandra-single-node.json b/experiments/configurations/cassandra-single-node.json new file mode 100644 index 00000000..036e48b4 --- /dev/null +++ b/experiments/configurations/cassandra-single-node.json @@ -0,0 +1,25 @@ +[ + { + "name": "cassandra-single-node", + "engine": "cassandra", + "host": "localhost", + "connection_params": { + "port": 9042, + "connect_timeout": 60 + }, + "collection_params": { + }, + "upload_params": { + "batch_size": 100, + "parallel": 4 + }, + "search_params": [ + { + "top": 10 + }, + { + "top": 100 + } + ] + } +] \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 65267b4f..99a5b2d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,6 +6,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -20,6 +21,7 @@ version = "4.5.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, @@ -33,7 +35,7 @@ typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21.0b1) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] trio = ["trio (>=0.26.1)"] [[package]] @@ -42,6 +44,8 @@ version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = ">=3.6" +groups = ["main"] +markers = "sys_platform == \"darwin\"" files = [ {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, @@ -53,6 +57,7 @@ version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, @@ -68,6 +73,8 @@ version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_full_version < \"3.11.3\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -79,6 +86,7 @@ version = "1.3.2" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "Authlib-1.3.2-py2.py3-none-any.whl", hash = "sha256:ede026a95e9f5cdc2d4364a52103f5405e75aa156357e831ef2bfd0bc5094dfc"}, {file = "authlib-1.3.2.tar.gz", hash = "sha256:4b16130117f9eb82aa6eec97f6dd4673c3f960ac0283ccdae2897ee4bc030ba2"}, @@ -93,6 +101,7 @@ version = "0.2.0" description = "Specifications for callback functions passed in to an API" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, @@ -104,6 +113,8 @@ version = "0.2.1" description = "Backport of the standard library zoneinfo module" optional = false python-versions = ">=3.6" +groups = ["main"] +markers = "python_version == \"3.8\"" files = [ {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, @@ -126,12 +137,61 @@ files = [ [package.extras] tzdata = ["tzdata"] +[[package]] +name = "cassandra-driver" +version = "3.29.2" +description = "DataStax Driver for Apache Cassandra" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "cassandra-driver-3.29.2.tar.gz", hash = "sha256:c4310a7d0457f51a63fb019d8ef501588c491141362b53097fbc62fa06559b7c"}, + {file = "cassandra_driver-3.29.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:957208093ff2353230d0d83edf8c8e8582e4f2999d9a33292be6558fec943562"}, + {file = "cassandra_driver-3.29.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d70353b6d9d6e01e2b261efccfe90ce0aa6f416588e6e626ca2ed0aff6b540cf"}, + {file = "cassandra_driver-3.29.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06ad489e4df2cc7f41d3aca8bd8ddeb8071c4fb98240ed07f1dcd9b5180fd879"}, + {file = "cassandra_driver-3.29.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7f1dfa33c3d93350057d6dc163bb92748b6e6a164c408c75cf2c59be0a203b7"}, + {file = "cassandra_driver-3.29.2-cp310-cp310-win32.whl", hash = "sha256:f9df1e6ae4201eb2eae899cb0649d46b3eb0843f075199b51360bc9d59679a31"}, + {file = "cassandra_driver-3.29.2-cp310-cp310-win_amd64.whl", hash = "sha256:c4a005bc0b4fd8b5716ad931e1cc788dbd45967b0bcbdc3dfde33c7f9fde40d4"}, + {file = "cassandra_driver-3.29.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e31cee01a6fc8cf7f32e443fa0031bdc75eed46126831b7a807ab167b4dc1316"}, + {file = "cassandra_driver-3.29.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52edc6d4bd7d07b10dc08b7f044dbc2ebe24ad7009c23a65e0916faed1a34065"}, + {file = "cassandra_driver-3.29.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb3a9f24fc84324d426a69dc35df66de550833072a4d9a4d63d72fda8fcaecb9"}, + {file = "cassandra_driver-3.29.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e89de04809d02bb1d5d03c0946a7baaaf85e93d7e6414885b4ea2616efe9de0"}, + {file = "cassandra_driver-3.29.2-cp311-cp311-win32.whl", hash = "sha256:7104e5043e9cc98136d7fafe2418cbc448dacb4e1866fe38ff5be76f227437ef"}, + {file = "cassandra_driver-3.29.2-cp311-cp311-win_amd64.whl", hash = "sha256:69aa53f1bdb23487765faa92eef57366637878eafc412f46af999e722353b22f"}, + {file = "cassandra_driver-3.29.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a1e994a82b2e6ab022c5aec24e03ad49fca5f3d47e566a145de34eb0e768473a"}, + {file = "cassandra_driver-3.29.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2039201ae5d9b7c7ce0930af7138d2637ca16a4c7aaae2fbdd4355fbaf3003c5"}, + {file = "cassandra_driver-3.29.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8067fad22e76e250c3846507d804f90b53e943bba442fa1b26583bcac692aaf1"}, + {file = "cassandra_driver-3.29.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee0ebe8eb4fb007d8001ffcd1c3828b74defeb01075d8a1f1116ae9c60f75541"}, + {file = "cassandra_driver-3.29.2-cp312-cp312-win32.whl", hash = "sha256:83dc9399cdabe482fd3095ca54ec227212d8c491b563a7276f6c100e30ee856c"}, + {file = "cassandra_driver-3.29.2-cp312-cp312-win_amd64.whl", hash = "sha256:6c74610f56a4c53863a5d44a2af9c6c3405da19d51966fabd85d7f927d5c6abc"}, + {file = "cassandra_driver-3.29.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c86b0a796ff67d66de7df5f85243832a4dc853217f6a3eade84694f6f4fae151"}, + {file = "cassandra_driver-3.29.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c53700b0d1f8c1d777eaa9e9fb6d17839d9a83f27a61649e0cbaa15d9d3df34b"}, + {file = "cassandra_driver-3.29.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d348c769aa6c37919e7d6247e8cf09c23d387b7834a340408bd7d611f174d80"}, + {file = "cassandra_driver-3.29.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8c496318e3c136cf12ab21e1598fee4b48ea1c71746ea8cc9d32e4dcd09cb93"}, + {file = "cassandra_driver-3.29.2-cp38-cp38-win32.whl", hash = "sha256:d180183451bec81c15e0441fa37a63dc52c6489e860e832cadd854373b423141"}, + {file = "cassandra_driver-3.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:a66b20c421d8fb21f18bd0ac713de6f09c5c25b6ab3d6043c3779b9c012d7c98"}, + {file = "cassandra_driver-3.29.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70d4d0dce373943308ad461a425fc70a23d0f524859367b8c6fc292400f39954"}, + {file = "cassandra_driver-3.29.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b86427fab4d5a96e91ad82bb9338d4101ae4d3758ba96c356e0198da3de4d350"}, + {file = "cassandra_driver-3.29.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c25b42e1a99f377a933d79ae93ea27601e337a5abb7bb843a0e951cf1b3836f7"}, + {file = "cassandra_driver-3.29.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36437288d6cd6f6c74b8ee5997692126e24adc2da3d031dc11c7dfea8bc220"}, + {file = "cassandra_driver-3.29.2-cp39-cp39-win32.whl", hash = "sha256:e967c1341a651f03bdc466f3835d72d3c0a0648b562035e6d780fa0b796c02f6"}, + {file = "cassandra_driver-3.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:c5a9aab2367e8aad48ae853847a5a8985749ac5f102676de2c119b33fef13b42"}, +] + +[package.dependencies] +geomet = ">=0.1,<0.3" + +[package.extras] +cle = ["cryptography (>=35.0)"] +graph = ["gremlinpython (==3.4.6)"] + [[package]] name = "certifi" version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -143,6 +203,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -222,6 +284,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -233,6 +296,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -334,6 +398,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -348,10 +413,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "sys_platform == \"win32\""} [[package]] name = "cryptography" @@ -359,6 +426,7 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -408,6 +476,7 @@ version = "5.2.1" description = "Decorators for Humans" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, @@ -419,6 +488,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -430,6 +500,7 @@ version = "8.17.1" description = "Transport classes and utilities shared among Python Elastic client libraries" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "elastic_transport-8.17.1-py3-none-any.whl", hash = "sha256:192718f498f1d10c5e9aa8b9cf32aed405e469a7f0e9d6a8923431dbb2c59fb8"}, {file = "elastic_transport-8.17.1.tar.gz", hash = "sha256:5edef32ac864dca8e2f0a613ef63491ee8d6b8cfb52881fa7313ba9290cac6d2"}, @@ -448,6 +519,7 @@ version = "8.17.2" description = "Python client for Elasticsearch" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "elasticsearch-8.17.2-py3-none-any.whl", hash = "sha256:2d058dcddd8f2686cd431a916cdf983f9fb7d211d902834f564ab7df05ba6478"}, {file = "elasticsearch-8.17.2.tar.gz", hash = "sha256:ff7f1db8aeefd87ceba4edce3aa4070994582e6cf029d2e67b74e66d634509db"}, @@ -471,6 +543,7 @@ version = "0.5" description = "Bringing the elegance of C# EventHandler to Python" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "Events-0.5-py3-none-any.whl", hash = "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd"}, ] @@ -481,6 +554,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version <= \"3.10\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -495,13 +570,14 @@ version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, ] [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "filelock" @@ -509,6 +585,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -517,7 +594,23 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] + +[[package]] +name = "geomet" +version = "0.2.1.post1" +description = "GeoJSON <-> WKT/WKB conversion utilities" +optional = false +python-versions = ">2.6, !=3.3.*, <4" +groups = ["main"] +files = [ + {file = "geomet-0.2.1.post1-py3-none-any.whl", hash = "sha256:a41a1e336b381416d6cbed7f1745c848e91defaa4d4c1bdc1312732e46ffad2b"}, + {file = "geomet-0.2.1.post1.tar.gz", hash = "sha256:91d754f7c298cbfcabd3befdb69c641c27fe75e808b27aa55028605761d17e95"}, +] + +[package.dependencies] +click = "*" +six = "*" [[package]] name = "grpcio" @@ -525,6 +618,7 @@ version = "1.67.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"}, {file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"}, @@ -592,6 +686,7 @@ version = "1.67.1" description = "Standard Health Checking Service for gRPC" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "grpcio_health_checking-1.67.1-py3-none-any.whl", hash = "sha256:93753da5062152660aef2286c9b261e07dd87124a65e4dc9fbd47d1ce966b39d"}, {file = "grpcio_health_checking-1.67.1.tar.gz", hash = "sha256:ca90fa76a6afbb4fda71d734cb9767819bba14928b91e308cffbb0c311eb941e"}, @@ -607,6 +702,7 @@ version = "1.67.1" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "grpcio_tools-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:c701aaa51fde1f2644bd94941aa94c337adb86f25cd03cf05e37387aaea25800"}, {file = "grpcio_tools-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:6a722bba714392de2386569c40942566b83725fa5c5450b8910e3832a5379469"}, @@ -676,6 +772,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -687,6 +784,7 @@ version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, @@ -702,6 +800,7 @@ version = "3.11.0" description = "Read and write HDF5 files from Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "h5py-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1625fd24ad6cfc9c1ccd44a66dac2396e7ee74940776792772819fc69f3a3731"}, {file = "h5py-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c072655ad1d5fe9ef462445d3e77a8166cbfa5e599045f8aa3c19b75315f10e5"}, @@ -735,6 +834,7 @@ version = "4.0.0" description = "Pure-Python HPACK header compression" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, @@ -746,6 +846,7 @@ version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, @@ -767,6 +868,7 @@ version = "0.27.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, @@ -781,7 +883,7 @@ idna = "*" sniffio = "*" [package.extras] -brotli = ["brotli", "brotlicffi"] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -792,6 +894,7 @@ version = "6.0.1" description = "HTTP/2 framing layer for Python" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, @@ -803,6 +906,7 @@ version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, @@ -817,6 +921,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -831,6 +936,7 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -842,6 +948,7 @@ version = "0.13.13" description = "IPython-enabled pdb" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] files = [ {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, @@ -858,6 +965,7 @@ version = "8.12.3" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, @@ -897,6 +1005,7 @@ version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, @@ -916,6 +1025,7 @@ version = "1.6.3" description = "For serializing Python objects to JSON (dicts) and back" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "jsons-1.6.3-py3-none-any.whl", hash = "sha256:f07f8919316f72a3843c7ca6cc6c900513089f10092626934d1bfe4b5cf15401"}, {file = "jsons-1.6.3.tar.gz", hash = "sha256:cd5815c7c6790ae11c70ad9978e0aa850d0d08a643a5105cc604eac8b29a30d7"}, @@ -925,7 +1035,7 @@ files = [ typish = ">=1.9.2" [package.extras] -test = ["attrs", "codecov", "coverage", "dataclasses", "pytest", "scons", "tzdata"] +test = ["attrs", "codecov", "coverage", "dataclasses ; python_version == \"3.6\"", "pytest", "scons", "tzdata ; python_version >= \"3.9\""] [[package]] name = "matplotlib-inline" @@ -933,6 +1043,7 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, @@ -947,6 +1058,8 @@ version = "2.4.12" description = "A lightweight version of Milvus wrapped with Python." optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "sys_platform != \"win32\"" files = [ {file = "milvus_lite-2.4.12-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:e8d4f7cdd5f731efd6faeee3715d280fd91a5f9b4d89312664d56401f65b1473"}, {file = "milvus_lite-2.4.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:20087663e7b4385050b7ad08f1f03404426d4c87b1ff91d5a8723eee7fd49e88"}, @@ -963,6 +1076,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -974,6 +1088,7 @@ version = "1.24.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, @@ -1011,6 +1126,7 @@ version = "2.8.0" description = "Python client for OpenSearch" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "opensearch_py-2.8.0-py3-none-any.whl", hash = "sha256:52c60fdb5d4dcf6cce3ee746c13b194529b0161e0f41268b98ab8f1624abe2fa"}, {file = "opensearch_py-2.8.0.tar.gz", hash = "sha256:6598df0bc7a003294edd0ba88a331e0793acbb8c910c43edf398791e3b2eccda"}, @@ -1038,6 +1154,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1049,6 +1166,7 @@ version = "2.0.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, @@ -1080,8 +1198,8 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, + {version = ">=1.21.0", markers = "python_version == \"3.10\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -1116,6 +1234,7 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, @@ -1131,6 +1250,8 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -1145,6 +1266,7 @@ version = "0.2.5" description = "pgvector support for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pgvector-0.2.5-py2.py3-none-any.whl", hash = "sha256:5e5e93ec4d3c45ab1fa388729d56c602f6966296e19deee8878928c6d567e41b"}, ] @@ -1158,6 +1280,7 @@ version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, @@ -1169,6 +1292,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1185,6 +1309,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1200,6 +1325,7 @@ version = "2.10.1" description = "Wraps the portalocker recipe for easy usage" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf"}, {file = "portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f"}, @@ -1219,6 +1345,7 @@ version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, @@ -1237,6 +1364,7 @@ version = "3.0.50" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, @@ -1251,6 +1379,7 @@ version = "5.29.4" description = "" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7"}, {file = "protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d"}, @@ -1271,6 +1400,7 @@ version = "3.2.6" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "psycopg-3.2.6-py3-none-any.whl", hash = "sha256:f3ff5488525890abb0566c429146add66b329e20d6d4835662b920cbbf90ac58"}, {file = "psycopg-3.2.6.tar.gz", hash = "sha256:16fa094efa2698f260f2af74f3710f781e4a6f226efe9d1fd0c37f384639ed8a"}, @@ -1283,8 +1413,8 @@ typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.6)"] -c = ["psycopg-c (==3.2.6)"] +binary = ["psycopg-binary (==3.2.6) ; implementation_name != \"pypy\""] +c = ["psycopg-c (==3.2.6) ; implementation_name != \"pypy\""] dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] @@ -1296,6 +1426,8 @@ version = "3.2.6" description = "PostgreSQL database adapter for Python -- C optimisation distribution" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"pypy\"" files = [ {file = "psycopg_binary-3.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1b639acb3e24243c23f75700bf6e3af7b76da92523ec7c3196a13aaf0b578453"}, {file = "psycopg_binary-3.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1b5c359173726b38d7acbb9f73270f269591d8031d099c1a70dd3f3d22b0e8a8"}, @@ -1370,6 +1502,8 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -1381,6 +1515,7 @@ version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, @@ -1395,6 +1530,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1406,6 +1543,7 @@ version = "2.10.6" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, @@ -1418,7 +1556,7 @@ typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" @@ -1426,6 +1564,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -1538,6 +1677,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -1552,6 +1692,7 @@ version = "2.5.6" description = "Python Sdk for Milvus" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pymilvus-2.5.6-py3-none-any.whl", hash = "sha256:19796f328278974f04632a1531e602070614f6ab0865cc97e27755f622e50a6d"}, {file = "pymilvus-2.5.6.tar.gz", hash = "sha256:2bea0b03ed9ac3daadb1b2df8e38aa5c8f4aabd00b23a4999abb4adaebf54f59"}, @@ -1581,6 +1722,7 @@ version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, @@ -1603,6 +1745,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1617,6 +1760,7 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -1631,6 +1775,7 @@ version = "2025.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, @@ -1642,6 +1787,8 @@ version = "310" description = "Python for Window Extensions" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, {file = "pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d"}, @@ -1667,6 +1814,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1729,6 +1877,7 @@ version = "1.12.1" description = "Client library for the Qdrant vector search engine" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "qdrant_client-1.12.1-py3-none-any.whl", hash = "sha256:b2d17ce18e9e767471368380dd3bbc4a0e3a0e2061fedc9af3542084b48451e0"}, {file = "qdrant_client-1.12.1.tar.gz", hash = "sha256:35e8e646f75b7b883b3d2d0ee4c69c5301000bba41c82aa546e985db0f1aeb72"}, @@ -1744,8 +1893,8 @@ pydantic = ">=1.10.8" urllib3 = ">=1.26.14,<3" [package.extras] -fastembed = ["fastembed (==0.3.6)"] -fastembed-gpu = ["fastembed-gpu (==0.3.6)"] +fastembed = ["fastembed (==0.3.6) ; python_version < \"3.13\""] +fastembed-gpu = ["fastembed-gpu (==0.3.6) ; python_version < \"3.13\""] [[package]] name = "redis" @@ -1753,6 +1902,7 @@ version = "5.2.1" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, @@ -1771,6 +1921,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1792,6 +1943,8 @@ version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.8\"" files = [ {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, @@ -1799,7 +1952,7 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov ; platform_python_implementation != \"PyPy\"", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "setuptools" @@ -1807,19 +1960,21 @@ version = "78.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version > \"3.8\"" files = [ {file = "setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8"}, {file = "setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -1827,6 +1982,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1838,6 +1994,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1849,6 +2006,7 @@ version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, @@ -1868,6 +2026,7 @@ version = "1.1.2" description = "Timeout control decorator and context managers, raise any exception in another thread" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "stopit-1.1.2.tar.gz", hash = "sha256:f7f39c583fd92027bd9d06127b259aee7a5b7945c1f1fa56263811e1e766996d"}, ] @@ -1878,6 +2037,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version <= \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1919,6 +2080,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -1940,6 +2102,7 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, @@ -1955,6 +2118,7 @@ version = "0.6.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "typer-0.6.1-py3-none-any.whl", hash = "sha256:54b19e5df18654070a82f8c2aa1da456a4ac16a2a83e6dcd9f170e291c56338e"}, {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"}, @@ -1975,6 +2139,7 @@ version = "4.13.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5"}, {file = "typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b"}, @@ -1986,6 +2151,7 @@ version = "1.9.3" description = "Functionality for types" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "typish-1.9.3-py3-none-any.whl", hash = "sha256:03cfee5e6eb856dbf90244e18f4e4c41044c8790d5779f4e775f63f982e2f896"}, ] @@ -1999,6 +2165,7 @@ version = "2025.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main"] files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, @@ -2010,6 +2177,7 @@ version = "5.10.0" description = "Ultra fast JSON encoder and decoder for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, @@ -2097,14 +2265,16 @@ version = "1.26.20" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] +markers = "python_version < \"3.10\"" files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, ] [package.extras] -brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -2113,13 +2283,15 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\"" files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2130,6 +2302,7 @@ version = "0.33.0" description = "Python Data Validation for Humans™" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "validators-0.33.0-py3-none-any.whl", hash = "sha256:134b586a98894f8139865953899fc2daeb3d0c35569552c5518f089ae43ed075"}, {file = "validators-0.33.0.tar.gz", hash = "sha256:535867e9617f0100e676a1257ba1e206b9bfd847ddc171e4d44811f07ff0bfbf"}, @@ -2144,6 +2317,7 @@ version = "20.30.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"}, {file = "virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"}, @@ -2156,7 +2330,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "wcwidth" @@ -2164,6 +2338,7 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -2175,6 +2350,7 @@ version = "4.6.7" description = "A python native Weaviate client" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "weaviate_client-4.6.7-py3-none-any.whl", hash = "sha256:8793de35264cab33a84fe8cb8c422a257fe4d8334657aaddd8ead853da3fb34a"}, {file = "weaviate_client-4.6.7.tar.gz", hash = "sha256:202b32e160536f5f44e4a635d30c3d3a0790b1a7ff997f5e243919d1ac5b68a1"}, @@ -2191,6 +2367,6 @@ requests = ">=2.30.0,<3.0.0" validators = "0.33.0" [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.8,<3.12" -content-hash = "d32a1f687bc36b705e59cb7aec1062a3ddd4f8ec314c93f3a8a3cbb952cb62d3" +content-hash = "52fb56aca1cd418f37489f06e24b77c7c01273d1d077937ef8bbe1851249d1fe" diff --git a/pyproject.toml b/pyproject.toml index 07588cda..4f2b1135 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ opensearch-py = "^2.3.2" tqdm = "^4.66.1" psycopg = {extras = ["binary"], version = "^3.1.17"} pgvector = "^0.2.4" +cassandra-driver = ">=3.29.2" [tool.poetry.group.dev.dependencies] pre-commit = "^2.20.0" From d82801c6a3ed8bd1f99cad88d4305549943e8a76 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 20:34:27 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- engine/clients/cassandra/README.md | 6 +-- engine/clients/cassandra/__init__.py | 6 +-- engine/clients/cassandra/config.py | 2 +- engine/clients/cassandra/configure.py | 46 ++++++++++++--------- engine/clients/cassandra/parser.py | 20 ++++----- engine/clients/cassandra/search.py | 59 +++++++++++++++------------ engine/clients/cassandra/upload.py | 47 ++++++++++++--------- engine/clients/client_factory.py | 10 ++--- 8 files changed, 107 insertions(+), 89 deletions(-) diff --git a/engine/clients/cassandra/README.md b/engine/clients/cassandra/README.md index f4f6c1bd..7cdf0ebb 100644 --- a/engine/clients/cassandra/README.md +++ b/engine/clients/cassandra/README.md @@ -9,9 +9,9 @@ Run the following command to start the server (alternatively, run `docker compos $ docker compose up [+] Running 1/1 - ✔ cassandra Pulled 1.4s + ✔ cassandra Pulled 1.4s [+] Running 1/1 - ✔ Container cassandra-benchmark Recreated 0.1s + ✔ Container cassandra-benchmark Recreated 0.1s Attaching to cassandra-benchmark cassandra-benchmark | CompileCommand: dontinline org/apache/cassandra/db/Columns$Serializer.deserializeLargeSubset(Lorg/apache/cassandra/io/util/DataInputPlus;Lorg/apache/cassandra/db/Columns;I)Lorg/apache/cassandra/db/Columns; bool dontinline = true ... @@ -29,7 +29,7 @@ cassandra-benchmark | INFO [main] 2025-04-04 22:28:25,091 StorageService.java: ### Start up the client benchmark Run the following command to start the client benchmark using `glove-25-angular` dataset as an example: ```bash -% python3 -m run --engines cassandra-single-node --datasets glove-25-angular +% python3 -m run --engines cassandra-single-node --datasets glove-25-angular ``` and you'll see the following output: ```bash diff --git a/engine/clients/cassandra/__init__.py b/engine/clients/cassandra/__init__.py index 4e3e7e54..54b47b0f 100644 --- a/engine/clients/cassandra/__init__.py +++ b/engine/clients/cassandra/__init__.py @@ -2,8 +2,4 @@ from engine.clients.cassandra.search import CassandraSearcher from engine.clients.cassandra.upload import CassandraUploader -__all__ = [ - "CassandraConfigurator", - "CassandraSearcher", - "CassandraUploader" -] \ No newline at end of file +__all__ = ["CassandraConfigurator", "CassandraSearcher", "CassandraUploader"] diff --git a/engine/clients/cassandra/config.py b/engine/clients/cassandra/config.py index ca3ff758..97db40ce 100644 --- a/engine/clients/cassandra/config.py +++ b/engine/clients/cassandra/config.py @@ -4,4 +4,4 @@ CASSANDRA_TABLE = os.getenv("CASSANDRA_TABLE", "vectors") ASTRA_API_ENDPOINT = os.getenv("ASTRA_API_ENDPOINT", None) ASTRA_API_KEY = os.getenv("ASTRA_API_KEY", None) -ASTRA_SCB_PATH = os.getenv("ASTRA_SCB_PATH", None) \ No newline at end of file +ASTRA_SCB_PATH = os.getenv("ASTRA_SCB_PATH", None) diff --git a/engine/clients/cassandra/configure.py b/engine/clients/cassandra/configure.py index 5fe7093c..30c6f734 100644 --- a/engine/clients/cassandra/configure.py +++ b/engine/clients/cassandra/configure.py @@ -1,6 +1,10 @@ -from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT -from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy, ExponentialReconnectionPolicy from cassandra import ConsistencyLevel, ProtocolVersion +from cassandra.cluster import EXEC_PROFILE_DEFAULT, Cluster, ExecutionProfile +from cassandra.policies import ( + DCAwareRoundRobinPolicy, + ExponentialReconnectionPolicy, + TokenAwarePolicy, +) from benchmark.dataset import Dataset from engine.base_client.configure import BaseConfigurator @@ -13,26 +17,28 @@ class CassandraConfigurator(BaseConfigurator): DISTANCE_MAPPING = { Distance.L2: "euclidean", Distance.COSINE: "cosine", - Distance.DOT: "dot_product" + Distance.DOT: "dot_product", } def __init__(self, host, collection_params: dict, connection_params: dict): super().__init__(host, collection_params, connection_params) - + # Set up execution profiles for consistency and performance profile = ExecutionProfile( load_balancing_policy=TokenAwarePolicy(DCAwareRoundRobinPolicy()), consistency_level=ConsistencyLevel.LOCAL_QUORUM, - request_timeout=60 + request_timeout=60, ) - + # Initialize Cassandra cluster connection self.cluster = Cluster( contact_points=[host], execution_profiles={EXEC_PROFILE_DEFAULT: profile}, protocol_version=ProtocolVersion.V4, - reconnection_policy=ExponentialReconnectionPolicy(base_delay=1, max_delay=60), - **connection_params + reconnection_policy=ExponentialReconnectionPolicy( + base_delay=1, max_delay=60 + ), + **connection_params, ) self.session = self.cluster.connect() @@ -44,17 +50,17 @@ def recreate(self, dataset: Dataset, collection_params): """Create keyspace and table for vector search""" # Create keyspace if not exists self.session.execute( - f"""CREATE KEYSPACE IF NOT EXISTS {CASSANDRA_KEYSPACE} + f"""CREATE KEYSPACE IF NOT EXISTS {CASSANDRA_KEYSPACE} WITH REPLICATION = {{ 'class': 'SimpleStrategy', 'replication_factor': 1 }}""" ) - + # Use the keyspace self.session.execute(f"USE {CASSANDRA_KEYSPACE}") - + # Get the distance metric distance_metric = self.DISTANCE_MAPPING.get(dataset.config.distance) vector_size = dataset.config.vector_size - + # Create vector table # Using a simple schema that supports vector similarity search self.session.execute( @@ -64,14 +70,14 @@ def recreate(self, dataset: Dataset, collection_params): metadata map )""" ) - + # Create vector index using the appropriate distance metric self.session.execute( - f"""CREATE CUSTOM INDEX IF NOT EXISTS vector_index ON {CASSANDRA_TABLE}(embedding) - USING 'StorageAttachedIndex' + f"""CREATE CUSTOM INDEX IF NOT EXISTS vector_index ON {CASSANDRA_TABLE}(embedding) + USING 'StorageAttachedIndex' WITH OPTIONS = {{ 'similarity_function': '{distance_metric}' }}""" ) - + # Add additional schema fields based on collection_params if needed for field_name, field_type in dataset.config.schema.items(): if field_type in ["keyword", "text"]: @@ -81,7 +87,7 @@ def recreate(self, dataset: Dataset, collection_params): # For numeric fields that need separate indexing # In a real implementation, we might alter the table to add these columns pass - + return collection_params def execution_params(self, distance, vector_size) -> dict: @@ -90,7 +96,7 @@ def execution_params(self, distance, vector_size) -> dict: def delete_client(self): """Close the Cassandra connection""" - if hasattr(self, 'session') and self.session: + if hasattr(self, "session") and self.session: self.session.shutdown() - if hasattr(self, 'cluster') and self.cluster: - self.cluster.shutdown() \ No newline at end of file + if hasattr(self, "cluster") and self.cluster: + self.cluster.shutdown() diff --git a/engine/clients/cassandra/parser.py b/engine/clients/cassandra/parser.py index 363e5c92..29a32e5a 100644 --- a/engine/clients/cassandra/parser.py +++ b/engine/clients/cassandra/parser.py @@ -11,23 +11,23 @@ def build_condition( Build a CQL condition expression that combines AND and OR subfilters """ conditions = [] - + # Add AND conditions if and_subfilters and len(and_subfilters) > 0: and_conds = " AND ".join([f"({cond})" for cond in and_subfilters if cond]) if and_conds: conditions.append(f"({and_conds})") - + # Add OR conditions if or_subfilters and len(or_subfilters) > 0: or_conds = " OR ".join([f"({cond})" for cond in or_subfilters if cond]) if or_conds: conditions.append(f"({or_conds})") - + # Combine all conditions if not conditions: return None - + return " AND ".join(conditions) def build_exact_match_filter(self, field_name: str, value: FieldValue) -> Any: @@ -52,31 +52,31 @@ def build_range_filter( Build a CQL range filter condition """ conditions = [] - + if lt is not None: if isinstance(lt, str): conditions.append(f"metadata['{field_name}'] < '{lt}'") else: conditions.append(f"metadata['{field_name}'] < '{str(lt)}'") - + if gt is not None: if isinstance(gt, str): conditions.append(f"metadata['{field_name}'] > '{gt}'") else: conditions.append(f"metadata['{field_name}'] > '{str(gt)}'") - + if lte is not None: if isinstance(lte, str): conditions.append(f"metadata['{field_name}'] <= '{lte}'") else: conditions.append(f"metadata['{field_name}'] <= '{str(lte)}'") - + if gte is not None: if isinstance(gte, str): conditions.append(f"metadata['{field_name}'] >= '{gte}'") else: conditions.append(f"metadata['{field_name}'] >= '{str(gte)}'") - + return " AND ".join(conditions) def build_geo_filter( @@ -89,4 +89,4 @@ def build_geo_filter( """ # In a real implementation with a geo extension, we'd implement proper geo filtering # For this benchmark, we'll return a placeholder condition that doesn't filter - return "1=1" # Always true condition as a placeholder \ No newline at end of file + return "1=1" # Always true condition as a placeholder diff --git a/engine/clients/cassandra/search.py b/engine/clients/cassandra/search.py index 8cb9443b..7bb526ab 100644 --- a/engine/clients/cassandra/search.py +++ b/engine/clients/cassandra/search.py @@ -1,9 +1,13 @@ import multiprocessing as mp from typing import List, Tuple -from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT -from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy, ExponentialReconnectionPolicy from cassandra import ConsistencyLevel, ProtocolVersion +from cassandra.cluster import EXEC_PROFILE_DEFAULT, Cluster, ExecutionProfile +from cassandra.policies import ( + DCAwareRoundRobinPolicy, + ExponentialReconnectionPolicy, + TokenAwarePolicy, +) from dataset_reader.base_reader import Query from engine.base_client.distances import Distance @@ -24,20 +28,22 @@ def init_client(cls, host, distance, connection_params: dict, search_params: dic profile = ExecutionProfile( load_balancing_policy=TokenAwarePolicy(DCAwareRoundRobinPolicy()), consistency_level=ConsistencyLevel.LOCAL_ONE, - request_timeout=60 + request_timeout=60, ) - + # Initialize Cassandra cluster connection cls.cluster = Cluster( - contact_points=[host], + contact_points=[host], execution_profiles={EXEC_PROFILE_DEFAULT: profile}, - reconnection_policy=ExponentialReconnectionPolicy(base_delay=1, max_delay=60), + reconnection_policy=ExponentialReconnectionPolicy( + base_delay=1, max_delay=60 + ), protocol_version=ProtocolVersion.V4, - **connection_params + **connection_params, ) cls.session = cls.cluster.connect(CASSANDRA_KEYSPACE) cls.search_params = search_params - + # Update prepared statements with current search parameters cls.update_prepared_statements(distance) @@ -50,7 +56,7 @@ def update_prepared_statements(cls, distance): """Create prepared statements for vector searches""" # Prepare a vector similarity search query limit = cls.search_params.get("top", 10) - + if distance == Distance.COSINE: SIMILARITY_FUNC = "similarity_cosine" elif distance == Distance.L2: @@ -61,48 +67,49 @@ def update_prepared_statements(cls, distance): raise ValueError(f"Unsupported distance metric: {distance}") cls.ann_search_stmt = cls.session.prepare( - f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance - FROM {CASSANDRA_TABLE} + f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance + FROM {CASSANDRA_TABLE} ORDER BY embedding ANN OF ? LIMIT {limit}""" ) - + # Prepare a statement for filtered vector search - cls.filtered_search_query_template = ( - f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance - FROM {CASSANDRA_TABLE} + cls.filtered_search_query_template = f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance + FROM {CASSANDRA_TABLE} WHERE {{conditions}} ORDER BY embedding ANN OF ? LIMIT {limit}""" - ) @classmethod def search_one(cls, query: Query, top: int) -> List[Tuple[int, float]]: """Execute a vector similarity search with optional filters""" # Convert query vector to a format Cassandra can use - query_vector = query.vector.tolist() if hasattr(query.vector, 'tolist') else query.vector - + query_vector = ( + query.vector.tolist() if hasattr(query.vector, "tolist") else query.vector + ) + # Generate filter conditions if metadata conditions exist filter_conditions = cls.parser.parse(query.meta_conditions) - + try: if filter_conditions: # Use the filtered search query - query_with_conditions = cls.filtered_search_query_template.format(conditions=filter_conditions) + query_with_conditions = cls.filtered_search_query_template.format( + conditions=filter_conditions + ) results = cls.session.execute( cls.session.prepare(query_with_conditions), - (query_vector, query_vector) + (query_vector, query_vector), ) else: # Use the basic ANN search query results = cls.session.execute( - cls.ann_search_stmt, - (query_vector, query_vector) + cls.ann_search_stmt, (query_vector, query_vector) ) - + # Extract and return results return [(row.id, row.distance) for row in results] - + except Exception as ex: print(f"Error during Cassandra vector search: {ex}") raise ex @@ -113,4 +120,4 @@ def delete_client(cls): if cls.session: cls.session.shutdown() if cls.cluster: - cls.cluster.shutdown() \ No newline at end of file + cls.cluster.shutdown() diff --git a/engine/clients/cassandra/upload.py b/engine/clients/cassandra/upload.py index 1879465c..e2473812 100644 --- a/engine/clients/cassandra/upload.py +++ b/engine/clients/cassandra/upload.py @@ -1,14 +1,19 @@ import time from typing import List -from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT, ResultSet -from cassandra.policies import DCAwareRoundRobinPolicy, TokenAwarePolicy, ExponentialReconnectionPolicy from cassandra import ConsistencyLevel, ProtocolVersion +from cassandra.cluster import EXEC_PROFILE_DEFAULT, Cluster, ExecutionProfile, ResultSet +from cassandra.policies import ( + DCAwareRoundRobinPolicy, + ExponentialReconnectionPolicy, + TokenAwarePolicy, +) from dataset_reader.base_reader import Record from engine.base_client.upload import BaseUploader from engine.clients.cassandra.config import CASSANDRA_KEYSPACE, CASSANDRA_TABLE + class CassandraUploader(BaseUploader): client = None upload_params = {} @@ -19,20 +24,22 @@ def init_client(cls, host, distance, connection_params, upload_params): profile = ExecutionProfile( load_balancing_policy=TokenAwarePolicy(DCAwareRoundRobinPolicy()), consistency_level=ConsistencyLevel.LOCAL_QUORUM, - request_timeout=60 + request_timeout=60, ) - + # Initialize Cassandra cluster connection cls.cluster = Cluster( - contact_points=[host], + contact_points=[host], execution_profiles={EXEC_PROFILE_DEFAULT: profile}, protocol_version=ProtocolVersion.V4, - reconnection_policy=ExponentialReconnectionPolicy(base_delay=1, max_delay=60), - **connection_params + reconnection_policy=ExponentialReconnectionPolicy( + base_delay=1, max_delay=60 + ), + **connection_params, ) cls.session = cls.cluster.connect(CASSANDRA_KEYSPACE) cls.upload_params = upload_params - + # Prepare statements for faster uploads cls.insert_stmt = cls.session.prepare( f"""INSERT INTO {CASSANDRA_TABLE} (id, embedding, metadata) VALUES (?, ?, ?)""" @@ -48,16 +55,17 @@ def upload_batch(cls, batch: List[Record]): for key, value in point.metadata.items(): # Convert all values to strings for simplicity metadata[str(key)] = str(value) - + # Cassandra vector type requires a list of float values - vector = point.vector.tolist() if hasattr(point.vector, 'tolist') else point.vector - - # Execute the prepared statement - cls.session.execute( - cls.insert_stmt, - (int(point.id), vector, metadata) + vector = ( + point.vector.tolist() + if hasattr(point.vector, "tolist") + else point.vector ) + # Execute the prepared statement + cls.session.execute(cls.insert_stmt, (int(point.id), vector, metadata)) + @classmethod def check_index_status(cls) -> ResultSet: """ @@ -65,7 +73,8 @@ def check_index_status(cls) -> ResultSet: See https://docs.datastax.com/en/cql/cassandra-5.0/develop/indexing/sai/sai-monitor.html """ assert cls.session is not None, "CQL session is not initialized" - return cls.session.execute(f""" + return cls.session.execute( + f""" SELECT is_queryable, is_building FROM system_views.sai_column_indexes WHERE keyspace_name='{CASSANDRA_KEYSPACE}' AND table_name='{CASSANDRA_TABLE}' AND index_name='vector_index'; @@ -89,7 +98,7 @@ def post_upload(cls, _distance): @classmethod def delete_client(cls): """Close the Cassandra connection""" - if hasattr(cls, 'session') and cls.session: + if hasattr(cls, "session") and cls.session: cls.session.shutdown() - if hasattr(cls, 'cluster') and cls.cluster: - cls.cluster.shutdown() \ No newline at end of file + if hasattr(cls, "cluster") and cls.cluster: + cls.cluster.shutdown() diff --git a/engine/clients/client_factory.py b/engine/clients/client_factory.py index 371604a2..9f4535fd 100644 --- a/engine/clients/client_factory.py +++ b/engine/clients/client_factory.py @@ -7,6 +7,11 @@ BaseSearcher, BaseUploader, ) +from engine.clients.cassandra import ( + CassandraConfigurator, + CassandraSearcher, + CassandraUploader, +) from engine.clients.elasticsearch import ( ElasticConfigurator, ElasticSearcher, @@ -30,11 +35,6 @@ WeaviateSearcher, WeaviateUploader, ) -from engine.clients.cassandra import ( - CassandraConfigurator, - CassandraSearcher, - CassandraUploader, -) ENGINE_CONFIGURATORS = { "qdrant": QdrantConfigurator, From 050a501167763d65c159adb83f7fc2fd8932440c Mon Sep 17 00:00:00 2001 From: Madhavan Date: Thu, 1 May 2025 17:38:15 -0400 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- engine/clients/cassandra/search.py | 3 +++ engine/clients/cassandra/upload.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/clients/cassandra/search.py b/engine/clients/cassandra/search.py index 7bb526ab..427dde8c 100644 --- a/engine/clients/cassandra/search.py +++ b/engine/clients/cassandra/search.py @@ -69,6 +69,9 @@ def update_prepared_statements(cls, distance): cls.ann_search_stmt = cls.session.prepare( f"""SELECT id, {SIMILARITY_FUNC}(embedding, ?) as distance FROM {CASSANDRA_TABLE} + -- The 'ANN' clause is used for Approximate Nearest Neighbor (ANN) search. + -- It orders results based on proximity to the query vector using the specified similarity function. + -- Ensure that the Cassandra setup supports ANN queries, as this is not standard CQL. ORDER BY embedding ANN OF ? LIMIT {limit}""" ) diff --git a/engine/clients/cassandra/upload.py b/engine/clients/cassandra/upload.py index e2473812..d529ce41 100644 --- a/engine/clients/cassandra/upload.py +++ b/engine/clients/cassandra/upload.py @@ -72,7 +72,8 @@ def check_index_status(cls) -> ResultSet: Check the status of the index See https://docs.datastax.com/en/cql/cassandra-5.0/develop/indexing/sai/sai-monitor.html """ - assert cls.session is not None, "CQL session is not initialized" + if cls.session is None: + raise RuntimeError("CQL session is not initialized") return cls.session.execute( f""" SELECT is_queryable, is_building From 5b69537910b111298e4eaa5b1348ec7d48a48d71 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 21:45:48 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- engine/clients/qdrant/configure.py | 6 ++++-- engine/clients/qdrant/search.py | 10 +++++----- engine/clients/qdrant/upload.py | 6 ++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/engine/clients/qdrant/configure.py b/engine/clients/qdrant/configure.py index de716ff9..fcb43ab5 100644 --- a/engine/clients/qdrant/configure.py +++ b/engine/clients/qdrant/configure.py @@ -4,7 +4,7 @@ from benchmark.dataset import Dataset from engine.base_client.configure import BaseConfigurator from engine.base_client.distances import Distance -from engine.clients.qdrant.config import QDRANT_COLLECTION_NAME, QDRANT_API_KEY +from engine.clients.qdrant.config import QDRANT_API_KEY, QDRANT_COLLECTION_NAME class QdrantConfigurator(BaseConfigurator): @@ -32,7 +32,9 @@ class QdrantConfigurator(BaseConfigurator): def __init__(self, host, collection_params: dict, connection_params: dict): super().__init__(host, collection_params, connection_params) - self.client = QdrantClient(url=host, api_key=QDRANT_API_KEY, **connection_params) + self.client = QdrantClient( + url=host, api_key=QDRANT_API_KEY, **connection_params + ) def clean(self): self.client.delete_collection(collection_name=QDRANT_COLLECTION_NAME) diff --git a/engine/clients/qdrant/search.py b/engine/clients/qdrant/search.py index b6b00908..b664db28 100644 --- a/engine/clients/qdrant/search.py +++ b/engine/clients/qdrant/search.py @@ -2,13 +2,12 @@ from typing import List, Tuple import httpx -from qdrant_client import QdrantClient +from qdrant_client import QdrantClient, models from qdrant_client._pydantic_compat import construct -from qdrant_client import models from dataset_reader.base_reader import Query from engine.base_client.search import BaseSearcher -from engine.clients.qdrant.config import QDRANT_COLLECTION_NAME, QDRANT_API_KEY +from engine.clients.qdrant.config import QDRANT_API_KEY, QDRANT_COLLECTION_NAME from engine.clients.qdrant.parser import QdrantConditionParser @@ -48,7 +47,6 @@ def search_one(cls, query: Query, top: int) -> List[Tuple[int, float]]: values=query.sparse_vector.values, ) - prefetch = cls.search_params.get("prefetch") if prefetch: @@ -65,7 +63,9 @@ def search_one(cls, query: Query, top: int) -> List[Tuple[int, float]]: query=query_vector, query_filter=cls.parser.parse(query.meta_conditions), limit=top, - search_params=models.SearchParams(**cls.search_params.get("config", {})), + search_params=models.SearchParams( + **cls.search_params.get("config", {}) + ), with_payload=cls.search_params.get("with_payload", False), ) except Exception as ex: diff --git a/engine/clients/qdrant/upload.py b/engine/clients/qdrant/upload.py index 18dcedbb..ca55b55d 100644 --- a/engine/clients/qdrant/upload.py +++ b/engine/clients/qdrant/upload.py @@ -13,7 +13,7 @@ from dataset_reader.base_reader import Record from engine.base_client.upload import BaseUploader -from engine.clients.qdrant.config import QDRANT_COLLECTION_NAME, QDRANT_API_KEY +from engine.clients.qdrant.config import QDRANT_API_KEY, QDRANT_COLLECTION_NAME class QdrantUploader(BaseUploader): @@ -24,7 +24,9 @@ class QdrantUploader(BaseUploader): def init_client(cls, host, distance, connection_params, upload_params): os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "true" os.environ["GRPC_POLL_STRATEGY"] = "epoll,poll" - cls.client = QdrantClient(url=host, prefer_grpc=True, api_key=QDRANT_API_KEY, **connection_params) + cls.client = QdrantClient( + url=host, prefer_grpc=True, api_key=QDRANT_API_KEY, **connection_params + ) cls.upload_params = upload_params @classmethod